feat(entity): add client-to-server entity creation and destruction requests

Extend the network protocol with `C2SEntityCreateRequest` and `C2SEntityDestoryRequest` packets. Refactor entity managers to split public client-facing create/destroy methods that send requests over the network, from internal handlers that process received packets. Add utility functions for converting protobuf Vec3 to glm::vec3.
This commit is contained in:
2026-07-30 15:53:09 +08:00
parent 826c6600d0
commit d6f304e6ca
13 changed files with 102 additions and 10 deletions

View File

@@ -7,6 +7,10 @@
#include "Cubed/gameplay/ecs/transform.hpp"
#include "Cubed/render/model_manager.hpp"
#include "Cubed/tools/cubed_assert.hpp"
#include "Cubed/tools/net_utils.hpp"
using namespace google::protobuf;
namespace Cubed {
ClientEntityManager::ClientEntityManager(ClientWorld& world) : m_world(world) {}
void ClientEntityManager::update() { handle_task(); }
@@ -20,8 +24,9 @@ void ClientEntityManager::init() {
});
}
// not thread safe
void ClientEntityManager::add_entity(EntityID id, std::string_view name,
const glm::vec3& pos) {
void ClientEntityManager::handle_entity_create(EntityID id,
std::string_view name,
const glm::vec3& pos) {
ASSERT(m_factories.contains(name));
m_factories[name](id);
acc a;
@@ -39,10 +44,28 @@ void ClientEntityManager::receive_entity_create(S2CEntityCreate& s2c) {
m_tasks.emplace(Command::CREATE, std::move(c));
}
void ClientEntityManager::destory(EntityID id) {
void ClientEntityManager::receive_entity_destory(EntityID id) {
m_tasks.emplace(Command::DESTORY, id);
}
void ClientEntityManager::destory(EntityID id) {
auto client = m_world.get_client();
Arena arena;
auto* msg = Arena::Create<C2SEntityDestoryRequest>(&arena);
msg->set_id(id);
msg->set_uuid(m_world.get_player().get_uuid());
client->send(make_packet(*msg));
}
void ClientEntityManager::create(std::string_view name, const glm::vec3& pos) {
auto client = m_world.get_client();
Arena arena;
auto* msg = Arena::Create<C2SEntityCreateRequest>(&arena);
msg->set_name(name);
msg->set_uuid(m_world.get_player().get_uuid());
Tools::set_net_pos(msg, pos);
client->send(make_packet(msg));
}
void ClientEntityManager::handle_task() {
TaskPair pair;
while (m_tasks.try_pop(pair)) {
@@ -50,7 +73,7 @@ void ClientEntityManager::handle_task() {
case Command::CREATE: {
auto* p = std::get_if<EntityCreateElement>(&pair.second);
ASSERT(p);
add_entity(p->id, p->name, p->pos);
handle_entity_create(p->id, p->name, p->pos);
} break;
case Command::DESTORY: {

View File

@@ -682,6 +682,9 @@ Config& ClientWorld::get_config() { return m_config; }
WorldScene& ClientWorld::world_scene() { return m_world_scene; }
ClientPlayerManager& ClientWorld::player_manager() { return m_player_manager; }
ClientEntityManager& ClientWorld::entity_manager() { return m_entity_manager; }
std::shared_ptr<NetworkClient> ClientWorld::get_client() const {
return m_client;
}
void ClientWorld::set_direct_exit() { m_exit_direct = true; }
void ClientWorld::request_exit() {
if (m_receive_exit) {

View File

@@ -153,7 +153,7 @@ asio::awaitable<void> NetworkClient::read_loop() {
case std::to_underlying(PacketEnum::S2C_ENTITY_DESTORY): {
auto* msg = Arena::Create<S2CEntityDestory>(&arena);
if (decode_packet(*msg, body_data, header)) {
m_world.entity_manager().destory(msg->id());
m_world.entity_manager().receive_entity_destory(msg->id());
}
} break;
}

View File

@@ -17,8 +17,9 @@ void ServerEntityManager::init() {
m_factories.try_emplace("cubed:pig", [this]() {
BaseServerCreature c;
c.hitbox = HitboxManager::instance().get_hitbox_id("cubed:pig");
return add_entity(Entity{m_next}, EntityInfo{"cubed:pig", ""},
std::move(c), PigTag{});
return create_entity_in_factory(Entity{m_next},
EntityInfo{"cubed:pig", ""},
std::move(c), PigTag{});
});
}
void ServerEntityManager::update() { handle_task(); }

View File

@@ -5,6 +5,7 @@
#include "Cubed/tools/cubed_assert.hpp"
#include "Cubed/tools/log.hpp"
#include "Cubed/tools/math_tools.hpp"
#include "Cubed/tools/net_utils.hpp"
#include "Cubed/tools/uuid.hpp"
#include <nlohmann/json.hpp>
@@ -872,6 +873,14 @@ void ServerWorld::handle_block_change(const BlockChangeReq& req) {
}
}
void ServerWorld::handle_entity_create(C2SEntityCreateRequest& req) {
m_entity_manager.add_entity(req.name(), Tools::get_net_pos(req.pos()));
}
void ServerWorld::handle_entity_destory(C2SEntityDestoryRequest& req) {
m_entity_manager.destory(req.id());
}
int ServerWorld::rendering_distance() const {
return m_rendering_distance.load();
}

View File

@@ -111,6 +111,20 @@ asio::awaitable<void> Session::read_loop() {
m_server_world.handle_voice_message(*msg);
}
}
if (cmd_id ==
std::to_underlying(PacketEnum::C2S_ENTITY_CREATE_REQUEST)) {
auto* msg = Arena::Create<C2SEntityCreateRequest>(&arena);
if (decode_packet(*msg, body_data, header)) {
m_server_world.handle_entity_create(*msg);
}
}
if (cmd_id ==
std::to_underlying(PacketEnum::C2S_ENTITY_DESTORY_REQUEST)) {
auto* msg = Arena::Create<C2SEntityDestoryRequest>(&arena);
if (decode_packet(*msg, body_data, header)) {
m_server_world.handle_entity_destory(*msg);
}
}
}
} catch (const asio::system_error& e) {
auto ec = e.code();

View File

@@ -10,4 +10,15 @@ message S2CEntityCreate {
message S2CEntityDestory {
uint64 id = 1;
}
message C2SEntityDestoryRequest {
string uuid = 1;
uint64 id = 2;
}
message C2SEntityCreateRequest {
string uuid = 1;
string name = 2;
Vec3 pos = 3;
}