From d6f304e6cad44b4ff2856f05435beaae0a0b4d5c Mon Sep 17 00:00:00 2001 From: zhenyan121 <3367366583@qq.com> Date: Thu, 30 Jul 2026 15:53:09 +0800 Subject: [PATCH] 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. --- .../Cubed/gameplay/client_entity_manager.hpp | 9 ++++-- include/Cubed/gameplay/client_world.hpp | 1 + include/Cubed/gameplay/packet.hpp | 14 +++++++++ .../Cubed/gameplay/server_entity_manager.hpp | 3 +- include/Cubed/gameplay/server_world.hpp | 4 +++ include/Cubed/tools/net_utils.hpp | 6 ++++ src/gameplay/client_entity_manager.cpp | 31 ++++++++++++++++--- src/gameplay/client_world.cpp | 3 ++ src/gameplay/network_client.cpp | 2 +- src/gameplay/server_entity_manager.cpp | 5 +-- src/gameplay/server_world.cpp | 9 ++++++ src/gameplay/session.cpp | 14 +++++++++ src/proto/world/entity.proto | 11 +++++++ 13 files changed, 102 insertions(+), 10 deletions(-) diff --git a/include/Cubed/gameplay/client_entity_manager.hpp b/include/Cubed/gameplay/client_entity_manager.hpp index 39739d2..2485bc5 100644 --- a/include/Cubed/gameplay/client_entity_manager.hpp +++ b/include/Cubed/gameplay/client_entity_manager.hpp @@ -15,11 +15,13 @@ public: ClientEntityManager(ClientWorld& world); void update(); void init(); - // not thread safe - void add_entity(EntityID id, std::string_view name, const glm::vec3& pos); void receive_entity_create(S2CEntityCreate& s2c); + void receive_entity_destory(EntityID id); + void destory(EntityID id); + void create(std::string_view name, const glm::vec3& pos); + const entt::registry& get_registry() const; private: @@ -43,6 +45,9 @@ private: void handle_task(); void handle_entity_destory(EntityID id); + // not thread safe + void handle_entity_create(EntityID id, std::string_view name, + const glm::vec3& pos); template void create_entity_in_registry(EntityID id, Args&&... args) { { diff --git a/include/Cubed/gameplay/client_world.hpp b/include/Cubed/gameplay/client_world.hpp index cc858ed..9bdcf15 100644 --- a/include/Cubed/gameplay/client_world.hpp +++ b/include/Cubed/gameplay/client_world.hpp @@ -86,6 +86,7 @@ public: WorldScene& world_scene(); ClientPlayerManager& player_manager(); ClientEntityManager& entity_manager(); + std::shared_ptr get_client() const; void set_direct_exit(); void receive_chat_message(ChatMsg& msg); diff --git a/include/Cubed/gameplay/packet.hpp b/include/Cubed/gameplay/packet.hpp index f83fad3..c5f8e62 100644 --- a/include/Cubed/gameplay/packet.hpp +++ b/include/Cubed/gameplay/packet.hpp @@ -63,6 +63,8 @@ enum class PacketEnum : uint16_t { UPDATE_TIME = 3006, S2C_ENTITY_CREATE = 3007, S2C_ENTITY_DESTORY = 3008, + C2S_ENTITY_CREATE_REQUEST = 3009, + C2S_ENTITY_DESTORY_REQUEST = 3010, CHAT_MSG = 4001, VOICE_MSG = 4002, PING = 9001, @@ -119,6 +121,12 @@ template <> constexpr uint16_t get_packet_id() { template <> constexpr uint16_t get_packet_id() { return std::to_underlying(PacketEnum::S2C_ENTITY_DESTORY); } +template <> constexpr uint16_t get_packet_id() { + return std::to_underlying(PacketEnum::C2S_ENTITY_CREATE_REQUEST); +} +template <> constexpr uint16_t get_packet_id() { + return std::to_underlying(PacketEnum::C2S_ENTITY_DESTORY_REQUEST); +} template <> constexpr uint16_t get_packet_id() { return std::to_underlying(PacketEnum::UPDATE_TIME); } @@ -188,6 +196,12 @@ Packet make_packet(const T& msg) { return packet; } +template + requires std::derived_from +Packet make_packet(const T* msg) { + return make_packet(*msg); +} + inline PacketHeader decode_packet_header(std::span header) { if (header.size() < HEADER_LEN) throw std::runtime_error("Invalid header"); diff --git a/include/Cubed/gameplay/server_entity_manager.hpp b/include/Cubed/gameplay/server_entity_manager.hpp index e924ec5..3c95860 100644 --- a/include/Cubed/gameplay/server_entity_manager.hpp +++ b/include/Cubed/gameplay/server_entity_manager.hpp @@ -44,7 +44,8 @@ private: void handle_entity_destory(EntityID id); void handle_task(); void send_all_entities(std::shared_ptr& session); - template EntityID add_entity(Args&&... args) { + template + EntityID create_entity_in_factory(Args&&... args) { auto entity = m_registry.create(); ((m_registry.emplace>( diff --git a/include/Cubed/gameplay/server_world.hpp b/include/Cubed/gameplay/server_world.hpp index 971c282..d229e1f 100644 --- a/include/Cubed/gameplay/server_world.hpp +++ b/include/Cubed/gameplay/server_world.hpp @@ -86,6 +86,10 @@ public: void handle_chat_message(ChatMsg& msg); void handle_voice_message(VoiceMsg& msg); + + void handle_entity_create(C2SEntityCreateRequest& req); + void handle_entity_destory(C2SEntityDestoryRequest& req); + int chunk_size() const; std::vector> get_all_session() const; diff --git a/include/Cubed/tools/net_utils.hpp b/include/Cubed/tools/net_utils.hpp index c9a47ac..aa05e9f 100644 --- a/include/Cubed/tools/net_utils.hpp +++ b/include/Cubed/tools/net_utils.hpp @@ -11,5 +11,11 @@ template void set_net_pos(T ptr, const glm::vec3& pos) { p->set_y(pos.y); p->set_z(pos.z); } +inline glm::vec3 get_net_pos(const Vec3* p) { + return glm::vec3{p->x(), p->y(), p->z()}; +} +inline glm::vec3 get_net_pos(const Vec3& p) { + return glm::vec3{p.x(), p.y(), p.z()}; +} } // namespace Tools } // namespace Cubed \ No newline at end of file diff --git a/src/gameplay/client_entity_manager.cpp b/src/gameplay/client_entity_manager.cpp index 88a34f9..7dc0937 100644 --- a/src/gameplay/client_entity_manager.cpp +++ b/src/gameplay/client_entity_manager.cpp @@ -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(&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(&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(&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: { diff --git a/src/gameplay/client_world.cpp b/src/gameplay/client_world.cpp index 36a1107..abfc59d 100644 --- a/src/gameplay/client_world.cpp +++ b/src/gameplay/client_world.cpp @@ -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 ClientWorld::get_client() const { + return m_client; +} void ClientWorld::set_direct_exit() { m_exit_direct = true; } void ClientWorld::request_exit() { if (m_receive_exit) { diff --git a/src/gameplay/network_client.cpp b/src/gameplay/network_client.cpp index 066995b..82e65fe 100644 --- a/src/gameplay/network_client.cpp +++ b/src/gameplay/network_client.cpp @@ -153,7 +153,7 @@ asio::awaitable NetworkClient::read_loop() { case std::to_underlying(PacketEnum::S2C_ENTITY_DESTORY): { auto* msg = Arena::Create(&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; } diff --git a/src/gameplay/server_entity_manager.cpp b/src/gameplay/server_entity_manager.cpp index 97b59f7..0dc6d1b 100644 --- a/src/gameplay/server_entity_manager.cpp +++ b/src/gameplay/server_entity_manager.cpp @@ -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(); } diff --git a/src/gameplay/server_world.cpp b/src/gameplay/server_world.cpp index 53e84d1..13e02ba 100644 --- a/src/gameplay/server_world.cpp +++ b/src/gameplay/server_world.cpp @@ -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 @@ -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(); } diff --git a/src/gameplay/session.cpp b/src/gameplay/session.cpp index a610350..65128e1 100644 --- a/src/gameplay/session.cpp +++ b/src/gameplay/session.cpp @@ -111,6 +111,20 @@ asio::awaitable 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(&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(&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(); diff --git a/src/proto/world/entity.proto b/src/proto/world/entity.proto index d4b9575..14e1ee6 100644 --- a/src/proto/world/entity.proto +++ b/src/proto/world/entity.proto @@ -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; } \ No newline at end of file