From f40d09cb8f712e87691af4912f3630d92d692779 Mon Sep 17 00:00:00 2001 From: untodesu Date: Thu, 11 Dec 2025 15:14:26 +0500 Subject: Shuffle stuff around - Use the new and improved hierarchy I figured out when making Prospero chat - Re-add NSIS scripts, again from Prospero - Update most build and utility scripts with their most recent versions --- game/server/world/CMakeLists.txt | 12 -- game/server/world/inhabited.hh | 6 - game/server/world/overworld.cc | 385 --------------------------------------- game/server/world/overworld.hh | 68 ------- game/server/world/random_tick.cc | 40 ---- game/server/world/random_tick.hh | 14 -- game/server/world/universe.cc | 222 ---------------------- game/server/world/universe.hh | 25 --- game/server/world/unloader.cc | 78 -------- game/server/world/unloader.hh | 13 -- game/server/world/worldgen.cc | 151 --------------- game/server/world/worldgen.hh | 21 --- 12 files changed, 1035 deletions(-) delete mode 100644 game/server/world/CMakeLists.txt delete mode 100644 game/server/world/inhabited.hh delete mode 100644 game/server/world/overworld.cc delete mode 100644 game/server/world/overworld.hh delete mode 100644 game/server/world/random_tick.cc delete mode 100644 game/server/world/random_tick.hh delete mode 100644 game/server/world/universe.cc delete mode 100644 game/server/world/universe.hh delete mode 100644 game/server/world/unloader.cc delete mode 100644 game/server/world/unloader.hh delete mode 100644 game/server/world/worldgen.cc delete mode 100644 game/server/world/worldgen.hh (limited to 'game/server/world') diff --git a/game/server/world/CMakeLists.txt b/game/server/world/CMakeLists.txt deleted file mode 100644 index 58a2216..0000000 --- a/game/server/world/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -target_sources(vserver PRIVATE - "${CMAKE_CURRENT_LIST_DIR}/inhabited.hh" - "${CMAKE_CURRENT_LIST_DIR}/overworld.cc" - "${CMAKE_CURRENT_LIST_DIR}/overworld.hh" - "${CMAKE_CURRENT_LIST_DIR}/random_tick.cc" - "${CMAKE_CURRENT_LIST_DIR}/random_tick.hh" - "${CMAKE_CURRENT_LIST_DIR}/universe.cc" - "${CMAKE_CURRENT_LIST_DIR}/universe.hh" - "${CMAKE_CURRENT_LIST_DIR}/unloader.cc" - "${CMAKE_CURRENT_LIST_DIR}/unloader.hh" - "${CMAKE_CURRENT_LIST_DIR}/worldgen.cc" - "${CMAKE_CURRENT_LIST_DIR}/worldgen.hh") diff --git a/game/server/world/inhabited.hh b/game/server/world/inhabited.hh deleted file mode 100644 index 5eba9ef..0000000 --- a/game/server/world/inhabited.hh +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -namespace world -{ -struct Inhabited final {}; -} // namespace world diff --git a/game/server/world/overworld.cc b/game/server/world/overworld.cc deleted file mode 100644 index b72a816..0000000 --- a/game/server/world/overworld.cc +++ /dev/null @@ -1,385 +0,0 @@ -#include "server/pch.hh" - -#include "server/world/overworld.hh" - -#include "core/math/vectors.hh" - -#include "shared/world/voxel.hh" -#include "shared/world/voxel_storage.hh" - -#include "shared/coord.hh" -#include "shared/game_voxels.hh" - -// FIXME: load these from a file -static void compute_tree_feature(unsigned int height, world::Feature& feature, const world::Voxel* log_voxel, - const world::Voxel* leaves_voxel) -{ - // Ensure the tree height is too small - height = glm::max(height, 4U); - - // Put down a single piece of dirt - feature.push_back({ voxel_pos(0, -1, 0), game_voxels::dirt, true }); - - // Generate tree stem - for(unsigned int i = 0; i < height; ++i) { - feature.push_back({ voxel_pos(0, i, 0), log_voxel, true }); - } - - auto leaves_start = height - 3U; - auto leaves_thick_end = height - 2U; - auto leaves_thin_end = height - 1U; - - // Generate the thin 3x3 layer of leaves that - // starts from leaves_start and ends at leaves_thin_end - for(unsigned int i = leaves_start; i <= leaves_thin_end; ++i) { - feature.push_back({ local_pos(-1, i, -1), leaves_voxel, false }); - feature.push_back({ local_pos(-1, i, +0), leaves_voxel, false }); - feature.push_back({ local_pos(-1, i, +1), leaves_voxel, false }); - feature.push_back({ local_pos(+0, i, -1), leaves_voxel, false }); - feature.push_back({ local_pos(+0, i, +1), leaves_voxel, false }); - feature.push_back({ local_pos(+1, i, -1), leaves_voxel, false }); - feature.push_back({ local_pos(+1, i, +0), leaves_voxel, false }); - feature.push_back({ local_pos(+1, i, +1), leaves_voxel, false }); - } - - // Generate the tree cap; a 3x3 patch of leaves - // that is slapped right on top of the thin 3x3 layer - feature.push_back({ local_pos(-1, height, +0), leaves_voxel, false }); - feature.push_back({ local_pos(+0, height, -1), leaves_voxel, false }); - feature.push_back({ local_pos(+0, height, +0), leaves_voxel, false }); - feature.push_back({ local_pos(+0, height, +1), leaves_voxel, false }); - feature.push_back({ local_pos(+1, height, +0), leaves_voxel, false }); - - // Generate the thin 5x5 layer of leaves that - // starts from leaves_start and ends at leaves_thin_end - for(unsigned int i = leaves_start; i <= leaves_thick_end; ++i) { - feature.push_back({ local_pos(-1, i, -2), leaves_voxel, false }); - feature.push_back({ local_pos(-1, i, +2), leaves_voxel, false }); - feature.push_back({ local_pos(-2, i, -1), leaves_voxel, false }); - feature.push_back({ local_pos(-2, i, -2), leaves_voxel, false }); - feature.push_back({ local_pos(-2, i, +0), leaves_voxel, false }); - feature.push_back({ local_pos(-2, i, +1), leaves_voxel, false }); - feature.push_back({ local_pos(-2, i, +2), leaves_voxel, false }); - feature.push_back({ local_pos(+0, i, -2), leaves_voxel, false }); - feature.push_back({ local_pos(+0, i, +2), leaves_voxel, false }); - feature.push_back({ local_pos(+1, i, -2), leaves_voxel, false }); - feature.push_back({ local_pos(+1, i, +2), leaves_voxel, false }); - feature.push_back({ local_pos(+2, i, -1), leaves_voxel, false }); - feature.push_back({ local_pos(+2, i, -2), leaves_voxel, false }); - feature.push_back({ local_pos(+2, i, +0), leaves_voxel, false }); - feature.push_back({ local_pos(+2, i, +1), leaves_voxel, false }); - feature.push_back({ local_pos(+2, i, +2), leaves_voxel, false }); - } -} - -world::Overworld::Overworld(std::string_view name) : Dimension(name, -30.0f) -{ - m_bottommost_chunk.set_limits(-64, -4); - m_terrain_variation.set_limits(16, 256); - - compute_tree_feature(4U, m_feat_tree[0], game_voxels::oak_log, game_voxels::oak_leaves); - compute_tree_feature(5U, m_feat_tree[1], game_voxels::oak_log, game_voxels::oak_leaves); - compute_tree_feature(6U, m_feat_tree[2], game_voxels::oak_log, game_voxels::oak_leaves); - compute_tree_feature(8U, m_feat_tree[3], game_voxels::oak_log, game_voxels::oak_leaves); -} - -void world::Overworld::init(io::ConfigMap& config) -{ - m_terrain_variation.set_value(64); - m_bottommost_chunk.set_value(-4); - - config.add_value("overworld.terrain_variation", m_terrain_variation); - config.add_value("overworld.bottommost_chunk", m_bottommost_chunk); -} - -void world::Overworld::init_late(std::uint64_t global_seed) -{ - std::mt19937 twister(global_seed); - - m_fnl_variation = fnlCreateState(); - m_fnl_variation.seed = static_cast(twister()); - m_fnl_variation.noise_type = FNL_NOISE_PERLIN; - m_fnl_variation.frequency = 0.001f; - - m_fnl_terrain = fnlCreateState(); - m_fnl_terrain.seed = static_cast(twister()); - m_fnl_terrain.noise_type = FNL_NOISE_OPENSIMPLEX2S; - m_fnl_terrain.fractal_type = FNL_FRACTAL_FBM; - m_fnl_terrain.frequency = 0.005f; - m_fnl_terrain.octaves = 4; - - m_fnl_caves_a = fnlCreateState(); - m_fnl_caves_a.seed = static_cast(twister()); - m_fnl_caves_a.noise_type = FNL_NOISE_PERLIN; - m_fnl_caves_a.fractal_type = FNL_FRACTAL_RIDGED; - m_fnl_caves_a.frequency = 0.0125f; - m_fnl_caves_a.octaves = 1; - - m_fnl_caves_b = fnlCreateState(); - m_fnl_caves_b.seed = static_cast(twister()); - m_fnl_caves_b.noise_type = FNL_NOISE_OPENSIMPLEX2S; - m_fnl_caves_b.fractal_type = FNL_FRACTAL_RIDGED; - m_fnl_caves_b.frequency = 0.0125f; - m_fnl_caves_b.octaves = 1; - - m_fnl_nvdi = fnlCreateState(); - m_fnl_nvdi.seed = static_cast(twister()); - m_fnl_nvdi.noise_type = FNL_NOISE_OPENSIMPLEX2S; - m_fnl_nvdi.frequency = 1.0f; - - m_metamap.clear(); -} - -bool world::Overworld::generate(const chunk_pos& cpos, VoxelStorage& voxels) -{ - if(cpos.y <= m_bottommost_chunk.get_value()) { - // If the player asks the generator - // to generate a lot of stuff below - // the surface, it will happily chew - // through all the server threads - return false; - } - - voxels.fill(NULL_VOXEL_ID); - - m_mutex.lock(); - generate_terrain(cpos, voxels); - m_mutex.unlock(); - - m_mutex.lock(); - generate_surface(cpos, voxels); - m_mutex.unlock(); - - m_mutex.lock(); - generate_caves(cpos, voxels); - m_mutex.unlock(); - - m_mutex.lock(); - generate_features(cpos, voxels); - m_mutex.unlock(); - - return true; -} - -bool world::Overworld::is_inside_cave(const voxel_pos& vpos) -{ - auto noise_a = fnlGetNoise3D(&m_fnl_caves_a, vpos.x, vpos.y * 2.0f, vpos.z); - auto noise_b = fnlGetNoise3D(&m_fnl_caves_b, vpos.x, vpos.y * 2.0f, vpos.z); - return (noise_a > 0.95f) && (noise_b > 0.85f); -} - -bool world::Overworld::is_inside_terrain(const voxel_pos& vpos) -{ - auto variation_noise = fnlGetNoise3D(&m_fnl_terrain, vpos.x, vpos.y, vpos.z); - auto variation = m_terrain_variation.get_value() * (1.0f - (variation_noise * variation_noise)); - auto noise = variation * fnlGetNoise3D(&m_fnl_terrain, vpos.x, vpos.y, vpos.z) - vpos.y; - return noise > 0.0f; -} - -const world::Overworld_Metadata& world::Overworld::get_or_create_metadata(const chunk_pos_xz& cpos) -{ - auto it = m_metamap.find(cpos); - - if(it != m_metamap.cend()) { - // Metadata is present - return it->second; - } - - auto& metadata = m_metamap.insert_or_assign(cpos, Overworld_Metadata()).first->second; - metadata.entropy.fill(std::numeric_limits::max()); - metadata.heightmap.fill(std::numeric_limits::min()); - - auto twister = std::mt19937_64(std::hash()(cpos)); - auto variation = m_terrain_variation.get_value(); - - // Generator might need some randomness - // that depends on 2D coordinates, so we - // generate this entropy ahead of time - for(int i = 0; i < CHUNK_AREA; ++i) { - metadata.entropy[i] = twister(); - } - - // Generate speculative heightmap; - // Cave generation might have issues with placing - // surface features such as trees but I genuinely don't give a shit - for(int lx = 0; lx < CHUNK_SIZE; lx += 1) { - for(int lz = 0; lz < CHUNK_SIZE; lz += 1) { - auto hdx = static_cast(lx + lz * CHUNK_SIZE); - auto vpos = coord::to_voxel(chunk_pos(cpos.x, 0, cpos.y), local_pos(lx, 0, lz)); - - for(vpos.y = variation; vpos.y >= -variation; vpos.y -= 1) { - if(is_inside_terrain(vpos)) { - metadata.heightmap[hdx] = vpos.y; - break; - } - } - } - } - - auto nvdi_value = 0.5f + 0.5f * fnlGetNoise2D(&m_fnl_nvdi, cpos.x, cpos.y); - auto tree_density = (nvdi_value >= 0.33f) ? static_cast(glm::floor(nvdi_value * 4.0f)) : 0U; - - for(unsigned int i = 0U; i < tree_density; ++i) { - auto lpos = local_pos((twister() % CHUNK_SIZE), (twister() % OW_NUM_TREES), (twister() % CHUNK_SIZE)); - auto is_unique = true; - - for(const auto& check_lpos : metadata.trees) { - if(math::distance2(check_lpos, lpos) <= 9) { - is_unique = false; - break; - } - } - - if(is_unique) { - metadata.trees.push_back(lpos); - } - } - - return metadata; -} - -void world::Overworld::generate_terrain(const chunk_pos& cpos, VoxelStorage& voxels) -{ - auto& metadata = get_or_create_metadata(chunk_pos_xz(cpos.x, cpos.z)); - auto variation = m_terrain_variation.get_value(); - - for(unsigned long i = 0; i < CHUNK_VOLUME; ++i) { - auto lpos = coord::to_local(i); - auto vpos = coord::to_voxel(cpos, lpos); - - if(vpos.y > variation) { - voxels[i] = NULL_VOXEL_ID; - continue; - } - - if(vpos.y < -variation) { - voxels[i] = game_voxels::stone->get_id(); - continue; - } - - if(is_inside_terrain(vpos)) { - voxels[i] = game_voxels::stone->get_id(); - continue; - } - } -} - -void world::Overworld::generate_surface(const chunk_pos& cpos, VoxelStorage& voxels) -{ - auto& metadata = get_or_create_metadata(chunk_pos_xz(cpos.x, cpos.z)); - auto variation = m_terrain_variation.get_value(); - - for(unsigned long i = 0; i < CHUNK_VOLUME; ++i) { - auto lpos = coord::to_local(i); - auto vpos = coord::to_voxel(cpos, lpos); - auto hdx = static_cast(lpos.x + lpos.z * CHUNK_SIZE); - - if((vpos.y > variation) || (vpos.y < -variation)) { - // Speculative optimization - continue; - } - - if(voxels[i] == NULL_VOXEL_ID) { - // Surface voxel checks only apply for solid voxels; - // it's kind of obvious you can't replace air with grass - continue; - } - - unsigned int depth = 0U; - - for(unsigned int dy = 0U; dy < 5U; dy += 1U) { - auto d_lpos = local_pos(lpos.x, lpos.y + dy + 1, lpos.z); - auto d_vpos = coord::to_voxel(cpos, d_lpos); - auto d_index = coord::to_index(d_lpos); - - if(d_lpos.y >= CHUNK_SIZE) { - if(!is_inside_terrain(d_vpos)) { - break; - } - - depth += 1U; - } - else { - if(voxels[d_index] == NULL_VOXEL_ID) { - break; - } - - depth += 1U; - } - } - - if(depth < 5U) { - if(depth == 0U) { - voxels[i] = game_voxels::grass->get_id(); - } - else { - voxels[i] = game_voxels::dirt->get_id(); - } - } - } -} - -void world::Overworld::generate_caves(const chunk_pos& cpos, VoxelStorage& voxels) -{ - auto& metadata = get_or_create_metadata(chunk_pos_xz(cpos.x, cpos.z)); - auto variation = m_terrain_variation.get_value(); - - for(unsigned long i = 0U; i < CHUNK_VOLUME; ++i) { - auto lpos = coord::to_local(i); - auto vpos = coord::to_voxel(cpos, lpos); - - if(vpos.y > variation) { - // Speculative optimization - there's no solid - // terrain above variation to carve caves out from - continue; - } - - if(is_inside_cave(vpos)) { - voxels[i] = NULL_VOXEL_ID; - continue; - } - } -} - -void world::Overworld::generate_features(const chunk_pos& cpos, VoxelStorage& voxels) -{ - const chunk_pos_xz tree_chunks[] = { - chunk_pos_xz(cpos.x - 0, cpos.z - 1), - chunk_pos_xz(cpos.x - 1, cpos.z - 1), - chunk_pos_xz(cpos.x - 1, cpos.z + 0), - chunk_pos_xz(cpos.x - 1, cpos.z + 1), - chunk_pos_xz(cpos.x + 0, cpos.z + 0), - chunk_pos_xz(cpos.x + 0, cpos.z + 1), - chunk_pos_xz(cpos.x + 1, cpos.z - 1), - chunk_pos_xz(cpos.x + 1, cpos.z + 0), - chunk_pos_xz(cpos.x + 1, cpos.z + 1), - }; - - for(unsigned int i = 0U; i < math::array_size(tree_chunks); ++i) { - const auto& cpos_xz = tree_chunks[i]; - const auto& metadata = get_or_create_metadata(cpos_xz); - - for(const auto& tree_info : metadata.trees) { - auto hdx = static_cast(tree_info.x + tree_info.z * CHUNK_SIZE); - auto height = metadata.heightmap[hdx]; - - if(height == std::numeric_limits::min()) { - // What happened? Cave happened - continue; - } - - auto cpos_xyz = chunk_pos(cpos_xz.x, 0, cpos_xz.y); - auto lpos_xyz = local_pos(tree_info.x, 0, tree_info.z); - - auto vpos = coord::to_voxel(cpos_xyz, lpos_xyz); - vpos.y = height; - - if(is_inside_cave(vpos)) { - // Cave is in the way - continue; - } - - m_feat_tree[tree_info.y].place(vpos + DIR_UP, cpos, voxels); - } - } -} diff --git a/game/server/world/overworld.hh b/game/server/world/overworld.hh deleted file mode 100644 index a8112cf..0000000 --- a/game/server/world/overworld.hh +++ /dev/null @@ -1,68 +0,0 @@ -#pragma once - -#include "core/config/number.hh" - -#include "core/io/config_map.hh" - -#include "shared/world/dimension.hh" -#include "shared/world/feature.hh" - -#include "shared/const.hh" - -constexpr static unsigned int OW_NUM_TREES = 4U; - -namespace world -{ -struct Overworld_Metadata final { - world::dimension_entropy_map entropy; - world::dimension_height_map heightmap; - std::vector trees; -}; -} // namespace world - -namespace world -{ -class Overworld final : public Dimension { -public: - explicit Overworld(std::string_view name); - virtual ~Overworld(void) = default; - -public: - virtual void init(io::ConfigMap& config) override; - virtual void init_late(std::uint64_t global_seed) override; - virtual bool generate(const chunk_pos& cpos, VoxelStorage& voxels) override; - -private: - bool is_inside_cave(const voxel_pos& vpos); - bool is_inside_terrain(const voxel_pos& vpos); - -private: - const Overworld_Metadata& get_or_create_metadata(const chunk_pos_xz& cpos); - -private: - void generate_terrain(const chunk_pos& cpos, VoxelStorage& voxels); - void generate_surface(const chunk_pos& cpos, VoxelStorage& voxels); - void generate_caves(const chunk_pos& cpos, VoxelStorage& voxels); - void generate_features(const chunk_pos& cpos, VoxelStorage& voxels); - -private: - config::Int m_terrain_variation; - config::Int m_bottommost_chunk; - -private: - emhash8::HashMap m_metamap; - -private: - fnl_state m_fnl_variation; - fnl_state m_fnl_terrain; - fnl_state m_fnl_caves_a; - fnl_state m_fnl_caves_b; - fnl_state m_fnl_nvdi; - -private: - Feature m_feat_tree[OW_NUM_TREES]; - -private: - std::mutex m_mutex; -}; -} // namespace world diff --git a/game/server/world/random_tick.cc b/game/server/world/random_tick.cc deleted file mode 100644 index c5fa47c..0000000 --- a/game/server/world/random_tick.cc +++ /dev/null @@ -1,40 +0,0 @@ -#include "server/pch.hh" - -#include "server/world/random_tick.hh" - -#include "core/config/number.hh" - -#include "core/io/config_map.hh" - -#include "shared/world/chunk.hh" -#include "shared/world/dimension.hh" -#include "shared/world/voxel.hh" - -#include "shared/coord.hh" - -#include "server/globals.hh" - -static config::Int random_tick_speed(2, 1, 1000); -static std::mt19937_64 random_source; - -void world::random_tick::init(void) -{ - globals::server_config.add_value("world.random_tick_speed", random_tick_speed); - - random_source.seed(std::random_device {}()); -} - -void world::random_tick::tick(const chunk_pos& cpos, Chunk* chunk) -{ - assert(chunk); - - for(int i = 0; i < random_tick_speed.get_value(); ++i) { - auto voxel_index = random_source() % CHUNK_VOLUME; - auto lpos = coord::to_local(voxel_index); - auto vpos = coord::to_voxel(cpos, lpos); - - if(auto voxel = chunk->get_voxel(lpos)) { - voxel->on_tick(chunk->get_dimension(), vpos); - } - } -} diff --git a/game/server/world/random_tick.hh b/game/server/world/random_tick.hh deleted file mode 100644 index 4ef1691..0000000 --- a/game/server/world/random_tick.hh +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "shared/types.hh" - -namespace world -{ -class Chunk; -} // namespace world - -namespace world::random_tick -{ -void init(void); -void tick(const chunk_pos& cpos, Chunk* chunk); -} // namespace world::random_tick diff --git a/game/server/world/universe.cc b/game/server/world/universe.cc deleted file mode 100644 index b27a8de..0000000 --- a/game/server/world/universe.cc +++ /dev/null @@ -1,222 +0,0 @@ -#include "server/pch.hh" - -#include "server/world/universe.hh" - -#include "core/config/number.hh" -#include "core/config/string.hh" - -#include "core/io/buffer.hh" -#include "core/io/config_map.hh" -#include "core/io/physfs.hh" - -#include "core/utils/epoch.hh" - -#include "shared/world/chunk.hh" -#include "shared/world/dimension.hh" - -#include "server/world/inhabited.hh" -#include "server/world/overworld.hh" - -#include "server/globals.hh" - -struct DimensionMetadata final { - std::string config_path; - std::string zvox_dir; - io::ConfigMap config; -}; - -static config::String universe_name("save"); - -static io::ConfigMap universe_config; -static config::Unsigned64 universe_config_seed; -static config::String universe_spawn_dimension("world"); - -static std::string universe_config_path; -static std::unordered_map metadata_map; - -static std::string make_chunk_filename(const DimensionMetadata* metadata, const chunk_pos& cpos) -{ - const auto unsigned_x = static_cast(cpos.x); - const auto unsigned_y = static_cast(cpos.y); - const auto unsigned_z = static_cast(cpos.z); - return std::format("{}/{:08X}-{:08X}-{:08X}.zvox", metadata->zvox_dir, unsigned_x, unsigned_y, unsigned_z); -} - -static void add_new_dimension(world::Dimension* dimension) -{ - if(globals::dimensions.count(std::string(dimension->get_name()))) { - spdlog::critical("universe: dimension named {} already exists", dimension->get_name()); - std::terminate(); - } - - auto dimension_dir = std::format("{}/{}", universe_name.get(), dimension->get_name()); - - if(!PHYSFS_mkdir(dimension_dir.c_str())) { - spdlog::critical("universe: {}: {}", dimension_dir, io::physfs_error()); - std::terminate(); - } - - auto metadata = new DimensionMetadata; - metadata->config_path = std::format("{}/dimension.conf", dimension_dir); - metadata->zvox_dir = std::format("{}/chunk", dimension_dir); - - if(!PHYSFS_mkdir(metadata->zvox_dir.c_str())) { - spdlog::critical("universe: {}: {}", metadata->zvox_dir, io::physfs_error()); - std::terminate(); - } - - globals::dimensions.insert_or_assign(std::string(dimension->get_name()), dimension); - - auto& mapped_metadata = metadata_map.insert_or_assign(dimension, metadata).first->second; - - dimension->init(mapped_metadata->config); - - mapped_metadata->config.load_file(mapped_metadata->config_path.c_str()); - - dimension->init_late(universe_config_seed.get_value()); -} - -static void internal_save_chunk(const DimensionMetadata* metadata, const world::Dimension* dimension, const chunk_pos& cpos, - const world::Chunk* chunk) -{ - auto path = make_chunk_filename(metadata, cpos); - - io::WriteBuffer buffer; - chunk->get_voxels().serialize(buffer); - - if(auto file = buffer.to_file(path.c_str())) { - PHYSFS_close(file); - return; - } -} - -void world::universe::init(void) -{ - // If the world is newly created, the seed will - // be chosed based on the current system's view on UNIX time - universe_config_seed.set_value(utils::unix_microseconds()); - - // We're going to read files from directory named with - // the value of this config value. Since config is also - // read from command line, the [--universe ] parameter still works - globals::server_config.add_value("universe", universe_name); - - universe_config.add_value("global_seed", universe_config_seed); - universe_config.add_value("spawn_dimension", universe_spawn_dimension); -} - -void world::universe::init_late(void) -{ - const auto universe_dir = std::string(universe_name.get()); - - if(!PHYSFS_mkdir(universe_dir.c_str())) { - spdlog::critical("universe: {}: {}", universe_dir, io::physfs_error()); - std::terminate(); - } - - universe_config_path = std::format("{}/universe.conf", universe_dir); - universe_config.load_file(universe_config_path.c_str()); - - add_new_dimension(new Overworld("world")); - - // UNDONE: lua scripts to setup dimensions - if(globals::dimensions.empty()) { - spdlog::critical("universe: no dimensions"); - std::terminate(); - } - - auto spawn_dimension = globals::dimensions.find(universe_spawn_dimension.get_value()); - - if(spawn_dimension == globals::dimensions.cend()) { - spdlog::critical("universe: {} is not a valid dimension name", universe_spawn_dimension.get()); - std::terminate(); - } - - globals::spawn_dimension = spawn_dimension->second; -} - -void world::universe::shutdown(void) -{ - for(const auto metadata : metadata_map) { - metadata.second->config.save_file(metadata.second->config_path.c_str()); - delete metadata.second; - } - - metadata_map.clear(); - - for(const auto dimension : globals::dimensions) { - world::universe::save_all_chunks(dimension.second); - delete dimension.second; - } - - globals::dimensions.clear(); - globals::spawn_dimension = nullptr; - - universe_config.save_file(universe_config_path.c_str()); -} - -world::Chunk* world::universe::load_chunk(Dimension* dimension, const chunk_pos& cpos) -{ - if(auto chunk = dimension->find_chunk(cpos)) { - // Just return the existing chunk which is - // most probable to be up to date compared to - // whatever the hell is currently stored on disk - return chunk; - } - - auto metadata = metadata_map.find(dimension); - - if(metadata == metadata_map.cend()) { - // The dimension is for sure a weird one - return nullptr; - } - - if(auto file = PHYSFS_openRead(make_chunk_filename(metadata->second, cpos).c_str())) { - VoxelStorage voxels; - io::ReadBuffer buffer(file); - voxels.deserialize(buffer); - - PHYSFS_close(file); - - auto chunk = dimension->create_chunk(cpos); - chunk->set_voxels(voxels); - - // Make sure we're going to save it later - dimension->chunks.emplace_or_replace(chunk->get_entity()); - - return chunk; - } - - return nullptr; -} - -void world::universe::save_chunk(Dimension* dimension, const chunk_pos& cpos) -{ - auto metadata = metadata_map.find(dimension); - - if(metadata == metadata_map.cend()) { - // Cannot save a chunk in a dimension - // that doesn't have a metadata struct - return; - } - - if(auto chunk = dimension->find_chunk(cpos)) { - internal_save_chunk(metadata->second, dimension, cpos, chunk); - } -} - -void world::universe::save_all_chunks(Dimension* dimension) -{ - auto group = dimension->chunks.group(entt::get); - auto metadata = metadata_map.find(dimension); - - if(metadata == metadata_map.cend()) { - // Cannot save a chunk in a dimension - // that doesn't have a metadata struct - return; - } - - for(auto [entity, chunk] : group.each()) { - internal_save_chunk(metadata->second, dimension, chunk.cpos, chunk.chunk); - } -} diff --git a/game/server/world/universe.hh b/game/server/world/universe.hh deleted file mode 100644 index 966ac70..0000000 --- a/game/server/world/universe.hh +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "shared/types.hh" - -namespace world -{ -class Chunk; -class Dimension; -} // namespace world - -class Session; - -namespace world::universe -{ -void init(void); -void init_late(void); -void shutdown(void); -} // namespace world::universe - -namespace world::universe -{ -Chunk* load_chunk(Dimension* dimension, const chunk_pos& cpos); -void save_chunk(Dimension* dimension, const chunk_pos& cpos); -void save_all_chunks(Dimension* dimension); -} // namespace world::universe diff --git a/game/server/world/unloader.cc b/game/server/world/unloader.cc deleted file mode 100644 index 371a96f..0000000 --- a/game/server/world/unloader.cc +++ /dev/null @@ -1,78 +0,0 @@ -#include "server/pch.hh" - -#include "server/world/unloader.hh" - -#include "core/config/number.hh" - -#include "shared/entity/player.hh" -#include "shared/entity/transform.hh" - -#include "shared/world/chunk.hh" -#include "shared/world/chunk_aabb.hh" -#include "shared/world/dimension.hh" - -#include "server/world/inhabited.hh" -#include "server/world/universe.hh" - -#include "server/game.hh" -#include "server/globals.hh" - -static void on_chunk_update(const world::ChunkUpdateEvent& event) -{ - event.dimension->chunks.emplace_or_replace(event.chunk->get_entity()); -} - -static void on_voxel_set(const world::VoxelSetEvent& event) -{ - event.dimension->chunks.emplace_or_replace(event.chunk->get_entity()); -} - -void world::unloader::init(void) -{ - globals::dispatcher.sink().connect<&on_chunk_update>(); - globals::dispatcher.sink().connect<&on_voxel_set>(); -} - -void world::unloader::init_late(void) -{ -} - -void world::unloader::fixed_update_late(Dimension* dimension) -{ - auto group = dimension->entities.group(entt::get); - auto boxes = std::vector(); - - for(const auto [entity, transform] : group.each()) { - ChunkAABB aabb; - aabb.min = transform.chunk - static_cast(server_game::view_distance.get_value()); - aabb.max = transform.chunk + static_cast(server_game::view_distance.get_value()); - boxes.push_back(aabb); - } - - auto view = dimension->chunks.view(); - auto chunk_in_view = false; - - for(const auto [entity, chunk] : view.each()) { - chunk_in_view = false; - - for(const auto& aabb : boxes) { - if(aabb.contains(chunk.cpos)) { - chunk_in_view = true; - break; - } - } - - if(chunk_in_view) { - // The chunk is within view box of at least - // a single player; we shouldn't unload it now - continue; - } - - if(dimension->chunks.any_of(entity)) { - // Only store inhabited chunks on disk - world::universe::save_chunk(dimension, chunk.cpos); - } - - dimension->remove_chunk(entity); - } -} diff --git a/game/server/world/unloader.hh b/game/server/world/unloader.hh deleted file mode 100644 index a5b1da1..0000000 --- a/game/server/world/unloader.hh +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -namespace world -{ -class Dimension; -} // namespace world - -namespace world::unloader -{ -void init(void); -void init_late(void); -void fixed_update_late(Dimension* dimension); -} // namespace world::unloader diff --git a/game/server/world/worldgen.cc b/game/server/world/worldgen.cc deleted file mode 100644 index 3d8154c..0000000 --- a/game/server/world/worldgen.cc +++ /dev/null @@ -1,151 +0,0 @@ -#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>> 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(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>()).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()).first->second; - sessions.insert(session); - - threading::submit(session->dimension, cpos); - - return; - } - - it->second.insert(session); - } -} diff --git a/game/server/world/worldgen.hh b/game/server/world/worldgen.hh deleted file mode 100644 index eeb3e19..0000000 --- a/game/server/world/worldgen.hh +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include "shared/types.hh" - -namespace world -{ -class Dimension; -} // namespace world - -class Session; - -namespace world::worldgen -{ -void init(void); -} // namespace world::worldgen - -namespace world::worldgen -{ -bool is_generating(Dimension* dimension, const chunk_pos& cpos); -void request_chunk(Session* session, const chunk_pos& cpos); -} // namespace world::worldgen -- cgit