From b166bab4f3d2251292ab0bb7f693f051a9bbec57 Mon Sep 17 00:00:00 2001 From: zhenyan121 <3367366583@qq.com> Date: Thu, 30 Jul 2026 15:05:56 +0800 Subject: [PATCH] feat(gameplay): implement entity system with concurrent task handling and fix model loading --- .../Cubed/gameplay/client_entity_manager.hpp | 11 ++++- .../Cubed/gameplay/server_entity_manager.hpp | 19 +++++++- src/gameplay/client_entity_manager.cpp | 17 ++++--- src/gameplay/client_world.cpp | 1 + src/gameplay/network_client.cpp | 3 +- src/gameplay/server_entity_manager.cpp | 44 +++++++++++++++++++ src/gameplay/server_world.cpp | 4 +- src/gameplay/session.cpp | 2 +- src/render/model_manager.cpp | 6 +-- src/render/world_renderer.cpp | 32 +++++++------- 10 files changed, 109 insertions(+), 30 deletions(-) diff --git a/include/Cubed/gameplay/client_entity_manager.hpp b/include/Cubed/gameplay/client_entity_manager.hpp index 81ee129..99d2e9b 100644 --- a/include/Cubed/gameplay/client_entity_manager.hpp +++ b/include/Cubed/gameplay/client_entity_manager.hpp @@ -20,6 +20,8 @@ public: void receive_entity_create(S2CEntityCreate& s2c); + const entt::registry& get_registry() const; + private: struct EntityCreateElement { EntityID id; @@ -41,7 +43,14 @@ private: void handle_task(); - template void add_entity(EntityID id, Args&&... args) { + template + void create_entity_in_registry(EntityID id, Args&&... args) { + { + cacc a; + if (m_entities.find(a, id)) { + return; + } + } auto entity = m_registry.create(); ((m_registry.emplace>( diff --git a/include/Cubed/gameplay/server_entity_manager.hpp b/include/Cubed/gameplay/server_entity_manager.hpp index 7186b14..a97ef57 100644 --- a/include/Cubed/gameplay/server_entity_manager.hpp +++ b/include/Cubed/gameplay/server_entity_manager.hpp @@ -4,30 +4,45 @@ #include #include +#include namespace Cubed { class ServerWorld; +class Session; class ServerEntityManager { public: ServerEntityManager(ServerWorld& world); void init(); + void update(); // not thread safe void add_entity(std::string_view name, const glm::vec3& pos); + void handle_player_login(std::shared_ptr session); + private: + enum class Command { CREATE, SEND_ALL_ENTITIES }; + struct EntityCreateElement { + std::string name; + glm::vec3 pos; + }; using EntityMap = tbb::concurrent_hash_map; using acc = EntityMap::accessor; using cacc = EntityMap::const_accessor; using CreateFunc = std::function; + using TaskElement = + std::variant, EntityCreateElement>; + using TaskPair = std::pair; ServerWorld& m_world; + tbb::concurrent_queue m_tasks; entt::registry m_registry; EntityID m_next = 0; EntityMap m_entities; std::unordered_map m_factories; - + void create_entity(std::string_view name, const glm::vec3& pos); void send_entity_create(EntityID id, std::string_view name, const glm::vec3& pos); - + void handle_task(); + void send_all_entities(std::shared_ptr& session); template EntityID add_entity(Args&&... args) { auto entity = m_registry.create(); diff --git a/src/gameplay/client_entity_manager.cpp b/src/gameplay/client_entity_manager.cpp index 4b2c36a..1cdc8a1 100644 --- a/src/gameplay/client_entity_manager.cpp +++ b/src/gameplay/client_entity_manager.cpp @@ -1,5 +1,6 @@ #include "Cubed/gameplay/client_entity_manager.hpp" +#include "Cubed/gameplay/client_world.hpp" #include "Cubed/gameplay/creatures/pig.hpp" #include "Cubed/gameplay/ecs/client_entity.hpp" #include "Cubed/gameplay/ecs/identity.hpp" @@ -9,24 +10,25 @@ namespace Cubed { ClientEntityManager::ClientEntityManager(ClientWorld& world) : m_world(world) {} void ClientEntityManager::update() { handle_task(); } + void ClientEntityManager::init() { m_factories.emplace("cubed:pig", [this](EntityID id) { BaseClientCreature c; c.model = ModelManager::instance().get_model_id("cubed:pig"); - add_entity(id, Entity{id}, EntityInfo{"cubed:pig", ""}, std::move(c), - PigTag{}); + create_entity_in_registry(id, Entity{id}, EntityInfo{"cubed:pig", ""}, + std::move(c), PigTag{}); }); } // not thread safe void ClientEntityManager::add_entity(EntityID id, std::string_view name, const glm::vec3& pos) { - + ASSERT(m_factories.contains(name)); m_factories[name](id); acc a; ASSERT(m_entities.find(a, id)); - auto* c = m_registry.try_get(a->second); + auto* c = m_registry.try_get(a->second); ASSERT(c); - c->position.value = pos; + c->transform.position.value = pos; } void ClientEntityManager::receive_entity_create(S2CEntityCreate& s2c) { @@ -44,10 +46,13 @@ void ClientEntityManager::handle_task() { case Command::CREATE: auto* p = std::get_if(&pair.second); ASSERT(p); - add_entity(p->id, p->name, p->pos); + break; } } } +const entt::registry& ClientEntityManager::get_registry() const { + return m_registry; +} } // namespace Cubed \ No newline at end of file diff --git a/src/gameplay/client_world.cpp b/src/gameplay/client_world.cpp index da45397..36a1107 100644 --- a/src/gameplay/client_world.cpp +++ b/src/gameplay/client_world.cpp @@ -731,6 +731,7 @@ void ClientWorld::send_chat_message(ChatMessage& message) { void ClientWorld::update(float delta_time) { m_player_manager.update(delta_time); + m_entity_manager.update(); { std::lock_guard lk(m_delete_vbo_mutex); m_pending_delete_vbo.clear(); diff --git a/src/gameplay/network_client.cpp b/src/gameplay/network_client.cpp index 914299d..971b874 100644 --- a/src/gameplay/network_client.cpp +++ b/src/gameplay/network_client.cpp @@ -93,7 +93,7 @@ asio::awaitable NetworkClient::read_loop() { } break; case std::to_underlying(PacketEnum::BLOCK_CHANGE_RSP): { auto* rsp = Arena::Create(&arena); - Logger::info("Client: Receive Block Change rsp"); + if (decode_packet(*rsp, body_data, header)) { m_world.receive_block_change(*rsp); } @@ -148,6 +148,7 @@ asio::awaitable NetworkClient::read_loop() { if (decode_packet(*msg, body_data, header)) { m_world.entity_manager().receive_entity_create(*msg); } + } break; } } diff --git a/src/gameplay/server_entity_manager.cpp b/src/gameplay/server_entity_manager.cpp index 5bb68fe..3bd6885 100644 --- a/src/gameplay/server_entity_manager.cpp +++ b/src/gameplay/server_entity_manager.cpp @@ -21,9 +21,39 @@ void ServerEntityManager::init() { std::move(c), PigTag{}); }); } +void ServerEntityManager::update() { handle_task(); } + +void ServerEntityManager::handle_task() { + TaskPair pair; + while (m_tasks.try_pop(pair)) { + switch (pair.first) { + case Command::CREATE: { + auto* c = std::get_if(&pair.second); + ASSERT(c); + create_entity(c->name, c->pos); + } break; + case Command::SEND_ALL_ENTITIES: { + auto* c = std::get_if>(&pair.second); + ASSERT(c); + send_all_entities(*c); + } break; + } + } +} void ServerEntityManager::add_entity(std::string_view name, const glm::vec3& pos) { + m_tasks.emplace(Command::CREATE, + EntityCreateElement{std::string(name), pos}); +} + +void ServerEntityManager::handle_player_login( + std::shared_ptr session) { + m_tasks.emplace(Command::SEND_ALL_ENTITIES, std::move(session)); +} + +void ServerEntityManager::create_entity(std::string_view name, + const glm::vec3& pos) { ASSERT(m_factories.contains(name)); auto e = m_factories[name](); acc c; @@ -35,6 +65,20 @@ void ServerEntityManager::add_entity(std::string_view name, send_entity_create(e, name, pos); } +void ServerEntityManager::send_all_entities(std::shared_ptr& session) { + auto view = m_registry.view(); + for (auto& entity : view) { + auto [e, info, base] = + view.get(entity); + Arena arena; + auto* s2c = Arena::Create(&arena); + s2c->set_id(e.id); + s2c->set_name(info.name); + Tools::set_net_pos(s2c, base.transform.position.value); + session->send(make_packet(*s2c)); + } +} + void ServerEntityManager::send_entity_create(EntityID id, std::string_view name, const glm::vec3& pos) { auto sessions = m_world.get_all_session(); diff --git a/src/gameplay/server_world.cpp b/src/gameplay/server_world.cpp index 48709ae..53e84d1 100644 --- a/src/gameplay/server_world.cpp +++ b/src/gameplay/server_world.cpp @@ -178,7 +178,7 @@ void ServerWorld::send_chunk(int task_id, const std::string& uuid, void ServerWorld::init_world() { m_entity_manager.init(); - + m_entity_manager.add_entity("cubed:pig", {0, 90, 0}); register_timer("player disconnect", 5, [this]() { std::vector disconnect; { @@ -527,6 +527,7 @@ void ServerWorld::hot_reload() { void ServerWorld::update() { // poll_finished_chunks(); + m_entity_manager.update(); { bool consumed = false; std::unique_ptr chunk; @@ -706,6 +707,7 @@ void ServerWorld::handle_player_login(const std::string& name, boardcast_message("Server", std::format("Player {} Join Game", name), Color::YELLOW, true); + m_entity_manager.handle_player_login(session); } void ServerWorld::handle_player_exit(const std::string& uuid) { diff --git a/src/gameplay/session.cpp b/src/gameplay/session.cpp index 615b794..a610350 100644 --- a/src/gameplay/session.cpp +++ b/src/gameplay/session.cpp @@ -82,7 +82,7 @@ asio::awaitable Session::read_loop() { } if (cmd_id == std::to_underlying(PacketEnum::BLOCK_CHANGE_REQ)) { auto* req = Arena::Create(&arena); - Logger::info("Session: Receive Block Change req"); + if (decode_packet(*req, body_data, header)) { m_server_world.handle_block_change(*req); } diff --git a/src/render/model_manager.cpp b/src/render/model_manager.cpp index 0201783..27da052 100644 --- a/src/render/model_manager.cpp +++ b/src/render/model_manager.cpp @@ -34,7 +34,7 @@ ModelManager::Handle ModelManager::get_model(const std::string& model_name) { ModelManager::Handle ModelManager::get_model(ModelID id) { ModelMap::const_accessor cacc; - if (!m_models.find(cacc, id)) { + if (m_models.find(cacc, id)) { return {cacc->second, cacc->first}; } return load_model(get_model_name(id)); @@ -73,10 +73,10 @@ ModelManager::Handle ModelManager::load_model(std::string_view model_name) { } std::string path; if (space[0] == "cubed") { - path = std::format("{}model/creature/{}/{}.gbl", ASSETS_PATH, space[1], + path = std::format("{}model/creature/{}/{}.glb", ASSETS_PATH, space[1], space[1]); } else { - path = std::format("./{}/model/creature/{}/{}.gbl", space[0], space[1], + path = std::format("./{}/model/creature/{}/{}.glb", space[0], space[1], space[1]); } auto model = m_loader.load(path); diff --git a/src/render/world_renderer.cpp b/src/render/world_renderer.cpp index 20629e8..9920e9c 100644 --- a/src/render/world_renderer.cpp +++ b/src/render/world_renderer.cpp @@ -3,6 +3,7 @@ #include "Cubed/camera.hpp" #include "Cubed/debug_collector.hpp" #include "Cubed/gameplay/client_world.hpp" +#include "Cubed/gameplay/ecs/client_entity.hpp" #include "Cubed/render/renderer.hpp" #include "Cubed/render/renderer_constants.hpp" #include "Cubed/scene/world_scene.hpp" @@ -294,14 +295,15 @@ void WorldRenderer::shadow_entity(ClientWorld& world, auto& shader = m_renderer.get_shader("depth_model"); shader.use(); shader.set_loc("lightSpaceMatrix", light_matrix); - /* - auto& registry = world.get_registry(); -auto view = registry.view(); -for (auto entity : view) { -auto [transform, model] = view.get(entity); -m_renderer.model_renderer().shadow_pass(model.name, transform.pos, - world.world_scene().camera()); -}*/ + glEnable(GL_DEPTH_TEST); + auto& registry = world.entity_manager().get_registry(); + auto view = registry.view(); + for (auto entity : view) { + auto& creature = view.get(entity); + m_renderer.model_renderer().shadow_pass( + creature.model, creature.transform.position.value, + world.world_scene().camera()); + } m_player_renderer.render(shader, world, true); } @@ -714,15 +716,15 @@ void WorldRenderer::render_entity(ClientWorld& world) { m_depth_map_texture->bind(0); glEnable(GL_DEPTH_TEST); - /* - auto& registry = world.get_registry(); - auto view = registry.view(); + + auto& registry = world.entity_manager().get_registry(); + auto view = registry.view(); for (auto entity : view) { - auto [transform, model] = view.get(entity); - m_renderer.model_renderer().render_model(model.name, transform.pos, - world.world_scene().camera()); + auto& creature = view.get(entity); + m_renderer.model_renderer().render_model( + creature.model, creature.transform.position.value, + world.world_scene().camera()); } - */ m_player_renderer.render(shader, world, false); }