mirror of
https://github.com/zhenyan121/Cubed.git
synced 2026-08-08 17:57:02 +08:00
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:
@@ -29,8 +29,7 @@ public:
|
||||
void init(std::string_view player_name,
|
||||
std::shared_ptr<NetworkClient> client);
|
||||
void update(float delta_time);
|
||||
const std::optional<LookBlock>&
|
||||
get_look_block_pos(const std::string& name) const;
|
||||
const std::optional<LookBlock>& get_look_block_pos() const;
|
||||
ClientPlayer& get_player();
|
||||
int get_block(const glm::ivec3& block_pos) const;
|
||||
bool is_solid(const glm::ivec3& block_pos) const;
|
||||
@@ -66,8 +65,9 @@ public:
|
||||
const std::vector<RemotePlayerRenderData>& render_player_data() const;
|
||||
glm::vec3 sunlight_dir() const;
|
||||
void receive_chunk(ChunkDataRsp data);
|
||||
void receive_exit();
|
||||
void request_exit();
|
||||
bool is_receive_exit();
|
||||
|
||||
template <typename Fn>
|
||||
void register_timer(std::string_view id, TickType threshold, Fn&& f) {
|
||||
m_timers.emplace(std::piecewise_construct,
|
||||
|
||||
@@ -26,6 +26,7 @@ public:
|
||||
enum class ThreadPoolKind { NET, GEN };
|
||||
ServerWorld();
|
||||
~ServerWorld();
|
||||
void stop();
|
||||
void handle_player_exit(const std::string& uuid);
|
||||
void init_world();
|
||||
void need_gen(std::string uuid);
|
||||
@@ -78,7 +79,6 @@ public:
|
||||
void handle_block_change(const BlockChangeReq& req);
|
||||
|
||||
int chunk_size() const;
|
||||
|
||||
template <typename Fn>
|
||||
void register_timer(std::string_view id, TickType threshold, Fn&& f) {
|
||||
m_timers.emplace(std::piecewise_construct,
|
||||
@@ -100,12 +100,12 @@ private:
|
||||
ChunkPos pos;
|
||||
};
|
||||
struct PendingChunk {
|
||||
ServerChunk chunk;
|
||||
std::unique_ptr<ServerChunk> chunk;
|
||||
std::future<void> future;
|
||||
};
|
||||
struct FinishedChunk {
|
||||
ChunkPos pos;
|
||||
std::shared_ptr<ServerChunk> chunk;
|
||||
std::unique_ptr<ServerChunk> chunk;
|
||||
};
|
||||
|
||||
using ChunkHashMap =
|
||||
@@ -136,6 +136,7 @@ private:
|
||||
std::atomic<bool> m_need_gen_chunk{false};
|
||||
std::atomic<bool> m_is_rebuilding{false};
|
||||
std::atomic<bool> m_init{false};
|
||||
std::atomic<bool> m_stopped{false};
|
||||
std::atomic<int> m_rendering_distance{24};
|
||||
std::atomic<int> m_gen_pool_threads{0};
|
||||
std::atomic<int> m_net_pool_threads{0};
|
||||
@@ -185,5 +186,6 @@ private:
|
||||
int
|
||||
change_pool_threads(std::atomic<std::shared_ptr<ThreadPool>>& thread_pool,
|
||||
int threads);
|
||||
void send_server_stop();
|
||||
};
|
||||
} // namespace Cubed
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -15,5 +15,6 @@ message LogoutReq {
|
||||
|
||||
message LogoutRsp {
|
||||
string uuid = 1;
|
||||
bool server_stop = 2;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user