summaryrefslogtreecommitdiffstats
path: root/game/server/worldgen.cc
diff options
context:
space:
mode:
Diffstat (limited to 'game/server/worldgen.cc')
-rw-r--r--game/server/worldgen.cc128
1 files changed, 128 insertions, 0 deletions
diff --git a/game/server/worldgen.cc b/game/server/worldgen.cc
new file mode 100644
index 0000000..83fddbb
--- /dev/null
+++ b/game/server/worldgen.cc
@@ -0,0 +1,128 @@
+#include "server/pch.hh"
+#include "server/worldgen.hh"
+
+#include "shared/chunk.hh"
+#include "shared/dimension.hh"
+#include "shared/protocol.hh"
+#include "shared/threading.hh"
+
+#include "server/sessions.hh"
+
+static emhash8::HashMap<Dimension *, emhash8::HashMap<chunk_pos, std::unordered_set<Session *>>> active_tasks;
+
+class WorldgenTask final : public Task {
+public:
+ explicit WorldgenTask(Dimension *dimension, const chunk_pos &cpos);
+ virtual ~WorldgenTask(void) = default;
+ virtual void process(void) override;
+ virtual void finalize(void) override;
+
+private:
+ Dimension *m_dimension;
+ VoxelStorage m_voxels;
+ chunk_pos m_cpos;
+};
+
+WorldgenTask::WorldgenTask(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);
+
+ 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);
+ }
+}
+
+bool 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 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);
+ }
+}