From 9788f68b1208087f08d9987d6501f5d9b971d552 Mon Sep 17 00:00:00 2001 From: zhenyan121 <3367366583@qq.com> Date: Sun, 28 Jun 2026 20:11:14 +0800 Subject: [PATCH] refactor(server): replace chunk set with flat_hash_set and add ref count - Replace std::unordered_set with absl::flat_hash_set for chunk position sets. - Add ref_count field to ChunkEntity and implement update_ref_count(). - Remove clear_unused_chunks() and use ref counting for chunk lifetime. - Add get_chunk_pos_set() accessors to ServerPlayer. --- include/Cubed/gameplay/server_player.hpp | 9 ++-- include/Cubed/gameplay/server_world.hpp | 7 +-- src/gameplay/server_player.cpp | 9 ++++ src/gameplay/server_world.cpp | 69 +++++++++++++++++------- 4 files changed, 67 insertions(+), 27 deletions(-) diff --git a/include/Cubed/gameplay/server_player.hpp b/include/Cubed/gameplay/server_player.hpp index 99c9631..db7aa3e 100644 --- a/include/Cubed/gameplay/server_player.hpp +++ b/include/Cubed/gameplay/server_player.hpp @@ -2,20 +2,20 @@ #include "Cubed/gameplay/chunk_pos.hpp" #include "Cubed/gameplay/game_time.hpp" +#include #include #include #include #include #include #include -#include namespace Cubed { class ServerWorld; class Session; class ServerPlayer { - using ChunkPosSet = std::unordered_set; public: + using ChunkPosSet = absl::flat_hash_set; ServerPlayer(const ServerPlayer&) = delete; ServerPlayer(ServerPlayer&&) = delete; ServerPlayer& operator=(const ServerPlayer&) = delete; @@ -23,7 +23,6 @@ public: ServerPlayer(std::string_view name, std::string_view uuid, ServerWorld& m_world, std::shared_ptr session, TickType gametick); - using PlayerChunkPosSet = std::unordered_set; const glm::vec3& get_pos() const; const std::string& get_name() const; @@ -36,6 +35,8 @@ public: void task_id(int id); bool has_player(ChunkPos pos) const; void update_chunk_set(const ChunkPosSet& set); + const ChunkPosSet& get_chunk_pos_set() const; + ChunkPosSet& get_chunk_pos_set(); private: static constexpr TickType TIMEOUT = 200; @@ -48,6 +49,6 @@ private: std::atomic m_last_gametick{0}; std::atomic m_chunk_task_id{0}; mutable std::shared_mutex m_chunk_pos_mutex; - PlayerChunkPosSet m_player_chunk_pos_set; + ChunkPosSet m_player_chunk_pos_set; }; } // namespace Cubed diff --git a/include/Cubed/gameplay/server_world.hpp b/include/Cubed/gameplay/server_world.hpp index 0c2c2a6..df62e79 100644 --- a/include/Cubed/gameplay/server_world.hpp +++ b/include/Cubed/gameplay/server_world.hpp @@ -10,13 +10,13 @@ #include "Cubed/tools/thread_pool.hpp" #include "world/block_change.pb.h" +#include #include #include #include #include #include #include -#include #include #include namespace Cubed { @@ -91,6 +91,7 @@ private: struct ChunkEntity { ChunkState state; std::shared_ptr chunk; + uint32_t ref_count = 0; }; enum class ChunkLoadStyle { RANDOM, CENTER }; @@ -113,7 +114,7 @@ private: using PlayerHashMap = std::unordered_map; using PendingChunkHashMap = std::unordered_map; - using ChunkPosSet = std::unordered_set; + using ChunkPosSet = absl::flat_hash_set; using PlayerUUIDMap = tbb::concurrent_hash_map; using uuid_acc = PlayerUUIDMap::accessor; @@ -177,7 +178,7 @@ private: void poll_finished_chunks(); void wait_all_chunk_tasks(); - void clear_unused_chunks(); + void update_ref_count(const ChunkPosSet& old, const ChunkPosSet& now); void send_time(); diff --git a/src/gameplay/server_player.cpp b/src/gameplay/server_player.cpp index e7fc820..c8ee2bf 100644 --- a/src/gameplay/server_player.cpp +++ b/src/gameplay/server_player.cpp @@ -44,4 +44,13 @@ void ServerPlayer::update_chunk_set(const ChunkPosSet& set) { m_player_chunk_pos_set.insert(set.begin(), set.end()); } +const ServerPlayer::ChunkPosSet& ServerPlayer::get_chunk_pos_set() const { + std::shared_lock lock(m_chunk_pos_mutex); + return m_player_chunk_pos_set; +} + +ServerPlayer::ChunkPosSet& ServerPlayer::get_chunk_pos_set() { + std::lock_guard lock(m_chunk_pos_mutex); + return m_player_chunk_pos_set; +} } // namespace Cubed \ No newline at end of file diff --git a/src/gameplay/server_world.cpp b/src/gameplay/server_world.cpp index cda2189..fd7cbf7 100644 --- a/src/gameplay/server_world.cpp +++ b/src/gameplay/server_world.cpp @@ -52,22 +52,44 @@ void ServerWorld::wait_all_chunk_tasks() { } } -void ServerWorld::clear_unused_chunks() { +void ServerWorld::update_ref_count(const ChunkPosSet& old, + const ChunkPosSet& now) { + std::lock_guard lock(m_chunks_mutex); - std::scoped_lock lock(m_chunks_mutex, m_player_mutex); - Logger::info("before {}", m_chunks.size()); + // Elements in the old set that are not contained in now are not needed by + // the current player. - size_t removed = std::erase_if(m_chunks, [this](const auto& item) { - const auto& [pos, chunk] = item; - for (const auto& [uuid, player] : m_players) { - if (player.has_player(pos)) { - return false; + for (auto& pos : old) { + if (!now.contains(pos)) { + auto it = m_chunks.find(pos); + if (it == m_chunks.end()) { + Logger::warn( + "Update Ref Count Error, can't Find old pos in m_chunks"); + continue; + } + if (it->second.ref_count == 0) { + Logger::error("Chunk {} {} error, ref count is 0", pos.x, + pos.z); + m_chunks.erase(pos); + continue; + } + if (--it->second.ref_count == 0) { + m_chunks.erase(pos); } } - return true; - }); - Logger::info("removed: {}", removed); - Logger::info("after {}", m_chunks.size()); + } + + for (auto& pos : now) { + auto it = m_chunks.find(pos); + if (it == m_chunks.end()) { + Logger::warn( + "Update Ref Count Error, can't Find now pos in m_chunks"); + continue; + } + if (!old.contains(pos)) { + ++it->second.ref_count; + } + } } void ServerWorld::send_time() { @@ -212,22 +234,25 @@ void ServerWorld::gen_chunks_internal(const std::string& uuid) { // Logger::info("gen_chunks_internal"); m_chunk_gen_finished = false; - ChunkPosSet required_chunks; - compute_required_chunks(required_chunks, uuid); + ChunkPosSet required_chunks_set; + compute_required_chunks(required_chunks_set, uuid); std::vector need_gen_chunks_pos; - sync_and_collect_missing_chunks(need_gen_chunks_pos, required_chunks); + ChunkPosSet old_set; + sync_and_collect_missing_chunks(need_gen_chunks_pos, required_chunks_set); { std::lock_guard lock(m_player_mutex); auto it = m_players.find(uuid); if (it == m_players.end()) { return; } - it->second.update_chunk_set(required_chunks); + old_set = std::move(it->second.get_chunk_pos_set()); + it->second.update_chunk_set(required_chunks_set); } - ASSERT_MSG(!required_chunks.empty(), "required chunks is empty!!"); - clear_unused_chunks(); + update_ref_count(old_set, required_chunks_set); + ASSERT_MSG(!required_chunks_set.empty(), "required chunks is empty!!"); + Logger::info("New Gen Chunks Sum: {}", need_gen_chunks_pos.size()); if (need_gen_chunks_pos.empty() && m_new_chunks.empty()) { @@ -281,8 +306,8 @@ void ServerWorld::sync_and_collect_missing_chunks( auto it = m_chunks.find(pos); if (it == m_chunks.end()) { need_gen_chunks_pos.push_back(pos); - m_chunks.emplace(pos, - ChunkEntity{ChunkState::GENERATING, nullptr}); + m_chunks.emplace( + pos, ChunkEntity{ChunkState::GENERATING, nullptr, 0}); } } } @@ -673,12 +698,14 @@ void ServerWorld::handle_player_login(const std::string& name, void ServerWorld::handle_player_exit(const std::string& uuid) { std::shared_ptr exit_session; + ChunkPosSet old_set; { std::lock_guard lock(m_player_mutex); auto it = m_players.find(uuid); if (it != m_players.end()) { Logger::info("Player {} Exit the Server", it->second.get_name()); exit_session = it->second.get_session(); + old_set = std::move(it->second.get_chunk_pos_set()); m_players.erase(it); } else { Logger::error("Player {} isn't in Server", uuid); @@ -688,6 +715,8 @@ void ServerWorld::handle_player_exit(const std::string& uuid) { m_uuid_to_name.erase(uuid); + update_ref_count(old_set, {}); + Arena arena; auto* rsp = Arena::Create(&arena); rsp->set_uuid(uuid);