From 429520b1f7b09f57be9d7a65e3243828a4b7285a Mon Sep 17 00:00:00 2001 From: zhenyan121 <3367366583@qq.com> Date: Sun, 28 Jun 2026 15:04:21 +0800 Subject: [PATCH] feat(client_world): implement server exit acknowledgment with timeout --- include/Cubed/gameplay/client_world.hpp | 7 ++++++- src/app.cpp | 2 +- src/gameplay/client_world.cpp | 18 +++++++++++++++++- src/gameplay/server_world.cpp | 10 +++++++--- 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/include/Cubed/gameplay/client_world.hpp b/include/Cubed/gameplay/client_world.hpp index 187823a..6125abe 100644 --- a/include/Cubed/gameplay/client_world.hpp +++ b/include/Cubed/gameplay/client_world.hpp @@ -66,7 +66,8 @@ public: const std::vector& render_player_data() const; glm::vec3 sunlight_dir() const; void receive_chunk(ChunkDataRsp data); - void exit(); + void receive_exit(); + void request_exit(); template void register_timer(std::string_view id, TickType threshold, Fn&& f) { m_timers.emplace(std::piecewise_construct, @@ -82,6 +83,9 @@ private: using ChunkPosVector = std::vector; using OtherPlayerHashMap = std::unordered_map; + + static constexpr int WORLD_EXIT_TIMEOUT = 200; + ClientPlayer m_player; OtherPlayerHashMap m_other_players; ChunkHashMap m_chunks; @@ -104,6 +108,7 @@ private: std::vector m_render_player_data; tbb::concurrent_unordered_map m_timers; std::atomic m_game_running{false}; + std::atomic m_receive_exit{false}; std::atomic m_rendering_distance{24}; std::atomic m_game_ticks{0}; std::atomic m_day_tick{6000}; diff --git a/src/app.cpp b/src/app.cpp index c12fd25..bf61efb 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -305,7 +305,7 @@ void App::run() { update(); render(); } - m_client_world.exit(); + m_client_world.request_exit(); } static Gait player_gait = Gait::WALK; void App::update() { diff --git a/src/gameplay/client_world.cpp b/src/gameplay/client_world.cpp index d396ee9..44ee13b 100644 --- a/src/gameplay/client_world.cpp +++ b/src/gameplay/client_world.cpp @@ -240,6 +240,9 @@ void ClientWorld::receive_player_logout(const LogoutRsp& rsp) { Logger::info("Player {} erase", rsp.uuid()); } } + if (rsp.uuid() == m_player.get_uuid()) { + m_receive_exit = true; + } } void ClientWorld::init(std::string_view player_name, @@ -462,12 +465,25 @@ void ClientWorld::receive_chunk(ChunkDataRsp data) { } }); } +void ClientWorld::receive_exit() { m_receive_exit = true; } -void ClientWorld::exit() { +void ClientWorld::request_exit() { + if (m_receive_exit) { + return; + } Arena arena; auto* req = Arena::Create(&arena); req->set_uuid(m_player.get_uuid()); m_client->send(make_packet(*req)); + int cnt = 0; + while (!m_receive_exit) { + std::this_thread::sleep_for(milliseconds(DEFAULT_PER_TICK_TIME)); + ++cnt; + if (cnt >= WORLD_EXIT_TIMEOUT) { + Logger::warn("Can't Receive Server Exit Sign"); + break; + } + } } void ClientWorld::update(float delta_time) { diff --git a/src/gameplay/server_world.cpp b/src/gameplay/server_world.cpp index 0d5fb71..912a49f 100644 --- a/src/gameplay/server_world.cpp +++ b/src/gameplay/server_world.cpp @@ -664,11 +664,13 @@ void ServerWorld::handle_player_login(const std::string& name, } void ServerWorld::handle_player_exit(const std::string& uuid) { + std::shared_ptr exit_session; { 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(); m_players.erase(it); } else { Logger::error("Player {} isn't in Server", uuid); @@ -678,6 +680,11 @@ void ServerWorld::handle_player_exit(const std::string& uuid) { m_uuid_to_name.erase(uuid); + Arena arena; + auto* rsp = Arena::Create(&arena); + rsp->set_uuid(uuid); + exit_session->send(make_packet(*rsp)); + std::vector> sessions; { std::shared_lock lock(m_player_mutex); @@ -687,9 +694,6 @@ void ServerWorld::handle_player_exit(const std::string& uuid) { } for (auto& s : sessions) { - Arena arena; - auto* rsp = Arena::Create(&arena); - rsp->set_uuid(uuid); s->send(make_packet(*rsp)); } }