--port <number>
port number to connection to (client) or listen on (server)
+--player-name <name>
+ use given name to identify with the server (client mode)
+ default player name is "default"
+ the server will reject players with names that are already taken
+
World
-----
Q changes the face of the active block (loops over up, down, right, left,
front, and back) and E changes the turn (none, left, around, and right).
-Pressing B prints details about the block you're pointing at and P prints
-info about the active block. L spits out the player position and light
-level there. C dumps info about the chunk of the pointed at block.
-
Press N to toggle player/world collision.
F1 toggles UI rendering.
}
timer.Start();
- Spawn(world.Player().ChunkCoords(), { 0.5f, 0.5f, 0.5f });
}
Spawner::~Spawner() {
void Spawner::CheckDespawn() noexcept {
- const Entity &reference = world.Player();
+ const auto &refs = world.Players();
for (auto iter = controllers.begin(), end = controllers.end(); iter != end;) {
Entity &e = (*iter)->Controlled();
if (e.Dead()) {
iter = controllers.erase(iter);
continue;
}
- glm::vec3 diff(reference.AbsoluteDifference(e));
- if (dot(diff, diff) > despawn_range) {
+ bool safe = false;
+ for (const Entity *ref : refs) {
+ glm::vec3 diff(ref->AbsoluteDifference(e));
+ if (dot(diff, diff) < despawn_range) {
+ safe = true;
+ break;
+ }
+ }
+ if (!safe) {
e.Kill();
delete *iter;
iter = controllers.erase(iter);
void Spawner::TrySpawn() {
if (controllers.size() >= max_entities) return;
+ // select random player to punish
+ auto &players = world.Players();
+ if (players.size() == 0) return;
+ Entity &player = *players[random.Next<unsigned short>() % players.size()];
+
glm::ivec3 chunk(
(random.Next<unsigned char>() % (chunk_range * 2 + 1)) - chunk_range,
(random.Next<unsigned char>() % (chunk_range * 2 + 1)) - chunk_range,
// distance check
- glm::vec3 diff(glm::vec3(chunk * Chunk::Extent() - pos) + world.Player().Position());
+ glm::vec3 diff(glm::vec3(chunk * Chunk::Extent() - pos) + player.Position());
float dist = dot(diff, diff);
if (dist > despawn_range || dist < spawn_distance) {
return;
}
// check if the spawn block and the one above it are loaded and inhabitable
- BlockLookup spawn_block(&world.PlayerChunk(), chunk * Chunk::Extent() + pos);
+ BlockLookup spawn_block(
+ world.Loader().Loaded(player.ChunkCoords()),
+ chunk * Chunk::Extent() + pos);
if (!spawn_block || spawn_block.GetType().collide_block) {
return;
}
return;
}
- Spawn(world.Player().ChunkCoords() + chunk, glm::vec3(pos) + glm::vec3(0.5f));
+ Spawn(player, player.ChunkCoords() + chunk, glm::vec3(pos) + glm::vec3(0.5f));
}
-void Spawner::Spawn(const glm::ivec3 &chunk, const glm::vec3 &pos) {
+void Spawner::Spawn(Entity &reference, const glm::ivec3 &chunk, const glm::vec3 &pos) {
glm::vec3 rot(0.000001f);
rot.x *= (random.Next<unsigned short>() % 1024);
rot.y *= (random.Next<unsigned short>() % 1024);
if (random()) {
ctrl = new RandomWalk(e, random.Next<std::uint64_t>());
} else {
- ctrl = new Chaser(world, e, world.Player());
+ ctrl = new Chaser(world, e, reference);
}
controllers.emplace_back(ctrl);
}
namespace blank {
class Controller;
+class Entity;
class World;
class Spawner {
private:
void CheckDespawn() noexcept;
void TrySpawn();
- void Spawn(const glm::ivec3 &, const glm::vec3 &);
+ void Spawn(Entity &reference, const glm::ivec3 &, const glm::vec3 &);
private:
World &world;
interface.Update(dt);
spawner.Update(dt);
world.Update(dt);
- chunk_renderer.Rebase(world.Player().ChunkCoords());
+ chunk_renderer.Rebase(interface.Player().ChunkCoords());
chunk_renderer.Update(dt);
- glm::mat4 trans = world.Player().Transform(world.Player().ChunkCoords());
+ glm::mat4 trans = interface.Player().Transform(interface.Player().ChunkCoords());
glm::vec3 dir(trans * glm::vec4(0.0f, 0.0f, -1.0f, 0.0f));
glm::vec3 up(trans * glm::vec4(0.0f, 1.0f, 0.0f, 0.0f));
- env.audio.Position(world.Player().Position());
- env.audio.Velocity(world.Player().Velocity());
+ env.audio.Position(interface.Player().Position());
+ env.audio.Velocity(interface.Player().Velocity());
env.audio.Orientation(dir, up);
}
void WorldState::Render(Viewport &viewport) {
- viewport.WorldPosition(world.Player().Transform(world.Player().ChunkCoords()));
+ viewport.WorldPosition(interface.Player().Transform(interface.Player().ChunkCoords()));
chunk_renderer.Render(viewport);
world.Render(viewport);
interface.Render(viewport);
config.server.port = strtoul(argv[i], nullptr, 10);
config.client.port = config.server.port;
}
+ } else if (strcmp(param, "player-name") == 0) {
+ ++i;
+ if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
+ cerr << "missing argument to --player-name" << endl;
+ error = true;
+ } else {
+ config.interface.player_name = argv[i];
+ }
} else if (strcmp(param, "save-path") == 0) {
++i;
if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
public:
struct Config {
+ std::string player_name = "default";
+
float move_velocity = 0.005f;
float pitch_sensitivity = -0.0025f;
float yaw_sensitivity = -0.001f;
Interface(const Config &, Environment &, World &);
+ Entity &Player() noexcept { return ctrl.Controlled(); }
+ const Entity &Player() const noexcept { return ctrl.Controlled(); }
+
void HandlePress(const SDL_KeyboardEvent &);
void HandleRelease(const SDL_KeyboardEvent &);
void Handle(const SDL_MouseMotionEvent &);
void PlaceBlock();
void RemoveBlock() noexcept;
- void PrintBlockInfo();
- void PrintChunkInfo();
- void PrintLightInfo();
- void PrintSelectionInfo();
- void Print(const Block &);
-
void SelectNext();
void SelectPrevious();
TOGGLE_VISUAL,
TOGGLE_DEBUG,
- PRINT_BLOCK,
- PRINT_CHUNK,
- PRINT_LIGHT,
- PRINT_SELECTION,
-
EXIT,
};
World &world)
: env(env)
, world(world)
-, ctrl(world.Player())
+// let's assume this succeeds and hope for the best for now
+, ctrl(*world.AddPlayer(config.player_name))
, hud(world.BlockTypes(), env.assets.small_ui_font)
, aim{{ 0, 0, 0 }, { 0, 0, -1 }}
, aim_world()
ToggleCollision();
break;
- case Keymap::PRINT_BLOCK:
- PrintBlockInfo();
- break;
- case Keymap::PRINT_CHUNK:
- PrintChunkInfo();
- break;
- case Keymap::PRINT_LIGHT:
- PrintLightInfo();
- break;
- case Keymap::PRINT_SELECTION:
- PrintSelectionInfo();
- break;
-
case Keymap::TOGGLE_VISUAL:
ToggleVisual();
break;
}
}
-void Interface::PrintBlockInfo() {
- std::cout << std::endl;
- if (!aim_world) {
- PostMessage("not looking at any block");
- Ray aim = ctrl.Aim();
- std::stringstream s;
- s << "aim ray: " << aim.orig << ", " << aim.dir;
- PostMessage(s.str());
- return;
- }
- std::stringstream s;
- s << "looking at block " << aim_world.block
- << " " << aim_world.BlockCoords()
- << " of chunk " << aim_world.GetChunk().Position()
- ;
- PostMessage(s.str());
- Print(aim_world.GetBlock());
-}
-
-void Interface::PrintChunkInfo() {
- std::cout << std::endl;
- if (!aim_world) {
- PostMessage("not looking at any block");
- return;
- }
- std::stringstream s;
- s << "looking at chunk " << aim_world.GetChunk().Position();
- PostMessage(s.str());
-
- PostMessage(" neighbors:");
- if (aim_world.GetChunk().HasNeighbor(Block::FACE_LEFT)) {
- s.str("");
- s << " left " << aim_world.GetChunk().GetNeighbor(Block::FACE_LEFT).Position();
- PostMessage(s.str());
- }
- if (aim_world.GetChunk().HasNeighbor(Block::FACE_RIGHT)) {
- s.str("");
- s << " right " << aim_world.GetChunk().GetNeighbor(Block::FACE_RIGHT).Position();
- PostMessage(s.str());
- }
- if (aim_world.GetChunk().HasNeighbor(Block::FACE_UP)) {
- s.str("");
- s << " up " << aim_world.GetChunk().GetNeighbor(Block::FACE_UP).Position();
- PostMessage(s.str());
- }
- if (aim_world.GetChunk().HasNeighbor(Block::FACE_DOWN)) {
- s.str("");
- s << " down " << aim_world.GetChunk().GetNeighbor(Block::FACE_DOWN).Position();
- PostMessage(s.str());
- }
- if (aim_world.GetChunk().HasNeighbor(Block::FACE_FRONT)) {
- s.str("");
- s << " front " << aim_world.GetChunk().GetNeighbor(Block::FACE_FRONT).Position();
- PostMessage(s.str());
- }
- if (aim_world.GetChunk().HasNeighbor(Block::FACE_BACK)) {
- s.str("");
- s << " back " << aim_world.GetChunk().GetNeighbor(Block::FACE_BACK).Position();
- PostMessage(s.str());
- }
- std::cout << std::endl;
-}
-
-void Interface::PrintLightInfo() {
- std::stringstream s;
- s
- << "light level " << world.PlayerChunk().GetLight(world.Player().Position())
- << " at position " << world.Player().Position()
- ;
- PostMessage(s.str());
-}
-
-void Interface::PrintSelectionInfo() {
- std::cout << std::endl;
- Print(selection);
-}
-
-void Interface::Print(const Block &block) {
- std::stringstream s;
- s << "type: " << block.type
- << ", face: " << block.GetFace()
- << ", turn: " << block.GetTurn()
- ;
- PostMessage(s.str());
-}
-
void Interface::ToggleAudio() {
config.audio_disabled = !config.audio_disabled;
if (config.audio_disabled) {
outl_buf.Clear();
aim_world.GetType().FillOutlineModel(outl_buf);
outline.Update(outl_buf);
- outline_transform = aim_world.GetChunk().Transform(world.Player().ChunkCoords());
+ outline_transform = aim_world.GetChunk().Transform(Player().ChunkCoords());
outline_transform *= aim_world.BlockTransform();
outline_transform *= glm::scale(glm::vec3(1.005f));
}
Map(SDL_SCANCODE_F3, TOGGLE_DEBUG);
Map(SDL_SCANCODE_F4, TOGGLE_AUDIO);
- Map(SDL_SCANCODE_B, PRINT_BLOCK);
- Map(SDL_SCANCODE_C, PRINT_CHUNK);
- Map(SDL_SCANCODE_L, PRINT_LIGHT);
- Map(SDL_SCANCODE_P, PRINT_SELECTION);
-
Map(SDL_SCANCODE_ESCAPE, EXIT);
}
return "toggle_visual";
case TOGGLE_DEBUG:
return "toggle_debug";
- case PRINT_BLOCK:
- return "print_block";
- case PRINT_CHUNK:
- return "print_chunk";
- case PRINT_LIGHT:
- return "print_light";
- case PRINT_SELECTION:
- return "print_selection";
case EXIT:
return "exit";
}
return TOGGLE_VISUAL;
} else if (str == "toggle_debug") {
return TOGGLE_DEBUG;
- } else if (str == "print_block") {
- return PRINT_BLOCK;
- } else if (str == "print_chunk") {
- return PRINT_CHUNK;
- } else if (str == "print_light") {
- return PRINT_LIGHT;
- } else if (str == "print_selection") {
- return PRINT_SELECTION;
} else if (str == "exit") {
return EXIT;
} else {
namespace blank {
World::World(const BlockTypeRegistry &types, const Config &config, const WorldSave &save)
-: block_type(types)
+: config(config)
+, block_type(types)
, generate(config.gen)
, chunks(config.load, types, generate, save)
-, player()
+, players()
, entities()
, light_direction(config.light_direction)
, fog_density(config.fog_density) {
generate.Space(0);
generate.Light(13);
generate.Solids({ 1, 4, 7, 10 });
+}
- player = &AddEntity();
- player->Name("player");
- player->Bounds({ { -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f } });
- player->WorldCollidable(true);
- player->Position(config.spawn);
- chunks.QueueSurrounding(player->ChunkCoords());
+Entity *World::AddPlayer(const std::string &name) {
+ for (Entity *e : players) {
+ if (e->Name() == name) {
+ return nullptr;
+ }
+ }
+ Entity &player = AddEntity();
+ player.Name(name);
+ // TODO: load from save file here
+ player.Bounds({ { -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f } });
+ player.WorldCollidable(true);
+ player.Position(config.spawn);
+ players.push_back(&player);
+ chunks.QueueSurrounding(player.ChunkCoords());
+ return &player;
}
}
-Chunk &World::PlayerChunk() {
- return chunks.ForceLoad(player->ChunkCoords());
-}
-
-
namespace {
std::vector<WorldCollision> col;
++iter;
}
}
- chunks.Rebase(player->ChunkCoords());
+ // TODO: make flexible
+ chunks.Rebase(players[0]->ChunkCoords());
chunks.Update(dt);
}
entity_prog.SetFogDensity(fog_density);
for (Entity &entity : entities) {
- entity.Render(entity.ChunkTransform(player->ChunkCoords()), entity_prog);
+ entity.Render(entity.ChunkTransform(players[0]->ChunkCoords()), entity_prog);
}
}
const BlockTypeRegistry &BlockTypes() noexcept { return block_type; }
ChunkLoader &Loader() noexcept { return chunks; }
- Entity &Player() { return *player; }
+ /// add player with given name
+ /// returns nullptr if the name is already taken
+ Entity *AddPlayer(const std::string &name);
Entity &AddEntity() { entities.emplace_back(); return entities.back(); }
- Chunk &PlayerChunk();
+ const std::vector<Entity *> &Players() const noexcept { return players; }
+ const std::list<Entity> &Entities() const noexcept { return entities; }
void Update(int dt);
void Render(Viewport &);
private:
+ Config config;
+
const BlockTypeRegistry &block_type;
Generator generate;
ChunkLoader chunks;
- Entity *player;
+ std::vector<Entity *> players;
std::list<Entity> entities;
glm::vec3 light_direction;