From efa49a98b7eef3c1828498469bfe7b30f93e59cc Mon Sep 17 00:00:00 2001 From: zhenyan121 <3367366583@qq.com> Date: Wed, 29 Jul 2026 18:17:15 +0800 Subject: [PATCH] refactor(gameplay): replace client thread with timer-based system and add entity manager --- include/Cubed/gameplay/client_world.hpp | 16 +++++++-------- include/Cubed/gameplay/packet.hpp | 2 +- src/gameplay/client_world.cpp | 27 +++++++++++++++---------- src/gameplay/network_client.cpp | 8 +++++++- 4 files changed, 31 insertions(+), 22 deletions(-) diff --git a/include/Cubed/gameplay/client_world.hpp b/include/Cubed/gameplay/client_world.hpp index a1a777c..cc858ed 100644 --- a/include/Cubed/gameplay/client_world.hpp +++ b/include/Cubed/gameplay/client_world.hpp @@ -5,6 +5,7 @@ #include "Cubed/gameplay/chat_message.hpp" #include "Cubed/gameplay/chunk_pos.hpp" #include "Cubed/gameplay/client_chunk.hpp" +#include "Cubed/gameplay/client_entity_manager.hpp" #include "Cubed/gameplay/client_player_manager.hpp" #include "Cubed/gameplay/game_time.hpp" #include "Cubed/gameplay/local_player.hpp" @@ -84,6 +85,7 @@ public: Config& get_config(); WorldScene& world_scene(); ClientPlayerManager& player_manager(); + ClientEntityManager& entity_manager(); void set_direct_exit(); void receive_chat_message(ChatMsg& msg); @@ -92,10 +94,10 @@ public: bool enable_voice_chat() const; int get_per_tick_time() const override; template - void register_ticktimer(std::string_view id, TickType threshold, Fn&& f) { - m_ticktimers.emplace( - std::piecewise_construct, std::forward_as_tuple(std::string(id)), - std::forward_as_tuple(threshold, std::forward(f))); + void register_timer(std::string_view id, float threshold, Fn&& f) { + m_timers.emplace(std::piecewise_construct, + std::forward_as_tuple(std::string(id)), + std::forward_as_tuple(threshold, std::forward(f))); } private: @@ -126,14 +128,13 @@ private: static constexpr int WORLD_EXIT_TIMEOUT = 200; static constexpr int MAX_UPLOAD_CHUNK_SUM = 16; - + ClientEntityManager m_entity_manager; ClientPlayerManager m_player_manager; ChunkHashMap m_chunks; AudioEngine& m_audio; Config& m_config; WorldScene& m_world_scene; std::vector m_planes; - std::jthread m_client_thread; tbb::concurrent_queue> m_pending_upload_queue; tbb::concurrent_queue m_dirty_chunk_queue; @@ -144,7 +145,6 @@ private: std::deque m_dirty_queue; std::vector m_render_snapshots; - tbb::concurrent_unordered_map m_ticktimers; std::unordered_map m_timers; std::atomic m_exit_direct{false}; std::atomic m_game_running{false}; @@ -164,8 +164,6 @@ private: Random m_random; - void client_run(std::stop_token token); - void set_block(const glm::ivec3& pos, unsigned id); void update_chunk(const ChunkPosSet& old, const ChunkPosSet& now); diff --git a/include/Cubed/gameplay/packet.hpp b/include/Cubed/gameplay/packet.hpp index ef74c74..5508eb6 100644 --- a/include/Cubed/gameplay/packet.hpp +++ b/include/Cubed/gameplay/packet.hpp @@ -60,8 +60,8 @@ enum class PacketEnum : uint16_t { BLOCK_CHANGE_REQ = 3003, BLOCK_CHANGE_RSP = 3004, S2C_CLEAR_ALL_CHUNKS = 3005, - S2C_ENTITY_CREATE = 3006, UPDATE_TIME = 3006, + S2C_ENTITY_CREATE = 3007, CHAT_MSG = 4001, VOICE_MSG = 4002, PING = 9001, diff --git a/src/gameplay/client_world.cpp b/src/gameplay/client_world.cpp index 8ff7157..da45397 100644 --- a/src/gameplay/client_world.cpp +++ b/src/gameplay/client_world.cpp @@ -23,8 +23,8 @@ struct ChunkRenderData { } // namespace ClientWorld::ClientWorld(AudioEngine& auido, Config& config, WorldScene& scene) - : m_player_manager(*this), m_audio(auido), m_config(config), - m_world_scene(scene) {} + : m_entity_manager(*this), m_player_manager(*this), m_audio(auido), + m_config(config), m_world_scene(scene) {} ClientWorld::~ClientWorld() { m_client->close(); @@ -51,7 +51,7 @@ ClientWorld::~ClientWorld() { std::lock_guard lk(m_delete_vao_mutex); m_pending_delete_vao.clear(); } - m_ticktimers.clear(); + m_timers.clear(); } const std::optional& ClientWorld::get_look_block_pos() const { @@ -363,6 +363,7 @@ void ClientWorld::send_player_water_sound(bool underwater, void ClientWorld::init(std::string_view player_name, std::shared_ptr client) { + m_entity_manager.init(); m_player_manager.init(player_name); m_client = client; @@ -371,10 +372,10 @@ void ClientWorld::init(std::string_view player_name, m_random.init(ChunkGenerator::seed()); // timer - register_ticktimer("player_pos", 1, [this]() { + register_timer("player_pos", 0.05f, [this]() { m_player_manager.report_player_info(m_client.get()); }); - m_timers.try_emplace("Birds Sound", 60.0f, [this]() { + register_timer("Birds Sound", 60.0f, [this]() { auto player_pos = m_player_manager.get_local().get_player_pos(); if (player_pos.y < SEA_LEVEL) { return; @@ -390,7 +391,7 @@ void ClientWorld::init(std::string_view player_name, } }); - m_timers.try_emplace("Ocean Wave", 3.0f, [this]() { + register_timer("Ocean Wave", 3.0f, [this]() { auto player_pos = m_player_manager.get_local().get_player_pos(); if (player_pos.y < SEA_LEVEL - 10 || player_pos.y > SEA_LEVEL + 10) { return; @@ -410,7 +411,7 @@ void ClientWorld::init(std::string_view player_name, } }); - m_timers.try_emplace("under water bubble", 1.5f, [this]() { + register_timer("under water bubble", 1.5f, [this]() { if (m_player_manager.get_local().is_underwater()) { auto ans = m_random.random_int(1, 2); std::string sound = @@ -420,7 +421,7 @@ void ClientWorld::init(std::string_view player_name, } }); - m_timers.try_emplace("bgm change", 350.0f, [this]() { + register_timer("bgm change", 350.0f, [this]() { if (m_day_tick >= 17000 || m_day_tick < 5000) { m_audio.change_bgm("bgm/bgm002.ogg"); } else { @@ -455,11 +456,11 @@ void ClientWorld::start_client_thread(std::string_view uuid) { } // response m_player_manager.get_local().set_uuid(uuid); - + /* m_client_thread = std::jthread([this](std::stop_token token) { m_game_running = true; client_run(token); - }); + });*/ // Wait for 20 ticks, after the server's central chunk is generated, then // request chunks @@ -470,10 +471,11 @@ void ClientWorld::start_client_thread(std::string_view uuid) { } void ClientWorld::stop_client_thread() { + /* m_client_thread.request_stop(); if (m_client_thread.joinable()) { m_client_thread.join(); - } + }*/ m_game_running = false; } void ClientWorld::start_thread_pool() { @@ -512,6 +514,7 @@ void ClientWorld::reload_config(bool chunk_build) { m_player_manager.reload_config(); } +/* void ClientWorld::client_run(std::stop_token stoken) { Logger::info("Client Thread Started"); using Clock = std::chrono::steady_clock; @@ -527,6 +530,7 @@ void ClientWorld::client_run(std::stop_token stoken) { std::this_thread::sleep_until(next); } } +*/ void ClientWorld::update_chunk(const ChunkPosSet& old, const ChunkPosSet& now) { @@ -677,6 +681,7 @@ const AudioEngine& ClientWorld::get_audio() const { return m_audio; } Config& ClientWorld::get_config() { return m_config; } WorldScene& ClientWorld::world_scene() { return m_world_scene; } ClientPlayerManager& ClientWorld::player_manager() { return m_player_manager; } +ClientEntityManager& ClientWorld::entity_manager() { return m_entity_manager; } void ClientWorld::set_direct_exit() { m_exit_direct = true; } void ClientWorld::request_exit() { if (m_receive_exit) { diff --git a/src/gameplay/network_client.cpp b/src/gameplay/network_client.cpp index 8ceebcc..914299d 100644 --- a/src/gameplay/network_client.cpp +++ b/src/gameplay/network_client.cpp @@ -142,7 +142,13 @@ asio::awaitable NetworkClient::read_loop() { if (decode_packet(*msg, body_data, header)) { m_world.receive_voice_message(*msg); } - } + } break; + case std::to_underlying(PacketEnum::S2C_ENTITY_CREATE): { + auto* msg = Arena::Create(&arena); + if (decode_packet(*msg, body_data, header)) { + m_world.entity_manager().receive_entity_create(*msg); + } + } break; } } } catch (const asio::system_error& e) {