feat(network): integrate protobuf for player sync and session management

Add Protobuf dependency, define proto messages for player requests, positions, and chunk data. Refactor Session to use strand and async write. Implement player join/exit and position sync in ServerWorld.
This commit is contained in:
2026-06-23 20:08:40 +08:00
parent 1acfeef9e6
commit 4303c1cd32
12 changed files with 236 additions and 28 deletions

View File

@@ -9,7 +9,17 @@ NetworkServer::NetworkServer(int port) : m_port(port) {}
NetworkServer::~NetworkServer() { stop(); }
void NetworkServer::stop() {
if (m_stopped.exchange(true)) {
return;
}
for (auto& [key, s] : m_session) {
if (s) {
s->close();
}
}
m_io.stop();
m_session.clear();
if (m_server.joinable()) {
m_server.join();
}
@@ -19,10 +29,17 @@ void NetworkServer::stop() {
asio::awaitable<void> NetworkServer::listen() {
tcp::acceptor acceptor(m_io, tcp::endpoint(tcp::v4(), m_port));
while (true) {
tcp::socket socket =
co_await acceptor.async_accept(asio::use_awaitable);
if (m_stopped) {
break;
}
std::shared_ptr<Session> s =
std::make_shared<Session>(std::move(socket), m_world);
s->start();
m_session.emplace(s->uuid(), s);
}
co_return;
}
void NetworkServer::run() {

View File

@@ -0,0 +1,10 @@
#include "Cubed/gameplay/server_player.hpp"
namespace Cubed {
ServerPlayer::ServerPlayer(std::string_view name) : m_name(name) {}
const glm::vec3& ServerPlayer::get_pos() const { return m_pos; }
const std::string& ServerPlayer::get_name() const { return m_name; }
void ServerPlayer::update_pos(float x, float y, float z) {
m_pos = glm::vec3{x, y, z};
}
} // namespace Cubed

View File

@@ -83,12 +83,16 @@ void ServerWorld::gen_chunks_internal() {
submit_new_chunks();
m_chunk_gen_finished = true;
m_request_gen_name = std::nullopt;
}
void ServerWorld::compute_required_chunks(ChunkPosSet& required_chunks) {
glm::vec3 player_pos;
// sync_player_pos(player_pos);
ASSERT_MSG(false, "Player Pos");
if (m_request_gen_name == std::nullopt) {
player_pos = glm::vec3{0.0f};
} else {
player_pos = get_player_pos(m_request_gen_name.value());
}
int x = std::floor(player_pos.x);
int z = std::floor(player_pos.z);
auto [chunk_x, chunk_z] = get_chunk_pos(x, z);
@@ -147,7 +151,11 @@ void ServerWorld::submit_new_chunks() {
}
}
glm::vec3 player_pos;
sync_player_pos(player_pos);
if (m_request_gen_name == std::nullopt) {
player_pos = glm::vec3{0.0f};
} else {
player_pos = get_player_pos(m_request_gen_name.value());
}
auto dist2 = [player_pos](ChunkPos chunk_pos) {
ChunkPos player_chunk_pos =
get_chunk_pos(player_pos.x, player_pos.z);
@@ -339,6 +347,48 @@ void ServerWorld::rebuild_world() {
void ServerWorld::update() { poll_finished_chunks(); }
void ServerWorld::sync_player_pos(const std::string& name, float x, float y,
float z) {
auto it = m_players.find(name);
if (it == m_players.end()) {
Logger::warn("Player {} is not in this Server", it->first);
return;
}
it->second.update_pos(x, y, z);
}
void ServerWorld::handle_player_request(const PlayerRequest& request) {
if (request.join()) {
player_join(request.name());
}
if (request.exit()) {
player_exit(request.name());
}
if (request.chunk_send()) {
m_request_gen_name = request.name();
need_gen();
}
}
void ServerWorld::player_join(const std::string& name) {
m_players.emplace(name, name);
}
void ServerWorld::player_exit(const std::string& name) {
auto it = m_players.find(name);
if (it == m_players.end()) {
Logger::error("Player {} isn't in Server", it->first);
}
m_players.erase(it);
}
glm::vec3 ServerWorld::get_player_pos(const std::string& name) const {
auto it = m_players.find(name);
if (it == m_players.end()) {
return glm::vec3{0.0f};
}
return it->second.get_pos();
}
int ServerWorld::rendering_distance() const {
return m_rendering_distance.load();
}

View File

@@ -1,26 +1,41 @@
#include "Cubed/gameplay/session.hpp"
#include "Cubed/gameplay/server_world.hpp"
#include "Cubed/tools/log.hpp"
#include "Cubed/tools/uuid.hpp"
#include "proto/player_pos.pb.h"
#include "proto/player_request.pb.h"
using asio::ip::tcp;
namespace Cubed {
Session::Session(tcp::socket socket)
: m_socket(std::move(socket)), m_uuid(generate_uuid()) {}
Session::Session(tcp::socket socket, ServerWorld& server_world)
: m_socket(std::move(socket)), m_uuid(generate_uuid()),
m_server_world(server_world) {}
Session::~Session() {}
void Session::start() {
auto self = shared_from_this();
asio::co_spawn(
m_socket.get_executor(),
[self]() -> asio::awaitable<void> { co_await self->read(); },
m_strand,
[self]() -> asio::awaitable<void> { co_await self->read_loop(); },
asio::detached);
}
void Session::send(std::shared_ptr<std::vector<uint8_t>> packet) {
asio::post(m_strand, [self = shared_from_this(),
packet = std::move(packet)]() mutable {
bool idle = self->m_write_queue.empty();
self->m_write_queue.emplace_back(std::move(packet));
if (idle) {
self->do_write();
}
});
}
const std::string& Session::uuid() const { return m_uuid; }
asio::awaitable<void> Session::read() {
asio::awaitable<void> Session::read_loop() {
try {
while (true) {
std::array<char, HEADER_LEN> header;
@@ -46,6 +61,20 @@ asio::awaitable<void> Session::read() {
}
if (cmd_id == 1001) {
PlayerRequest player_request;
if (player_request.ParseFromArray(body_data.data(),
body_data.size())) {
}
}
if (cmd_id == 1002) {
PlayerPos player_pos;
if (player_pos.ParseFromArray(body_data.data(),
body_data.size())) {
m_server_world.sync_player_pos(
player_pos.name(), player_pos.x(), player_pos.y(),
player_pos.z());
} else {
}
}
}
} catch (const asio::system_error& e) {
@@ -53,7 +82,39 @@ asio::awaitable<void> Session::read() {
close();
} catch (...) {
Logger::error("Unknow Error");
close();
}
co_return;
}
void Session::do_write() {
auto self = shared_from_this();
asio::async_write(
m_socket, asio::buffer(*(m_write_queue.front())),
asio::bind_executor(m_strand, [self](std::error_code ec, size_t) {
if (ec) {
Logger::warn("Write Ec {}", ec.message());
self->close();
return;
}
self->m_write_queue.pop_front();
if (!self->m_write_queue.empty()) {
self->do_write();
}
}));
}
void Session::close() {
if (m_closed.exchange(true)) {
return;
}
std::error_code ec;
m_socket.shutdown(tcp::socket::shutdown_both, ec);
m_socket.close(ec);
}
} // namespace Cubed

View File

@@ -0,0 +1,12 @@
syntax = "proto3";
message ChunkData {
int32 pos_x = 1;
int32 pos_z = 2;
uint32 chunk_seed = 3;
repeated uint32 chunk_blocks = 4 [packed=true];
repeated uint32 neighbor_blocks_1 = 5 [packed=true];
repeated uint32 neighbor_blocks_2 = 6 [packed=true];
repeated uint32 neighbor_blocks_3 = 7 [packed=true];
repeated uint32 neighbor_blocks_4 = 8 [packed=true];
}

View File

@@ -0,0 +1,9 @@
syntax = "proto3";
message PlayerPos {
string name = 1;
float x = 2;
float y = 3;
float z = 4;
}

View File

@@ -0,0 +1,8 @@
syntax = "proto3";
message PlayerRequest {
string name = 1;
bool join = 2;
bool exit = 3;
bool chunk_send = 4;
}