diff options
Diffstat (limited to 'src/game/server/world/worldgen.cc')
| -rw-r--r-- | src/game/server/world/worldgen.cc | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/src/game/server/world/worldgen.cc b/src/game/server/world/worldgen.cc new file mode 100644 index 0000000..8b02b52 --- /dev/null +++ b/src/game/server/world/worldgen.cc @@ -0,0 +1,151 @@ +#include "server/pch.hh" + +#include "server/world/worldgen.hh" + +#include "core/io/cmdline.hh" + +#include "core/threading.hh" + +#include "shared/world/chunk.hh" +#include "shared/world/dimension.hh" + +#include "shared/protocol.hh" + +#include "server/world/inhabited.hh" + +#include "server/globals.hh" +#include "server/sessions.hh" + +static bool aggressive_caching; + +static emhash8::HashMap<world::Dimension*, emhash8::HashMap<chunk_pos, std::unordered_set<Session*>>> active_tasks; + +class WorldgenTask final : public Task { +public: + explicit WorldgenTask(world::Dimension* dimension, const chunk_pos& cpos); + virtual ~WorldgenTask(void) = default; + virtual void process(void) override; + virtual void finalize(void) override; + +private: + world::Dimension* m_dimension; + world::VoxelStorage m_voxels; + chunk_pos m_cpos; +}; + +WorldgenTask::WorldgenTask(world::Dimension* dimension, const chunk_pos& cpos) +{ + m_dimension = dimension; + m_voxels.fill(rand()); // trolling + m_cpos = cpos; +} + +void WorldgenTask::process(void) +{ + if(!m_dimension->generate(m_cpos, m_voxels)) { + set_status(task_status::CANCELLED); + } +} + +void WorldgenTask::finalize(void) +{ + auto dim_tasks = active_tasks.find(m_dimension); + + if(dim_tasks == active_tasks.cend()) { + // Normally this should never happen but + // one can never be sure about anything + // when that anything is threaded out + return; + } + + auto it = dim_tasks->second.find(m_cpos); + + if(it == dim_tasks->second.cend()) { + // Normally this should never happen but + // one can never be sure about anything + // when that anything is threaded out + return; + } + + auto chunk = m_dimension->create_chunk(m_cpos); + chunk->set_voxels(m_voxels); + + if(aggressive_caching) { + // Marking the chunk with InhabitedComponent makes + // it so that it is saved regardles of whether it was + // modified by players or not. This isn't particularly + // good for server-side disk usage but it might improve performance + m_dimension->chunks.emplace<world::Inhabited>(chunk->get_entity()); + } + + protocol::ChunkVoxels response; + response.voxels = m_voxels; + response.chunk = m_cpos; + + auto packet = protocol::encode(response); + + for(auto session : it->second) { + if(session->peer) { + // Respond with the voxels to every session + // that has requested this specific chunk for this dimension + enet_peer_send(session->peer, protocol::CHANNEL, packet); + } + } + + dim_tasks->second.erase(it); + + if(dim_tasks->second.empty()) { + // There are no more requests + // to generate a chunk for that + // dimension, at least for now + active_tasks.erase(dim_tasks); + } +} + +void world::worldgen::init(void) +{ + aggressive_caching = io::cmdline::contains("aggressive-caching"); +} + +bool world::worldgen::is_generating(Dimension* dimension, const chunk_pos& cpos) +{ + auto dim_tasks = active_tasks.find(dimension); + + if(dim_tasks == active_tasks.cend()) { + // No tasks for this dimension + return false; + } + + auto it = dim_tasks->second.find(cpos); + + if(it == dim_tasks->second.cend()) { + // Not generating this chunk + return false; + } + + return true; +} + +void world::worldgen::request_chunk(Session* session, const chunk_pos& cpos) +{ + if(session->dimension) { + auto dim_tasks = active_tasks.find(session->dimension); + + if(dim_tasks == active_tasks.cend()) { + dim_tasks = active_tasks.emplace(session->dimension, emhash8::HashMap<chunk_pos, std::unordered_set<Session*>>()).first; + } + + auto it = dim_tasks->second.find(cpos); + + if(it == dim_tasks->second.cend()) { + auto& sessions = dim_tasks->second.insert_or_assign(cpos, std::unordered_set<Session*>()).first->second; + sessions.insert(session); + + threading::submit<WorldgenTask>(session->dimension, cpos); + + return; + } + + it->second.insert(session); + } +} |
