feat(gameplay): add player logout and disconnect handling

This commit is contained in:
2026-06-26 17:05:09 +08:00
parent 25e9b0e0d3
commit e5e84848a9
11 changed files with 110 additions and 13 deletions

View File

@@ -284,6 +284,7 @@ void App::run() {
update();
render();
}
m_client_world.exit();
}
static Gait player_gait = Gait::WALK;
void App::update() {

View File

@@ -200,6 +200,18 @@ void ClientWorld::receive_other_player(const PlayerInfoRsp& rsp) {
}
}
void ClientWorld::receive_player_logout(const LogoutRsp& rsp) {
{
std::lock_guard lock(m_other_players_mutex);
int sum = m_other_players.erase(rsp.uuid());
if (sum == 0) {
Logger::warn("Player {} not find", rsp.uuid());
} else {
Logger::info("Player {} erase", rsp.uuid());
}
}
}
void ClientWorld::init(std::string_view player_name,
std::shared_ptr<NetworkClient> client) {
m_chunks.reserve(MAX_DISTANCE * MAX_DISTANCE * 4);
@@ -405,6 +417,12 @@ void ClientWorld::receive_chunk(const ChunkDataRsp& data) {
});
}
void ClientWorld::exit() {
LogoutReq req;
req.set_uuid(m_player.get_uuid());
m_client->send(make_packet(req));
}
void ClientWorld::update(float delta_time) {
m_player.update(delta_time);
{

View File

@@ -99,13 +99,19 @@ asio::awaitable<void> NetworkClient::read_loop() {
if (rsp.ParseFromArray(body_data.data(), body_data.size())) {
m_world.receive_time(rsp);
}
}
} break;
case to_num(PacketEnum::PLAYER_INFO_RSP): {
PlayerInfoRsp rsp;
if (rsp.ParseFromArray(body_data.data(), body_data.size())) {
m_world.receive_other_player(rsp);
}
} break;
case to_num(PacketEnum::LOGOUT_RSP): {
LogoutRsp rsp;
if (rsp.ParseFromArray(body_data.data(), body_data.size())) {
m_world.receive_player_logout(rsp);
}
} break;
}
}
} catch (const asio::system_error& e) {

View File

@@ -3,8 +3,10 @@
#include "Cubed/gameplay/server_world.hpp"
namespace Cubed {
ServerPlayer::ServerPlayer(std::string_view name, std::string_view uuid,
ServerWorld& world, std::shared_ptr<Session> session)
: m_name(name), m_uuid(uuid), m_world(world), m_session(session) {}
ServerWorld& world, std::shared_ptr<Session> session,
TickType gametick)
: m_name(name), m_uuid(uuid), m_world(world), m_session(session),
m_last_gametick(gametick) {}
const glm::vec3& ServerPlayer::get_pos() const { return m_pos; }
const std::string& ServerPlayer::get_name() const { return m_name; }
const std::string& ServerPlayer::get_uuid() const { return m_uuid; }
@@ -18,4 +20,15 @@ void ServerPlayer::update_pos(float x, float y, float z) {
m_last_chunk_pos = chunk_pos;
}
}
void ServerPlayer::update_sync_gametick(TickType gametick) {
m_last_gametick = gametick;
}
bool ServerPlayer::is_disconnect(TickType current_gametick) const {
if (current_gametick - m_last_gametick > TIMEOUT) {
return true;
}
return false;
}
} // namespace Cubed

View File

@@ -46,6 +46,22 @@ void ServerWorld::send_time() {
}
void ServerWorld::init_world() {
register_timer("player disconnect", 5, [this]() {
std::vector<std::string> disconnect;
{
std::shared_lock lock(m_player_mutex);
for (auto& [uuid, player] : m_players) {
if (player.is_disconnect(m_game_ticks)) {
disconnect.emplace_back(uuid);
}
}
}
for (auto& uuid : disconnect) {
handle_player_exit(uuid);
}
});
m_cave_carcer.init(ChunkGenerator::seed());
m_river_worm.init(ChunkGenerator::seed());
m_chunks.reserve(MAX_DISTANCE * MAX_DISTANCE * 4);
@@ -53,9 +69,6 @@ void ServerWorld::init_world() {
auto t1 = std::chrono::system_clock::now();
// init players
// m_players.emplace(HASH::str("TestPlayer"), Player(*this, "TestPlayer"));
start_gen_thread();
init_chunks();
auto t2 = std::chrono::system_clock::now();
@@ -409,6 +422,7 @@ void ServerWorld::sync_player_pos(const std::string& uuid, float x, float y,
return;
}
it->second.update_pos(x, y, z);
it->second.update_sync_gametick(m_game_ticks);
name = it->second.get_name();
}
@@ -442,9 +456,9 @@ void ServerWorld::handle_player_login(const std::string& name,
Logger::info("Player {} (uuid {}) join the world", name, uuid);
{
std::lock_guard lock(m_player_mutex);
m_players.emplace(std::piecewise_construct,
std::forward_as_tuple(std::string(uuid)),
std::forward_as_tuple(name, uuid, *this, session));
m_players.emplace(
std::piecewise_construct, std::forward_as_tuple(std::string(uuid)),
std::forward_as_tuple(name, uuid, *this, session, m_game_ticks));
}
m_uuid_to_name.emplace(uuid, name);
LoginRsp rsp;
@@ -458,13 +472,29 @@ void ServerWorld::handle_player_exit(const std::string& uuid) {
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());
m_players.erase(it);
} else {
Logger::error("Player {} isn't in Server", uuid);
return;
}
}
m_uuid_to_name.erase(uuid);
std::vector<std::shared_ptr<Session>> sessions;
{
std::shared_lock lock(m_player_mutex);
for (auto& [uuid, player] : m_players) {
sessions.emplace_back(player.get_session());
}
}
for (auto& s : sessions) {
LogoutRsp rsp;
rsp.set_uuid(uuid);
s->send(make_packet(rsp));
}
}
glm::vec3 ServerWorld::get_player_pos(const std::string& uuid) const {
@@ -519,6 +549,7 @@ void ServerWorld::handle_chunk_req(const std::string& uuid, ChunkPos pos) {
auto it = m_players.find(uuid);
if (it != m_players.end()) {
s = it->second.get_session();
it->second.update_sync_gametick(m_game_ticks);
}
}
if (!s) {

View File

@@ -90,6 +90,12 @@ asio::awaitable<void> Session::read_loop() {
m_server_world.handle_block_change(req);
}
}
if (cmd_id == to_num(PacketEnum::LOGOUT_REQ)) {
LogoutReq req;
if (req.ParseFromArray(body_data.data(), body_data.size())) {
m_server_world.handle_player_exit(req.uuid());
}
}
}
} catch (const asio::system_error& e) {
auto ec = e.code();

View File

@@ -9,3 +9,11 @@ message LoginRsp {
string uuid = 2;
}
message LogoutReq {
string uuid = 1;
}
message LogoutRsp {
string uuid = 1;
}