#pragma once #include #include #include #include #include #include #include "copium/ecs/ComponentPool.h" #include "copium/ecs/Config.h" #include "copium/ecs/Signal.h" #include "copium/ecs/SystemPool.h" #include "copium/util/Common.h" #include "copium/util/GenericType.h" #include "copium/util/Trace.h" #include "copium/util/Uuid.h" namespace Copium { class ECSManager final { CP_DELETE_COPY_AND_MOVE_CTOR(ECSManager); private: std::unordered_set entities; std::map componentPools; std::map> systemPools; EntityId currentEntityId = 1; std::set destroyedEntityIds; std::map globalDatas; public: static std::vector emptyEntities; ECSManager(); ~ECSManager(); template SystemOrderer& AddSystem(const Uuid& systemPoolId) { auto it = systemPools.find(systemPoolId); if (it == systemPools.end()) it = systemPools.emplace(systemPoolId, std::make_unique(this)).first; return it->second->AddSystem(typeid(SystemClass), new SystemClass{}); } template void RemoveSystem(const Uuid& systemPoolId) { auto it = systemPools.find(systemPoolId); CP_ASSERT(it != systemPools.end(), "SystemPool doesn't exist with Uuid={}", systemPoolId); it->second->RemoveSystem(typeid(SystemClass)); if (it->second->IsEmpty()) systemPools.erase(it); } void CommitEntityUpdates(); void UpdateSystems(const Uuid& systemPoolId); template void SendSignal(const Args&... args) { Signal::ValidateSignal(); for (auto& systemPool : systemPools) { S* newSignal = new S{args...}; newSignal->uuid = S::UUID; systemPool.second->SendSignal(newSignal); } } EntityId CreateEntity(); void DestroyEntity(EntityId entity); size_t GetEntityCount() const; bool ValidEntity(EntityId entity); void Each(std::function function); template void SetComponentListener(const Args&... args) { using Component = typename Listener::component_type; auto pool = GetComponentPool(); Listener* listener = new Listener{args...}; listener->manager = this; if (pool) { pool->SetComponentListener(listener); } else { ComponentPool* pool{new ComponentPool{}}; componentPools.emplace(GetComponentId(), pool); pool->SetComponentListener(listener); } } template void AddComponents(EntityId entity, Components&&... components) { (AddComponent(entity, components), ...); } template void AddComponent(EntityId entity, Args&&... args) { AddComponent(entity, Component{args...}); } template void AddComponent(EntityId entity, const Component& component) { auto pool = GetComponentPool(); if (pool) { pool->Emplace(entity, component); } else { ComponentPool* pool{new ComponentPool{entity, component}}; auto ret = componentPools.emplace(GetComponentId(), pool); } } template void RemoveComponent(EntityId entity) { auto pool = GetComponentPoolAssure(); pool->Erase(entity); } template void RemoveComponents(EntityId entity) { (RemoveComponent(entity), ...); } template Component& GetComponent(EntityId entity) { auto pool = GetComponentPoolAssure(); Component* component = pool->FindComponent(entity); CP_ASSERT( component, "Entity did not contain component (entity={}, Component={})", entity, typeid(Component).name()); return *component; } template bool HasComponent(EntityId entity) { auto pool = GetComponentPool(); if (pool) return pool->Find(entity) != pool->Size(); return false; } template bool HasComponents(EntityId entity) { return (HasComponent(entity) && ...); } template bool HasAnyComponent(EntityId entity) { return (HasComponent(entity) || ...); } template void Each(Func function) { auto pool = GetComponentPool(); if (pool) { size_t i = 0; for (auto entity : pool->GetEntities()) { if (HasComponents(entity)) { std::apply(function, std::forward_as_tuple(entity, pool->At(i), GetComponent(entity)...)); } i++; } } } template void Each(std::function function) { auto pool = GetComponentPool(); if (pool) { size_t i = 0; for (auto e : pool->GetEntities()) { function(e, pool->At(i)); i++; } } } template EntityId Find(Func function) { auto pool = GetComponentPool(); if (pool) { size_t i = 0; for (auto entity : pool->GetEntities()) { if (HasComponents(entity)) { if (std::apply(function, std::forward_as_tuple(entity, pool->At(i), GetComponent(entity)...))) return entity; } i++; } } return 0; } template EntityId Find(std::function function) { auto pool = GetComponentPool(); if (pool) { size_t i = 0; for (auto e : pool->GetEntities()) { if (function(e, pool->At(i))) return e; i++; } } return 0; } template EntityId Find() { return Find([](EntityId, const Component& component, const Components&... components) { return true; }); } template std::type_index GetComponentId() { return std::type_index(typeid(T)); } template void AddGlobalData(const Args&... args) { auto it = globalDatas.find(typeid(T)); CP_ASSERT(!HasGlobalData(), "Global with typeid={} already exists. Do nothing", typeid(T).name()); globalDatas.emplace(typeid(T), GenericType::Create(args...)); } template T& GetGlobalData() { auto it = globalDatas.find(typeid(T)); CP_ASSERT(it != globalDatas.end(), "Global with typeid={} doesn't exist"); return it->second.Get(); } template bool HasGlobalData() { return globalDatas.find(typeid(T)) != globalDatas.end(); } template void RemoveGlobalData() { auto it = globalDatas.find(typeid(T)); CP_ASSERT("Global with typeid={} doesn't exist. Do nothing", typeid(T).name()); globalDatas.erase(it); } template ComponentPool>* GetComponentPool() { auto it = componentPools.find(GetComponentId()); return it == componentPools.end() ? nullptr : static_cast>*>(it->second); } template ComponentPool>* GetComponentPoolAssure() { auto it = componentPools.find(GetComponentId()); CP_ASSERT(it != componentPools.end(), "Component has not been added to an entity (Component={})", typeid(Component).name()); return static_cast>*>(it->second); } }; }