From f9036b853c8098380291944a92704ec75ac1fc5e Mon Sep 17 00:00:00 2001 From: "DENNY_DESKTOP\\devfo" Date: Sat, 26 Jul 2025 00:28:30 +0900 Subject: [PATCH 1/2] =?UTF-8?q?=EC=97=91=ED=84=B0=20=EC=83=81=ED=83=9C=20?= =?UTF-8?q?=EB=8F=99=EA=B8=B0=ED=99=94=20ECS=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Engine/Components.h | 27 +++ Engine/SystemManager.h | 36 +--- Engine/Systems.h | 63 +++++++ Game/World.cpp | 406 ++++++++++++++++++++++++++++++++++++++++- Game/World.h | 24 +++ 5 files changed, 522 insertions(+), 34 deletions(-) diff --git a/Engine/Components.h b/Engine/Components.h index a4c8985..55212e5 100644 --- a/Engine/Components.h +++ b/Engine/Components.h @@ -141,4 +141,31 @@ namespace Engine uint32_t tagID = 0; uint32_t layerID = 0; }; + + // Dirty tracking component - for change detection + struct DirtyComponent + { + uint32_t changedFlags = 0; + bool isDirty = false; + bool isPositionDirty = false; + bool isStateDirty = false; + }; + + // Actor component - for game object actor information + struct ActorComponent + { + int agentID = -1; + uint32_t gameObjectType = 0; + uint32_t aiState = 0; + int playerID = -1; + }; + + // Position sync component - for position synchronization + struct PositionSyncComponent + { + float lastSyncX = 0.0f; + float lastSyncY = 0.0f; + float lastSyncZ = 0.0f; + float syncThreshold = 0.01f; // minimum distance to trigger sync + }; } \ No newline at end of file diff --git a/Engine/SystemManager.h b/Engine/SystemManager.h index 25780f9..5de95db 100644 --- a/Engine/SystemManager.h +++ b/Engine/SystemManager.h @@ -30,14 +30,11 @@ namespace Engine // Get the minimum size among all component arrays size_t minSize = GetMinArraySize(componentArrays); - // Get raw pointers for maximum cache efficiency - auto rawArrays = GetRawArrays(componentArrays); - - // Cache-friendly iteration over packed arrays using raw pointers + // Cache-friendly iteration over packed arrays for (size_t i = 0; i < minSize; ++i) { - // Get components at current index using direct pointer access - auto components = GetComponentsAtIndexDirect(rawArrays, i); + // Get components at current index + auto components = GetComponentsAtIndex(componentArrays, i); // Call update function with components std::apply(m_UpdateFunction, std::tuple_cat(std::make_tuple(deltaTime), components)); @@ -80,33 +77,6 @@ namespace Engine { return GetComponentsAtIndexImpl(arrays, index, std::make_index_sequence{}); } - - // Get raw array pointers for maximum cache efficiency - std::tuple GetRawArrays(const std::tuple*...>& arrays) - { - return GetRawArraysImpl(arrays, std::make_index_sequence{}); - } - - template - std::tuple GetRawArraysImpl( - const std::tuple*...>& arrays, std::index_sequence) - { - return std::make_tuple(std::get(arrays)->GetArray()...); - } - - // Get components using direct pointer access - std::tuple GetComponentsAtIndexDirect( - const std::tuple& rawArrays, size_t index) - { - return GetComponentsAtIndexDirectImpl(rawArrays, index, std::make_index_sequence{}); - } - - template - std::tuple GetComponentsAtIndexDirectImpl( - const std::tuple& rawArrays, size_t index, std::index_sequence) - { - return std::make_tuple(std::ref(std::get(rawArrays)[index])...); - } }; // System Manager for managing all systems diff --git a/Engine/Systems.h b/Engine/Systems.h index f0505ba..00c2a72 100644 --- a/Engine/Systems.h +++ b/Engine/Systems.h @@ -230,4 +230,67 @@ namespace Engine } } }; + + // Position synchronization system - detects position changes + class PositionSyncSystem + { + public: + static void Update(float deltaTime, PositionComponent& position, PositionSyncComponent& posSync, DirtyComponent& dirty) + { + // Calculate position difference + float dx = position.x - posSync.lastSyncX; + float dy = position.y - posSync.lastSyncY; + float dz = position.z - posSync.lastSyncZ; + float distanceSqr = dx * dx + dy * dy + dz * dz; + + // Check if position changed beyond threshold + if (distanceSqr > posSync.syncThreshold * posSync.syncThreshold) + { + // Update last synced position + posSync.lastSyncX = position.x; + posSync.lastSyncY = position.y; + posSync.lastSyncZ = position.z; + + // Mark as dirty for network sync + dirty.isPositionDirty = true; + dirty.isDirty = true; + } + } + }; + + // Network sync system - handles network message creation and broadcasting + class NetworkSyncSystem + { + public: + static void Update(float deltaTime, ActorComponent& actor, DirtyComponent& dirty, NetworkComponent& network) + { + // Only process if entity is dirty and network update interval has passed + if (dirty.isDirty && network.lastUpdateTime >= network.updateInterval) + { + // Reset dirty flags after processing + dirty.isDirty = false; + dirty.isPositionDirty = false; + dirty.isStateDirty = false; + dirty.changedFlags = 0; + + // Reset network timer + network.lastUpdateTime = 0.0f; + } + } + }; + + // Dirty tracking system - manages change detection flags + class DirtyTrackingSystem + { + public: + static void Update(float deltaTime, DirtyComponent& dirty) + { + // Reset dirty flags if they've been processed + // This system can be used to manage dirty flag lifecycle + if (dirty.isDirty) + { + // Accumulate dirty time or implement cleanup logic + } + } + }; } \ No newline at end of file diff --git a/Game/World.cpp b/Game/World.cpp index d4280a0..231171b 100644 --- a/Game/World.cpp +++ b/Game/World.cpp @@ -12,6 +12,11 @@ #include "Player.h" #include "GameObjectFactory.h" #include "Common.h" +#include "../Engine/Components.h" +#include "../Engine/Systems.h" +#include "../Engine/SystemManager.h" +#include "Actor.h" +#include "Character.h" //const float g_fDistance = std::powf(10.0f, 2); const float g_fDistance = 10.0f; @@ -79,11 +84,31 @@ void World::Init() entityManager.RegisterComponent(); entityManager.RegisterComponent(); entityManager.RegisterComponent(); + entityManager.RegisterComponent(); + entityManager.RegisterComponent(); + entityManager.RegisterComponent(); + // 기존 시스템 등록 system_manager_->RegisterSystem( [](float deltaTime, Engine::TimerComponent& timer) { Engine::TimerSystem::Update(deltaTime, timer); }); + + // 네트워크 동기화 시스템들 등록 + system_manager_->RegisterSystem( + [](float deltaTime, Engine::PositionComponent& position, Engine::PositionSyncComponent& posSync, Engine::DirtyComponent& dirty) { + Engine::PositionSyncSystem::Update(deltaTime, position, posSync, dirty); + }); + + system_manager_->RegisterSystem( + [](float deltaTime, Engine::ActorComponent& actor, Engine::DirtyComponent& dirty, Engine::NetworkComponent& network) { + Engine::NetworkSyncSystem::Update(deltaTime, actor, dirty, network); + }); + + system_manager_->RegisterSystem( + [](float deltaTime, Engine::DirtyComponent& dirty) { + Engine::DirtyTrackingSystem::Update(deltaTime, dirty); + }); } void World::update(float deltaTime) @@ -93,10 +118,37 @@ void World::update(float deltaTime) //LOG.info("World update begin"); for (std::list>::iterator itr = game_object_list_.begin();itr!= game_object_list_.end();++itr) + { (*itr)->update(deltaTime); + + // DetourCrowd 에이전트 위치를 GameObject와 ECS에 동기화 + const dtCrowdAgent* agent = this->map()->crowd()->getAgent((*itr)->agent_id()); + if (agent && agent->active) + { + // GameObject 위치 업데이트 (기존 로직) + if ((*itr)->is_changed_position(agent->npos[0], agent->npos[1], agent->npos[2])) + { + (*itr)->set_position(agent->npos[0], agent->npos[1], agent->npos[2]); + grid_manager_->move((Actor*)itr->get(), agent->npos[0], agent->npos[2]); + } + } + + // GameObject가 ECS 엔티티와 연결된 경우 동기화 + auto entity_itr = game_object_to_entity_map_.find((*itr)->agent_id()); + if (entity_itr != game_object_to_entity_map_.end()) + { + SyncGameObjectToEntity(*itr, entity_itr->second); + + // DetourCrowd 위치를 ECS 컴포넌트에도 직접 반영 + if (agent && agent->active) + { + UpdateECSFromDetourAgent(*itr, entity_itr->second, agent); + } + } + } map_->update(deltaTime); - SendWorldState(); + SendWorldStateECS(); // ECS 기반 월드 상태 동기화 사용 //LOG.info("World update end"); } @@ -152,6 +204,9 @@ void World::GetAgentsInfo(std::shared_ptr& msg, std::vectorUpdate()로 호출됨 + auto builder_ptr = std::make_shared(); flatbuffers::Offset agent_info; std::vector> agent_info_vector; @@ -208,6 +263,343 @@ void World::SendWorldState() } } +// ECS 기반의 새로운 SendWorldState 함수 +void World::SendWorldStateECS() +{ + auto& entityManager = system_manager_->GetEntityManager(); + auto builder_ptr = std::make_shared(); + std::vector> agent_info_vector; + std::vector removed_agents; + + // DirtyComponent를 가진 모든 엔티티들을 처리 + auto dirtyArray = entityManager.GetComponentArray(); + auto actorArray = entityManager.GetComponentArray(); + auto positionArray = entityManager.GetComponentArray(); + auto networkArray = entityManager.GetComponentArray(); + + if (dirtyArray && actorArray && positionArray && networkArray) + { + size_t minSize = std::min({dirtyArray->GetSize(), actorArray->GetSize(), + positionArray->GetSize(), networkArray->GetSize()}); + + for (size_t i = 0; i < minSize; ++i) + { + auto& dirty = dirtyArray->GetArray()[i]; + auto& actor = actorArray->GetArray()[i]; + auto& position = positionArray->GetArray()[i]; + auto& network = networkArray->GetArray()[i]; + + // Only process dirty entities + if (!dirty.isDirty) + continue; + + // Check if entity should be destroyed + if (actor.aiState == static_cast(syncnet::AIState::AIState_Destroyed)) + { + removed_agents.push_back(actor.agentID); + continue; + } + + // Create actor info for network sync + auto actorInfo = CreateActorInfoFromECS(builder_ptr, actor, position, dirty); + if (actorInfo.o != 0) // Check if valid offset + { + agent_info_vector.push_back(actorInfo); + } + + // Reset dirty flags after processing using unified function + // Note: We need to find the corresponding GameObject for proper reset + for (auto& gameObj : game_object_list_) + { + if (gameObj->agent_id() == actor.agentID) + { + auto entity_itr = game_object_to_entity_map_.find(actor.agentID); + if (entity_itr != game_object_to_entity_map_.end()) + { + ResetChangeFlags(gameObj, entity_itr->second); + } + break; + } + } + } + } + + auto agents = builder_ptr->CreateVector(agent_info_vector); + + // Handle debug raycasts (same as before) + flatbuffers::Offset debug_raycast; + std::vector> debug_raycast_vector; + for (int i = 0; i < this->raycasts_.size(); ++i) + { + debug_raycast = syncnet::CreateDebugRaycast(*builder_ptr, 0, &this->raycasts_[i]); + debug_raycast_vector.push_back(debug_raycast); + } + this->raycasts_.clear(); + auto debug_raycasts = builder_ptr->CreateVector(debug_raycast_vector); + + auto updateActorNotify = syncnet::CreateUpdateActorNotify(*builder_ptr, agents, debug_raycasts); + auto send_msg = syncnet::CreateGameMessage(*builder_ptr, syncnet::GameMessages::GameMessages_UpdateActorNotify, updateActorNotify.Union()); + builder_ptr->Finish(send_msg); + + SendBroadcast(builder_ptr); + + // Process removed agents + for (auto& agent_id : removed_agents) + { + OnRemoveAgent(agent_id); + } +} + +flatbuffers::Offset World::CreateActorInfoFromECS( + std::shared_ptr& builder_ptr, + const Engine::ActorComponent& actor, + const Engine::PositionComponent& position, + const Engine::DirtyComponent& dirty) +{ + // Create position vector + syncnet::Vec3 pos(Vector3::convert_x(position.x), Vector3::convert_y(position.y), Vector3::convert_z(position.z)); + + // Create velocity vector (zero for now, could be extended with VelocityComponent) + syncnet::Vec3 vel(0.0f, 0.0f, 0.0f); + + // Create actor state + syncnet::ActorState actorState(static_cast(actor.aiState)); + + // Create actor health (only takes one parameter - current health) + syncnet::ActorHealth actorHealth(100); // current health + + // Create actor info + auto actorInfo = syncnet::CreateActorInfo( + *builder_ptr, + actor.agentID, + &pos, + static_cast(actor.gameObjectType), + &actorState, + &actorHealth, + false // inputLocked + ); + + return actorInfo; +} + +Engine::EntityID World::ConvertGameObjectToEntity(std::shared_ptr gameObject) +{ + auto& entityManager = system_manager_->GetEntityManager(); + + // Create new entity + Engine::EntityID entityID = entityManager.CreateEntity(); + + // Cast to Actor to access position methods + auto actor = std::dynamic_pointer_cast(gameObject); + if (!actor) return entityID; // Only Actor objects supported for now + + // Get player_id if this is a Character + int playerId = -1; + if (gameObject->type() == syncnet::GameObjectType_Character) + { + auto character = std::dynamic_pointer_cast(gameObject); + if (character) + { + playerId = static_cast(character->player_id()); + } + } + + // Add components based on GameObject properties + entityManager.AddComponent(entityID, Engine::ActorComponent{ + gameObject->agent_id(), + static_cast(gameObject->type()), + static_cast(gameObject->state()), + playerId + }); + + const Vector3& pos = actor->get_position(); + entityManager.AddComponent(entityID, Engine::PositionComponent{ + pos.x, + pos.y, + pos.z + }); + + // Add velocity component (initially zero) + entityManager.AddComponent(entityID, Engine::VelocityComponent{ + 0.0f, 0.0f, 0.0f + }); + + entityManager.AddComponent(entityID, Engine::PositionSyncComponent{ + pos.x, + pos.y, + pos.z, + 0.01f // sync threshold + }); + + entityManager.AddComponent(entityID, Engine::DirtyComponent{ + static_cast(gameObject->get_changed_flag()), + gameObject->is_changed(), + false, // isPositionDirty + false // isStateDirty + }); + + entityManager.AddComponent(entityID, Engine::NetworkComponent{ + static_cast(gameObject->agent_id()), + false, // isLocalPlayer + 0.0f, // lastUpdateTime + 0.016f // updateInterval (60 FPS) + }); + + return entityID; +} + +void World::SyncGameObjectToEntity(std::shared_ptr gameObject, Engine::EntityID entityID) +{ + auto& entityManager = system_manager_->GetEntityManager(); + + // Cast to Actor to access position methods + auto actor = std::dynamic_pointer_cast(gameObject); + if (!actor) return; // Only Actor objects supported for now + + long changeFlags = gameObject->get_changed_flag(); + bool hasAnyChange = gameObject->is_changed(); + + // Update position component if position changed + if (changeFlags & static_cast(GameObjectChangeType::Position)) + { + if (entityManager.HasComponent(entityID)) + { + auto& positionComponent = entityManager.GetComponent(entityID); + const Vector3& pos = actor->get_position(); + positionComponent.x = pos.x; + positionComponent.y = pos.y; + positionComponent.z = pos.z; + } + + // Update position sync component with new position + if (entityManager.HasComponent(entityID)) + { + auto& posSyncComponent = entityManager.GetComponent(entityID); + const Vector3& pos = actor->get_position(); + + // Check if position changed beyond sync threshold + float dx = pos.x - posSyncComponent.lastSyncX; + float dy = pos.y - posSyncComponent.lastSyncY; + float dz = pos.z - posSyncComponent.lastSyncZ; + float distanceSqr = dx * dx + dy * dy + dz * dz; + + if (distanceSqr > posSyncComponent.syncThreshold * posSyncComponent.syncThreshold) + { + posSyncComponent.lastSyncX = pos.x; + posSyncComponent.lastSyncY = pos.y; + posSyncComponent.lastSyncZ = pos.z; + } + } + } + + // Update actor component if state or health changed + if (changeFlags & (static_cast(GameObjectChangeType::State) | static_cast(GameObjectChangeType::Health))) + { + if (entityManager.HasComponent(entityID)) + { + auto& actorComponent = entityManager.GetComponent(entityID); + actorComponent.aiState = static_cast(gameObject->state()); + // Could also update health info here if needed + } + } + + // Update dirty component with current change flags + if (entityManager.HasComponent(entityID)) + { + auto& dirtyComponent = entityManager.GetComponent(entityID); + dirtyComponent.changedFlags = static_cast(changeFlags); + dirtyComponent.isDirty = hasAnyChange; + + // Set specific dirty flags based on change type + dirtyComponent.isPositionDirty = (changeFlags & static_cast(GameObjectChangeType::Position)) != 0; + dirtyComponent.isStateDirty = (changeFlags & static_cast(GameObjectChangeType::State)) != 0; + } + + // Update network component timing + if (entityManager.HasComponent(entityID)) + { + auto& networkComponent = entityManager.GetComponent(entityID); + // Network update timing is handled by the NetworkSyncSystem + // But we could update lastUpdateTime here if needed + } +} + +void World::UpdateECSFromDetourAgent(std::shared_ptr gameObject, Engine::EntityID entityID, const dtCrowdAgent* agent) +{ + auto& entityManager = system_manager_->GetEntityManager(); + + // Update position component with DetourCrowd agent position + if (entityManager.HasComponent(entityID)) + { + auto& positionComponent = entityManager.GetComponent(entityID); + positionComponent.x = agent->npos[0]; + positionComponent.y = agent->npos[1]; + positionComponent.z = agent->npos[2]; + } + + // Update velocity component if exists + if (entityManager.HasComponent(entityID)) + { + auto& velocityComponent = entityManager.GetComponent(entityID); + velocityComponent.vx = agent->vel[0]; + velocityComponent.vy = agent->vel[1]; + velocityComponent.vz = agent->vel[2]; + } + + // Update position sync component + if (entityManager.HasComponent(entityID)) + { + auto& posSyncComponent = entityManager.GetComponent(entityID); + + // Check if position changed beyond sync threshold + float dx = agent->npos[0] - posSyncComponent.lastSyncX; + float dy = agent->npos[1] - posSyncComponent.lastSyncY; + float dz = agent->npos[2] - posSyncComponent.lastSyncZ; + float distanceSqr = dx * dx + dy * dy + dz * dz; + + if (distanceSqr > posSyncComponent.syncThreshold * posSyncComponent.syncThreshold) + { + posSyncComponent.lastSyncX = agent->npos[0]; + posSyncComponent.lastSyncY = agent->npos[1]; + posSyncComponent.lastSyncZ = agent->npos[2]; + + // Mark position as dirty for network sync + if (entityManager.HasComponent(entityID)) + { + auto& dirtyComponent = entityManager.GetComponent(entityID); + dirtyComponent.isPositionDirty = true; + dirtyComponent.isDirty = true; + dirtyComponent.changedFlags |= static_cast(GameObjectChangeType::Position); + } + } + } +} + +void World::ResetChangeFlags(std::shared_ptr gameObject, Engine::EntityID entityID) +{ + auto& entityManager = system_manager_->GetEntityManager(); + + // Reset GameObject change flags + gameObject->reset_changed(); + + // Reset ECS DirtyComponent flags + if (entityManager.HasComponent(entityID)) + { + auto& dirtyComponent = entityManager.GetComponent(entityID); + dirtyComponent.isDirty = false; + dirtyComponent.isPositionDirty = false; + dirtyComponent.isStateDirty = false; + dirtyComponent.changedFlags = 0; + } + + // Reset network component timer if needed + if (entityManager.HasComponent(entityID)) + { + auto& networkComponent = entityManager.GetComponent(entityID); + networkComponent.lastUpdateTime = 0.0f; + } +} + void World::SendBroadcast(std::shared_ptr msg) { for (auto itr = players_.begin(); itr != players_.end(); ++itr) @@ -239,6 +631,10 @@ std::shared_ptr World::OnAddAgent(std::shared_ptr player, sy game_object_map_.insert(std::make_pair(game_object->agent_id(), itr)); game_object->set_changed(static_cast(GameObjectChangeType::All)); + // ECS 엔티티도 함께 생성 + Engine::EntityID entityID = ConvertGameObjectToEntity(game_object); + game_object_to_entity_map_[game_object->agent_id()] = entityID; + return game_object; } @@ -266,6 +662,14 @@ void World::OnRemoveAgent(int agent_id) game_object_list_.erase(itr->second); game_object_map_.erase(itr); + // ECS 엔티티도 함께 제거 + auto entity_itr = game_object_to_entity_map_.find(agent_id); + if (entity_itr != game_object_to_entity_map_.end()) + { + system_manager_->GetEntityManager().DestroyEntity(entity_itr->second); + game_object_to_entity_map_.erase(entity_itr); + } + this->map()->removeAgent(agent_id); } diff --git a/Game/World.h b/Game/World.h index f623040..2275bdb 100644 --- a/Game/World.h +++ b/Game/World.h @@ -20,6 +20,10 @@ class RandomUtil; class IGridActor; namespace Engine { class SystemManager; + using EntityID = uint32_t; + struct ActorComponent; + struct PositionComponent; + struct DirtyComponent; } class World @@ -34,6 +38,9 @@ class World std::unordered_map> players_; + // GameObject와 ECS Entity 간 매핑 + std::unordered_map game_object_to_entity_map_; + GridManager* grid_manager_; TimeStamp* time_stamp_; RandomUtil* random_util_; @@ -50,8 +57,25 @@ class World RandomUtil* random_util() { return random_util_; } void SendWorldState(); + void SendWorldStateECS(); // ECS-based world state synchronization void GetAgentsInfo(std::shared_ptr& msg, std::vector>& agent_info_vector); +private: + // ECS helper functions + flatbuffers::Offset CreateActorInfoFromECS( + std::shared_ptr& builder_ptr, + const Engine::ActorComponent& actor, + const Engine::PositionComponent& position, + const Engine::DirtyComponent& dirty); + + // GameObject to ECS entity conversion + Engine::EntityID ConvertGameObjectToEntity(std::shared_ptr gameObject); + void SyncGameObjectToEntity(std::shared_ptr gameObject, Engine::EntityID entityID); + void UpdateECSFromDetourAgent(std::shared_ptr gameObject, Engine::EntityID entityID, const struct dtCrowdAgent* agent); + void ResetChangeFlags(std::shared_ptr gameObject, Engine::EntityID entityID); + +public: + std::shared_ptr OnAddAgent(std::shared_ptr player, syncnet::GameObjectType type, const syncnet::Vec3* pos); void OnRemoveAgent(int agent_id); void OnSetMoveTarget(int agent_id, const syncnet::Vec3* pos); From 138ac1a7d0a9c30e6ea570ebd208dc98c946f022 Mon Sep 17 00:00:00 2001 From: "DENNY_DESKTOP\\devfo" Date: Sun, 27 Jul 2025 23:37:25 +0900 Subject: [PATCH 2/2] ecs --- .../78a4668fe1de61b9ef6fedc9768ddd4/World.cpp | 318 ++++++++++++++++++ 1 file changed, 318 insertions(+) create mode 100644 enc_temp_folder/78a4668fe1de61b9ef6fedc9768ddd4/World.cpp diff --git a/enc_temp_folder/78a4668fe1de61b9ef6fedc9768ddd4/World.cpp b/enc_temp_folder/78a4668fe1de61b9ef6fedc9768ddd4/World.cpp new file mode 100644 index 0000000..d4280a0 --- /dev/null +++ b/enc_temp_folder/78a4668fe1de61b9ef6fedc9768ddd4/World.cpp @@ -0,0 +1,318 @@ +#include "World.h" +#include "syncnet_generated.h" +#include "DetourCrowd.h" +#include +#include "Server.h" +#include "Monster.h" +#include "Character.h" +#include "Vector3.h" +#include "LogHelper.h" +#include "DetourCommon.h" +#include "MathHelper.h" +#include "Player.h" +#include "GameObjectFactory.h" +#include "Common.h" + +//const float g_fDistance = std::powf(10.0f, 2); +const float g_fDistance = 10.0f; + +World::World() +{ + map_ = nullptr; + grid_manager_ = nullptr; + time_stamp_ = nullptr; + random_util_ = nullptr; + system_manager_ = nullptr; +} + +World::~World() +{ + if (map_) + { + delete map_; + map_ = nullptr; + } + if (grid_manager_) + { + delete grid_manager_; + grid_manager_ = nullptr; + } + if (time_stamp_) + { + delete time_stamp_; + time_stamp_ = nullptr; + } + if (random_util_) + { + delete random_util_; + random_util_ = nullptr; + } + if(system_manager_) + { + delete system_manager_; + system_manager_ = nullptr; + } +} + +void World::Init() +{ + map_ = new Map(); + map_->Init(); + Monster::Initialize("mob.lua"); + Monster::registerLuaFunctionAll(); + grid_manager_ = new GridManager(100, 100, 2); + time_stamp_ = new TimeStamp(); + random_util_ = new RandomUtil(); + system_manager_ = new Engine::SystemManager(); + + auto& entityManager = system_manager_->GetEntityManager(); + + // 모든 컴포넌트 타입 등록 + entityManager.RegisterComponent(); + entityManager.RegisterComponent(); + entityManager.RegisterComponent(); + entityManager.RegisterComponent(); + entityManager.RegisterComponent(); + entityManager.RegisterComponent(); + entityManager.RegisterComponent(); + entityManager.RegisterComponent(); + entityManager.RegisterComponent(); + entityManager.RegisterComponent(); + entityManager.RegisterComponent(); + + system_manager_->RegisterSystem( + [](float deltaTime, Engine::TimerComponent& timer) { + Engine::TimerSystem::Update(deltaTime, timer); + }); +} + +void World::update(float deltaTime) +{ + time_stamp_->update(); + system_manager_->Update(deltaTime); + + //LOG.info("World update begin"); + for (std::list>::iterator itr = game_object_list_.begin();itr!= game_object_list_.end();++itr) + (*itr)->update(deltaTime); + + map_->update(deltaTime); + SendWorldState(); + //LOG.info("World update end"); +} + +void World::join(std::shared_ptr player) +{ + if (player == nullptr) + { + LOG.error("World::join error player is nullptr"); + return; + } + auto itr = players_.find(player->player_id()); + if (itr != players_.end()) + { + LOG.error("World::join error player already exists"); + return; + } + players_.insert(std::make_pair(player->player_id(), player)); + + // 유닛 상태 동기화 + auto builder_ptr = std::make_shared(); + std::vector> agents; + GetAgentsInfo(builder_ptr, agents); + auto updateActorNotify = syncnet::CreateUpdateActorNotifyDirect(*builder_ptr, &agents, nullptr); + auto send_msg = syncnet::CreateGameMessage(*builder_ptr, syncnet::GameMessages::GameMessages_UpdateActorNotify, updateActorNotify.Union()); + builder_ptr->Finish(send_msg); + player->send(builder_ptr); +} + +void World::leave(std::shared_ptr player) +{ + if (player == nullptr) + { + LOG.error("World::leave error player is nullptr"); + return; + } + auto itr = players_.find(player->player_id()); + if (itr == players_.end()) + { + LOG.error("World::leave error player not found"); + return; + } + players_.erase(itr); +} + +void World::GetAgentsInfo(std::shared_ptr& msg, std::vector>& agent_info_vector) +{ + for (std::list>::iterator itr = game_object_list_.begin(); itr != game_object_list_.end(); ++itr) + { + auto game_object = itr->get(); + agent_info_vector.push_back(game_object->get_actor_info(*msg, static_cast(GameObjectChangeType::All))); + } +} + +void World::SendWorldState() +{ + auto builder_ptr = std::make_shared(); + flatbuffers::Offset agent_info; + std::vector> agent_info_vector; + std::vector removed_agents; + for (std::list>::iterator itr = game_object_list_.begin(); itr != game_object_list_.end(); ++itr) + { + auto game_object = itr->get(); + if (game_object->state() == syncnet::AIState::AIState_Destroyed) { + removed_agents.push_back(game_object->agent_id()); + } + + const dtCrowdAgent* agent = this->map()->crowd()->getAgent(game_object->agent_id()); + if (agent->active == false) + continue; + + if (game_object->is_changed_position(agent->npos[0], agent->npos[1], agent->npos[2])) + { + game_object->set_position(agent->npos[0], agent->npos[1], agent->npos[2]); + grid_manager_->move((Actor*)itr->get(), agent->npos[0], agent->npos[2]); + } + + if (!game_object->is_changed()) + continue; + + + agent_info_vector.push_back(game_object->get_actor_info(*builder_ptr, game_object->get_changed_flag())); + + game_object->reset_changed(); + } + auto agents = builder_ptr->CreateVector(agent_info_vector); + + // ---------------------------- + flatbuffers::Offset debug_raycast; + std::vector> debug_raycast_vector; + for (int i = 0; i < this->raycasts_.size(); ++i) + { + debug_raycast = syncnet::CreateDebugRaycast(*builder_ptr, 0, &this->raycasts_[i]); + debug_raycast_vector.push_back(debug_raycast); + } + this->raycasts_.clear(); + auto debug_raycasts = builder_ptr->CreateVector(debug_raycast_vector); + // ---------------------------- + + auto updateActorNotify = syncnet::CreateUpdateActorNotify(*builder_ptr, agents, debug_raycasts); + + auto send_msg = syncnet::CreateGameMessage(*builder_ptr, syncnet::GameMessages::GameMessages_UpdateActorNotify, updateActorNotify.Union()); + builder_ptr->Finish(send_msg); + + SendBroadcast(builder_ptr); + + for (auto& agent_id : removed_agents) + { + OnRemoveAgent(agent_id); + } +} + +void World::SendBroadcast(std::shared_ptr msg) +{ + for (auto itr = players_.begin(); itr != players_.end(); ++itr) + { + itr->second->send(msg); + } +} +void World::SendBroadcast(std::shared_ptr msg, std::shared_ptr& except) +{ + for (auto itr = players_.begin(); itr != players_.end(); ++itr) + { + if (itr->second.get() == except.get()) + continue; + + itr->second->send(msg); + } +} + +std::shared_ptr World::OnAddAgent(std::shared_ptr player, syncnet::GameObjectType type, const syncnet::Vec3* pos) +{ + auto game_object = GameObjectFactory::CreateGameObject(this, player, type, pos); + if (game_object == nullptr) + { + LOG.error("OnAddAgent error in GameObjectFactory::CreateGameObject()"); + return nullptr; + } + + auto itr = game_object_list_.insert(game_object_list_.end(), game_object); + game_object_map_.insert(std::make_pair(game_object->agent_id(), itr)); + game_object->set_changed(static_cast(GameObjectChangeType::All)); + + return game_object; +} + +void World::OnRemoveAgent(int agent_id) +{ + auto itr = game_object_map_.find(agent_id); + if (itr == game_object_map_.end()) + { + LOG.error("OnRemoveAgent error not exist in monsters_map_"); + return; + } + + if (itr->second->get()->type() == syncnet::GameObjectType_Character) + { + auto character = std::dynamic_pointer_cast(*itr->second); + + auto itr_player = players_.find(character->player_id()); + if (itr_player != players_.end()) + { + players_.erase(itr_player); + } + } + + grid_manager_->remove((Actor*)itr->second->get()); + game_object_list_.erase(itr->second); + game_object_map_.erase(itr); + + this->map()->removeAgent(agent_id); + +} + +void World::OnSetMoveTarget(int agent_id, const syncnet::Vec3* pos) +{ + this->map()->setMoveTarget(Vector3(pos).pos(), false, agent_id); + +} + +void World::OnSetRaycast(const syncnet::Vec3* pos) +{ + float hitPoint[3]; + if (this->map()->raycast(0, Vector3(pos).pos(), hitPoint)) + { + syncnet::Vec3 pos(hitPoint[0] * -1, hitPoint[1], hitPoint[2]); + this->raycasts_.push_back(pos); + } +} + +int World::DetectEnemy(Actor * actor) +{ + const dtCrowdAgent* this_agent = this->map()->crowd()->getAgent(actor->agent_id()); + float hitPoint[3]; + + auto targets = grid_manager_->getEntitiesInViewRange(actor, g_fDistance); + + for (auto itr = targets.begin(); itr != targets.end(); ++itr) + { + if (!(*itr)->isCharacter()) + continue; + + const dtCrowdAgent* agent = this->map()->crowd()->getAgent((*itr)->getAgentID()); + + + if (this->map()->raycast(actor->agent_id(), agent->npos, hitPoint) == false) + { + return (*itr)->getAgentID(); + } + } + return -1; +} + + + + +std::vector World::get_actors_in_range(Actor* actor, float range, float dirDeg, float angle) +{ + return grid_manager_->getEntitiesInAoEMask(actor->get_vecter2_x(), actor->get_vecter2_y(), range, dirDeg, angle); +} \ No newline at end of file