mirror of
https://github.com/zhenyan121/Cubed.git
synced 2026-08-09 02:07:04 +08:00
feat(gameplay): add player logout and disconnect handling
This commit is contained in:
@@ -46,7 +46,7 @@ public:
|
|||||||
void receive_time(const UpdateTime& rsp);
|
void receive_time(const UpdateTime& rsp);
|
||||||
|
|
||||||
void receive_other_player(const PlayerInfoRsp& rsp);
|
void receive_other_player(const PlayerInfoRsp& rsp);
|
||||||
|
void receive_player_logout(const LogoutRsp& rsp);
|
||||||
int rendering_distance() const;
|
int rendering_distance() const;
|
||||||
void rendering_distance(int rendering_distance);
|
void rendering_distance(int rendering_distance);
|
||||||
void start_client_thread(std::string_view uuid);
|
void start_client_thread(std::string_view uuid);
|
||||||
@@ -62,6 +62,7 @@ public:
|
|||||||
const std::vector<RenderPlayerData>& render_player_data() const;
|
const std::vector<RenderPlayerData>& render_player_data() const;
|
||||||
glm::vec3 sunlight_dir() const;
|
glm::vec3 sunlight_dir() const;
|
||||||
void receive_chunk(const ChunkDataRsp& data);
|
void receive_chunk(const ChunkDataRsp& data);
|
||||||
|
void exit();
|
||||||
template <typename Fn>
|
template <typename Fn>
|
||||||
void register_timer(std::string_view id, TickType threshold, Fn&& f) {
|
void register_timer(std::string_view id, TickType threshold, Fn&& f) {
|
||||||
m_timers.emplace(std::piecewise_construct,
|
m_timers.emplace(std::piecewise_construct,
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ using Packet = std::shared_ptr<std::vector<uint8_t>>;
|
|||||||
enum class PacketEnum {
|
enum class PacketEnum {
|
||||||
LOGIN_REQ = 1001,
|
LOGIN_REQ = 1001,
|
||||||
LOGIN_RSP = 1002,
|
LOGIN_RSP = 1002,
|
||||||
|
LOGOUT_REQ = 1003,
|
||||||
|
LOGOUT_RSP = 1004,
|
||||||
PLAYER_INFO = 2001,
|
PLAYER_INFO = 2001,
|
||||||
PLAYER_POS = 2002,
|
PLAYER_POS = 2002,
|
||||||
PLAYER_INFO_RSP = 2003,
|
PLAYER_INFO_RSP = 2003,
|
||||||
@@ -35,6 +37,10 @@ template <typename T> constexpr uint16_t get_packet_id() {
|
|||||||
return to_num(LOGIN_REQ);
|
return to_num(LOGIN_REQ);
|
||||||
} else if constexpr (is_same_v<U, LoginRsp>) {
|
} else if constexpr (is_same_v<U, LoginRsp>) {
|
||||||
return to_num(LOGIN_RSP);
|
return to_num(LOGIN_RSP);
|
||||||
|
} else if constexpr (is_same_v<U, LogoutReq>) {
|
||||||
|
return to_num(LOGOUT_REQ);
|
||||||
|
} else if constexpr (is_same_v<U, LogoutRsp>) {
|
||||||
|
return to_num(LOGOUT_RSP);
|
||||||
} else if constexpr (is_same_v<U, PlayerInfo>) {
|
} else if constexpr (is_same_v<U, PlayerInfo>) {
|
||||||
return to_num(PLAYER_INFO);
|
return to_num(PLAYER_INFO);
|
||||||
} else if constexpr (is_same_v<U, PlayerPos>) {
|
} else if constexpr (is_same_v<U, PlayerPos>) {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "Cubed/gameplay/chunk_pos.hpp"
|
#include "Cubed/gameplay/chunk_pos.hpp"
|
||||||
|
#include "Cubed/gameplay/game_time.hpp"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -11,19 +13,24 @@ class Session;
|
|||||||
class ServerPlayer {
|
class ServerPlayer {
|
||||||
public:
|
public:
|
||||||
ServerPlayer(std::string_view name, std::string_view uuid,
|
ServerPlayer(std::string_view name, std::string_view uuid,
|
||||||
ServerWorld& m_world, std::shared_ptr<Session> session);
|
ServerWorld& m_world, std::shared_ptr<Session> session,
|
||||||
|
TickType gametick);
|
||||||
const glm::vec3& get_pos() const;
|
const glm::vec3& get_pos() const;
|
||||||
const std::string& get_name() const;
|
const std::string& get_name() const;
|
||||||
const std::string& get_uuid() const;
|
const std::string& get_uuid() const;
|
||||||
std::shared_ptr<Session> get_session() const;
|
std::shared_ptr<Session> get_session() const;
|
||||||
void update_pos(float x, float y, float z);
|
void update_pos(float x, float y, float z);
|
||||||
|
void update_sync_gametick(TickType gametick);
|
||||||
|
bool is_disconnect(TickType current_gametick) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static constexpr TickType TIMEOUT = 200;
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
std::string m_uuid;
|
std::string m_uuid;
|
||||||
glm::vec3 m_pos{0.0f};
|
glm::vec3 m_pos{0.0f};
|
||||||
ServerWorld& m_world;
|
ServerWorld& m_world;
|
||||||
ChunkPos m_last_chunk_pos{0, 0};
|
ChunkPos m_last_chunk_pos{0, 0};
|
||||||
std::shared_ptr<Session> m_session;
|
std::shared_ptr<Session> m_session;
|
||||||
|
std::atomic<TickType> m_last_gametick{0};
|
||||||
};
|
};
|
||||||
} // namespace Cubed
|
} // namespace Cubed
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ public:
|
|||||||
ServerWorld();
|
ServerWorld();
|
||||||
~ServerWorld();
|
~ServerWorld();
|
||||||
void player_join(std::string_view name, std::string_view uuid);
|
void player_join(std::string_view name, std::string_view uuid);
|
||||||
void handle_player_exit(const std::string& name);
|
void handle_player_exit(const std::string& uuid);
|
||||||
void init_world();
|
void init_world();
|
||||||
void need_gen(std::optional<std::string> uuid);
|
void need_gen(std::optional<std::string> uuid);
|
||||||
void update();
|
void update();
|
||||||
|
|||||||
@@ -284,6 +284,7 @@ void App::run() {
|
|||||||
update();
|
update();
|
||||||
render();
|
render();
|
||||||
}
|
}
|
||||||
|
m_client_world.exit();
|
||||||
}
|
}
|
||||||
static Gait player_gait = Gait::WALK;
|
static Gait player_gait = Gait::WALK;
|
||||||
void App::update() {
|
void App::update() {
|
||||||
|
|||||||
@@ -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,
|
void ClientWorld::init(std::string_view player_name,
|
||||||
std::shared_ptr<NetworkClient> client) {
|
std::shared_ptr<NetworkClient> client) {
|
||||||
m_chunks.reserve(MAX_DISTANCE * MAX_DISTANCE * 4);
|
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) {
|
void ClientWorld::update(float delta_time) {
|
||||||
m_player.update(delta_time);
|
m_player.update(delta_time);
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -99,13 +99,19 @@ asio::awaitable<void> NetworkClient::read_loop() {
|
|||||||
if (rsp.ParseFromArray(body_data.data(), body_data.size())) {
|
if (rsp.ParseFromArray(body_data.data(), body_data.size())) {
|
||||||
m_world.receive_time(rsp);
|
m_world.receive_time(rsp);
|
||||||
}
|
}
|
||||||
}
|
} break;
|
||||||
case to_num(PacketEnum::PLAYER_INFO_RSP): {
|
case to_num(PacketEnum::PLAYER_INFO_RSP): {
|
||||||
PlayerInfoRsp rsp;
|
PlayerInfoRsp rsp;
|
||||||
if (rsp.ParseFromArray(body_data.data(), body_data.size())) {
|
if (rsp.ParseFromArray(body_data.data(), body_data.size())) {
|
||||||
m_world.receive_other_player(rsp);
|
m_world.receive_other_player(rsp);
|
||||||
}
|
}
|
||||||
} break;
|
} 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) {
|
} catch (const asio::system_error& e) {
|
||||||
|
|||||||
@@ -3,8 +3,10 @@
|
|||||||
#include "Cubed/gameplay/server_world.hpp"
|
#include "Cubed/gameplay/server_world.hpp"
|
||||||
namespace Cubed {
|
namespace Cubed {
|
||||||
ServerPlayer::ServerPlayer(std::string_view name, std::string_view uuid,
|
ServerPlayer::ServerPlayer(std::string_view name, std::string_view uuid,
|
||||||
ServerWorld& world, std::shared_ptr<Session> session)
|
ServerWorld& world, std::shared_ptr<Session> session,
|
||||||
: m_name(name), m_uuid(uuid), m_world(world), m_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 glm::vec3& ServerPlayer::get_pos() const { return m_pos; }
|
||||||
const std::string& ServerPlayer::get_name() const { return m_name; }
|
const std::string& ServerPlayer::get_name() const { return m_name; }
|
||||||
const std::string& ServerPlayer::get_uuid() const { return m_uuid; }
|
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;
|
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
|
} // namespace Cubed
|
||||||
@@ -46,6 +46,22 @@ void ServerWorld::send_time() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ServerWorld::init_world() {
|
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_cave_carcer.init(ChunkGenerator::seed());
|
||||||
m_river_worm.init(ChunkGenerator::seed());
|
m_river_worm.init(ChunkGenerator::seed());
|
||||||
m_chunks.reserve(MAX_DISTANCE * MAX_DISTANCE * 4);
|
m_chunks.reserve(MAX_DISTANCE * MAX_DISTANCE * 4);
|
||||||
@@ -53,9 +69,6 @@ void ServerWorld::init_world() {
|
|||||||
|
|
||||||
auto t1 = std::chrono::system_clock::now();
|
auto t1 = std::chrono::system_clock::now();
|
||||||
|
|
||||||
// init players
|
|
||||||
// m_players.emplace(HASH::str("TestPlayer"), Player(*this, "TestPlayer"));
|
|
||||||
|
|
||||||
start_gen_thread();
|
start_gen_thread();
|
||||||
init_chunks();
|
init_chunks();
|
||||||
auto t2 = std::chrono::system_clock::now();
|
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;
|
return;
|
||||||
}
|
}
|
||||||
it->second.update_pos(x, y, z);
|
it->second.update_pos(x, y, z);
|
||||||
|
it->second.update_sync_gametick(m_game_ticks);
|
||||||
name = it->second.get_name();
|
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);
|
Logger::info("Player {} (uuid {}) join the world", name, uuid);
|
||||||
{
|
{
|
||||||
std::lock_guard lock(m_player_mutex);
|
std::lock_guard lock(m_player_mutex);
|
||||||
m_players.emplace(std::piecewise_construct,
|
m_players.emplace(
|
||||||
std::forward_as_tuple(std::string(uuid)),
|
std::piecewise_construct, std::forward_as_tuple(std::string(uuid)),
|
||||||
std::forward_as_tuple(name, uuid, *this, session));
|
std::forward_as_tuple(name, uuid, *this, session, m_game_ticks));
|
||||||
}
|
}
|
||||||
m_uuid_to_name.emplace(uuid, name);
|
m_uuid_to_name.emplace(uuid, name);
|
||||||
LoginRsp rsp;
|
LoginRsp rsp;
|
||||||
@@ -458,13 +472,29 @@ void ServerWorld::handle_player_exit(const std::string& uuid) {
|
|||||||
std::lock_guard lock(m_player_mutex);
|
std::lock_guard lock(m_player_mutex);
|
||||||
auto it = m_players.find(uuid);
|
auto it = m_players.find(uuid);
|
||||||
if (it != m_players.end()) {
|
if (it != m_players.end()) {
|
||||||
|
Logger::info("Player {} Exit the Server", it->second.get_name());
|
||||||
m_players.erase(it);
|
m_players.erase(it);
|
||||||
} else {
|
} else {
|
||||||
Logger::error("Player {} isn't in Server", uuid);
|
Logger::error("Player {} isn't in Server", uuid);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_uuid_to_name.erase(uuid);
|
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 {
|
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);
|
auto it = m_players.find(uuid);
|
||||||
if (it != m_players.end()) {
|
if (it != m_players.end()) {
|
||||||
s = it->second.get_session();
|
s = it->second.get_session();
|
||||||
|
it->second.update_sync_gametick(m_game_ticks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!s) {
|
if (!s) {
|
||||||
|
|||||||
@@ -90,6 +90,12 @@ asio::awaitable<void> Session::read_loop() {
|
|||||||
m_server_world.handle_block_change(req);
|
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) {
|
} catch (const asio::system_error& e) {
|
||||||
auto ec = e.code();
|
auto ec = e.code();
|
||||||
|
|||||||
@@ -9,3 +9,11 @@ message LoginRsp {
|
|||||||
string uuid = 2;
|
string uuid = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message LogoutReq {
|
||||||
|
string uuid = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message LogoutRsp {
|
||||||
|
string uuid = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user