diff --git a/CMakeLists.txt b/CMakeLists.txt index 9911fbd..45ab8bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.14 FATAL_ERROR) -project(UECS VERSION 0.11.4) +project(UECS VERSION 0.11.5) message(STATUS "[Project] ${PROJECT_NAME}") include(cmake/InitUCMake.cmake) diff --git a/include/UECS/ChunkView.h b/include/UECS/ChunkView.h index f76e049..d6c3c13 100644 --- a/include/UECS/ChunkView.h +++ b/include/UECS/ChunkView.h @@ -6,7 +6,6 @@ namespace Ubpa::UECS { class Archetype; struct Chunk; - // you shourld get singleton (registered in locator) by world class ChunkView { public: ChunkView(Archetype* archetype, size_t chunkIdx, Chunk* chunk) diff --git a/include/UECS/CmptPtr.h b/include/UECS/CmptPtr.h index d64aec9..c641b1e 100644 --- a/include/UECS/CmptPtr.h +++ b/include/UECS/CmptPtr.h @@ -25,6 +25,7 @@ namespace Ubpa::UECS { template auto As() const noexcept { + assert(type.GetAccessMode() == mode); if constexpr (mode == AccessMode::LAST_FRAME) return LastFrame{p}; else if constexpr (mode == AccessMode::WRITE) diff --git a/include/UECS/EntityMngr.h b/include/UECS/EntityMngr.h index b39f05d..072d0d0 100644 --- a/include/UECS/EntityMngr.h +++ b/include/UECS/EntityMngr.h @@ -41,6 +41,8 @@ namespace Ubpa::UECS { // use RTDCmptTraits void Attach(Entity, const CmptType* types, size_t num); + // if not exist cmpt, attach with Args... + // else return it directly template Cmpt* Emplace(Entity, Args&&...); @@ -85,6 +87,7 @@ namespace Ubpa::UECS { template Archetype* GetOrCreateArchetypeOf(); + // types not contain Entity Archetype* GetOrCreateArchetypeOf(const CmptType* types, size_t num); template diff --git a/include/UECS/Schedule.h b/include/UECS/Schedule.h index 3ebb1f5..4d069bf 100644 --- a/include/UECS/Schedule.h +++ b/include/UECS/Schedule.h @@ -25,6 +25,14 @@ namespace Ubpa::UECS { // schedule will be clear at the beginning of the next World::Update() class Schedule { public: + // Func's argument list: + // World* + // {LastFrame|Latest}> + // SingletonsView + // Entity + // size_t indexInQuery + // : {LastFrame|Write|Latest}... + // CmptsView template const SystemFunc* RegisterEntityJob( Func&&, @@ -34,6 +42,11 @@ namespace Ubpa::UECS { SingletonLocator = {} ); + // Func's argument list: + // World* + // {LastFrame|Latest}> + // SingletonsView + // ChunkView (necessary) template const SystemFunc* RegisterChunkJob( Func&&, @@ -41,8 +54,11 @@ namespace Ubpa::UECS { ArchetypeFilter = {}, SingletonLocator = {} ); - - // Mode::Job + + // Func's argument list: + // World* + // {LastFrame|Write|Latest}> + // SingletonsView template const SystemFunc* RegisterJob( Func&&, @@ -50,10 +66,10 @@ namespace Ubpa::UECS { SingletonLocator = {} ); - Schedule& LockFilter(std::string_view sys); - Schedule& Order(std::string_view x, std::string_view y); + Schedule& LockFilter(std::string_view sys); + Schedule& InsertAll(std::string_view sys, CmptType); Schedule& InsertAny(std::string_view sys, CmptType); Schedule& InsertNone(std::string_view sys, CmptType); diff --git a/include/UECS/SystemFunc.h b/include/UECS/SystemFunc.h index b425bb3..5b0cce5 100644 --- a/include/UECS/SystemFunc.h +++ b/include/UECS/SystemFunc.h @@ -13,17 +13,21 @@ namespace Ubpa::UECS { // [description] // system function registered by Schedule in ::OnUpdate(Schedule&) // name + query + function<...> - // name must be unique in global + // name('s hashcode) must be unique in global // query.filter can be change dynamically by other with Schedule // [system function kind] (distinguish by argument list) - // common : [World*], [{LastFrame|Write|Latest}Singleton], [SingletonsView] - // 1. per entity function - // * [Entity] - // * [size_t indexInQuery] - // * : {LastFrame|Write|Latest} - // * [CmptsView] - // 2. chunk: ChunkView - // 3. job + // common : World*, SingletonsView + // 1. Mode::Entity: per entity function + // * {LastFrame|Latest}> + // * Entity + // * size_t indexInQuery + // * : {LastFrame|Write|Latest}... + // * CmptsView + // 2. Mode::Chunk + // * {LastFrame|Latest}> + // * ChunkView (necessary) + // 3. Mode::Job + // * {LastFrame|Write|Latest}> class SystemFunc { public: enum class Mode { diff --git a/include/UECS/detail/Archetype.h b/include/UECS/detail/Archetype.h index 4fe5bd5..434c46d 100644 --- a/include/UECS/detail/Archetype.h +++ b/include/UECS/detail/Archetype.h @@ -41,7 +41,6 @@ namespace Ubpa::UECS { ~Archetype(); // Entity + Components - // without singleton std::tuple, std::vector>, std::vector> Locate(const CmptLocator& locator) const; @@ -64,6 +63,8 @@ namespace Ubpa::UECS { // size_t: index in archetype template std::tuple> Create(Entity); + + // use RTDCmptTraits's default constructor size_t Create(Entity); // return index in archetype @@ -100,6 +101,7 @@ namespace Ubpa::UECS { void SetLayout(); size_t Offsetof(CmptType type) const { return type2offset.find(type)->second; } + static bool NotContainEntity(const CmptType* types, size_t num); friend class EntityMngr; diff --git a/include/UECS/detail/Archetype.inl b/include/UECS/detail/Archetype.inl index e3d8b9f..d1a619f 100644 --- a/include/UECS/detail/Archetype.inl +++ b/include/UECS/detail/Archetype.inl @@ -8,7 +8,7 @@ namespace Ubpa::UECS { : types(GenCmptTypeSet()) { static_assert(IsSet_v>, - "Archetype::Archetype: must be different"); + "Archetype::Archetype: ... must be different"); cmptTraits.Register(); (cmptTraits.Register(), ...); SetLayout(); @@ -41,7 +41,7 @@ namespace Ubpa::UECS { static_assert((std::is_constructible_v &&...), "Archetype::Create: isn't constructible"); static_assert(IsSet_v>, - "Archetype::Create: must be different"); + "Archetype::Create: ... must be different"); size_t idx = RequestBuffer(); size_t idxInChunk = idx % chunkCapacity; diff --git a/include/UECS/detail/CmptTypeSet.h b/include/UECS/detail/CmptTypeSet.h index c5f74a3..fb14b46 100644 --- a/include/UECS/detail/CmptTypeSet.h +++ b/include/UECS/detail/CmptTypeSet.h @@ -17,10 +17,12 @@ namespace Ubpa::UECS { void Insert(const CmptType* types, size_t num); void Erase(const CmptType* types, size_t num); bool Contains(CmptType type) const; + bool Contains(const CmptType* types, size_t num) const; template bool Contains(const CmptTypeContainer& types) const; + bool ContainsAny(const CmptType* types, size_t num) const; template bool ContainsAny(const CmptTypeContainer& types) const; diff --git a/include/UECS/detail/CmptTypeSet.inl b/include/UECS/detail/CmptTypeSet.inl index fc0b6ff..423d7a8 100644 --- a/include/UECS/detail/CmptTypeSet.inl +++ b/include/UECS/detail/CmptTypeSet.inl @@ -15,6 +15,14 @@ namespace Ubpa::UECS { return data.find(type) != data.end(); } + inline bool CmptTypeSet::Contains(const CmptType* types, size_t num) const { + for (size_t i = 0; i < num; i++) { + if (!Contains(types[i])) + return false; + } + return true; + } + template bool CmptTypeSet::Contains(const CmptTypeContainer& types) const { for (const auto& type : types) { @@ -24,6 +32,14 @@ namespace Ubpa::UECS { return true; } + inline bool CmptTypeSet::ContainsAny(const CmptType* types, size_t num) const { + for (size_t i = 0; i < num; i++) { + if (Contains(types[i])) + return true; + } + return false; + } + template bool CmptTypeSet::ContainsAny(const CmptTypeContainer& types) const { if (types.empty()) diff --git a/include/UECS/detail/EntityMngr.inl b/include/UECS/detail/EntityMngr.inl index 3414072..98225f5 100644 --- a/include/UECS/detail/EntityMngr.inl +++ b/include/UECS/detail/EntityMngr.inl @@ -9,7 +9,7 @@ namespace Ubpa::UECS { template Archetype* EntityMngr::GetOrCreateArchetypeOf() { static_assert(IsSet_v>, - "EntityMngr::GetOrCreateArchetypeOf: must be different"); + "EntityMngr::GetOrCreateArchetypeOf: ... must be different"); const auto typeset = Archetype::GenCmptTypeSet(); auto target = ts2a.find(typeset); @@ -29,7 +29,7 @@ namespace Ubpa::UECS { template std::tuple EntityMngr::Create() { static_assert(IsSet_v>, - "EntityMngr::Create: must be different"); + "EntityMngr::Create: ... must be different"); Archetype* archetype = GetOrCreateArchetypeOf(); size_t entityIndex = RequestEntityFreeEntry(); EntityInfo& info = entityTable[entityIndex]; @@ -42,7 +42,9 @@ namespace Ubpa::UECS { template void EntityMngr::AttachWithoutInit(Entity e) { - assert(Exist(e)); + static_assert(IsSet_v>, + "EntityMngr::AttachWithoutInit: ... must be different"); + if (!Exist(e)) throw std::invalid_argument("Entity is invalid"); auto& info = entityTable[e.Idx()]; Archetype* srcArchetype = info.archetype; @@ -91,16 +93,15 @@ namespace Ubpa::UECS { template std::tuple EntityMngr::Attach(Entity e) { - static_assert((std::is_constructible_v &&...), + static_assert((std::is_default_constructible_v &&...), "EntityMngr::Attach: isn't default constructible"); - if (!Exist(e)) throw std::invalid_argument("Entity is invalid"); using CmptList = TypeList; const auto& cmptTypes = entityTable[e.Idx()].archetype->GetCmptTypeSet(); - std::array needAttach = { !cmptTypes.Contains(CmptType::Of)... }; + std::array needAttach = { !cmptTypes.Contains(CmptType::Of)... }; AttachWithoutInit(e); const auto& new_info = entityTable[e.Idx()]; - std::tuple cmpts{ new_info.archetype->At(new_info.idxInArchetype)... }; + std::tuple cmpts{ new_info.archetype->At(new_info.idxInArchetype)... }; ((std::get>(needAttach) ? new(std::get(cmpts))Cmpts : nullptr), ...); return cmpts; @@ -111,22 +112,17 @@ namespace Ubpa::UECS { static_assert(std::is_constructible_v || is_list_initializable_v, "EntityMngr::Emplace: isn't constructible/list_initializable with Args..."); - if (!Exist(e)) throw std::invalid_argument("EntityMngr::Emplace: Entity is invalid"); - bool needAttach = !entityTable[e.Idx()].archetype->GetCmptTypeSet().Contains(CmptType::Of); - if (needAttach) { + if (!Have(e, CmptType::Of)) { AttachWithoutInit(e); - const auto& info = entityTable[e.Idx()]; - Cmpt* cmpt = info.archetype->At(info.idxInArchetype); - return new(cmpt)Cmpt{ std::forward(args)... }; - } - else { - const auto& info = entityTable[e.Idx()]; - return info.archetype->At(info.idxInArchetype); + return new(Get(e))Cmpt{ std::forward(args)... }; } + else + return Get(e); } inline bool EntityMngr::Have(Entity e, CmptType type) const { + assert(!type.Is()); if (!Exist(e)) throw std::invalid_argument("EntityMngr::Have: Entity is invalid"); return entityTable[e.Idx()].archetype->GetCmptTypeSet().Contains(type); } @@ -134,12 +130,11 @@ namespace Ubpa::UECS { template Cmpt* EntityMngr::Get(Entity e) const { static_assert(!std::is_same_v, "EntityMngr::Get: != Entity"); - if (!Exist(e)) throw std::invalid_argument("EntityMngr::Get: Entity is invalid"); - const auto& info = entityTable[e.Idx()]; - return info.archetype->At(info.idxInArchetype); + return reinterpret_cast(Get(e, CmptType::Of).Ptr()); } inline CmptPtr EntityMngr::Get(Entity e, CmptType type) const { + assert(!type.Is()); if (!Exist(e)) throw std::invalid_argument("EntityMngr::Get: Entity is invalid"); const auto& info = entityTable[e.Idx()]; return { type, info.archetype->At(type, info.idxInArchetype) }; diff --git a/src/core/Archetype.cpp b/src/core/Archetype.cpp index ea22450..80256c1 100644 --- a/src/core/Archetype.cpp +++ b/src/core/Archetype.cpp @@ -3,6 +3,21 @@ using namespace Ubpa::UECS; using namespace std; +Archetype::~Archetype() { + for (const auto& type : types.data) { + size_t size = cmptTraits.Sizeof(type); + size_t offset = Offsetof(type); + for (size_t i = 0; i < entityNum; i++) { + size_t idxInChunk = i % chunkCapacity; + byte* buffer = chunks[i / chunkCapacity]->Data(); + byte* address = buffer + offset + idxInChunk * size; + cmptTraits.Destruct(type, address); + } + } + for (Chunk* chunk : chunks) + sharedChunkPool.Recycle(chunk); +} + void Archetype::SetLayout() { vector alignments; vector sizes; @@ -21,6 +36,8 @@ void Archetype::SetLayout() { } Archetype* Archetype::New(const CmptType* types, size_t num) { + assert(NotContainEntity(types, num)); + auto rst = new Archetype; rst->types.Insert(types, num); rst->types.data.insert(CmptType::Of); @@ -32,10 +49,8 @@ Archetype* Archetype::New(const CmptType* types, size_t num) { } Archetype* Archetype::Add(const Archetype* from, const CmptType* types, size_t num) { -#ifndef NDEBUG - for (size_t i = 0; i < num; i++) - assert(!from->types.Contains(types[i])); -#endif // !NDEBUG + assert(NotContainEntity(types, num)); + assert(!from->types.ContainsAny(types, num)); Archetype* rst = new Archetype; @@ -51,9 +66,8 @@ Archetype* Archetype::Add(const Archetype* from, const CmptType* types, size_t n } Archetype* Archetype::Remove(const Archetype* from, const CmptType* types, size_t num) { - for (size_t i = 0; i < num; i++) - assert(from->types.Contains(types[i])); - + assert(from->types.Contains(types, num)); + Archetype* rst = new Archetype; rst->types = from->types; @@ -93,21 +107,6 @@ size_t Archetype::Create(Entity e) { return idx; } -Archetype::~Archetype() { - for (const auto& type : types.data) { - size_t size = cmptTraits.Sizeof(type); - size_t offset = Offsetof(type); - for (size_t i = 0; i < entityNum; i++) { - size_t idxInChunk = i % chunkCapacity; - byte* buffer = chunks[i / chunkCapacity]->Data(); - byte* address = buffer + offset + idxInChunk * size; - cmptTraits.Destruct(type, address); - } - } - for (Chunk* chunk : chunks) - sharedChunkPool.Recycle(chunk); -} - size_t Archetype::RequestBuffer() { if (entityNum == chunks.size() * chunkCapacity) { auto chunk = sharedChunkPool.Request(); @@ -254,8 +253,18 @@ size_t Archetype::EntityNumOfChunk(size_t chunkIdx) const noexcept { } CmptTypeSet Archetype::GenCmptTypeSet(const CmptType* types, size_t num) { + assert(NotContainEntity(types, num)); CmptTypeSet typeset; typeset.Insert(types, num); typeset.data.insert(CmptType::Of); return typeset; } + + +bool Archetype::NotContainEntity(const CmptType* types, size_t num) { + for (size_t i = 0; i < num; i++) { + if (types[i].Is()) + return false; + } + return true; +} diff --git a/src/core/EntityMngr.cpp b/src/core/EntityMngr.cpp index 623044d..fc826f1 100644 --- a/src/core/EntityMngr.cpp +++ b/src/core/EntityMngr.cpp @@ -29,8 +29,6 @@ void EntityMngr::RecycleEntityEntry(Entity e) { } Archetype* EntityMngr::GetOrCreateArchetypeOf(const CmptType* types, size_t num) { - assert(IsSet(types, num)); - auto typeset = Archetype::GenCmptTypeSet(types, num); auto target = ts2a.find(typeset); if (target != ts2a.end()) @@ -48,8 +46,6 @@ Archetype* EntityMngr::GetOrCreateArchetypeOf(const CmptType* types, size_t num) } Entity EntityMngr::Create(const CmptType* types, size_t num) { - assert(IsSet(types, num)); - Archetype* archetype = GetOrCreateArchetypeOf(types, num); size_t entityIndex = RequestEntityFreeEntry(); EntityInfo& info = entityTable[entityIndex]; @@ -93,7 +89,7 @@ void EntityMngr::AttachWithoutInit(Entity e, const CmptType* types, size_t num) // move src to dst size_t dstIdxInArchetype = dstArchetype->RequestBuffer(); - auto srcCmptTraits = srcArchetype->GetRTSCmptTraits(); + const auto& srcCmptTraits = srcArchetype->GetRTSCmptTraits(); for (const auto& type : srcCmptTypeSet.data) { auto srcCmpt = srcArchetype->At(type, srcIdxInArchetype); auto dstCmpt = dstArchetype->At(type, dstIdxInArchetype); @@ -110,10 +106,6 @@ void EntityMngr::AttachWithoutInit(Entity e, const CmptType* types, size_t num) } void EntityMngr::Attach(Entity e, const CmptType* types, size_t num) { - assert(types != nullptr && num > 0); - assert(IsSet(types, num)); - if (!Exist(e)) throw std::invalid_argument("Entity is invalid"); - auto srcArchetype = entityTable[e.Idx()].archetype; AttachWithoutInit(e, types, num); const auto& new_info = entityTable[e.Idx()]; @@ -163,7 +155,7 @@ void EntityMngr::Detach(Entity e, const CmptType* types, size_t num) { // move src to dst size_t dstIdxInArchetype = dstArchetype->RequestBuffer(); - auto srcCmptTraits = srcArchetype->GetRTSCmptTraits(); + const auto& srcCmptTraits = srcArchetype->GetRTSCmptTraits(); for (const auto& type : srcCmptTypeSet.data) { auto srcCmpt = srcArchetype->At(type, srcIdxInArchetype); if (dstCmptTypeSet.Contains(type)) {