From 89a261e7a77803e0605c0d328e38f6b90fd8c8e7 Mon Sep 17 00:00:00 2001 From: muit Date: Fri, 13 Mar 2026 10:30:24 +0100 Subject: [PATCH 1/7] [UNSTABE] Added Modified, moving operations to IdOperations<>, renamed access to IdScope --- Include/Misc/PipeDebug.h | 10 +- Include/Pipe/Core/Templates.h | 282 +++++--- Include/Pipe/Core/TypeTraits.h | 46 +- Include/PipeECS.h | 1138 +++++++++++++++++++------------- Include/PipeECSFwd.h | 22 +- Include/PipeReflect.h | 6 +- Src/PipeECS.cpp | 100 +-- Tests/ECS/Access.spec.cpp | 26 +- Tests/ECS/Components.spec.cpp | 30 +- Tests/ECS/ECS.spec.cpp | 14 +- Tests/ECS/Filtering.spec.cpp | 24 +- Tests/ECS/Statics.spec.cpp | 12 +- 12 files changed, 1018 insertions(+), 692 deletions(-) diff --git a/Include/Misc/PipeDebug.h b/Include/Misc/PipeDebug.h index 4a9e9fa5..6cf49c36 100644 --- a/Include/Misc/PipeDebug.h +++ b/Include/Misc/PipeDebug.h @@ -176,14 +176,14 @@ namespace p DebugECSContext ecs; DebugReflectContext reflect; - EntityContext* ctx = nullptr; + IdContext* ctx = nullptr; bool initialized = false; bool isFirstDebug = true; DebugContext() = default; - DebugContext(EntityContext& ctx) : ctx{&ctx} {} + DebugContext(IdContext& ctx) : ctx{&ctx} {} }; bool BeginDebug(DebugContext& Context); @@ -221,7 +221,7 @@ namespace p // For internal use only - EntityContext& GetDebugCtx() + IdContext& GetDebugCtx() { return *currentContext->ctx; } @@ -552,7 +552,7 @@ namespace p #pragma region ECS i32 DebugECSInspector::uniqueIdCounter = 0; - using DrawNodeAccess = TAccessRef; + using DrawNodeAccess = TIdScopeRef; namespace details { bool ChooseTypePopup(const char* label, ImGuiTextFilter& filter, TypeId& selectedTypeId) @@ -1282,7 +1282,7 @@ namespace p return false; } - if (!P_EnsureMsg(context.ctx, "Debug Context does not contain a valid EntityContext.")) + if (!P_EnsureMsg(context.ctx, "Debug Context does not contain a valid IdContext.")) { return false; } diff --git a/Include/Pipe/Core/Templates.h b/Include/Pipe/Core/Templates.h index a67f6fb9..36ea8485 100644 --- a/Include/Pipe/Core/Templates.h +++ b/Include/Pipe/Core/Templates.h @@ -11,85 +11,6 @@ namespace p { - template - struct TTypeList - { - using type = TTypeList; - - static constexpr auto size = sizeof...(Type); - }; - - template - constexpr TTypeList operator+(TTypeList, TTypeList) - { - return {}; - } - - template - struct TTypeListIterator; - - template - struct TTypeListIterator> - : TTypeListIterator> - {}; - - template - struct TTypeListIterator<0u, TTypeList> - { - /*! @brief Searched type. */ - using type = Type; - }; - - template - struct TJoinList; - - template - struct TJoinList, TTypeList> - { - using Type = TTypeList; - }; - - template - struct TJoinList, BTypes...> - { - using Type = TTypeList; - }; - - template - using JoinList = TJoinList; - - - /** - * @brief Helper type. - * @tparam Index Index of the type to return. - * @tparam List Type list to search into. - */ - template - using TTypeListIndex = typename TTypeListIterator::type; - - - namespace Internal - { - template - struct TTypeListContains; - - template - struct TTypeListContains> - : std::disjunction...> - {}; - - template - struct TTypeListContains> : std::disjunction...> - {}; - } // namespace Internal - - template - constexpr bool ListContains() - { - return Internal::TTypeListContains::value; - } - - template struct TPair { @@ -198,7 +119,7 @@ namespace p using TTuple = std::tuple; - namespace Internal + namespace Detail { template struct TTupleContains; @@ -210,11 +131,208 @@ namespace p template struct TTupleContains> : std::disjunction...> {}; - } // namespace Internal + } // namespace Detail template constexpr bool TupleContains() { - return Internal::TTupleContains::value; + return Detail::TTupleContains::value; + } + + + template + struct TTypeList; + + namespace Detail + { + template + struct TIsTypeList : FalseType + {}; + template + struct TIsTypeList> : TrueType + {}; + + template + struct TTypeListUnique + { + using Type = ResultList; + }; + + template + struct TTypeListUnique + { + using NextList = Select(), ResultList, + typename ResultList::template Append>; + using Type = typename TTypeListUnique::Type; + }; + + // Used for when TypeList helpers need TyleList but it is not defined yet + template + struct TDummyList + { + using AsList = TTypeList; + }; + template + constexpr TDummyList operator+(TDummyList, TDummyList) + { + return {}; + } + }; // namespace Detail + + + template + concept IsTypeList = Detail::TIsTypeList::value; + + + template + struct TTypeList + { + using type = TTypeList; + + static constexpr auto size = sizeof...(T); + + + template + static consteval bool Contains() + { + return (std::is_same_v || ...); + } + + private: + template + struct TAppend + { + using Type = TTypeList; + }; + template + struct TAppend> + { + using Type = TTypeList; + }; + template + struct TPrepend + { + using Type = TTypeList; + }; + template + struct TPrepend> + { + using Type = TTypeList; + }; + + template + struct TAppendUnique : public Detail::TTypeListUnique, S...> + {}; + template + struct TAppendUnique> + : public Detail::TTypeListUnique, S...> + {}; + + template + struct TPrependUnique : public Detail::TTypeListUnique, T...> + {}; + template + struct TPrependUnique> + : public Detail::TTypeListUnique, T...> + {}; + + template typename Predicate, typename... Remaining> + struct TFilter + { + using type = TTypeList<>; + }; + + template + struct TFirst : TypeIdentity + {}; + template + struct TFirst : TypeIdentity + {}; + + + public: + template class W> + using Wrap = TTypeList...>; + + template class W> + using WrapPtr = TTypeList*...>; + + template class W> + using WrapConstPtr = TTypeList*...>; + + template class W> + using WrapRef = TTypeList&...>; + + template class W> + using WrapConstRef = TTypeList&...>; + + template typename Predicate> + using Filter = decltype(( + Detail::TDummyList<>{} + ... + + Select::value, Detail::TDummyList, Detail::TDummyList<>>{}))::AsList; + + template class M> + using Map = TTypeList::Type...>; + + template + using Append = TAppend::Type; + + template + using Prepend = TPrepend::Type; + + template + using AppendUnique = TAppendUnique::Type; + + template + using PrependUnique = TPrependUnique::Type; + + using Deduplicate = typename Detail::template TTypeListUnique, T...>::Type; + + // Conversion + template class U> + using To = U; + + template + using First = typename TFirst::Type; + + static constexpr auto Call(auto predicate) + { + return predicate.template operator()(); + } + static constexpr void ForEach(auto predicate) + { + (predicate.template operator()(), ...); + } + }; + + template + constexpr TTypeList operator+(TTypeList, TTypeList) + { + return {}; } + + + template + struct TTypeListIterator; + + template + struct TTypeListIterator> + : TTypeListIterator> + {}; + + template + struct TTypeListIterator<0u, TTypeList> + { + /*! @brief Searched type. */ + using Type = T; + }; + + + /** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ + template + using TTypeListIndex = typename TTypeListIterator::Type; } // namespace p diff --git a/Include/Pipe/Core/TypeTraits.h b/Include/Pipe/Core/TypeTraits.h index 52ec43fa..3cbafb2f 100644 --- a/Include/Pipe/Core/TypeTraits.h +++ b/Include/Pipe/Core/TypeTraits.h @@ -9,41 +9,61 @@ namespace p { + template + struct TypeIdentity + { + using Type = T; + }; + + template + struct Constant + { + static constexpr T value = v; + using ValueType = T; + using Type = Constant; + constexpr operator T() const noexcept + { + return value; + } + }; + using TrueType = Constant; + using FalseType = Constant; + namespace details { template - struct TIsRValueReference : std::false_type + struct TIsRValueReference : FalseType {}; template - struct TIsRValueReference : std::true_type + struct TIsRValueReference : TrueType {}; template - struct TIsLValueReference : std::false_type + struct TIsLValueReference : FalseType {}; template - struct TIsLValueReference : std::true_type + struct TIsLValueReference : TrueType {}; template - struct TIsChar : std::false_type + struct TIsChar : FalseType {}; template<> - struct TIsChar : std::true_type + struct TIsChar : TrueType {}; template<> - struct TIsChar : std::true_type + struct TIsChar : TrueType {}; template<> - struct TIsChar : std::true_type + struct TIsChar : TrueType {}; template<> - struct TIsChar : std::true_type + struct TIsChar : TrueType {}; // determine whether _Ty can be copy-initialized with {} template - struct TIsImplicitlyDefaultConstructible : std::false_type + struct TIsImplicitlyDefaultConstructible : FalseType {}; template @@ -51,7 +71,7 @@ namespace p template struct TIsImplicitlyDefaultConstructible({}))>> : std::true_type + std::void_t({}))>> : TrueType {}; } // namespace details @@ -294,10 +314,10 @@ namespace p template - struct HasInlineCapacityMember : std::false_type + struct HasInlineCapacityMember : FalseType {}; template - struct HasInlineCapacityMember : std::true_type + struct HasInlineCapacityMember : TrueType {}; template diff --git a/Include/PipeECS.h b/Include/PipeECS.h index dab482c1..2070f163 100644 --- a/Include/PipeECS.h +++ b/Include/PipeECS.h @@ -1,7 +1,6 @@ // Copyright 2015-2026 Piperift. All Rights Reserved. #pragma once -#include "Pipe/Core/Broadcast.h" #include "Pipe/Core/Map.h" #include "Pipe/Core/PageBuffer.h" #include "Pipe/Core/Templates.h" @@ -24,7 +23,7 @@ namespace p //////////////////////////////// // FORWARD DECLARATIONS // - struct EntityContext; + struct IdContext; //////////////////////////////// @@ -162,23 +161,43 @@ namespace p /** * Resolve an entity id from an string. * The expected format is {}:{} where first is the index and second is the version. - * If a context is provided, providing the index alone as a number will resolve it slast + * If a context is provided, providing the index alone as a number will resolve its last * valid version */ - P_API Id IdFromString(String str, EntityContext* context); + P_API Id IdFromString(String str, IdContext* context); #pragma endregion Ids #pragma region Components + // clang-format off + template + concept StoresLastModified = !IsEmpty && HasAnyTypeStaticFlags(TF_ECS_StoreLastModified) + && (IsCopyConstructible || IsMoveConstructible); + // clang-format on + + template + struct CMdfdWithLast + { + P_STRUCT(CMdfdWithLast, TF_NotSerialized) + + P_PROP(Last) + T Last; + }; + + struct CMdfdWithoutLast + {}; + /** * Modified component * Optionally, a component can be marked modified when written. This can be used when * filtering. */ template - struct CMdfd + struct CMdfd : public Select, CMdfdWithLast, CMdfdWithoutLast> { P_STRUCT(CMdfd, TF_NotSerialized) + + using Type = T; }; /** @@ -282,7 +301,7 @@ namespace p using Super = Reader; P_STRUCT(EntityReader) - EntityContext& context; + IdContext& context; // While serializing we create ids as Ids appear and link them. TArray ids; @@ -290,8 +309,7 @@ namespace p public: - EntityReader(const p::Reader& parent, EntityContext& context) - : Reader(parent), context{context} + EntityReader(const p::Reader& parent, IdContext& context) : Reader(parent), context{context} {} void SerializeEntities(p::TArray& entities, TFunction onReadPools); @@ -308,7 +326,7 @@ namespace p } const TArray& GetIds() const; - EntityContext& GetContext(); + IdContext& GetContext(); protected: TypeId ProvideTypeId() const override @@ -323,7 +341,7 @@ namespace p using Super = Writer; P_STRUCT(EntityWriter) - EntityContext& context; + IdContext& context; // While serializing we create ids as Ids appear and link them. TArray ids; @@ -332,9 +350,7 @@ namespace p public: - EntityWriter(const Writer& parent, EntityContext& context) - : Writer(parent), context{context} - {} + EntityWriter(const Writer& parent, IdContext& context) : Writer(parent), context{context} {} void SerializeEntities(const TArray& entities, TFunction onWritePools, bool includeChildren = true); @@ -544,9 +560,6 @@ namespace p i32 lastRemovedIndex = NO_INDEX; PoolRemovePolicy removePolicy; - TBroadcast> onAdd; - TBroadcast> onRemove; - ComponentPool(TypeId typeId, PoolRemovePolicy removePolicy, Arena& arena); ComponentPool(const ComponentPool& other); @@ -554,21 +567,6 @@ namespace p ComponentPool& operator=(const ComponentPool& other) noexcept; ComponentPool& operator=(ComponentPool&& other) noexcept; - void OnAdded(TView ids) - { - if (!ids.IsEmpty()) - { - onAdd.Broadcast(ids); - } - } - - void OnRemoved(TView ids) - { - if (!ids.IsEmpty()) - { - onRemove.Broadcast(ids); - } - } public: virtual ~ComponentPool() {} @@ -614,16 +612,6 @@ namespace p return idList; } - TBroadcast>& OnAdd() - { - return onAdd; - } - - TBroadcast>& OnRemove() - { - return onRemove; - } - protected: Index EmplaceId(const Id id, bool forceBack); @@ -648,7 +636,7 @@ namespace p public: - TPool(p::EntityContext& ctx, Arena& arena = GetCurrentArena()) + TPool(p::IdContext& ctx, Arena& arena = GetCurrentArena()) : ComponentPool(p::GetTypeId(), PoolRemovePolicy::InPlace, arena), data{arena} {} TPool(const TPool& other) : ComponentPool(other), data{*other.arena} @@ -736,10 +724,8 @@ namespace p { data.Reserve(index + 1u); T* const value = data.Insert(index, p::Forward(args)...); - OnAdded({id}); return *value; } - OnAdded({id}); } } @@ -777,7 +763,6 @@ namespace p } } } - OnAdded(ids); } template @@ -826,12 +811,6 @@ namespace p } ++from; } - OnAdded(ids); - } - - T& GetOrAdd(const Id id) requires(!p::IsEmpty) - { - return Has(id) ? Get(id) : Add(id); } bool Remove(Id id) override @@ -847,7 +826,6 @@ namespace p void RemoveUnsafe(Id id) override { P_Check(Has(id)); - OnRemoved({id}); if (removePolicy == PoolRemovePolicy::InPlace) { Pop(id); @@ -860,7 +838,6 @@ namespace p i32 Remove(TView ids) override { - OnRemoved(ids); i32 removed = 0; if (removePolicy == PoolRemovePolicy::InPlace) { @@ -889,7 +866,6 @@ namespace p void RemoveUnsafe(TView ids) override { - OnRemoved(ids); if (removePolicy == PoolRemovePolicy::InPlace) { for (Id id : ids) @@ -960,12 +936,20 @@ namespace p void Reserve(sizet size) { idList.Reserve(size); - if (size > Size()) + if constexpr (!p::IsEmpty) { - data.Reserve(size); + if (size > Size()) + { + data.Reserve(size); + } } } + void ReserveMore(sizet size) + { + Reserve(Size() + size); + } + void Shrink() { idList.Shrink(); @@ -1059,13 +1043,13 @@ namespace p template<> struct P_API TPool : public IPool { - friend EntityContext; + friend IdContext; using T = CRemoved; p::IdRegistry* idRegistry = nullptr; public: - TPool(p::EntityContext& ctx, Arena& arena = GetCurrentArena()); + TPool(p::IdContext& ctx, Arena& arena = GetCurrentArena()); TPool(const TPool& other) = delete; TPool(TPool&& other) = delete; TPool& operator=(const TPool& other) @@ -1159,6 +1143,247 @@ namespace p #pragma endregion Pools +//////////////////////////////// +// OPERATIONS +// +#pragma region Operations + /** + * IdOperations contains shared logic to view and edit components and statics in Contexts and + * Scopes This class assumes GetPool and AssurePool are present in Parent + */ + template + struct TIdOperations + { + const Parent& AsParent() const + { + return *static_cast(this); + } + + template + CopyConst>, Component>& AssurePool() const + { + return AsParent().AssurePool(); + } + + template + CopyConst>, Component>* GetPool() const + { + return AsParent().GetPool(); + } + + template + IdContext& GetContext() const + { + return AsParent().GetContext(); + } + + template + i32 Size() const + { + return GetPool()->Size(); + } + + bool IsValid(Id id) const + { + return GetContext().IsValid(id); + } + + template + bool Has(Id id) const requires(sizeof...(Component) >= 1) + { + return (GetPool()->Has(id) && ...); + } + + template + void MarkModified(TView ids, const TPool* pool = nullptr) const + { + auto& mdfdPool = AssurePool>(); + if constexpr (StoresLastModified) + { + if (!pool) + { + pool = GetPool(); + if (!pool) + { + return; + } + } + + mdfdPool.ReserveMore(ids.Size()); + for (Id id : ids) + { + if (!mdfdPool.Has(id)) // If we store value, we only add if it it wasn't + // modified already + { + Component* comp = pool->TryGet(id); + if constexpr (IsMoveConstructible()) + { + mdfdPool.Add( + id, comp ? CMdfd{p::Move(*comp)} : CMdfd{}); + } + else + { + mdfdPool.Add(id, comp ? CMdfd{*comp} : CMdfd{}); + } + } + } + } + else + { + mdfdPool.Add(ids.begin(), ids.end(), CMdfd{}); + } + } + + template + decltype(auto) Add(Id id, Component&& value = {}) const requires(IsMutable) + { + auto& pool = AssurePool(); + if constexpr (HasAnyTypeStaticFlags(TF_ECS_AutoModify)) + { + MarkModified(id, &pool); + } + return pool.Add(id, p::Forward(value)); + } + template + decltype(auto) Add(Id id, const Component& value) const requires(IsMutable) + { + auto& pool = AssurePool(); + if constexpr (HasAnyTypeStaticFlags(TF_ECS_AutoModify)) + { + MarkModified(id, &pool); + } + return pool.Add(id, value); + } + + // Add component to an entities (if they dont have it already) + template + void Add(Id id) const requires((IsMutable && ...) && sizeof...(Component) > 1) + { + (Add(id), ...); + } + + // Add component to many entities (if they dont have it already) + template + decltype(auto) AddN(TView ids, const Component& value = {}) const + { + auto& pool = AssurePool(); + if constexpr (HasAnyTypeStaticFlags(TF_ECS_AutoModify)) + { + MarkModified(ids, &pool); + } + return pool.Add(ids.begin(), ids.end(), value); + } + + template + void AddN(TView ids, const TView& values) + { + P_Check(ids.Size() == values.Size()); + auto& pool = AssurePool(); + if constexpr (HasAnyTypeStaticFlags(TF_ECS_AutoModify)) + { + MarkModified(ids, &pool); + } + pool.Add(ids.begin(), ids.end(), values.begin()); + } + + // Add components to many entities (if they dont have it already) + template + void AddN(TView ids) const + requires((IsMutable && ...) && sizeof...(Component) > 1) + { + (AddN(ids), ...); + } + + + template + void Remove(const Id id) const requires(IsMutable) + { + if (auto* pool = GetPool()) + { + if constexpr (HasAnyTypeStaticFlags(TF_ECS_AutoModify)) + { + MarkModified(id, pool); + } + pool->Remove(id); + } + } + template + void Remove(const Id id) const requires(sizeof...(Component) > 1) + { + (Remove(id), ...); + } + template + void Remove(TView ids) const requires(IsMutable) + { + if (auto* pool = GetPool()) + { + if constexpr (HasAnyTypeStaticFlags(TF_ECS_AutoModify)) + { + MarkModified(ids, pool); + } + pool->Remove(ids); + } + } + template + void Remove(TView ids) const requires(sizeof...(Component) > 1) + { + (Remove(ids), ...); + } + + template + Component& Get(Id id) const + { + auto* const pool = GetPool(); + P_Check(pool); + if constexpr (IsMutable + && HasAnyTypeStaticFlags(TF_ECS_AutoModify)) + { + MarkModified(id, pool); + } + return pool->Get(id); + } + + template + Component* TryGet(Id id) const + { + auto* const pool = GetPool(); + P_Check(pool); + Component* value = pool->TryGet(id); + if constexpr (IsMutable + && HasAnyTypeStaticFlags(TF_ECS_AutoModify)) + { + if (value) + { + MarkModified(id, pool); + } + } + return value; + } + + template + Component& GetOrAdd(Id id) const requires(IsMutable) + { + auto* pool = GetPool(); + if (pool->Has(id)) + { + if constexpr (HasAnyTypeStaticFlags(TF_ECS_AutoModify)) + { + MarkModified(id, pool); + } + return pool->Get(id); + } + return pool->Add(id); + } + + template + void ClearPool() const + { + (GetPool()->Clear(), ...); + } + }; +#pragma endregion Operations + + //////////////////////////////// // CONTEXT // @@ -1187,7 +1412,7 @@ namespace p } }; - struct P_API EntityContext + struct P_API IdContext /* : public TIdOperations*/ { private: IdRegistry idRegistry; @@ -1197,33 +1422,81 @@ namespace p public: - EntityContext(); - ~EntityContext() + IdContext(); + ~IdContext() { Reset(); } - explicit EntityContext(const EntityContext& other) noexcept; - explicit EntityContext(EntityContext&& other) noexcept; - EntityContext& operator=(const EntityContext& other) noexcept; - EntityContext& operator=(EntityContext&& other) noexcept; + explicit IdContext(const IdContext& other) noexcept; + explicit IdContext(IdContext&& other) noexcept; + IdContext& operator=(const IdContext& other) noexcept; + IdContext& operator=(IdContext&& other) noexcept; #pragma region Entities // Reflection helpers void* AddDefault(TypeId typeId, Id id); void Remove(TypeId typeId, Id id); + + template + void MarkModified(TView ids, const TPool* pool = nullptr) const + { + if (!pool) + { + pool = GetPool(); + if (!pool) + { + return; + } + } + + auto& mdfdPool = AssurePool>(); + if constexpr (StoresLastModified) + { + mdfdPool.ReserveMore(ids.Size()); + for (Id id : ids) + { + Component* comp = pool->TryGet(id); + if constexpr (IsMoveConstructible()) + { + mdfdPool.Add( + id, comp ? CMdfd{p::Move(*comp)} : CMdfd{}); + } + else + { + mdfdPool.Add(id, comp ? CMdfd{*comp} : CMdfd{}); + } + } + } + else + { + mdfdPool.Add(ids.begin(), ids.end(), CMdfd{}); + } + } + + // Adds Component to an entity (if the entity doesnt have it already) - template - decltype(auto) Add(Id id, C&& value = {}) const requires(IsSame>) + template + decltype(auto) Add(Id id, Component&& value = {}) const requires(IsMutable) { P_Check(IsValid(id)); - return AssurePool().Add(id, p::Forward(value)); + auto& pool = AssurePool(); + if constexpr (HasAnyTypeStaticFlags(TF_ECS_AutoModify)) + { + MarkModified(id, &pool); + } + return pool.Add(id, p::Forward(value)); } - template - decltype(auto) Add(Id id, const C& value) const requires(IsSame>) + template + decltype(auto) Add(Id id, const Component& value) const requires(IsMutable) { P_Check(IsValid(id)); - return AssurePool().Add(id, value); + auto& pool = AssurePool(); + if constexpr (HasAnyTypeStaticFlags(TF_ECS_AutoModify)) + { + MarkModified(id, &pool); + } + return pool.Add(id, value); } // Adds Component to an entity (if the entity doesnt have it already) template @@ -1237,7 +1510,24 @@ namespace p template decltype(auto) AddN(TView ids, const Component& value = {}) { - return AssurePool().Add(ids.begin(), ids.end(), value); + auto& pool = AssurePool(); + if constexpr (HasAnyTypeStaticFlags(TF_ECS_AutoModify)) + { + MarkModified(ids, &pool); + } + return pool.Add(ids.begin(), ids.end(), value); + } + + template + void AddN(TView ids, const TView& values) + { + P_Check(ids.Size() == values.Size()); + auto& pool = AssurePool(); + if constexpr (HasAnyTypeStaticFlags(TF_ECS_AutoModify)) + { + MarkModified(ids, &pool); + } + pool.Add(ids.begin(), ids.end(), values.begin()); } // Add Components to many entities (if they don't have it already) @@ -1247,22 +1537,20 @@ namespace p (Add(ids), ...); } - template - void AddN(TView ids, const TView& values) - { - P_Check(ids.Size() == values.Size()); - AssurePool().Add(ids.begin(), ids.end(), values.begin()); - } - template void Remove(const Id id) { if (auto* pool = GetPool()) { + if constexpr (HasAnyTypeStaticFlags(TF_ECS_AutoModify)) + { + MarkModified(id, pool); + } pool->Remove(id); } } + template void Remove(const Id id) requires(sizeof...(Component) > 1) { @@ -1273,14 +1561,14 @@ namespace p { if (auto* pool = GetPool()) { + if constexpr (HasAnyTypeStaticFlags(TF_ECS_AutoModify)) + { + MarkModified(ids, pool); + } pool->Remove(ids); } - - if constexpr (HasAnyTypeStaticFlags(TF_ECS_AutoModify)) - { - AssurePool>().Add(ids.begin(), ids.end(), {}); - } } + template void Remove(TView ids) requires(sizeof...(Component) > 1) { @@ -1292,6 +1580,11 @@ namespace p { auto* const pool = GetPool(); P_Check(pool); + if constexpr (IsMutable + && HasAnyTypeStaticFlags(TF_ECS_AutoModify)) + { + MarkModified(id, pool); + } return pool->Get(id); } template @@ -1303,7 +1596,17 @@ namespace p Component* TryGet(const Id id) const { auto* const pool = GetPool(); - return pool ? pool->TryGet(id) : nullptr; + P_Check(pool); + Component* value = pool->TryGet(id); + if constexpr (IsMutable + && HasAnyTypeStaticFlags(TF_ECS_AutoModify)) + { + if (value) + { + MarkModified(id, pool); + } + } + return value; } template TTuple TryGet(const Id id) const requires(sizeof...(Component) > 1) @@ -1314,7 +1617,16 @@ namespace p template Component& GetOrAdd(Id id) { - return AssurePool().GetOrAdd(id); + auto* pool = GetPool(); + if (pool->Has(id)) + { + if constexpr (HasAnyTypeStaticFlags(TF_ECS_AutoModify)) + { + MarkModified(id, pool); + } + return pool->Get(id); + } + return pool->Add(id); } template @@ -1381,18 +1693,6 @@ namespace p void Reset(bool keepStatics = false); - template - TBroadcast>& OnAdd() - { - return AssurePool().OnAdd(); - } - - template - TBroadcast>& OnRemove() - { - return AssurePool().OnRemove(); - } - // Finds or creates a pool template TPool>& AssurePool() const; @@ -1467,8 +1767,8 @@ namespace p #pragma endregion Statics private: - void CopyFrom(const EntityContext& other); - void MoveFrom(EntityContext&& other); + void CopyFrom(const IdContext& other); + void MoveFrom(IdContext&& other); static OwnPtr& FindOrAddStaticPtr( TArray& statics, const TypeId typeId, bool* bAdded = nullptr); @@ -1480,105 +1780,89 @@ namespace p //////////////////////////////// -// ACCESSES +// ID SCOPES // -#pragma region Accesses - enum class AccessMode : u8 - { - Read, - Write - }; - - struct TypeAccess - { - TypeId typeId = TypeId::None(); - AccessMode mode = AccessMode::Read; - - constexpr TypeAccess() = default; - constexpr TypeAccess(TypeId typeId, AccessMode mode) : typeId{typeId}, mode{mode} {} - }; - - template - struct TTypeAccess : TypeAccess - { - using Type = Mut; - - constexpr TTypeAccess() : TypeAccess(GetTypeId(), inMode) {} - }; - - template - struct TRead : public TTypeAccess - {}; - - template - struct TWrite : public TTypeAccess - {}; - - template - struct TTypeAccessInfo - { - using Type = Mut; - static constexpr AccessMode mode = AccessMode::Read; - }; +#pragma region IdScopes template - requires Derived - struct TTypeAccessInfo + struct TIsAutoModified { - using Type = typename T::Type; - static constexpr AccessMode mode = T().mode; + static constexpr bool value = HasAnyTypeStaticFlags(TF_ECS_AutoModify); }; - template - using AsComponent = typename TTypeAccessInfo::Type; - - template - struct TAccess + template + struct TIdScopeBase : public TIdOperations> { - template - friend struct TAccess; - - using Components = TTypeList; - using RawComponents = TTypeList...>; + template + friend struct TIdScopeBase; private: - TypeId typeId; - EntityContext& context; - TTuple>*...> pools; + using RawWrites = W::template Wrap; + using RawReads = R::template Wrap; + using ModifyWrites = RawWrites::template Filter::template Wrap; + using ModifyReads = RawReads::template Filter::template Wrap; + public: + using WriteDependencies = RawWrites::template Append; + using ReadDependencies = RawReads::template Append; + using Dependencies = ReadDependencies::template Append::Deduplicate; + using Pools = Dependencies::template WrapPtr; + using Tuple = Pools::template To; + + // template + // using Reads = TIdScope; + // template + // using Writes = TIdScope, R...>; + + protected: + IdContext& context; + Tuple pools; public: - TAccess(EntityContext& context) - : context{context}, pools{&context.AssurePool>()...} + TIdScopeBase(IdContext& context) + : context{context}, pools(Dependencies::Call([&context] { + return Tuple{&context.AssurePool()...}; + })) {} - TAccess(const TAccess& other) : context{other.context}, pools{other.pools} {} + TIdScopeBase(const TIdScopeBase& other) : context{other.context}, pools{other.pools} {} - // Construct a child access (super-set) from another access + // Construct a child scope (super-set) from another scope template - TAccess(const TAccess& other) : context{other.context} + TIdScopeBase(const TIdScopeBase& other) : context{other.context} { - using Other = TAccess; + using Other = TIdScopeBase; + + + constexpr bool validReads = Dependencies::Call([]() { + return (Other::template IsReadable() && ...); + }); + + constexpr bool validWrites = WriteDependencies::Call([]() { + return (Other::template IsWritable() && ...); + }); - constexpr bool validConstants = (Other::template HasType() && ...); - constexpr bool validMutables = - ((Other::template IsWritable() || TTypeAccessInfo::mode != AccessMode::Write) - && ...); - static_assert(validConstants, "Parent access lacks dependencies from this access."); static_assert( - validMutables, "Parent access lacks *mutable* dependencies from this access."); + validReads, "Parent scope lacks read dependencies required by this scope."); + static_assert( + validWrites, "Parent scope lacks *write* dependencies required by this scope."); - if constexpr (validConstants && validMutables) + // Prevent compiler errors, we already have static_asserts + if constexpr (validReads && validWrites) { - pools = {std::get>*>(other.pools)...}; + pools = Dependencies::Call([&other] { + return Tuple{std::get*>(other.pools)...}; + }); } } - template - TPool>* GetPool() const requires(IsMutable) + + template + TPool>* GetPool() const requires(IsMutable) { - static_assert(IsWritable(), "Can't modify components of this type"); - if constexpr (IsWritable()) // Prevent missleading errors if condition fails + static_assert(IsWritable(), "Can't modify components of this type"); + if constexpr (IsWritable()) // Prevent missleading errors if condition + // fails { - return std::get>*>(pools); + return std::get>*>(pools); } else { @@ -1586,13 +1870,14 @@ namespace p } } - template - const TPool>* GetPool() const requires(IsConst) + template + const TPool>* GetPool() const requires(IsConst) { - static_assert(IsReadable(), "Can't read components of this type"); - if constexpr (IsReadable()) // Prevent missleading errors if condition fails + static_assert(IsReadable(), "Can't read components of this type"); + if constexpr (IsReadable()) // Prevent missleading errors if condition + // fails { - return std::get>*>(pools); + return std::get>*>(pools); } else { @@ -1600,184 +1885,93 @@ namespace p } } - template - TPool>& AssurePool() const requires(IsMutable) - { - return *GetPool(); - } - - template - const TPool>& AssurePool() const requires(IsConst) - { - return *GetPool(); - } - - bool IsValid(Id id) const - { - return context.IsValid(id); - } - - template - bool Has(Id id) const requires(sizeof...(C) >= 1) - { - return (GetPool()->Has(id) && ...); - } - - template - decltype(auto) Add(Id id, C&& value = {}) const requires(IsSame>) - { - return GetPool()->Add(id, p::Forward(value)); - } - template - decltype(auto) Add(Id id, const C& value) const requires(IsSame>) - { - return GetPool()->Add(id, value); - } - - // Add component to an entities (if they dont have it already) - template - void Add(Id id) const requires((IsSame> && ...) && sizeof...(C) > 1) - { - (Add(id), ...); - } - - // Add component to many entities (if they dont have it already) - template - decltype(auto) AddN(TView ids, const C& value = {}) const - { - return GetPool()->Add(ids.begin(), ids.end(), value); - } - - // Add components to many entities (if they dont have it already) - template - void AddN(TView ids) const - requires((IsSame> && ...) && sizeof...(C) > 1) - { - (AddN(ids), ...); - } - - - template - void Remove(const Id id) const requires(IsSame>) - { - if (auto* pool = GetPool()) - { - pool->Remove(id); - } - } - template - void Remove(const Id id) const requires(sizeof...(C) > 1) - { - (Remove(id), ...); - } - template - void Remove(TView ids) const requires(IsSame>) - { - if (auto* pool = GetPool()) - { - pool->Remove(ids); - } - } - template - void Remove(TView ids) const requires(sizeof...(C) > 1) - { - (Remove(ids), ...); - } - - template - C& Get(Id id) const + template + TPool& AssurePool() const requires(IsMutable) { - return GetPool()->Get(id); + return *GetPool(); } - template - C* TryGet(Id id) const + template + const TPool>& AssurePool() const requires(IsConst) { - return GetPool()->TryGet(id); + return *GetPool(); } - template - C& GetOrAdd(Id id) const requires(IsMutable) - { - return GetPool()->GetOrAdd(id); - } - i32 Size() const + IdContext& GetContext() const { - static_assert(sizeof...(T) == 1, "Can only get the size of single component accesses"); - return GetPool()->Size(); + return context; } - template - i32 Size() const + template + static constexpr bool IsMdfdType() requires(!HasTypeMember) { - return GetPool()->Size(); + return false; } - - template - void ClearPool() const + template + static constexpr bool IsMdfdType() requires(HasTypeMember) { - GetPool()->Clear(); + return IsSame, Component>; } - template - void ClearPool() const requires(sizeof...(C) > 1) + template + static constexpr bool IsReadable() { - (ClearPool(), ...); + return Dependencies::template Contains>(); } - EntityContext& GetContext() const + template + static constexpr bool IsWritable() { - return context; + return IsMutable && WriteDependencies::template Contains(); } + }; - template - static constexpr bool HasType() - { - return ListContains>(); - } + template + struct Writes : public TTypeList + {}; - template - static constexpr bool IsReadable() - { - return HasType(); - } + template + struct TIdScope : public TIdScopeBase, TTypeList> + { + using TIdScopeBase, TTypeList>::TIdScopeBase; + }; - template - static constexpr bool IsWritable() - { - return IsMutable && ListContains>>(); - } + template + struct TIdScope, R...> : public TIdScopeBase, TTypeList> + { + using TIdScopeBase, TTypeList>::TIdScopeBase; }; + template - using TAccessRef = const TAccess&; + using TIdScopeRef = const TIdScope&; - struct Access + struct IdScope : public TIdOperations { protected: - EntityContext& ast; - TArray types; + IdContext& context; + // TArray types; TArray pools; public: - Access(EntityContext& ast, const TArray& types) : ast{ast} {} + IdScope(IdContext& ast, const TArray& types) : context{context} {} template - Access(TAccessRef access) : ast{access.ast} + IdScope(TIdScopeRef scope) : context{scope.context} {} - template - TPool>* GetPool() const requires(IsMutable) + template + TPool>* GetPool() const requires(IsMutable) { return nullptr; } - template - const TPool>* GetPool() const requires(IsConst) + template + const TPool>* GetPool() const requires(IsConst) { return nullptr; } @@ -1789,7 +1983,7 @@ namespace p return 0; } }; -#pragma endregion Accesses +#pragma endregion IdScopees //////////////////////////////// @@ -1868,21 +2062,21 @@ namespace p /** * Remove ids containing a component from 'ids'. Does not guarantee order. * - * @param access from where to access pools + * @param scope from where to scope pools * @param ids array that will be modified * @param shouldShrink if true, the ids array will be shrink at the end * @see ExcludeIdsWithStable(), ExcludeIdsWithout() */ - template - void ExcludeIdsWith(const AccessType& access, TArray& ids, const bool shouldShrink = true) + template + void ExcludeIdsWith(const Scope& scope, TArray& ids, const bool shouldShrink = true) { - ExcludeIdsWith(&access.template AssurePool(), ids, shouldShrink); + ExcludeIdsWith(&scope.template AssurePool(), ids, shouldShrink); } - template - void ExcludeIdsWith(const AccessType& access, TArray& ids, const bool shouldShrink = true) - requires(sizeof...(C) > 1) + template + void ExcludeIdsWith(const Scope& scope, TArray& ids, const bool shouldShrink = true) + requires(sizeof...(Component) > 1) { - (ExcludeIdsWith(access, ids, shouldShrink), ...); + (ExcludeIdsWith(scope, ids, shouldShrink), ...); } template @@ -1904,82 +2098,79 @@ namespace p /** * Remove ids containing a component from 'ids'. Guarantees order. * - * @param access from where to access pools + * @param scope from where to scope pools * @param ids array that will be modified * @param shouldShrink if true, the ids array will be shrink at the end * @see ExcludeIdsWith(), ExcludeIdsWithoutStable() */ - template - void ExcludeIdsWithStable( - const AccessType& access, TArray& ids, const bool shouldShrink = true) + template + void ExcludeIdsWithStable(const Scope& scope, TArray& ids, const bool shouldShrink = true) { - ExcludeIdsWithStable(&access.template AssurePool(), ids, shouldShrink); + ExcludeIdsWithStable(&scope.template AssurePool(), ids, shouldShrink); } - template - void ExcludeIdsWithStable(const AccessType& access, TArray& ids, - const bool shouldShrink = true) requires(sizeof...(C) > 1) + template + void ExcludeIdsWithStable(const Scope& scope, TArray& ids, const bool shouldShrink = true) + requires(sizeof...(Component) > 1) { - (ExcludeIdsWithStable(access, ids, shouldShrink), ...); + (ExcludeIdsWithStable(scope, ids, shouldShrink), ...); } /** * Remove ids NOT containing a component from 'ids'. Does not guarantee order. * - * @param access from where to access pools + * @param scope from where to scope pools * @param ids array that will be modified * @param shouldShrink if true, the ids array will be shrink at the end * @see ExcludeIdsWithoutStable(), ExcludeIdsWith() */ - template - void ExcludeIdsWithout( - const AccessType& access, TArray& ids, const bool shouldShrink = true) + template + void ExcludeIdsWithout(const Scope& scope, TArray& ids, const bool shouldShrink = true) { - ExcludeIdsWithout(&access.template AssurePool(), ids, shouldShrink); + ExcludeIdsWithout(&scope.template AssurePool(), ids, shouldShrink); } - template - void ExcludeIdsWithout(const AccessType& access, TArray& ids, - const bool shouldShrink = true) requires(sizeof...(C) > 1) + template + void ExcludeIdsWithout(const Scope& scope, TArray& ids, const bool shouldShrink = true) + requires(sizeof...(Component) > 1) { - (ExcludeIdsWithout(access, ids, shouldShrink), ...); + (ExcludeIdsWithout(scope, ids, shouldShrink), ...); } /** * Remove ids NOT containing a component from 'ids'. Guarantees order. * - * @param access from where to access pools + * @param scope from where to scope pools * @param ids array that will be modified * @param shouldShrink if true, the ids array will be shrink at the end * @see ExcludeIdsWithout(), ExcludeIdsWithStable() */ - template + template void ExcludeIdsWithoutStable( - const AccessType& access, TArray& ids, const bool shouldShrink = true) + const Scope& scope, TArray& ids, const bool shouldShrink = true) { - ExcludeIdsWithoutStable(&access.template AssurePool(), ids, shouldShrink); + ExcludeIdsWithoutStable(&scope.template AssurePool(), ids, shouldShrink); } - template - void ExcludeIdsWithoutStable(const AccessType& access, TArray& ids, - const bool shouldShrink = true) requires(sizeof...(C) > 1) + template + void ExcludeIdsWithoutStable(const Scope& scope, TArray& ids, + const bool shouldShrink = true) requires(sizeof...(Component) > 1) { - (ExcludeIdsWithoutStable(access, ids, shouldShrink), ...); + (ExcludeIdsWithoutStable(scope, ids, shouldShrink), ...); } /** * Remove ids that are invalid. * - * @param access + * @param scope * @param ids array that will be modified * @param shouldShrink if true, the ids array will be shrink at the end * @see ExcludeIdsInvalidStable() */ - template - void ExcludeIdsInvalid( - const AccessType& access, TArray& ids, const bool shouldShrink = true) + template + void ExcludeIdsInvalid(const Scope& scope, TArray& ids, const bool shouldShrink = true) { ids.RemoveIfSwap( - [&access](Id id) { - return !access.IsValid(id); + [&scope](Id id) { + return !scope.IsValid(id); }, shouldShrink); } @@ -1987,55 +2178,55 @@ namespace p /** * Remove ids that are invalid. Guarantees order. * - * @param access + * @param scope * @param ids array that will be modified * @param shouldShrink if true, the ids array will be shrink at the end * @see ExcludeIdsInvalid() */ - template + template void ExcludeIdsInvalidStable( - const AccessType& access, TArray& ids, const bool shouldShrink = true) + const Scope& scope, TArray& ids, const bool shouldShrink = true) { ids.RemoveIf( - [&access](Id id) { - return !access.IsValid(id); + [&scope](Id id) { + return !scope.IsValid(id); }, shouldShrink); } /** Find ids containing a component from a list 'source' into 'results'. */ - template - void FindIdsWith(const AccessType& access, const TView& source, TArray& results) + template + void FindIdsWith(const Scope& scope, const TView& source, TArray& results) { - FindIdsWith(&access.template AssurePool(), source, results); + FindIdsWith(&scope.template AssurePool(), source, results); } - template - void FindIdsWith(const AccessType& access, const TView& source, TArray& results) - requires(sizeof...(C) > 1) + template + void FindIdsWith(const Scope& scope, const TView& source, TArray& results) + requires(sizeof...(Component) > 1) { - FindIdsWith({&access.template AssurePool()...}, source, results); + FindIdsWith({&scope.template AssurePool()...}, source, results); } - template - TArray FindIdsWith(const AccessType& access, const TView& source) + template + TArray FindIdsWith(const Scope& scope, const TView& source) { TArray results; - FindIdsWith(access, source, results); + FindIdsWith(scope, source, results); return Move(results); } /** Find ids NOT containing a component from a list 'source' into 'results'. */ - template - void FindIdsWithout(const AccessType& access, const TArray& source, TArray& results) + template + void FindIdsWithout(const Scope& scope, const TArray& source, TArray& results) { - FindIdsWithout(&access.template AssurePool(), source, results); + FindIdsWithout(&scope.template AssurePool(), source, results); } - template - TArray FindIdsWithout(const AccessType& access, const TArray& source) + template + TArray FindIdsWithout(const Scope& scope, const TArray& source) { TArray results; - FindIdsWithout(access, source, results); + FindIdsWithout(scope, source, results); return Move(results); } @@ -2043,18 +2234,18 @@ namespace p * Find and remove ids containing a component from list 'source' into 'results'. * Does not guarantee order. */ - template - void ExtractIdsWith(const AccessType& access, TArray& source, TArray& results, - const bool shouldShrink = true) + template + void ExtractIdsWith( + const Scope& scope, TArray& source, TArray& results, const bool shouldShrink = true) { - ExtractIdsWith(&access.template AssurePool(), source, results); + ExtractIdsWith(&scope.template AssurePool(), source, results); } - template + template TArray ExtractIdsWith( - const AccessType& access, TArray& source, const bool shouldShrink = true) + const Scope& scope, TArray& source, const bool shouldShrink = true) { TArray results; - ExtractIdsWith(access, source, results); + ExtractIdsWith(scope, source, results); return Move(results); } @@ -2062,18 +2253,18 @@ namespace p * Find and remove ids containing a component from list 'source' into 'results'. * Guarantees order. */ - template - void ExtractIdsWithStable(const AccessType& access, TArray& source, TArray& results, - const bool shouldShrink = true) + template + void ExtractIdsWithStable( + const Scope& scope, TArray& source, TArray& results, const bool shouldShrink = true) { - ExtractIdsWithStable(&access.template AssurePool(), source, results); + ExtractIdsWithStable(&scope.template AssurePool(), source, results); } - template + template TArray ExtractIdsWithStable( - const AccessType& access, TArray& source, const bool shouldShrink = true) + const Scope& scope, TArray& source, const bool shouldShrink = true) { TArray results; - ExtractIdsWithStable(access, source, results); + ExtractIdsWithStable(scope, source, results); return Move(results); } @@ -2081,18 +2272,18 @@ namespace p * Find and remove ids containing a component from list 'source' into 'results'. * Does not guarantee order. */ - template - void ExtractIdsWithout(const AccessType& access, TArray& source, TArray& results, - const bool shouldShrink = true) + template + void ExtractIdsWithout( + const Scope& scope, TArray& source, TArray& results, const bool shouldShrink = true) { - ExtractIdsWithout(&access.template AssurePool(), source, results); + ExtractIdsWithout(&scope.template AssurePool(), source, results); } - template + template TArray ExtractIdsWithout( - const AccessType& access, TArray& source, const bool shouldShrink = true) + const Scope& scope, TArray& source, const bool shouldShrink = true) { TArray results; - ExtractIdsWithout(access, source, results); + ExtractIdsWithout(scope, source, results); return Move(results); } @@ -2100,18 +2291,18 @@ namespace p * Find and remove ids not containing a component from list 'source' into 'results'. * Guarantees order. */ - template - void ExtractIdsWithoutStable(const AccessType& access, TArray& source, TArray& results, - const bool shouldShrink = true) + template + void ExtractIdsWithoutStable( + const Scope& scope, TArray& source, TArray& results, const bool shouldShrink = true) { - ExtractIdsWithoutStable(&access.template AssurePool(), source, results); + ExtractIdsWithoutStable(&scope.template AssurePool(), source, results); } - template + template TArray ExtractIdsWithoutStable( - const AccessType& access, TArray& source, const bool shouldShrink = true) + const Scope& scope, TArray& source, const bool shouldShrink = true) { TArray results; - ExtractIdsWithoutStable(access, source, results); + ExtractIdsWithoutStable(scope, source, results); return Move(results); } @@ -2119,29 +2310,29 @@ namespace p /** * Find all ids containing all of the components * - * @param access from where to access pools + * @param scope from where to scope pools * @param ids array where matching ids will be added * @see FindAllIdsWithAny() */ - template - void FindAllIdsWith(const AccessType& access, TArray& ids) requires(sizeof...(C) >= 1) + template + void FindAllIdsWith(const Scope& scope, TArray& ids) requires(sizeof...(Component) >= 1) { - FindAllIdsWith({access.template GetPool()...}, ids); + FindAllIdsWith({scope.template GetPool()...}, ids); } /** * Find all ids containing all of the components * - * @param access from where to access pools + * @param scope from where to scope pools * @return ids array with matching ids * @see FindAllIdsWithAny() */ - template - TArray FindAllIdsWith(const AccessType& access) requires(sizeof...(C) >= 1) + template + TArray FindAllIdsWith(const Scope& scope) requires(sizeof...(Component) >= 1) { TArray ids; - FindAllIdsWith(access, ids); + FindAllIdsWith(scope, ids); return Move(ids); } @@ -2149,44 +2340,44 @@ namespace p * Find all ids containing any of the components. * Includes possible duplicates * - * @param access from where to access pools + * @param scope from where to scope pools * @param ids array where matching ids will be added * @see FindAllIdsWith() */ - template - void FindAllIdsWithAny(const AccessType& access, TArray& ids) requires(sizeof...(C) >= 1) + template + void FindAllIdsWithAny(const Scope& scope, TArray& ids) requires(sizeof...(Component) >= 1) { - FindAllIdsWithAny({access.template GetPool()...}, ids); + FindAllIdsWithAny({scope.template GetPool()...}, ids); } /** * Find all ids containing any of the components. * Prevents duplicates * - * @param access from where to access pools + * @param scope from where to scope pools * @param ids array where matching ids will be added * @see FindAllIdsWithAnyUnique() */ - template - void FindAllIdsWithAnyUnique(const AccessType& access, TArray& ids) - requires(sizeof...(C) >= 1) + template + void FindAllIdsWithAnyUnique(const Scope& scope, TArray& ids) + requires(sizeof...(Component) >= 1) { - FindAllIdsWithAnyUnique({access.template GetPool()...}, ids); + FindAllIdsWithAnyUnique({scope.template GetPool()...}, ids); } /** * Find all ids containing any of the components. * Includes possible duplicates * - * @param access from where to access pools + * @param scope from where to scope pools * @return ids array with matching ids * @see FindAllIdsWith() */ - template - TArray FindAllIdsWithAny(const AccessType& access) requires(sizeof...(C) >= 1) + template + TArray FindAllIdsWithAny(const Scope& scope) requires(sizeof...(Component) >= 1) { TArray ids; - FindAllIdsWithAny(access, ids); + FindAllIdsWithAny(scope, ids); return Move(ids); } @@ -2194,22 +2385,22 @@ namespace p * Find all ids containing any of the components. * Prevents duplicates * - * @param access from where to access pools + * @param scope from where to scope pools * @return ids array with matching ids * @see FindAllIdsWithAny() */ - template - TArray FindAllIdsWithAnyUnique(const AccessType& access) requires(sizeof...(C) >= 1) + template + TArray FindAllIdsWithAnyUnique(const Scope& scope) requires(sizeof...(Component) >= 1) { TArray ids; - FindAllIdsWithAnyUnique(access, ids); + FindAllIdsWithAnyUnique(scope, ids); return Move(ids); } - template - Id GetFirstIdWith(const AccessType& access) + template + Id GetFirstIdWith(const Scope& scope) { - return GetFirstIdWith({access.template GetPool()...}); + return GetFirstIdWith({scope.template GetPool()...}); } #pragma endregion Filtering @@ -2220,12 +2411,12 @@ namespace p #pragma region Editing // Create - P_API Id AddId(EntityContext& ctx); - P_API void AddId(EntityContext& ctx, TView Ids); + P_API Id AddId(IdContext& ctx); + P_API void AddId(IdContext& ctx, TView Ids); // Remove - P_API bool RmId(EntityContext& ctx, TView ids, RmIdFlags flags = RmIdFlags::None); - P_API bool FlushDeferredRemovals(EntityContext& ctx); + P_API bool RmId(IdContext& ctx, TView ids, RmIdFlags flags = RmIdFlags::None); + P_API bool FlushDeferredRemovals(IdContext& ctx); #pragma endregion Editing @@ -2236,23 +2427,23 @@ namespace p // Link a list of nodes at the end of the parent children list P_API void AttachId( - TAccessRef, TWrite> access, Id parent, TView children); + TIdScopeRef> scope, Id parent, TView children); // Link a list of nodes after prevChild in the list of children nodes - P_API void AttachIdAfter(TAccessRef, TWrite> access, Id parent, - TView childrenIds, Id prevChild); - P_API void TransferIdChildren(TAccessRef, TWrite> access, - TView childrenIds, Id destination); - // TODO: void TransferAllChildren(Tree& ast, Id origin, Id destination); - P_API void DetachIdParent(TAccessRef, TWrite> access, + P_API void AttachIdAfter( + TIdScopeRef> scope, Id parent, TView childrenIds, Id prevChild); + P_API void TransferIdChildren( + TIdScopeRef> scope, TView childrenIds, Id destination); + // TODO: void TransferAllChildren(IdContext& context, Id origin, Id destination); + P_API void DetachIdParent(TIdScopeRef> scope, TView childrenIds, bool keepComponents); - P_API void DetachIdChildren(TAccessRef, TWrite> access, - TView parents, bool keepComponents = false); + P_API void DetachIdChildren(TIdScopeRef> scope, TView parents, + bool keepComponents = false); /** Obtain direct children ids from the provided parent Id. Examples: * - Children of A (where A->B->C) is B. * - Children of A (where A->B, A->C) are B and C. */ - P_API const TArray* GetIdChildren(TAccessRef access, Id node); + P_API const TArray* GetIdChildren(TIdScopeRef scope, Id node); /** Obtain direct children ids from the provided parent Ids. Examples: * - Children of A (where A->B->C) is B. @@ -2260,7 +2451,7 @@ namespace p * - Children of A and B (where A->B->C) are B, C. */ P_API void GetIdChildren( - TAccessRef access, TView nodes, TArray& outChildrenIds); + TIdScopeRef scope, TView nodes, TArray& outChildrenIds); /** Obtain all children ids from the provided parent Ids. Examples: * - All children of A (where A->B->C) are B and C. @@ -2268,25 +2459,25 @@ namespace p * - All children of A and B (where A->B->C->D) are B, C, D, C, D (duplicates are not * handled). */ - P_API void GetAllIdChildren(TAccessRef access, TView parentIds, + P_API void GetAllIdChildren(TIdScopeRef scope, TView parentIds, TArray& outChildrenIds, u32 depth = 1); - P_API Id GetIdParent(TAccessRef access, Id childId); + P_API Id GetIdParent(TIdScopeRef scope, Id childId); P_API void GetIdParent( - TAccessRef access, TView childrenIds, TArray& outParents); + TIdScopeRef scope, TView childrenIds, TArray& outParents); P_API void GetAllIdParents( - TAccessRef access, TView childrenIds, TArray& outParents); + TIdScopeRef scope, TView childrenIds, TArray& outParents); /** * Find a parent id matching a delegate */ - P_API Id FindIdParent(TAccessRef access, Id child, const TFunction& callback); - P_API void FindIdParents(TAccessRef access, TView childrenIds, + P_API Id FindIdParent(TIdScopeRef scope, Id child, const TFunction& callback); + P_API void FindIdParents(TIdScopeRef scope, TView childrenIds, TArray& outParents, const TFunction& callback); - // void Copy(Tree& ast, t TArray& nodes, TArray& outNewNodes); - // void CopyDeep(Tree& ast, const TArray& rootNodes, TArray& outNewRootNodes); - // void CopyAndTransferAllChildrenDeep(Tree& ast, Id root, Id otherRoot); + // void Copy(IdContext& context, t TArray& nodes, TArray& outNewNodes); + // void CopyDeep(IdContext& context, const TArray& rootNodes, TArray& outNewRootNodes); + // void CopyAndTransferAllChildrenDeep(IdContext& context, Id root, Id otherRoot); /** * Iterates children nodes making sure child->parent links are correct or fixed @@ -2295,7 +2486,7 @@ namespace p * @parents: where to look for children to fix up * @return true if an incorrect link was found and fixed */ - P_API bool FixParentIdLinks(TAccessRef, CParent> access, TView parents); + P_API bool FixParentIdLinks(TIdScopeRef, CParent> scope, TView parents); /** * Iterates children nodes looking for invalid child->parent links @@ -2304,9 +2495,9 @@ namespace p * @parents: where to look for children * @return true if an incorrect link was found */ - P_API bool ValidateParentIdLinks(TAccessRef access, TView parents); + P_API bool ValidateParentIdLinks(TIdScopeRef scope, TView parents); - P_API void GetRootIds(TAccessRef access, TArray& outRoots); + P_API void GetRootIds(TIdScopeRef scope, TArray& outRoots); #pragma endregion Hierarchy @@ -2352,7 +2543,7 @@ namespace p BeginObject(); for (i32 i = 0; i < ids.Size(); ++i) { - const Id node = ids[i]; + const Id id = ids[i]; key.clear(); Strings::FormatTo(key, "{}", i); @@ -2360,12 +2551,12 @@ namespace p { if constexpr (!IsEmpty) { - T& comp = pool.GetOrAdd(node); + T& comp = pool.Has(id) ? pool.Get(id) : pool.Add(id); Serialize(comp); } else { - pool.Add(node); + pool.Add(id); } Leave(); } @@ -2375,7 +2566,8 @@ namespace p { if constexpr (!IsEmpty) { - T& comp = pool.GetOrAdd(ids[0]); + const Id id = ids[0]; + T& comp = pool.Has(id) ? pool.Get(id) : pool.Add(id); Serialize(comp); } else @@ -2453,7 +2645,7 @@ namespace p template - inline TPool>& EntityContext::AssurePool() const + inline TPool>& IdContext::AssurePool() const { const TypeId componentId = RegisterTypeId>(); @@ -2475,17 +2667,17 @@ namespace p } template - inline PoolInstance EntityContext::CreatePoolInstance() const + inline PoolInstance IdContext::CreatePoolInstance() const { constexpr TypeId componentId = GetTypeId>(); - auto& self = const_cast(*this); + auto& self = const_cast(*this); PoolInstance instance{componentId, MakeUnique>>(self)}; return Move(instance); } template - inline Static& EntityContext::SetStatic() + inline Static& IdContext::SetStatic() { OwnPtr& ptr = FindOrAddStaticPtr(statics, GetTypeId()); ptr = MakeOwned(); @@ -2493,7 +2685,7 @@ namespace p } template - inline Static& EntityContext::SetStatic(Static&& value) + inline Static& IdContext::SetStatic(Static&& value) { OwnPtr& ptr = FindOrAddStaticPtr(statics, GetTypeId()); ptr = MakeOwned(p::Forward(value)); @@ -2501,7 +2693,7 @@ namespace p } template - inline Static& EntityContext::SetStatic(const Static& value) + inline Static& IdContext::SetStatic(const Static& value) { OwnPtr& ptr = FindOrAddStaticPtr(statics, GetTypeId()); ptr = MakeOwned(value); @@ -2509,7 +2701,7 @@ namespace p } template - inline Static& EntityContext::GetOrSetStatic() + inline Static& IdContext::GetOrSetStatic() { bool bAdded = false; OwnPtr& ptr = FindOrAddStaticPtr(statics, GetTypeId(), &bAdded); @@ -2521,7 +2713,7 @@ namespace p } template - inline Static& EntityContext::GetOrSetStatic(Static&& value) + inline Static& IdContext::GetOrSetStatic(Static&& value) { bool bAdded = false; OwnPtr& ptr = FindOrAddStaticPtr(statics, GetTypeId(), &bAdded); @@ -2533,7 +2725,7 @@ namespace p } template - inline Static& EntityContext::GetOrSetStatic(const Static& value) + inline Static& IdContext::GetOrSetStatic(const Static& value) { bool bAdded = false; OwnPtr& ptr = FindOrAddStaticPtr(statics, GetTypeId(), &bAdded); diff --git a/Include/PipeECSFwd.h b/Include/PipeECSFwd.h index 41df992f..ccb25959 100644 --- a/Include/PipeECSFwd.h +++ b/Include/PipeECSFwd.h @@ -25,23 +25,17 @@ namespace p::ecs struct SortLessStatics; - struct EntityContext; + struct IdContext; + + template + struct TTypeList; // TODO: Move to a TemplatesFwd header + + struct IdScope; - enum class AccessMode : u8; - struct TypeAccess; - template - struct TTypeAccess; - template - struct TRead; - template - struct TWrite; - template - struct TTypeAccessInfo; template - struct TAccess; + struct TIdScope; template - using TAccessRef = const TAccess&; - struct Access; + using TIdScopeRef = const TIdScope&; struct CChild; struct CParent; diff --git a/Include/PipeReflect.h b/Include/PipeReflect.h index 61e3b51e..c6b07d63 100644 --- a/Include/PipeReflect.h +++ b/Include/PipeReflect.h @@ -198,8 +198,10 @@ namespace p TF_Object = 1 << 4, TF_Container = 1 << 6, - TF_ECS_AutoModify = 1 << 7 // -> In ECS, should this type be marked modified - // automatically when added, written or removed? + TF_ECS_AutoModify = 1 << 7, // -> In ECS, should this type be marked modified + // automatically when added, written or removed? + TF_ECS_StoreLastModified = 1 << 8 // -> In ECS, should this type's last value be stored + // in the CMdfd component before being removed? // Any other flags up to 64 bytes are available to the user }; diff --git a/Src/PipeECS.cpp b/Src/PipeECS.cpp index 82dc04fa..fde4555c 100644 --- a/Src/PipeECS.cpp +++ b/Src/PipeECS.cpp @@ -9,7 +9,7 @@ namespace p { - Id IdFromString(String str, EntityContext* context) + Id IdFromString(String str, IdContext* context) { if (str == "NoId") { @@ -255,7 +255,7 @@ namespace p return ids; } - EntityContext& EntityReader::GetContext() + IdContext& EntityReader::GetContext() { return context; } @@ -326,7 +326,7 @@ namespace p void EntityWriter::RemoveIgnoredEntities(TArray& entities) { - TAccess access{context}; + TIdScope access{context}; for (i32 i = 0; i < entities.Size(); ++i) { if (access.Has(entities[i])) @@ -547,7 +547,7 @@ namespace p }; } - TPool::TPool(p::EntityContext& ctx, Arena& arena) + TPool::TPool(p::IdContext& ctx, Arena& arena) : IPool(p::GetTypeId()), idRegistry{&ctx.GetIdRegistry()} {} @@ -638,33 +638,33 @@ namespace p } - EntityContext::EntityContext() + IdContext::IdContext() { AssurePool(); } - EntityContext::EntityContext(const EntityContext& other) noexcept + IdContext::IdContext(const IdContext& other) noexcept { CopyFrom(other); } - EntityContext::EntityContext(EntityContext&& other) noexcept + IdContext::IdContext(IdContext&& other) noexcept { MoveFrom(Move(other)); } - EntityContext& EntityContext::operator=(const EntityContext& other) noexcept + IdContext& IdContext::operator=(const IdContext& other) noexcept { Reset(); CopyFrom(other); return *this; } - EntityContext& EntityContext::operator=(EntityContext&& other) noexcept + IdContext& IdContext::operator=(IdContext&& other) noexcept { Reset(); MoveFrom(Move(other)); return *this; } - void* EntityContext::AddDefault(TypeId typeId, Id id) + void* IdContext::AddDefault(TypeId typeId, Id id) { if (IPool* pool = GetPool(typeId)) { @@ -673,7 +673,7 @@ namespace p return nullptr; } - void EntityContext::Remove(TypeId typeId, Id id) + void IdContext::Remove(TypeId typeId, Id id) { if (IPool* pool = GetPool(typeId)) { @@ -681,13 +681,13 @@ namespace p } } - IPool* EntityContext::GetPool(TypeId componentId) const + IPool* IdContext::GetPool(TypeId componentId) const { const i32 index = pools.FindSorted(PoolInstance{componentId, {}}); return index != NO_INDEX ? pools[index].GetPool() : nullptr; } - void EntityContext::GetPools(TView componentIds, TArray& outPools) const + void IdContext::GetPools(TView componentIds, TArray& outPools) const { for (const TypeId componentId : componentIds) { @@ -699,7 +699,7 @@ namespace p } } - void EntityContext::CopyFrom(const EntityContext& other) + void IdContext::CopyFrom(const IdContext& other) { // Copy entities idRegistry = other.idRegistry; @@ -719,7 +719,7 @@ namespace p // TODO: Cache pools } - void EntityContext::MoveFrom(EntityContext&& other) + void IdContext::MoveFrom(IdContext&& other) { idRegistry = Move(other.idRegistry); pools = Move(other.pools); @@ -731,17 +731,17 @@ namespace p // TODO: Cache pools } - bool EntityContext::IsValid(Id id) const + bool IdContext::IsValid(Id id) const { return idRegistry.IsValid(id); } - bool EntityContext::WasRemoved(Id id) const + bool IdContext::WasRemoved(Id id) const { return idRegistry.WasRemoved(id); } - bool EntityContext::IsOrphan(const Id id) const + bool IdContext::IsOrphan(const Id id) const { for (const auto& instance : pools) { @@ -753,21 +753,21 @@ namespace p return true; } - void* EntityContext::TryGetStatic(TypeId typeId) + void* IdContext::TryGetStatic(TypeId typeId) { const i32 index = statics.FindSorted(typeId); return index != NO_INDEX ? statics[index].Get() : nullptr; } - const void* EntityContext::TryGetStatic(TypeId typeId) const + const void* IdContext::TryGetStatic(TypeId typeId) const { const i32 index = statics.FindSorted(typeId); return index != NO_INDEX ? statics[index].Get() : nullptr; } - bool EntityContext::HasStatic(TypeId typeId) const + bool IdContext::HasStatic(TypeId typeId) const { return statics.FindSorted(typeId) != NO_INDEX; } - bool EntityContext::RemoveStatic(TypeId typeId) + bool IdContext::RemoveStatic(TypeId typeId) { const i32 index = statics.FindSorted(typeId); if (index != NO_INDEX) @@ -778,7 +778,7 @@ namespace p return false; } - void EntityContext::Reset(bool keepStatics) + void IdContext::Reset(bool keepStatics) { idRegistry = {}; pools.Clear(); @@ -788,7 +788,7 @@ namespace p } } - OwnPtr& EntityContext::FindOrAddStaticPtr( + OwnPtr& IdContext::FindOrAddStaticPtr( TArray& statics, const TypeId typeId, bool* bAdded) { i32 index = statics.LowerBound(typeId); @@ -1100,7 +1100,7 @@ namespace p } - void RemoveChildFromCParent(TAccessRef> access, Id parent, Id child) + void RemoveChildFromCParent(TIdScopeRef> access, Id parent, Id child) { if (auto* cParent = access.TryGet(parent)) { @@ -1113,16 +1113,16 @@ namespace p } - Id AddId(EntityContext& ctx) + Id AddId(IdContext& ctx) { return ctx.GetIdRegistry().Create(); } - void AddId(EntityContext& ctx, TView ids) + void AddId(IdContext& ctx, TView ids) { ctx.GetIdRegistry().Create(ids); } - bool RmId(EntityContext& ctx, TView ids, RmIdFlags flags) + bool RmId(IdContext& ctx, TView ids, RmIdFlags flags) { TArray allIds; // Only used when removing children. Here for scope purposes. if (!HasFlag(flags, p::RmIdFlags::KeepChildren)) @@ -1147,7 +1147,7 @@ namespace p } } - bool FlushDeferredRemovals(EntityContext& ctx) + bool FlushDeferredRemovals(IdContext& ctx) { TView ids = ctx.GetIdRegistry().GetDeferredRemovals(); for (auto& pool : ctx.GetPools()) @@ -1158,8 +1158,7 @@ namespace p } - void AttachId( - TAccessRef, TWrite> access, Id parent, TView children) + void AttachId(TIdScopeRef> access, Id parent, TView children) { children.Each([&access, parent](Id childId) { if (auto* cChild = access.TryGet(childId)) @@ -1181,8 +1180,8 @@ namespace p access.GetOrAdd(parent).children.Append(children); } - void AttachIdAfter(TAccessRef, TWrite> access, Id parent, - TView childrenIds, Id prevChild) + void AttachIdAfter( + TIdScopeRef> access, Id parent, TView childrenIds, Id prevChild) { childrenIds.Each([&access, parent](Id child) { if (auto* cChild = access.TryGet(child)) @@ -1205,15 +1204,15 @@ namespace p childrenList.Insert(prevIndex, childrenIds); } - void TransferIdChildren(TAccessRef, TWrite> access, - TView childrenIds, Id destination) + void TransferIdChildren( + TIdScopeRef> access, TView childrenIds, Id destination) { DetachIdParent(access, childrenIds, true); AttachId(access, destination, childrenIds); } - void DetachIdParent(TAccessRef, TWrite> access, - TView childrenIds, bool keepComponents) + void DetachIdParent(TIdScopeRef> access, TView childrenIds, + bool keepComponents) { TArray parents; parents.Reserve(childrenIds.Size()); @@ -1275,8 +1274,8 @@ namespace p } } - void DetachIdChildren(TAccessRef, TWrite> access, - TView parents, bool keepComponents) + void DetachIdChildren( + TIdScopeRef> access, TView parents, bool keepComponents) { if (keepComponents) { @@ -1306,14 +1305,14 @@ namespace p } } - const TArray* GetIdChildren(TAccessRef access, Id node) + const TArray* GetIdChildren(TIdScopeRef access, Id node) { auto* cParent = access.TryGet(node); return cParent ? &cParent->children : nullptr; } void GetIdChildren( - TAccessRef access, TView parentIds, TArray& outChildrenIds) + TIdScopeRef access, TView parentIds, TArray& outChildrenIds) { parentIds.Each([&access, &outChildrenIds](Id id) { if (const auto* cParent = access.TryGet(id)) @@ -1323,7 +1322,7 @@ namespace p }); } - void GetAllIdChildren(TAccessRef access, TView parentIds, + void GetAllIdChildren(TIdScopeRef access, TView parentIds, TArray& outChildrenIds, u32 depth) { P_Check(depth > 0); @@ -1339,7 +1338,7 @@ namespace p } } - Id GetIdParent(TAccessRef access, Id childId) + Id GetIdParent(TIdScopeRef access, Id childId) { if (const auto* cChild = access.TryGet(childId)) { @@ -1348,7 +1347,8 @@ namespace p return NoId; } - void GetIdParent(TAccessRef access, TView childrenIds, TArray& outParents) + void GetIdParent( + TIdScopeRef access, TView childrenIds, TArray& outParents) { outParents.Clear(false); for (Id childId : childrenIds) @@ -1361,7 +1361,7 @@ namespace p } } void GetAllIdParents( - TAccessRef access, TView childrenIds, TArray& outParents) + TIdScopeRef access, TView childrenIds, TArray& outParents) { outParents.Clear(false); @@ -1377,7 +1377,7 @@ namespace p } } - Id FindIdParent(TAccessRef access, Id childId, const TFunction& callback) + Id FindIdParent(TIdScopeRef access, Id childId, const TFunction& callback) { while (!IsNone(childId)) { @@ -1389,7 +1389,7 @@ namespace p } return NoId; } - void FindIdParents(TAccessRef access, TView childrenIds, + void FindIdParents(TIdScopeRef access, TView childrenIds, TArray& outParentIds, const TFunction& callback) { outParentIds.Clear(false); @@ -1419,7 +1419,7 @@ namespace p } - bool FixParentIdLinks(TAccessRef, CParent> access, TView parents) + bool FixParentIdLinks(TIdScopeRef, CParent> access, TView parents) { bool fixed = false; for (Id parentId : parents) @@ -1447,7 +1447,7 @@ namespace p return fixed; } - bool ValidateParentIdLinks(TAccessRef access, TView parents) + bool ValidateParentIdLinks(TIdScopeRef access, TView parents) { for (Id parentId : parents) { @@ -1466,7 +1466,7 @@ namespace p return true; } - void GetRootIds(TAccessRef access, TArray& outRoots) + void GetRootIds(TIdScopeRef access, TArray& outRoots) { outRoots = FindAllIdsWith(access); ExcludeIdsWith(access, outRoots); diff --git a/Tests/ECS/Access.spec.cpp b/Tests/ECS/Access.spec.cpp index a4ef8efe..8d1c98a6 100644 --- a/Tests/ECS/Access.spec.cpp +++ b/Tests/ECS/Access.spec.cpp @@ -19,8 +19,8 @@ go_bandit([]() { describe("ECS.Access", []() { describe("Templated", []() { it("Can cache pools", [&]() { - EntityContext ctx; - TAccess, TypeB> access{ctx}; + IdContext ctx; + TIdScope> access{ctx}; AssertThat(access.GetPool(), Equals(ctx.GetPool())); AssertThat(access.GetPool(), Equals(ctx.GetPool())); @@ -28,10 +28,10 @@ go_bandit([]() { }); it("Can check if contained", [&]() { - EntityContext ctx; + IdContext ctx; TPool& pool = ctx.AssurePool(); - TAccess> access{ctx}; - TAccess accessConst{ctx}; + TIdScope> access{ctx}; + TIdScope accessConst{ctx}; Id id = NoId; AssertThat(access.Has(id), Is().False()); AssertThat(accessConst.Has(id), Is().False()); @@ -44,25 +44,25 @@ go_bandit([]() { AssertThat(access.Has(id), Is().True()); AssertThat(accessConst.Has(id), Is().True()); - TAccess access2{ctx}; + TIdScope access2{ctx}; ctx.Add(id); AssertThat(access2.Has(id), Is().True()); }); it("Can initialize superset", [&]() { - EntityContext ctx; + IdContext ctx; TPool& typePool = ctx.AssurePool(); - TAccess, TWrite> access1{ctx}; - TAccess> superset1{access1}; + TIdScope> access1{ctx}; + TIdScope> superset1{access1}; AssertThat(superset1.GetPool(), Equals(&typePool)); - TAccess, TWrite> access2{ctx}; - TAccess superset2{access2}; + TIdScope> access2{ctx}; + TIdScope superset2{access2}; AssertThat(superset2.GetPool(), Equals(&typePool)); - TAccess, TWrite> access3{ctx}; - TAccess superset3{access3}; + TIdScope> access3{ctx}; + TIdScope superset3{access3}; AssertThat(superset1.GetPool(), Equals(&typePool)); }); }); diff --git a/Tests/ECS/Components.spec.cpp b/Tests/ECS/Components.spec.cpp index 24e57cde..c511ecc7 100644 --- a/Tests/ECS/Components.spec.cpp +++ b/Tests/ECS/Components.spec.cpp @@ -63,7 +63,7 @@ u32 TestComponent::destructed = 0; go_bandit([]() { describe("ECS.Components", []() { it("Can add one component", [&]() { - EntityContext ctx; + IdContext ctx; Id id = AddId(ctx); AssertThat(ctx.Has(id), Is().False()); AssertThat(ctx.TryGet(id), Equals(nullptr)); @@ -79,7 +79,7 @@ go_bandit([]() { }); it("Can remove one component", [&]() { - EntityContext ctx; + IdContext ctx; Id id = AddId(ctx); ctx.Add(id); @@ -95,7 +95,7 @@ go_bandit([]() { }); it("Can add many components", [&]() { - EntityContext ctx; + IdContext ctx; TArray ids{3}; AddId(ctx, ids); ctx.AddN(ids, NonEmptyComponent{2}); @@ -109,7 +109,7 @@ go_bandit([]() { }); it("Can remove many components", [&]() { - EntityContext ctx; + IdContext ctx; TArray ids{3}; AddId(ctx, ids); ctx.AddN(ids, NonEmptyComponent{2}); @@ -135,7 +135,7 @@ go_bandit([]() { }); it("Components are removed after node is deleted", [&]() { - EntityContext ctx; + IdContext ctx; Id id = AddId(ctx); ctx.Add(id); @@ -149,7 +149,7 @@ go_bandit([]() { }); it("Components are removed after node is deleted (deferred)", [&]() { - EntityContext ctx; + IdContext ctx; Id id = AddId(ctx); ctx.Add(id); @@ -169,7 +169,7 @@ go_bandit([]() { }); it("Components keep state when added", [&]() { - EntityContext ctx; + IdContext ctx; Id id = AddId(ctx); ctx.AddN(id, NonEmptyComponent{2}); AssertThat(ctx.TryGet(id), !Equals(nullptr)); @@ -177,14 +177,14 @@ go_bandit([]() { }); it("Can copy registry", []() { - EntityContext ctxa; + IdContext ctxa; Id id = AddId(ctxa); ctxa.Add(id); Id id2 = AddId(ctxa); ctxa.AddN(id2, NonEmptyComponent{2}); - EntityContext ctxb{ctxa}; + IdContext ctxb{ctxa}; AssertThat(ctxb.Has(id), Is().True()); AssertThat(ctxb.Has(id), Is().True()); AssertThat(ctxb.TryGet(id), !Equals(nullptr)); @@ -195,7 +195,7 @@ go_bandit([]() { }); it("Can check components", [&]() { - EntityContext ctx; + IdContext ctx; Id id = NoId; AssertThat(ctx.Has(id), Is().False()); AssertThat(ctx.Has(id), Is().False()); @@ -213,7 +213,7 @@ go_bandit([]() { NonEmptyComponent::destructed = 0; TestComponent::destructed = 0; - EntityContext ctx; + IdContext ctx; TArray ids{3}; AddId(ctx, ids); ctx.AddN(ids, NonEmptyComponent{2}); @@ -234,7 +234,7 @@ go_bandit([]() { }); it("Components are removed with the entity", [&]() { - EntityContext ctx; + IdContext ctx; Id id = AddId(ctx); ctx.Add(id); RmId(ctx, id, p::RmIdFlags::Instant); @@ -247,7 +247,7 @@ go_bandit([]() { }); it("Components are removed with the entity (deferred)", [&]() { - EntityContext ctx; + IdContext ctx; Id id = AddId(ctx); ctx.Add(id); RmId(ctx, id); @@ -266,7 +266,7 @@ go_bandit([]() { }); it("Can access components on recicled entities", [&]() { - EntityContext ctx; + IdContext ctx; Id id = AddId(ctx); ctx.Add(id); RmId(ctx, id); @@ -279,7 +279,7 @@ go_bandit([]() { }); it("Can access CRemoved", [&]() { - EntityContext ctx; + IdContext ctx; Id id = AddId(ctx); ctx.Add(id); RmId(ctx, id); diff --git a/Tests/ECS/ECS.spec.cpp b/Tests/ECS/ECS.spec.cpp index 9eec9d05..a09ff99a 100644 --- a/Tests/ECS/ECS.spec.cpp +++ b/Tests/ECS/ECS.spec.cpp @@ -20,10 +20,10 @@ struct ATypeB go_bandit([]() { describe("ECS", []() { it("Can copy tree", [&]() { - static EntityContext* ctxPtr = nullptr; + static IdContext* ctxPtr = nullptr; static bool calledAdd; - EntityContext origin; + IdContext origin; Id id = AddId(origin); calledAdd = false; @@ -39,7 +39,7 @@ go_bandit([]() { origin.Add(id); AssertThat(calledAdd, Equals(true)); - EntityContext target{origin}; + IdContext target{origin}; AssertThat(origin.IsValid(id), Equals(true)); AssertThat(origin.Has(id), Equals(true)); AssertThat(target.IsValid(id), Equals(true)); @@ -60,10 +60,10 @@ go_bandit([]() { }); it("Can move tree", [&]() { - static EntityContext* ctxPtr = nullptr; + static IdContext* ctxPtr = nullptr; static bool calledAdd; - EntityContext origin; + IdContext origin; Id id = AddId(origin); calledAdd = false; @@ -79,7 +79,7 @@ go_bandit([]() { origin.Add(id); AssertThat(calledAdd, Equals(true)); - EntityContext target{Move(origin)}; + IdContext target{Move(origin)}; AssertThat(origin.IsValid(id), Equals(false)); AssertThat(target.IsValid(id), Equals(true)); @@ -100,7 +100,7 @@ go_bandit([]() { }); it("Can assure pool", [&]() { - EntityContext origin; + IdContext origin; TPool& pool = origin.AssurePool(); AssertThat(pool.Size(), Equals(0)); }); diff --git a/Tests/ECS/Filtering.spec.cpp b/Tests/ECS/Filtering.spec.cpp index 28c9c982..d56759bb 100644 --- a/Tests/ECS/Filtering.spec.cpp +++ b/Tests/ECS/Filtering.spec.cpp @@ -34,7 +34,7 @@ struct TypeC go_bandit([]() { - EntityContext ctx; + IdContext ctx; Id id1; Id id2; Id id3; @@ -57,7 +57,7 @@ go_bandit([]() { describe("FindAllIdsWith/FindAllIdsWithAny", [&]() { it("Can get list matching all", [&]() { - TAccess access{ctx}; + TIdScope access{ctx}; TArray typeIds = FindAllIdsWith(access); AssertThat(typeIds.Contains(id1), Is().True()); AssertThat(typeIds.Contains(id2), Is().True()); @@ -70,7 +70,7 @@ go_bandit([]() { }); it("Can get list matching any", [&]() { - TAccess access{ctx}; + TIdScope access{ctx}; TArray typeIds = FindAllIdsWithAny(access); AssertThat(typeIds.Contains(id1), Is().True()); AssertThat(typeIds.Contains(id2), Is().True()); @@ -83,7 +83,7 @@ go_bandit([]() { }); it("Doesn't list removed ids", [&]() { - TAccess access{ctx}; + TIdScope access{ctx}; RmId(ctx, id2, RmIdFlags::Instant); // Remove first in the pool RmId(ctx, id3, RmIdFlags::Instant); // Remove last in the pool RmId(ctx, id4, RmIdFlags::Instant); // Remove last in the pool @@ -94,7 +94,7 @@ go_bandit([]() { }); it("Doesn't list (deferred) removed ids", [&]() { - TAccess access{ctx}; + TIdScope access{ctx}; RmId(ctx, id2); // Remove first in the pool RmId(ctx, id3); // Remove last in the pool RmId(ctx, id4); // Remove last in the pool @@ -109,7 +109,7 @@ go_bandit([]() { describe("ExcludeIdsWith", [&]() { it("Removes ids containing component", [&]() { - TAccess access{ctx}; + TIdScope access{ctx}; TArray typeIds = FindAllIdsWithAny(access); ExcludeIdsWith(access, typeIds); @@ -119,7 +119,7 @@ go_bandit([]() { }); it("Removes ids not containing component", [&]() { - TAccess access{ctx}; + TIdScope access{ctx}; TArray typeIds = FindAllIdsWithAny(access); ExcludeIdsWithout(access, typeIds); @@ -129,7 +129,7 @@ go_bandit([]() { }); it("Removes ids containing multiple component", [&]() { - TAccess access{ctx}; + TIdScope access{ctx}; TArray typeIds = FindAllIdsWithAny(access); ExcludeIdsWith(access, typeIds); @@ -143,7 +143,7 @@ go_bandit([]() { it("Finds ids containing a component from a list", [&]() { TArray source{id1, id2, id3}; - TAccess access{ctx}; + TIdScope access{ctx}; TArray typeIds = FindIdsWith(access, source); AssertThat(typeIds.Contains(id1), Is().True()); AssertThat(typeIds.Contains(id2), Is().True()); @@ -153,7 +153,7 @@ go_bandit([]() { it("Finds ids not containing a component from a list", [&]() { TArray source{id1, id2, id3}; - TAccess access{ctx}; + TIdScope access{ctx}; TArray ids = FindIdsWithout(access, source); AssertThat(ids.Contains(id1), Is().False()); AssertThat(ids.Contains(id2), Is().False()); @@ -165,7 +165,7 @@ go_bandit([]() { it("Finds and removes ids containing a component from a list", [&]() { TArray source{id1, id2, id3}; - TAccess access{ctx}; + TIdScope access{ctx}; TArray ids = ExtractIdsWith(access, source); AssertThat(ids.Contains(id1), Is().True()); AssertThat(ids.Contains(id2), Is().True()); @@ -178,7 +178,7 @@ go_bandit([]() { it("Finds and removes ids not containing a component from a list", [&]() { TArray source{id1, id2, id3}; - TAccess access{ctx}; + TIdScope access{ctx}; TArray ids = ExtractIdsWithout(access, source); AssertThat(ids.Contains(id1), Is().False()); AssertThat(ids.Contains(id2), Is().False()); diff --git a/Tests/ECS/Statics.spec.cpp b/Tests/ECS/Statics.spec.cpp index 39d1f35e..8d8a3bb4 100644 --- a/Tests/ECS/Statics.spec.cpp +++ b/Tests/ECS/Statics.spec.cpp @@ -27,7 +27,7 @@ struct StaticTypeThree go_bandit([]() { describe("ECS.Statics", []() { it("Can set an static", [&]() { - EntityContext ast; + IdContext ast; AssertThat(ast.HasStatic(), Equals(false)); auto& var = ast.SetStatic({4}); AssertThat(var.i, Equals(4)); @@ -35,7 +35,7 @@ go_bandit([]() { AssertThat(ast.HasStatic(), Equals(false)); }); it("Can set two statics", [&]() { - EntityContext ast; + IdContext ast; AssertThat(ast.HasStatic(), Equals(false)); AssertThat(ast.HasStatic(), Equals(false)); auto& var1 = ast.SetStatic({4}); @@ -46,7 +46,7 @@ go_bandit([]() { AssertThat(ast.HasStatic(), Equals(true)); }); it("Can replace an static", [&]() { - EntityContext ast; + IdContext ast; AssertThat(ast.HasStatic(), Equals(false)); ast.SetStatic({4}); ast.SetStatic({2}); @@ -54,14 +54,14 @@ go_bandit([]() { AssertThat(ast.HasStatic(), Equals(true)); }); it("Can get or set an static", [&]() { - EntityContext ast; + IdContext ast; // Can set AssertThat(ast.GetOrSetStatic({4}).i, Equals(4)); // Can get AssertThat(ast.GetOrSetStatic({10}).i, Equals(4)); }); it("Can remove an static", [&]() { - EntityContext ast; + IdContext ast; ast.SetStatic(); AssertThat(ast.HasStatic(), Equals(true)); AssertThat(ast.RemoveStatic(), Is().True()); @@ -71,7 +71,7 @@ go_bandit([]() { }); it("Can get statics", [&]() { - EntityContext ast; + IdContext ast; ast.SetStatic({4}); ast.SetStatic({2}); AssertThat(ast.GetStatic().i, Equals(4)); From 0cf5a7309063e65a92d74cbf9ad771a73bedd68f Mon Sep 17 00:00:00 2001 From: muit Date: Fri, 13 Mar 2026 18:25:00 +0100 Subject: [PATCH 2/7] Fixes for IdScopes and Contexts --- Include/Pipe/Core/Templates.h | 39 ++--- Include/PipeECS.h | 294 ++++++---------------------------- Src/PipeECS.cpp | 8 +- Tests/ECS/ECS.spec.cpp | 45 +----- 4 files changed, 77 insertions(+), 309 deletions(-) diff --git a/Include/Pipe/Core/Templates.h b/Include/Pipe/Core/Templates.h index 36ea8485..2cf537d1 100644 --- a/Include/Pipe/Core/Templates.h +++ b/Include/Pipe/Core/Templates.h @@ -166,17 +166,26 @@ namespace p using Type = typename TTypeListUnique::Type; }; - // Used for when TypeList helpers need TyleList but it is not defined yet - template - struct TDummyList + template typename Predicate> + struct TFilter; + + template typename Predicate> + struct TFilter, Predicate> { - using AsList = TTypeList; + using Type = TTypeList<>; // Empty result }; - template - constexpr TDummyList operator+(TDummyList, TDummyList) + + template typename Predicate> + struct TFilter, Predicate> { - return {}; - } + // Filter the remaining types (Tail...) + using FilteredTail = typename TFilter, Predicate>::Type; + // Include Head if predicate is true; otherwise, use FilteredTail directly + using Type = Select::value, + typename FilteredTail::template Prepend, // Prepend Head to FilteredTail + FilteredTail // Skip Head + >; + }; }; // namespace Detail @@ -236,12 +245,6 @@ namespace p : public Detail::TTypeListUnique, T...> {}; - template typename Predicate, typename... Remaining> - struct TFilter - { - using type = TTypeList<>; - }; - template struct TFirst : TypeIdentity {}; @@ -266,11 +269,6 @@ namespace p template class W> using WrapConstRef = TTypeList&...>; - template typename Predicate> - using Filter = decltype(( - Detail::TDummyList<>{} + ... - + Select::value, Detail::TDummyList, Detail::TDummyList<>>{}))::AsList; - template class M> using Map = TTypeList::Type...>; @@ -295,6 +293,9 @@ namespace p template using First = typename TFirst::Type; + template typename Predicate> + using Filter = Detail::TFilter, Predicate>::Type; + static constexpr auto Call(auto predicate) { return predicate.template operator()(); diff --git a/Include/PipeECS.h b/Include/PipeECS.h index 2070f163..4d21db67 100644 --- a/Include/PipeECS.h +++ b/Include/PipeECS.h @@ -1162,16 +1162,15 @@ namespace p template CopyConst>, Component>& AssurePool() const { - return AsParent().AssurePool(); + return AsParent().template AssurePool(); } template CopyConst>, Component>* GetPool() const { - return AsParent().GetPool(); + return AsParent().template GetPool(); } - template IdContext& GetContext() const { return AsParent().GetContext(); @@ -1215,8 +1214,8 @@ namespace p if (!mdfdPool.Has(id)) // If we store value, we only add if it it wasn't // modified already { - Component* comp = pool->TryGet(id); - if constexpr (IsMoveConstructible()) + Component* comp = pool->template TryGet(id); + if constexpr (IsMoveConstructible) { mdfdPool.Add( id, comp ? CMdfd{p::Move(*comp)} : CMdfd{}); @@ -1237,6 +1236,7 @@ namespace p template decltype(auto) Add(Id id, Component&& value = {}) const requires(IsMutable) { + P_Check(IsValid(id)); auto& pool = AssurePool(); if constexpr (HasAnyTypeStaticFlags(TF_ECS_AutoModify)) { @@ -1247,6 +1247,7 @@ namespace p template decltype(auto) Add(Id id, const Component& value) const requires(IsMutable) { + P_Check(IsValid(id)); auto& pool = AssurePool(); if constexpr (HasAnyTypeStaticFlags(TF_ECS_AutoModify)) { @@ -1363,16 +1364,18 @@ namespace p template Component& GetOrAdd(Id id) const requires(IsMutable) { - auto* pool = GetPool(); - if (pool->Has(id)) + auto& pool = AssurePool(); + + if constexpr (HasAnyTypeStaticFlags(TF_ECS_AutoModify)) { - if constexpr (HasAnyTypeStaticFlags(TF_ECS_AutoModify)) - { - MarkModified(id, pool); - } - return pool->Get(id); + MarkModified(id, *pool); + } + + if (pool.Has(id)) + { + return pool.Get(id); } - return pool->Add(id); + return pool.Add(id); } template @@ -1412,7 +1415,7 @@ namespace p } }; - struct P_API IdContext /* : public TIdOperations*/ + struct P_API IdContext : public TIdOperations { private: IdRegistry idRegistry; @@ -1434,213 +1437,8 @@ namespace p #pragma region Entities // Reflection helpers - void* AddDefault(TypeId typeId, Id id); - void Remove(TypeId typeId, Id id); - - - template - void MarkModified(TView ids, const TPool* pool = nullptr) const - { - if (!pool) - { - pool = GetPool(); - if (!pool) - { - return; - } - } - - auto& mdfdPool = AssurePool>(); - if constexpr (StoresLastModified) - { - mdfdPool.ReserveMore(ids.Size()); - for (Id id : ids) - { - Component* comp = pool->TryGet(id); - if constexpr (IsMoveConstructible()) - { - mdfdPool.Add( - id, comp ? CMdfd{p::Move(*comp)} : CMdfd{}); - } - else - { - mdfdPool.Add(id, comp ? CMdfd{*comp} : CMdfd{}); - } - } - } - else - { - mdfdPool.Add(ids.begin(), ids.end(), CMdfd{}); - } - } - - - // Adds Component to an entity (if the entity doesnt have it already) - template - decltype(auto) Add(Id id, Component&& value = {}) const requires(IsMutable) - { - P_Check(IsValid(id)); - auto& pool = AssurePool(); - if constexpr (HasAnyTypeStaticFlags(TF_ECS_AutoModify)) - { - MarkModified(id, &pool); - } - return pool.Add(id, p::Forward(value)); - } - template - decltype(auto) Add(Id id, const Component& value) const requires(IsMutable) - { - P_Check(IsValid(id)); - auto& pool = AssurePool(); - if constexpr (HasAnyTypeStaticFlags(TF_ECS_AutoModify)) - { - MarkModified(id, &pool); - } - return pool.Add(id, value); - } - // Adds Component to an entity (if the entity doesnt have it already) - template - void Add(Id id) requires(sizeof...(Component) > 1) - { - P_Check(IsValid(id)); - (Add(id), ...); - } - - // Add Component to many entities (if they dont have it already) - template - decltype(auto) AddN(TView ids, const Component& value = {}) - { - auto& pool = AssurePool(); - if constexpr (HasAnyTypeStaticFlags(TF_ECS_AutoModify)) - { - MarkModified(ids, &pool); - } - return pool.Add(ids.begin(), ids.end(), value); - } - - template - void AddN(TView ids, const TView& values) - { - P_Check(ids.Size() == values.Size()); - auto& pool = AssurePool(); - if constexpr (HasAnyTypeStaticFlags(TF_ECS_AutoModify)) - { - MarkModified(ids, &pool); - } - pool.Add(ids.begin(), ids.end(), values.begin()); - } - - // Add Components to many entities (if they don't have it already) - template - void AddN(TView ids) requires(sizeof...(Component) > 1) - { - (Add(ids), ...); - } - - - template - void Remove(const Id id) - { - if (auto* pool = GetPool()) - { - if constexpr (HasAnyTypeStaticFlags(TF_ECS_AutoModify)) - { - MarkModified(id, pool); - } - pool->Remove(id); - } - } - - template - void Remove(const Id id) requires(sizeof...(Component) > 1) - { - (Remove(id), ...); - } - template - void Remove(TView ids) - { - if (auto* pool = GetPool()) - { - if constexpr (HasAnyTypeStaticFlags(TF_ECS_AutoModify)) - { - MarkModified(ids, pool); - } - pool->Remove(ids); - } - } - - template - void Remove(TView ids) requires(sizeof...(Component) > 1) - { - (Remove(ids), ...); - } - - template - Component& Get(const Id id) const - { - auto* const pool = GetPool(); - P_Check(pool); - if constexpr (IsMutable - && HasAnyTypeStaticFlags(TF_ECS_AutoModify)) - { - MarkModified(id, pool); - } - return pool->Get(id); - } - template - TTuple Get(const Id id) const requires(sizeof...(Component) > 1) - { - return std::forward_as_tuple(Get(id)...); - } - template - Component* TryGet(const Id id) const - { - auto* const pool = GetPool(); - P_Check(pool); - Component* value = pool->TryGet(id); - if constexpr (IsMutable - && HasAnyTypeStaticFlags(TF_ECS_AutoModify)) - { - if (value) - { - MarkModified(id, pool); - } - } - return value; - } - template - TTuple TryGet(const Id id) const requires(sizeof...(Component) > 1) - { - return std::forward_as_tuple(TryGet(id)...); - } - - template - Component& GetOrAdd(Id id) - { - auto* pool = GetPool(); - if (pool->Has(id)) - { - if constexpr (HasAnyTypeStaticFlags(TF_ECS_AutoModify)) - { - MarkModified(id, pool); - } - return pool->Get(id); - } - return pool->Add(id); - } - - template - bool Has(Id id) const - { - const auto* pool = GetPool(); - return pool && pool->Has(id); - } - - template - bool Has(Id id) const requires(sizeof...(Component) >= 2) - { - return (Has(id) && ...); - } + void* AddByTypeId(TypeId typeId, Id id); + void RemoveByTypeId(TypeId typeId, Id id); const IdRegistry& GetIdRegistry() const { @@ -1693,15 +1491,20 @@ namespace p void Reset(bool keepStatics = false); + IdContext& GetContext() const + { + return *const_cast(this); + } + // Finds or creates a pool template TPool>& AssurePool() const; - IPool* GetPool(TypeId componentId) const; - void GetPools(TView componentIds, TArray& outPools) const; - void GetPools(TView componentIds, TArray& outPools) const + IPool* GetPool(TypeId typeId) const; + void GetPools(TView typeIds, TArray& outPools) const; + void GetPools(TView typeIds, TArray& outPools) const { - GetPools(componentIds, reinterpret_cast&>(outPools)); + GetPools(typeIds, reinterpret_cast&>(outPools)); } template @@ -1789,21 +1592,21 @@ namespace p static constexpr bool value = HasAnyTypeStaticFlags(TF_ECS_AutoModify); }; - template - struct TIdScopeBase : public TIdOperations> + + // Base for TIdScope. Assumes all types are mutable (not const) + template + struct TIdScopeBase : public TIdOperations> { - template + template friend struct TIdScopeBase; private: - using RawWrites = W::template Wrap; - using RawReads = R::template Wrap; - using ModifyWrites = RawWrites::template Filter::template Wrap; - using ModifyReads = RawReads::template Filter::template Wrap; + using ModifyWrites = W::template Filter; + using ModifyReads = TTypeList::template Filter::template Wrap; public: - using WriteDependencies = RawWrites::template Append; - using ReadDependencies = RawReads::template Append; + using WriteDependencies = W::template Append; + using ReadDependencies = TTypeList::template Append; using Dependencies = ReadDependencies::template Append::Deduplicate; using Pools = Dependencies::template WrapPtr; using Tuple = Pools::template To; @@ -1831,7 +1634,6 @@ namespace p { using Other = TIdScopeBase; - constexpr bool validReads = Dependencies::Call([]() { return (Other::template IsReadable() && ...); }); @@ -1933,15 +1735,15 @@ namespace p {}; template - struct TIdScope : public TIdScopeBase, TTypeList> + struct TIdScope : public TIdScopeBase, Mut...> { - using TIdScopeBase, TTypeList>::TIdScopeBase; + using TIdScopeBase, Mut...>::TIdScopeBase; }; template - struct TIdScope, R...> : public TIdScopeBase, TTypeList> + struct TIdScope, R...> : public TIdScopeBase...>, Mut...> { - using TIdScopeBase, TTypeList>::TIdScopeBase; + using TIdScopeBase...>, Mut...>::TIdScopeBase; }; @@ -1958,7 +1760,7 @@ namespace p public: - IdScope(IdContext& ast, const TArray& types) : context{context} {} + IdScope(IdContext& ctx, const TArray& types) : context{ctx} {} template IdScope(TIdScopeRef scope) : context{scope.context} @@ -2582,23 +2384,23 @@ namespace p template inline void EntityWriter::SerializePool() { - TArray> componentIds; // TODO: Make sure this is needed + TArray> typeIds; // TODO: Make sure this is needed auto* pool = context.GetPool(); if (pool) { - componentIds.Reserve(Min(i32(pool->Size()), ids.Size())); + typeIds.Reserve(Min(i32(pool->Size()), ids.Size())); for (i32 i = 0; i < ids.Size(); ++i) { const Id id = ids[i]; if (pool->Has(id)) { - componentIds.Add({i, id}); + typeIds.Add({i, id}); } } } - if (componentIds.IsEmpty()) + if (typeIds.IsEmpty()) { return; } @@ -2612,7 +2414,7 @@ namespace p { String key; BeginObject(); - for (auto id : componentIds) + for (auto id : typeIds) { key.clear(); Strings::FormatTo(key, "{}", id.first); @@ -2635,7 +2437,7 @@ namespace p } else { - Serialize(pool->Get(componentIds.First().second)); + Serialize(pool->Get(typeIds.First().second)); } } Leave(); diff --git a/Src/PipeECS.cpp b/Src/PipeECS.cpp index fde4555c..303aea01 100644 --- a/Src/PipeECS.cpp +++ b/Src/PipeECS.cpp @@ -664,7 +664,7 @@ namespace p return *this; } - void* IdContext::AddDefault(TypeId typeId, Id id) + void* IdContext::AddByTypeId(TypeId typeId, Id id) { if (IPool* pool = GetPool(typeId)) { @@ -673,7 +673,7 @@ namespace p return nullptr; } - void IdContext::Remove(TypeId typeId, Id id) + void IdContext::RemoveByTypeId(TypeId typeId, Id id) { if (IPool* pool = GetPool(typeId)) { @@ -687,9 +687,9 @@ namespace p return index != NO_INDEX ? pools[index].GetPool() : nullptr; } - void IdContext::GetPools(TView componentIds, TArray& outPools) const + void IdContext::GetPools(TView typeIds, TArray& outPools) const { - for (const TypeId componentId : componentIds) + for (const TypeId componentId : typeIds) { const i32 index = pools.FindSorted(PoolInstance{componentId, {}}); if (index != NO_INDEX) diff --git a/Tests/ECS/ECS.spec.cpp b/Tests/ECS/ECS.spec.cpp index a09ff99a..0d487993 100644 --- a/Tests/ECS/ECS.spec.cpp +++ b/Tests/ECS/ECS.spec.cpp @@ -21,23 +21,12 @@ go_bandit([]() { describe("ECS", []() { it("Can copy tree", [&]() { static IdContext* ctxPtr = nullptr; - static bool calledAdd; IdContext origin; Id id = AddId(origin); - calledAdd = false; - ctxPtr = &origin; - origin.OnAdd().Bind([&origin](auto ids) { - for (Id id : ids) - { - AssertThat(origin.Has(id), Equals(true)); - } - AssertThat(ctxPtr, Equals(&origin)); - calledAdd = true; - }); + ctxPtr = &origin; origin.Add(id); - AssertThat(calledAdd, Equals(true)); IdContext target{origin}; AssertThat(origin.IsValid(id), Equals(true)); @@ -45,18 +34,9 @@ go_bandit([]() { AssertThat(target.IsValid(id), Equals(true)); AssertThat(target.Has(id), Equals(true)); - calledAdd = false; - ctxPtr = ⌖ - target.OnAdd().Bind([&target](auto ids) { - for (Id id : ids) - { - AssertThat(target.Has(id), Equals(true)); - } - AssertThat(ctxPtr, Equals(&target)); - calledAdd = true; - }); + ctxPtr = ⌖ target.Add(id); - AssertThat(calledAdd, Equals(true)); + AssertThat(target.Has(id), Equals(true)); }); it("Can move tree", [&]() { @@ -68,15 +48,8 @@ go_bandit([]() { calledAdd = false; ctxPtr = &origin; - origin.OnAdd().Bind([&origin](auto ids) { - for (Id id : ids) - { - AssertThat(origin.Has(id), Equals(true)); - } - AssertThat(ctxPtr, Equals(&origin)); - calledAdd = true; - }); origin.Add(id); + AssertThat(origin.Has(id), Equals(true)); AssertThat(calledAdd, Equals(true)); IdContext target{Move(origin)}; @@ -87,16 +60,8 @@ go_bandit([]() { calledAdd = false; ctxPtr = ⌖ - target.OnAdd().Bind([&target](auto ids) { - for (Id id : ids) - { - AssertThat(target.Has(id), Equals(true)); - } - AssertThat(ctxPtr, Equals(&target)); - calledAdd = true; - }); target.Add(id); - AssertThat(calledAdd, Equals(true)); + AssertThat(target.Has(id), Equals(true)); }); it("Can assure pool", [&]() { From e4e3127dd73b2b8e70ee6e07819d947222faee61 Mon Sep 17 00:00:00 2001 From: muit Date: Fri, 13 Mar 2026 18:32:06 +0100 Subject: [PATCH 3/7] Fix compile error on Linux --- Src/PipeECS.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Src/PipeECS.cpp b/Src/PipeECS.cpp index 303aea01..e9a7347a 100644 --- a/Src/PipeECS.cpp +++ b/Src/PipeECS.cpp @@ -6,6 +6,8 @@ #include "Pipe/Core/Limits.h" #include "Pipe/Core/Set.h" +#include + namespace p { From fbea84c80fa8ce4a242095840ba8fa95859fce8a Mon Sep 17 00:00:00 2001 From: muit Date: Sun, 15 Mar 2026 13:30:31 +0100 Subject: [PATCH 4/7] Small fix, added Modify tests --- Include/Pipe/Core/Templates.h | 2 + Include/PipeECS.h | 35 ++++----- Tests/ECS/Access.spec.cpp | 70 ------------------ Tests/ECS/ECS.spec.cpp | 12 +-- Tests/ECS/IdScopes.spec.cpp | 135 ++++++++++++++++++++++++++++++++++ 5 files changed, 159 insertions(+), 95 deletions(-) delete mode 100644 Tests/ECS/Access.spec.cpp create mode 100644 Tests/ECS/IdScopes.spec.cpp diff --git a/Include/Pipe/Core/Templates.h b/Include/Pipe/Core/Templates.h index 2cf537d1..47fdcbe2 100644 --- a/Include/Pipe/Core/Templates.h +++ b/Include/Pipe/Core/Templates.h @@ -2,6 +2,7 @@ #pragma once +#include "Pipe/Core/TypeId.h" #include "Pipe/Core/TypeTraits.h" #include "Pipe/Core/Utility.h" @@ -199,6 +200,7 @@ namespace p using type = TTypeList; static constexpr auto size = sizeof...(T); + static constexpr TypeId typeIds[]{GetTypeId...}; template diff --git a/Include/PipeECS.h b/Include/PipeECS.h index 4d21db67..6f2fb430 100644 --- a/Include/PipeECS.h +++ b/Include/PipeECS.h @@ -1233,6 +1233,12 @@ namespace p } } + template + bool IsModified(Id id) const + { + return Has>>(id); + } + template decltype(auto) Add(Id id, Component&& value = {}) const requires(IsMutable) { @@ -1601,20 +1607,15 @@ namespace p friend struct TIdScopeBase; private: - using ModifyWrites = W::template Filter; + using ModifyWrites = W::template Filter::template Wrap; using ModifyReads = TTypeList::template Filter::template Wrap; public: - using WriteDependencies = W::template Append; - using ReadDependencies = TTypeList::template Append; - using Dependencies = ReadDependencies::template Append::Deduplicate; - using Pools = Dependencies::template WrapPtr; - using Tuple = Pools::template To; - - // template - // using Reads = TIdScope; - // template - // using Writes = TIdScope, R...>; + using WDependencies = W::template Append; + using RDependencies = TTypeList::template Append; + using RWDependencies = RDependencies::template Append::Deduplicate; + using Pools = RWDependencies::template WrapPtr; + using Tuple = Pools::template To; protected: IdContext& context; @@ -1622,7 +1623,7 @@ namespace p public: TIdScopeBase(IdContext& context) - : context{context}, pools(Dependencies::Call([&context] { + : context{context}, pools(RWDependencies::Call([&context] { return Tuple{&context.AssurePool()...}; })) {} @@ -1634,11 +1635,11 @@ namespace p { using Other = TIdScopeBase; - constexpr bool validReads = Dependencies::Call([]() { + constexpr bool validReads = RWDependencies::Call([]() { return (Other::template IsReadable() && ...); }); - constexpr bool validWrites = WriteDependencies::Call([]() { + constexpr bool validWrites = WDependencies::Call([]() { return (Other::template IsWritable() && ...); }); @@ -1650,7 +1651,7 @@ namespace p // Prevent compiler errors, we already have static_asserts if constexpr (validReads && validWrites) { - pools = Dependencies::Call([&other] { + pools = RWDependencies::Call([&other] { return Tuple{std::get*>(other.pools)...}; }); } @@ -1719,13 +1720,13 @@ namespace p template static constexpr bool IsReadable() { - return Dependencies::template Contains>(); + return RWDependencies::template Contains>(); } template static constexpr bool IsWritable() { - return IsMutable && WriteDependencies::template Contains(); + return IsMutable && WDependencies::template Contains(); } }; diff --git a/Tests/ECS/Access.spec.cpp b/Tests/ECS/Access.spec.cpp deleted file mode 100644 index 8d1c98a6..00000000 --- a/Tests/ECS/Access.spec.cpp +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2015-2026 Piperift. All Rights Reserved. - -#include -#include - - -using namespace snowhouse; -using namespace bandit; -using namespace p; - - -struct TypeA -{}; -struct TypeB -{}; - - -go_bandit([]() { - describe("ECS.Access", []() { - describe("Templated", []() { - it("Can cache pools", [&]() { - IdContext ctx; - TIdScope> access{ctx}; - - AssertThat(access.GetPool(), Equals(ctx.GetPool())); - AssertThat(access.GetPool(), Equals(ctx.GetPool())); - AssertThat(access.GetPool(), Equals(ctx.GetPool())); - }); - - it("Can check if contained", [&]() { - IdContext ctx; - TPool& pool = ctx.AssurePool(); - TIdScope> access{ctx}; - TIdScope accessConst{ctx}; - Id id = NoId; - AssertThat(access.Has(id), Is().False()); - AssertThat(accessConst.Has(id), Is().False()); - - id = AddId(ctx); - AssertThat(access.Has(id), Is().False()); - AssertThat(accessConst.Has(id), Is().False()); - - ctx.Add(id); - AssertThat(access.Has(id), Is().True()); - AssertThat(accessConst.Has(id), Is().True()); - - TIdScope access2{ctx}; - ctx.Add(id); - AssertThat(access2.Has(id), Is().True()); - }); - - it("Can initialize superset", [&]() { - IdContext ctx; - TPool& typePool = ctx.AssurePool(); - - TIdScope> access1{ctx}; - TIdScope> superset1{access1}; - AssertThat(superset1.GetPool(), Equals(&typePool)); - - TIdScope> access2{ctx}; - TIdScope superset2{access2}; - AssertThat(superset2.GetPool(), Equals(&typePool)); - - TIdScope> access3{ctx}; - TIdScope superset3{access3}; - AssertThat(superset1.GetPool(), Equals(&typePool)); - }); - }); - }); -}); diff --git a/Tests/ECS/ECS.spec.cpp b/Tests/ECS/ECS.spec.cpp index 0d487993..5dbb4e91 100644 --- a/Tests/ECS/ECS.spec.cpp +++ b/Tests/ECS/ECS.spec.cpp @@ -19,7 +19,7 @@ struct ATypeB go_bandit([]() { describe("ECS", []() { - it("Can copy tree", [&]() { + it("Can copy context", [&]() { static IdContext* ctxPtr = nullptr; IdContext origin; @@ -39,18 +39,15 @@ go_bandit([]() { AssertThat(target.Has(id), Equals(true)); }); - it("Can move tree", [&]() { + it("Can move context", [&]() { static IdContext* ctxPtr = nullptr; - static bool calledAdd; IdContext origin; Id id = AddId(origin); - calledAdd = false; - ctxPtr = &origin; + ctxPtr = &origin; origin.Add(id); AssertThat(origin.Has(id), Equals(true)); - AssertThat(calledAdd, Equals(true)); IdContext target{Move(origin)}; AssertThat(origin.IsValid(id), Equals(false)); @@ -58,8 +55,7 @@ go_bandit([]() { AssertThat(target.IsValid(id), Equals(true)); AssertThat(target.Has(id), Equals(true)); - calledAdd = false; - ctxPtr = ⌖ + ctxPtr = ⌖ target.Add(id); AssertThat(target.Has(id), Equals(true)); }); diff --git a/Tests/ECS/IdScopes.spec.cpp b/Tests/ECS/IdScopes.spec.cpp new file mode 100644 index 00000000..77bc12a0 --- /dev/null +++ b/Tests/ECS/IdScopes.spec.cpp @@ -0,0 +1,135 @@ +// Copyright 2015-2026 Piperift. All Rights Reserved. + +#include +#include + + +using namespace snowhouse; +using namespace bandit; +using namespace p; + + +struct TypeA +{}; +struct TypeB +{ + bool data; // Not empty type +}; +struct TypeC +{ + P_STRUCT(TypeC, TF_ECS_AutoModify) + + bool data; // Not empty type +}; + + +go_bandit([]() { + describe("ECS.IdScopes", []() { + describe("Templated", []() { + it("Can cache pools", [&]() { + IdContext ctx; + TIdScope> scope{ctx}; + + AssertThat(scope.GetPool(), Equals(ctx.GetPool())); + AssertThat(scope.GetPool(), Equals(ctx.GetPool())); + AssertThat(scope.GetPool(), Equals(ctx.GetPool())); + }); + + it("Can check if contained", [&]() { + IdContext ctx; + TPool& pool = ctx.AssurePool(); + TIdScope> scope{ctx}; + TIdScope scopeConst{ctx}; + Id id = NoId; + AssertThat(scope.Has(id), Is().False()); + AssertThat(scopeConst.Has(id), Is().False()); + + id = AddId(ctx); + AssertThat(scope.Has(id), Is().False()); + AssertThat(scopeConst.Has(id), Is().False()); + + ctx.Add(id); + AssertThat(scope.Has(id), Is().True()); + AssertThat(scopeConst.Has(id), Is().True()); + + TIdScope scope2{ctx}; + ctx.Add(id); + AssertThat(scope2.Has(id), Is().True()); + }); + + it("Can initialize superset", [&]() { + IdContext ctx; + TPool& typePool = ctx.AssurePool(); + + TIdScope> scope1{ctx}; + TIdScope> superset1{scope1}; + AssertThat(superset1.GetPool(), Equals(&typePool)); + + TIdScope> scope2{ctx}; + TIdScope superset2{scope2}; + AssertThat(superset2.GetPool(), Equals(&typePool)); + + TIdScope> scope3{ctx}; + TIdScope superset3{scope3}; + AssertThat(superset1.GetPool(), Equals(&typePool)); + }); + + it("Can mark modify", [&]() { + IdContext ctx; + Id id = AddId(ctx); + TIdScope>> scope1{ctx}; + AssertThat(scope1.Has>(id), Is().False()); + scope1.MarkModified(id); + AssertThat(scope1.Has>(id), Is().True()); + AssertThat(scope1.IsModified(id), Is().True()); + + scope1.Remove>(id); + AssertThat(scope1.Has>(id), Is().False()); + AssertThat(scope1.IsModified(id), Is().False()); + + scope1.MarkModified(id); + AssertThat(scope1.Has>(id), Is().True()); + AssertThat(scope1.IsModified(id), Is().True()); + }); + + it("Can mark modify automatically", [&]() { + IdContext ctx; + Id id = AddId(ctx); + using MyScope = TIdScope, CMdfd>; + MyScope scope{ctx}; + AssertThat(MyScope::WDependencies::Contains>(), Is().True()); + AssertThat(MyScope::WDependencies::Contains>(), Is().False()); + AssertThat(MyScope::RWDependencies::Contains>(), Is().True()); + AssertThat(MyScope::RWDependencies::Contains>(), Is().True()); + + scope.Add(id); // Type B should be auto modified + AssertThat(scope.IsModified(id), Is().True()); + scope.Add(id); // Type B should not be auto modified + AssertThat(scope.IsModified(id), Is().False()); + + scope.ClearPool>(); + AssertThat(scope.IsModified(id), Is().False()); + + scope.Has(id); // Has should never mark modify + AssertThat(scope.IsModified(id), Is().False()); + + scope.Get(id); + AssertThat(scope.IsModified(id), Is().False()); + scope.Get(id); + AssertThat(scope.IsModified(id), Is().True()); + scope.Add(id); // Type B should not be auto modified + AssertThat(scope.IsModified(id), Is().False()); + + scope.ClearPool>(); + + scope.Remove(id); + AssertThat(scope.Has(id), Is().False()); + AssertThat(scope.IsModified(id), Is().True()); + + scope.Remove(id); // Type B should not be auto modified + AssertThat(scope.Has(id), Is().False()); + AssertThat(scope.IsModified(id), Is().False()); + }); + }); + }); +}); From 67da0d4f379419b6f19056ed960e094f69c7d433 Mon Sep 17 00:00:00 2001 From: muit Date: Sun, 15 Mar 2026 13:46:13 +0100 Subject: [PATCH 5/7] Small changes --- Include/Pipe/Core/Templates.h | 2 -- Include/PipeECS.h | 11 +++++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Include/Pipe/Core/Templates.h b/Include/Pipe/Core/Templates.h index 47fdcbe2..2cf537d1 100644 --- a/Include/Pipe/Core/Templates.h +++ b/Include/Pipe/Core/Templates.h @@ -2,7 +2,6 @@ #pragma once -#include "Pipe/Core/TypeId.h" #include "Pipe/Core/TypeTraits.h" #include "Pipe/Core/Utility.h" @@ -200,7 +199,6 @@ namespace p using type = TTypeList; static constexpr auto size = sizeof...(T); - static constexpr TypeId typeIds[]{GetTypeId...}; template diff --git a/Include/PipeECS.h b/Include/PipeECS.h index 6f2fb430..39f32dc5 100644 --- a/Include/PipeECS.h +++ b/Include/PipeECS.h @@ -200,6 +200,17 @@ namespace p using Type = T; }; + template + static constexpr bool IsMdfdType() requires(!HasTypeMember) + { + return false; + } + template + static constexpr bool IsMdfdType() requires(HasTypeMember) + { + return IsSame, Component>; + } + /** * Removed component * Optionally, an entity can be marked removed instead of actually removed and then flushed From f48191f5f75b408f3b57101a4b217ced69c83d5a Mon Sep 17 00:00:00 2001 From: muit Date: Tue, 17 Mar 2026 12:25:57 +0100 Subject: [PATCH 6/7] Fixed template on non template function & Has on invalid pool --- Include/PipeECS.h | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/Include/PipeECS.h b/Include/PipeECS.h index 39f32dc5..5be0cfaa 100644 --- a/Include/PipeECS.h +++ b/Include/PipeECS.h @@ -1198,21 +1198,27 @@ namespace p return GetContext().IsValid(id); } + template + bool Has(Id id) const + { + const auto* pool = GetPool(); + return pool && pool->Has(id); + } template - bool Has(Id id) const requires(sizeof...(Component) >= 1) + bool Has(Id id) const requires(sizeof...(Component) > 1) { - return (GetPool()->Has(id) && ...); + return (Has(id) && ...); } template - void MarkModified(TView ids, const TPool* pool = nullptr) const + void MarkModified(TView ids, TPool* pool = nullptr) const { auto& mdfdPool = AssurePool>(); if constexpr (StoresLastModified) { if (!pool) { - pool = GetPool(); + pool = GetPool(); if (!pool) { return; @@ -1225,7 +1231,7 @@ namespace p if (!mdfdPool.Has(id)) // If we store value, we only add if it it wasn't // modified already { - Component* comp = pool->template TryGet(id); + Component* comp = pool->TryGet(id); if constexpr (IsMoveConstructible) { mdfdPool.Add( From 9540cc518b99902ac22406f2063971554da465b9 Mon Sep 17 00:00:00 2001 From: muit Date: Tue, 17 Mar 2026 14:00:52 +0100 Subject: [PATCH 7/7] Specific Modify Flags --- Include/Pipe/Core/EnumFlags.h | 58 ++++++++++------ Include/PipeECS.h | 54 ++++++++------- Include/PipeReflect.h | 19 ++++-- Tests/Containers/Arrays.spec.cpp | 109 ++++++++++++++++++++----------- Tests/ECS/IdScopes.spec.cpp | 6 +- 5 files changed, 155 insertions(+), 91 deletions(-) diff --git a/Include/Pipe/Core/EnumFlags.h b/Include/Pipe/Core/EnumFlags.h index 04a8e358..e0c01399 100644 --- a/Include/Pipe/Core/EnumFlags.h +++ b/Include/Pipe/Core/EnumFlags.h @@ -7,73 +7,91 @@ namespace p { + template + constexpr bool HasAllFlags(I value, I flags) noexcept + { + return (value & flags) == flags; + } + + template + constexpr bool HasAnyFlags(I value, I flags) noexcept + { + return (value & flags) != 0; + } + + template + constexpr bool HasFlag(I value, I flag) noexcept + { + return HasAllFlags(value, flag); + } + namespace EnumOperators { // Bitwise operators: ~, |, &, ^, |=, &=, ^= using namespace magic_enum::bitwise_operators; - template - constexpr UnderlyingType operator*(E value) noexcept requires(IsEnum) + template + constexpr UnderlyingType operator*(E value) noexcept { return static_cast>(value); } - template - constexpr E operator|(E lhs, E rhs) noexcept requires(IsEnum) + template + constexpr E operator|(E lhs, E rhs) noexcept { return static_cast( static_cast>(lhs) | static_cast>(rhs)); } } // namespace EnumOperators - template - constexpr bool HasAllFlags(E value, E flags) noexcept requires(IsEnum) + template + constexpr bool HasAllFlags(E value, E flags) noexcept { return (static_cast>(value) & static_cast>(flags)) == static_cast>(flags); } - template - constexpr bool HasAnyFlags(E value, E flags) noexcept requires(IsEnum) + template + constexpr bool HasAnyFlags(E value, E flags) noexcept { return (static_cast>(value) & static_cast>(flags)) != 0; } - template> - constexpr bool HasAllFlags(R value, E flags) noexcept requires(IsEnum) + template> + constexpr bool HasAllFlags(R value, E flags) noexcept { return (static_cast(value) & static_cast>(flags)) == static_cast>(flags); } - template> - constexpr bool HasAnyFlags(R value, E flags) noexcept requires(IsEnum) + template> + constexpr bool HasAnyFlags(R value, E flags) noexcept { return (static_cast(value) & static_cast>(flags)) != 0; } - template - constexpr bool HasFlag(E value, E flag) noexcept requires(IsEnum) + template + constexpr bool HasFlag(E value, E flag) noexcept { return HasAllFlags(value, flag); } - template> - constexpr bool HasFlag(R value, E flag) noexcept requires(IsEnum) + template> + constexpr bool HasFlag(R value, E flag) noexcept { return HasAllFlags(value, flag); } - template - void AddFlags(E& value, E flags) noexcept requires(IsEnum) + template + void AddFlags(E& value, E flags) noexcept { auto rawValue = static_cast>(value); rawValue |= static_cast>(flags); value = static_cast(rawValue); } - template - void RemoveFlags(E& value, E flags) noexcept requires(IsEnum) + template + void RemoveFlags(E& value, E flags) noexcept { auto rawValue = static_cast>(value); rawValue &= ~static_cast>(flags); diff --git a/Include/PipeECS.h b/Include/PipeECS.h index 5be0cfaa..377ec8fe 100644 --- a/Include/PipeECS.h +++ b/Include/PipeECS.h @@ -1211,7 +1211,7 @@ namespace p } template - void MarkModified(TView ids, TPool* pool = nullptr) const + void Modify(TView ids, TPool* pool = nullptr) const { auto& mdfdPool = AssurePool>(); if constexpr (StoresLastModified) @@ -1261,9 +1261,9 @@ namespace p { P_Check(IsValid(id)); auto& pool = AssurePool(); - if constexpr (HasAnyTypeStaticFlags(TF_ECS_AutoModify)) + if constexpr (HasAnyTypeStaticFlags(TF_ECS_ModifyOnAdd)) { - MarkModified(id, &pool); + Modify(id, &pool); } return pool.Add(id, p::Forward(value)); } @@ -1272,9 +1272,9 @@ namespace p { P_Check(IsValid(id)); auto& pool = AssurePool(); - if constexpr (HasAnyTypeStaticFlags(TF_ECS_AutoModify)) + if constexpr (HasAnyTypeStaticFlags(TF_ECS_ModifyOnAdd)) { - MarkModified(id, &pool); + Modify(id, &pool); } return pool.Add(id, value); } @@ -1291,9 +1291,9 @@ namespace p decltype(auto) AddN(TView ids, const Component& value = {}) const { auto& pool = AssurePool(); - if constexpr (HasAnyTypeStaticFlags(TF_ECS_AutoModify)) + if constexpr (HasAnyTypeStaticFlags(TF_ECS_ModifyOnAdd)) { - MarkModified(ids, &pool); + Modify(ids, &pool); } return pool.Add(ids.begin(), ids.end(), value); } @@ -1303,9 +1303,9 @@ namespace p { P_Check(ids.Size() == values.Size()); auto& pool = AssurePool(); - if constexpr (HasAnyTypeStaticFlags(TF_ECS_AutoModify)) + if constexpr (HasAnyTypeStaticFlags(TF_ECS_ModifyOnAdd)) { - MarkModified(ids, &pool); + Modify(ids, &pool); } pool.Add(ids.begin(), ids.end(), values.begin()); } @@ -1324,9 +1324,9 @@ namespace p { if (auto* pool = GetPool()) { - if constexpr (HasAnyTypeStaticFlags(TF_ECS_AutoModify)) + if constexpr (HasAnyTypeStaticFlags(TF_ECS_ModifyOnRm)) { - MarkModified(id, pool); + Modify(id, pool); } pool->Remove(id); } @@ -1341,9 +1341,9 @@ namespace p { if (auto* pool = GetPool()) { - if constexpr (HasAnyTypeStaticFlags(TF_ECS_AutoModify)) + if constexpr (HasAnyTypeStaticFlags(TF_ECS_ModifyOnRm)) { - MarkModified(ids, pool); + Modify(ids, pool); } pool->Remove(ids); } @@ -1360,9 +1360,9 @@ namespace p auto* const pool = GetPool(); P_Check(pool); if constexpr (IsMutable - && HasAnyTypeStaticFlags(TF_ECS_AutoModify)) + && HasAnyTypeStaticFlags(TF_ECS_ModifyOnGet)) { - MarkModified(id, pool); + Modify(id, pool); } return pool->Get(id); } @@ -1374,11 +1374,11 @@ namespace p P_Check(pool); Component* value = pool->TryGet(id); if constexpr (IsMutable - && HasAnyTypeStaticFlags(TF_ECS_AutoModify)) + && HasAnyTypeStaticFlags(TF_ECS_ModifyOnGet)) { if (value) { - MarkModified(id, pool); + Modify(id, pool); } } return value; @@ -1389,16 +1389,22 @@ namespace p { auto& pool = AssurePool(); - if constexpr (HasAnyTypeStaticFlags(TF_ECS_AutoModify)) - { - MarkModified(id, *pool); - } - if (pool.Has(id)) { + if constexpr (HasAnyTypeStaticFlags(TF_ECS_ModifyOnGet)) + { + Modify(id, *pool); + } return pool.Get(id); } - return pool.Add(id); + else + { + if constexpr (HasAnyTypeStaticFlags(TF_ECS_ModifyOnAdd)) + { + Modify(id, *pool); + } + return pool.Add(id); + } } template @@ -1612,7 +1618,7 @@ namespace p template struct TIsAutoModified { - static constexpr bool value = HasAnyTypeStaticFlags(TF_ECS_AutoModify); + static constexpr bool value = HasAnyTypeStaticFlags(TF_ECS_ModifyOnEdit); }; diff --git a/Include/PipeReflect.h b/Include/PipeReflect.h index c6b07d63..de637773 100644 --- a/Include/PipeReflect.h +++ b/Include/PipeReflect.h @@ -198,10 +198,17 @@ namespace p TF_Object = 1 << 4, TF_Container = 1 << 6, - TF_ECS_AutoModify = 1 << 7, // -> In ECS, should this type be marked modified - // automatically when added, written or removed? - TF_ECS_StoreLastModified = 1 << 8 // -> In ECS, should this type's last value be stored - // in the CMdfd component before being removed? + // -> In ECS, should this type's last value be stored in the CMdfd component before being + // removed? + TF_ECS_StoreLastModified = 1 << 7, + // -> In ECS, should this type be marked modified automatically on add? + TF_ECS_ModifyOnAdd = 1 << 8, + // -> In ECS, should this type be marked modified automatically on get? + TF_ECS_ModifyOnGet = 1 << 9, + // -> In ECS, should this type be marked modified automatically on remove? + TF_ECS_ModifyOnRm = 1 << 10, + // -> In ECS, should this type be marked modified automatically on add, get or remove? + TF_ECS_ModifyOnEdit = TF_ECS_ModifyOnAdd | TF_ECS_ModifyOnGet | TF_ECS_ModifyOnRm, // Any other flags up to 64 bytes are available to the user }; @@ -354,7 +361,7 @@ namespace p { if constexpr (HasStaticFlags()) { - return (T::staticFlags & flags) == flags; + return HasAllFlags(T::staticFlags, flags); } return false; } @@ -363,7 +370,7 @@ namespace p { if constexpr (HasStaticFlags()) { - return (T::staticFlags & flags) > 0; + return HasAnyFlags(T::staticFlags, flags); } return false; } diff --git a/Tests/Containers/Arrays.spec.cpp b/Tests/Containers/Arrays.spec.cpp index d48954f6..b69e0f8f 100644 --- a/Tests/Containers/Arrays.spec.cpp +++ b/Tests/Containers/Arrays.spec.cpp @@ -319,6 +319,7 @@ go_bandit([]() { AssertThat(data[0], Equals(0)); }); }); + describe("Append", []() { it("Can append defaulted", [&]() { TInlineArray data; @@ -384,6 +385,7 @@ go_bandit([]() { AssertThat(data.Data(), Equals(data.GetInlineBuffer())); }); }); + describe("Assign", []() { it("Can assign defaulted", [&]() { TInlineArray data; @@ -449,6 +451,7 @@ go_bandit([]() { AssertThat(data.Data(), Equals(data.GetInlineBuffer())); }); }); + describe("Insert", []() { it("Can insert at empty", [&]() { TInlineArray data; @@ -585,6 +588,7 @@ go_bandit([]() { AssertThat(data[11], Equals(3)); }); }); + describe("Remove", []() { it("Can remove at index", []() { TInlineArray data{1, 2, 3, 4}; @@ -659,54 +663,54 @@ go_bandit([]() { AssertThat(data.RemoveAtSwap(0, 2), Equals(true)); // Remove first AssertThat(data, Equals(TInlineArray{6})); }); - }); - it("Can RemoveLast", [&]() { - TArray data{1, 4, 6}; - data.RemoveLast(); - AssertThat(data.Size(), Equals(2)); - AssertThat(data[0], Equals(1)); - AssertThat(data[1], Equals(4)); - AssertThat(data.Capacity(), Equals(2)); - }); + it("Can RemoveLast", [&]() { + TArray data{1, 4, 6}; + data.RemoveLast(); + AssertThat(data.Size(), Equals(2)); + AssertThat(data[0], Equals(1)); + AssertThat(data[1], Equals(4)); + AssertThat(data.Capacity(), Equals(2)); + }); - it("Can RemoveLast N", [&]() { - TArray dataA{1, 4, 6}; - dataA.RemoveLast(2); - AssertThat(dataA.Size(), Equals(1)); - AssertThat(dataA[0], Equals(1)); - AssertThat(dataA.Capacity(), Equals(1)); - - TArray dataB{1, 4, 6}; - dataB.RemoveLast(3); - AssertThat(dataB.Size(), Equals(0)); - AssertThat(dataB.Capacity(), Equals(0)); - }); + it("Can RemoveLast N", [&]() { + TArray dataA{1, 4, 6}; + dataA.RemoveLast(2); + AssertThat(dataA.Size(), Equals(1)); + AssertThat(dataA[0], Equals(1)); + AssertThat(dataA.Capacity(), Equals(1)); - it("Can RemoveIf", [&]() { - TArray data{1, 4, 5, 6}; + TArray dataB{1, 4, 6}; + dataB.RemoveLast(3); + AssertThat(dataB.Size(), Equals(0)); + AssertThat(dataB.Capacity(), Equals(0)); + }); - AssertThat(data.Size(), Equals(4)); + it("Can RemoveIf", [&]() { + TArray data{1, 4, 5, 6}; - data.RemoveIf([](i32 v) { - return v == 1 || v == 6; + AssertThat(data.Size(), Equals(4)); + + data.RemoveIf([](i32 v) { + return v == 1 || v == 6; + }); + AssertThat(data.Size(), Equals(2)); + AssertThat(data[0], Equals(4)); + AssertThat(data[1], Equals(5)); }); - AssertThat(data.Size(), Equals(2)); - AssertThat(data[0], Equals(4)); - AssertThat(data[1], Equals(5)); - }); - it("Can RemoveIfSwap", [&]() { - TArray data{1, 4, 5, 6}; + it("Can RemoveIfSwap", [&]() { + TArray data{1, 4, 5, 6}; - AssertThat(data.Size(), Equals(4)); + AssertThat(data.Size(), Equals(4)); - data.RemoveIfSwap([](i32 v) { - return v == 1 || v == 6; + data.RemoveIfSwap([](i32 v) { + return v == 1 || v == 6; + }); + AssertThat(data.Size(), Equals(2)); + AssertThat(data[0], Equals(5)); + AssertThat(data[1], Equals(4)); }); - AssertThat(data.Size(), Equals(2)); - AssertThat(data[0], Equals(5)); - AssertThat(data[1], Equals(4)); }); it("Can Sort", [&]() { @@ -744,5 +748,34 @@ go_bandit([]() { AssertThat(data.AddUniqueSorted(36), Equals(6)); AssertThat(data.Size(), Equals(7)); }); + + describe("Iterate", []() { + it("Can iterate empty", [&]() { + TInlineArray data1{}; + i32 counter = 0; + for (i32 v : data1) + { + ++counter; + } + AssertThat(counter, Equals(0)); + TArray data2{}; + counter = 0; + for (i32 v : data2) + { + ++counter; + } + AssertThat(counter, Equals(0)); + }); + + it("Can iterate empty", [&]() { + TInlineArray data{}; + i32 counter = 0; + for (i32 v : data) + { + ++counter; + } + AssertThat(counter, Equals(0)); + }); + }); }); }); diff --git a/Tests/ECS/IdScopes.spec.cpp b/Tests/ECS/IdScopes.spec.cpp index 77bc12a0..0fa8da0c 100644 --- a/Tests/ECS/IdScopes.spec.cpp +++ b/Tests/ECS/IdScopes.spec.cpp @@ -17,7 +17,7 @@ struct TypeB }; struct TypeC { - P_STRUCT(TypeC, TF_ECS_AutoModify) + P_STRUCT(TypeC, TF_ECS_ModifyOnEdit) bool data; // Not empty type }; @@ -79,7 +79,7 @@ go_bandit([]() { Id id = AddId(ctx); TIdScope>> scope1{ctx}; AssertThat(scope1.Has>(id), Is().False()); - scope1.MarkModified(id); + scope1.Modify(id); AssertThat(scope1.Has>(id), Is().True()); AssertThat(scope1.IsModified(id), Is().True()); @@ -87,7 +87,7 @@ go_bandit([]() { AssertThat(scope1.Has>(id), Is().False()); AssertThat(scope1.IsModified(id), Is().False()); - scope1.MarkModified(id); + scope1.Modify(id); AssertThat(scope1.Has>(id), Is().True()); AssertThat(scope1.IsModified(id), Is().True()); });