feat(gameplay): implement server stop and client exit handling

Add server_stop flag to LogoutRsp protocol. Modify client_world to check for server stop or own logout to set exit flag. Add ServerWorld::stop() to broadcast stop and cleanly shut down. Refactor chunk ownership to unique_ptr. Remove name parameter from get_look_block_pos.
This commit is contained in:
2026-06-28 15:27:51 +08:00
parent 429520b1f7
commit a5764e901b
7 changed files with 54 additions and 22 deletions

View File

@@ -301,11 +301,16 @@ void App::run() {
last_time = glfwGetTime();
while (!glfwWindowShouldClose(m_window.get_glfw_window())) {
if (m_client_world.is_receive_exit()) {
break;
}
update();
render();
}
m_client_world.request_exit();
if (!m_argument.is_client) {
m_server.server_world().stop();
}
}
static Gait player_gait = Gait::WALK;
void App::update() {

View File

@@ -44,8 +44,7 @@ ClientWorld::~ClientWorld() {
m_timers.clear();
}
const std::optional<LookBlock>&
ClientWorld::get_look_block_pos(const std::string& name) const {
const std::optional<LookBlock>& ClientWorld::get_look_block_pos() const {
return m_player.get_look_block_pos();
}
@@ -231,6 +230,14 @@ void ClientWorld::receive_remote_player(const PlayerInfoRsp& rsp) {
}
void ClientWorld::receive_player_logout(const LogoutRsp& rsp) {
if (rsp.server_stop()) {
m_receive_exit = true;
return;
}
if (rsp.uuid() == m_player.get_uuid()) {
m_receive_exit = true;
return;
}
{
std::lock_guard lock(m_other_players_mutex);
int sum = m_other_players.erase(rsp.uuid());
@@ -240,9 +247,6 @@ 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,
@@ -465,8 +469,7 @@ void ClientWorld::receive_chunk(ChunkDataRsp data) {
}
});
}
void ClientWorld::receive_exit() { m_receive_exit = true; }
bool ClientWorld::is_receive_exit() { return m_receive_exit; }
void ClientWorld::request_exit() {
if (m_receive_exit) {
return;

View File

@@ -16,10 +16,16 @@ using namespace google::protobuf;
namespace Cubed {
ServerWorld::ServerWorld() {}
ServerWorld::~ServerWorld() {
ServerWorld::~ServerWorld() { stop(); }
void ServerWorld::stop() {
if (!m_init) {
return;
}
if (m_stopped.exchange(true)) {
return;
}
send_server_stop();
stop_gen_thread();
stop_server_thread();
wait_all_chunk_tasks();
@@ -115,12 +121,14 @@ void ServerWorld::send_chunk(int task_id, const std::string& uuid,
Logger::error("Chunk {} {} is invaild", pos.x, pos.z);
return;
}
rsp->set_chunk_seed(it->second.chunk->seed());
rsp->set_biome_type(std::to_underlying(it->second.chunk->biome()));
auto* blocks = rsp->mutable_chunk_blocks();
auto& chunk_blocks = it->second.chunk->get_chunk_blocks();
blocks->Assign(chunk_blocks.begin(), chunk_blocks.end());
auto& neighbor_blocks = it->second.chunk->get_neightbor_blocks();
auto assign = [](auto* nb,
const std::optional<std::vector<BlockType>>& blocks) {
if (!blocks) {
@@ -231,7 +239,8 @@ void ServerWorld::gen_chunks_internal(const std::string& uuid) {
// Create new chunk
std::lock_guard lock(m_new_chunk_mutex);
for (auto& pos : need_gen_chunks_pos) {
m_new_chunks.emplace(pos, ServerChunk(*this, pos));
m_new_chunks.emplace(
pos, std::make_unique<ServerChunk>(ServerChunk(*this, pos)));
}
}
@@ -292,7 +301,7 @@ void ServerWorld::submit_new_chunks(const std::string& uuid) {
for (auto& [pos, task] : m_new_chunks) {
if (!task.future.valid()) {
task.future =
pool_ptr->enqueue([&task]() { task.chunk.gen_chunk(); });
pool_ptr->enqueue([&task]() { task.chunk->gen_chunk(); });
}
}
break;
@@ -320,7 +329,7 @@ void ServerWorld::submit_new_chunks(const std::string& uuid) {
for (auto& [pos, task] : tasks) {
if (!task->future.valid()) {
task->future =
pool_ptr->enqueue([task]() { task->chunk.gen_chunk(); });
pool_ptr->enqueue([task]() { task->chunk->gen_chunk(); });
}
}
}
@@ -346,9 +355,8 @@ void ServerWorld::poll_finished_chunks() {
return true;
}
// Spawn complete, move away
m_new_finished_chunk.emplace_back(
pair.first,
std::make_shared<ServerChunk>(std::move(pending.chunk)));
m_new_finished_chunk.emplace_back(pair.first,
std::move(pending.chunk));
return true;
});
@@ -683,6 +691,7 @@ void ServerWorld::handle_player_exit(const std::string& uuid) {
Arena arena;
auto* rsp = Arena::Create<LogoutRsp>(&arena);
rsp->set_uuid(uuid);
rsp->set_server_stop(false);
exit_session->send(make_packet(*rsp));
std::vector<std::shared_ptr<Session>> sessions;
@@ -800,6 +809,18 @@ int ServerWorld::change_pool_threads(
thread_pool.store(std::make_shared<ThreadPool>(used_thread));
return used_thread;
}
void ServerWorld::send_server_stop() {
Arena arena;
auto* rsp = Arena::Create<LogoutRsp>(&arena);
rsp->set_server_stop(true);
std::shared_lock lock(m_player_mutex);
for (auto& [uuid, player] : m_players) {
player.get_session()->send(make_packet(*rsp));
}
Logger::info("Send Server Mesaage Success");
}
int ServerWorld::chunk_load_style() const {
return std::to_underlying(m_chunk_load_style.load());
}

View File

@@ -15,5 +15,6 @@ message LogoutReq {
message LogoutRsp {
string uuid = 1;
bool server_stop = 2;
}

View File

@@ -276,7 +276,7 @@ void Renderer::render_outline() {
const auto& shader = get_shader("outline");
shader.use();
const auto& block_pos = m_world.get_look_block_pos("TestPlayer");
const auto& block_pos = m_world.get_look_block_pos();
if (block_pos != std::nullopt) {