diff --git a/include/Ecs.h b/include/Ecs.h index a8796e5..144f6f8 100644 --- a/include/Ecs.h +++ b/include/Ecs.h @@ -4,8 +4,6 @@ #include "ecs/ComponentPoolIterator.h" #include "ecs/ComponentSpecs.h" #include "ecs/ComponentTraits.h" -#include "ecs/View.h" -// manual break #include "ecs/Domain.h" #include "ecs/Entity.h" #include "ecs/EntityPool.h" @@ -13,3 +11,4 @@ #include "ecs/EntityTraits.h" #include "ecs/InClassComponentSpecs.h" #include "ecs/SparseSet.h" +#include "ecs/View.h" diff --git a/include/ecs/CommonComponentPool.h b/include/ecs/CommonComponentPool.h index 5501322..0b35260 100644 --- a/include/ecs/CommonComponentPool.h +++ b/include/ecs/CommonComponentPool.h @@ -1,11 +1,13 @@ #pragma once +#include + #include "SparseSet.h" #include namespace arch::ecs { -template +template class View; } @@ -14,36 +16,24 @@ namespace arch::ecs::_details { // NOLINT /// @brief Abstract class with behavior shared between all ComponentPools /// @tparam E - entity type -template -class CommonComponentPool: public _details::SparseSet, public utils::ReadonlyCounter { +class CommonComponentPool: public _details::SparseSet, public utils::ReadonlyCounter { public: /// @brief Removes component from given entity /// @param entity - entity to remove component from /// @return If component was actually removed - virtual bool removeComponent(const E entity) noexcept = 0; - - using _details::SparseSet::contains; - - using utils::ReadonlyCounter::count; + virtual bool removeComponent(const Entity entity) noexcept = 0; protected: - template + template friend class ::arch::ecs::View; - using _details::SparseSet::_sparseAssure; - using _details::SparseSet::_sparseAssurePage; - using _details::SparseSet::_sparseGet; - using _details::SparseSet::_sparseTryGet; - - using utils::ReadonlyCounter::_counter; - using _details::SparseSet::_dense; - using _details::SparseSet::_sparse; + using EntitiesViewT = + decltype(std::views::filter(*std::declval(), _details::EntityTraits::Version::hasNotNull) + ); // range with all valid entities in _dense - auto _entitiesForView() const noexcept; - static auto _emptyEntitiesForView() noexcept; + EntitiesViewT _entitiesForView() const noexcept; + static EntitiesViewT _emptyEntitiesForView() noexcept; }; } // namespace arch::ecs::_details - -#include "CommonComponentPool.hpp" diff --git a/include/ecs/CommonComponentPool.hpp b/include/ecs/CommonComponentPool.hpp deleted file mode 100644 index 41d2359..0000000 --- a/include/ecs/CommonComponentPool.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#include - -#include "CommonComponentPool.h" - -namespace arch::ecs::_details { // NOLINT - -template -auto CommonComponentPool::_entitiesForView() const noexcept { - // entites with version != null are valid - return std::views::filter(_dense, _details::EntityTraits::Version::hasNotNull); -} - -template -auto CommonComponentPool::_emptyEntitiesForView() noexcept { - static const std::vector empty; - return std::views::filter(empty, _details::EntityTraits::Version::hasNotNull); -} - -} // namespace arch::ecs::_details diff --git a/include/ecs/ComponentPool.h b/include/ecs/ComponentPool.h index 3fd71f4..c27c72c 100644 --- a/include/ecs/ComponentPool.h +++ b/include/ecs/ComponentPool.h @@ -15,19 +15,19 @@ namespace arch::ecs { /// @brief Pool for creating and destroying components /// @details Uses sparse set data structure /// @tparam C - component type -/// @tparam E - entity type -template -class ComponentPool: public _details::CommonComponentPool { - using Base = _details::CommonComponentPool; +/// @tparam Entity - entity type +template +class ComponentPool: public _details::CommonComponentPool { + using Base = _details::CommonComponentPool; public: /// @brief ComponentTraits of component - using Traits = _details::ComponentTraits; + using Traits = _details::ComponentTraits; /// @brief EntityTraits of entity - using ETraits = _details::EntityTraits; + using ETraits = _details::EntityTraits; /// @brief Entity type - using EntityT = E; + using EntityT = Entity; /// @brief Id type using IdT = typename ETraits::IdT; /// @brief Version type @@ -44,7 +44,7 @@ class ComponentPool: public _details::CommonComponentPool { using ConstGetReference = std::conditional_t; /// @brief Iterator of component pool - using Iterator = _details::ComponentPoolIterator; + using Iterator = _details::ComponentPoolIterator; /// @brief Const iterator of component pool using ConstIterator = const Iterator; /// @brief Reverse iterator of component pool @@ -70,10 +70,7 @@ class ComponentPool: public _details::CommonComponentPool { /// @param entity - entity to remove component from /// @return Removed component C removeComponent(const EntityT entity, MoveFlag) noexcept - requires(std::movable && !_details::ComponentTraits::flag); - - using Base::contains; - using Base::count; + requires(std::movable && !_details::ComponentTraits::flag); /// @brief Returns component of given entity /// @details If component is non-flag and does not exist, the behavior is undefined @@ -86,11 +83,11 @@ class ComponentPool: public _details::CommonComponentPool { /// @brief Returns optional with reference to component of given entity /// @param entity - entity to get component of std::optional> tryGet(const EntityT entity) noexcept - requires(!_details::ComponentTraits::flag); + requires(!_details::ComponentTraits::flag); /// @brief Returns optional with readonly reference to component of given entity /// @param entity - entity to get component of std::optional> tryGet(const EntityT entity) const noexcept - requires(!_details::ComponentTraits::flag); + requires(!_details::ComponentTraits::flag); /// @brief Returns iterator to first (const entity, component) contained Iterator begin() noexcept; @@ -117,21 +114,14 @@ class ComponentPool: public _details::CommonComponentPool { /// @brief Returns reverse iterator to past-the-last (const entity, const component) contained in a reverse order ConstReverseIterator crend() const noexcept; - using Base::_entitiesForView; - private: - using Base::_sparseAssure; - using Base::_sparseAssurePage; - using Base::_sparseGet; - using Base::_sparseTryGet; - - friend arch::ecs::_details::ComponentPoolIterator; + friend arch::ecs::_details::ComponentPoolIterator; //// returns entity of given id from sparse, assuring it's page exists - // E& _sparseAssure(const IdT id) noexcept; + // Entity& _sparseAssure(const IdT id) noexcept; // returns new entity from dense, along with it's index - std::tuple _denseNew() noexcept; + std::tuple _denseNew() noexcept; // returns pointer (likely invalid) to component of given id, can be used for placement-new or Traits::constructAt() C* _componentAssure(const IdT id) noexcept; @@ -140,11 +130,6 @@ class ComponentPool: public _details::CommonComponentPool { // finds index of last valid component, 0 if none size_t _findLast() const noexcept; - // std::vector>> _sparse; // paged - // std::vector _dense; - using Base::_counter; - using Base::_dense; - using Base::_sparse; std::vector _components; // paged size_t _listHead = 0; @@ -162,12 +147,11 @@ class ComponentPool: public _details::CommonComponentPool { /// @brief Struct mimicing ComponentPool of any component type /// @brief Used to achieve simmilar behavior as std::any /// @details Usage conforms with Strict Aliasing Rule, making it safe -/// @tparam E - entity type -template -struct alignas(ComponentPool) ComponentPoolStorage { +/// @tparam Entity - entity type +struct alignas(ComponentPool) ComponentPoolStorage { // void* is an arbitrary type /// @brief Type of an arbitrary ComponentPool - using PoolT = ComponentPool; + using PoolT = ComponentPool; /// @brief std::array with size and alignment of ComponentPool /// @brief char is an exception to Strict Aliasing Rule alignas(PoolT) std::array storage{}; diff --git a/include/ecs/ComponentPool.hpp b/include/ecs/ComponentPool.hpp index 4247dd9..14f2eeb 100644 --- a/include/ecs/ComponentPool.hpp +++ b/include/ecs/ComponentPool.hpp @@ -1,17 +1,17 @@ #include "ComponentPool.h" #include -#define TEMPLATE_CE template -#define POOL_CE ComponentPool -#define TRAITS_CE _details::ComponentTraits +#define TEMPLATE_C template +#define POOL_C ComponentPool +#define TRAITS_C _details::ComponentTraits // https://miro.com/app/board/uXjVK4gF1DI=/?share_link_id=296698570044 // ^ picture explanations namespace arch::ecs { -TEMPLATE_CE -POOL_CE::~ComponentPool() noexcept { +TEMPLATE_C +POOL_C::~ComponentPool() noexcept { if constexpr (!Traits::flag) { for (size_t i = 0; i != _components.size(); ++i) { Traits::deletePage(_components.data(), i, _dense); @@ -19,10 +19,10 @@ POOL_CE::~ComponentPool() noexcept { } } -TEMPLATE_CE -std::tuple POOL_CE::_denseNew() noexcept { +TEMPLATE_C +std::tuple POOL_C::_denseNew() noexcept { if (_listHead == _dense.size()) { // dense is full - return { _dense.emplace_back(ETraits::Entity::null), _listHead++ }; + return { _dense.emplace_back(ETraits::Ent::null), _listHead++ }; } if constexpr (Traits::inPlace) { @@ -34,8 +34,8 @@ std::tuple POOL_CE::_denseNew() noexcept { } } -TEMPLATE_CE -C* POOL_CE::_componentAssure(const IdT id) noexcept { +TEMPLATE_C +C* POOL_C::_componentAssure(const IdT id) noexcept { const size_t pageNum = id / Traits::pageSize; // resize(n) only would make capacity == n (bad) @@ -52,8 +52,8 @@ C* POOL_CE::_componentAssure(const IdT id) noexcept { return pagePtr + id % Traits::pageSize; } -TEMPLATE_CE -size_t POOL_CE::_findFirst() const noexcept { +TEMPLATE_C +size_t POOL_C::_findFirst() const noexcept { if constexpr (Traits::inPlace) { for (size_t i = 0; i != _dense.size(); ++i) { if (!ETraits::Version::hasNull(_dense[i])) { @@ -65,8 +65,8 @@ size_t POOL_CE::_findFirst() const noexcept { return 0; } -TEMPLATE_CE -size_t POOL_CE::_findLast() const noexcept { +TEMPLATE_C +size_t POOL_C::_findLast() const noexcept { if constexpr (Traits::inPlace) { for (size_t i = _dense.size() - 1; i != (size_t)-1; --i) { if (!ETraits::Version::hasNull(_dense[i - 1])) { @@ -78,13 +78,13 @@ size_t POOL_CE::_findLast() const noexcept { return _listHead - 1; } -TEMPLATE_CE +TEMPLATE_C template -POOL_CE::GetReference POOL_CE::addComponent(const EntityT entity, Args&&... args) noexcept { +POOL_C::GetReference POOL_C::addComponent(const EntityT entity, Args&&... args) noexcept { const auto id = ETraits::Id::part(entity); auto&& sparseEntity = _sparseAssure(id); - if (sparseEntity != ETraits::Entity::null) { + if (sparseEntity != ETraits::Ent::null) { if constexpr (Traits::flag) { return false; } else { @@ -95,7 +95,7 @@ POOL_CE::GetReference POOL_CE::addComponent(const EntityT entity, Args&&... args auto&& [denseEntity, denseIdx] = _denseNew(); - sparseEntity = ETraits::Entity::fromParts(denseIdx, ETraits::Version::part(entity)); + sparseEntity = ETraits::Ent::fromParts(denseIdx, ETraits::Version::part(entity)); denseEntity = entity; ++_counter; @@ -106,8 +106,8 @@ POOL_CE::GetReference POOL_CE::addComponent(const EntityT entity, Args&&... args } } -TEMPLATE_CE -C POOL_CE::removeComponent(const EntityT entity, MoveFlag) noexcept requires(std::movable && !TRAITS_CE::flag) +TEMPLATE_C +C POOL_C::removeComponent(const EntityT entity, MoveFlag) noexcept requires(std::movable && !TRAITS_C::flag) { // user must assume that component exists const size_t id = ETraits::Id::part(entity); @@ -116,8 +116,8 @@ C POOL_CE::removeComponent(const EntityT entity, MoveFlag) noexcept requires(std auto&& fromSparse = _sparseGet(id); if constexpr (Traits::inPlace) { - const size_t idx = ETraits::Id::part(std::exchange(fromSparse, ETraits::Entity::null)); - _dense[idx] = ETraits::Entity::fromParts(std::exchange(_listHead, idx), ETraits::Version::null); + const size_t idx = ETraits::Id::part(std::exchange(fromSparse, ETraits::Ent::null)); + _dense[idx] = ETraits::Ent::fromParts(std::exchange(_listHead, idx), ETraits::Version::null); auto&& component = (*_components[idx / Traits::pageSize])[idx % Traits::pageSize]; auto toMove = std::move(component); Traits::destroyAt(&component); @@ -131,14 +131,14 @@ C POOL_CE::removeComponent(const EntityT entity, MoveFlag) noexcept requires(std if (&sparseSwap != &fromSparse) { // first sparse swap, id at listHead = id of given entity sparseSwap = - ETraits::Entity::fromRawParts(ETraits::Id::rawPart(entity), ETraits::Version::rawPart(sparseSwap)); + ETraits::Ent::fromRawParts(ETraits::Id::rawPart(entity), ETraits::Version::rawPart(sparseSwap)); } // second sparse swap, entity at id = null // also obtain index to dense - const size_t idx = ETraits::Id::part(std::exchange(fromSparse, ETraits::Entity::null)); + const size_t idx = ETraits::Id::part(std::exchange(fromSparse, ETraits::Ent::null)); _dense[idx] = _dense[_listHead]; - _dense[_listHead] = ETraits::Entity::fromParts(_listHead + 1, ETraits::Version::null); + _dense[_listHead] = ETraits::Ent::fromParts(_listHead + 1, ETraits::Version::null); auto&& atIdx = _components[idx / Traits::pageSize][idx % Traits::pageSize]; auto&& atListHead = _components[_listHead / Traits::pageSize][_listHead % Traits::pageSize]; @@ -158,18 +158,18 @@ C POOL_CE::removeComponent(const EntityT entity, MoveFlag) noexcept requires(std } } -TEMPLATE_CE -bool POOL_CE::removeComponent(const EntityT entity) noexcept { +TEMPLATE_C +bool POOL_C::removeComponent(const EntityT entity) noexcept { const size_t id = ETraits::Id::part(entity); auto sparsePtr = _sparseTryGet(id); - if (!sparsePtr || *sparsePtr == ETraits::Entity::null) { + if (!sparsePtr || *sparsePtr == ETraits::Ent::null) { return false; } if constexpr (Traits::inPlace) { - const size_t idx = ETraits::Id::part(std::exchange(*sparsePtr, ETraits::Entity::null)); - _dense[idx] = ETraits::Entity::fromParts(std::exchange(_listHead, idx), ETraits::Version::null); + const size_t idx = ETraits::Id::part(std::exchange(*sparsePtr, ETraits::Ent::null)); + _dense[idx] = ETraits::Ent::fromParts(std::exchange(_listHead, idx), ETraits::Version::null); if constexpr (!Traits::flag) { Traits::destroyAt(_components[idx / Traits::pageSize] + idx % Traits::pageSize); } @@ -181,14 +181,14 @@ bool POOL_CE::removeComponent(const EntityT entity) noexcept { if (&sparseSwap != sparsePtr) { // first sparse swap, id at listHead = id of given entity sparseSwap = - ETraits::Entity::fromRawParts(ETraits::Id::rawPart(entity), ETraits::Version::rawPart(sparseSwap)); + ETraits::Ent::fromRawParts(ETraits::Id::rawPart(entity), ETraits::Version::rawPart(sparseSwap)); } // second sparse swap, entity at id = null // also obtain index to dense - const size_t idx = ETraits::Id::part(std::exchange(*sparsePtr, ETraits::Entity::null)); + const size_t idx = ETraits::Id::part(std::exchange(*sparsePtr, ETraits::Ent::null)); _dense[idx] = _dense[_listHead]; - _dense[_listHead] = ETraits::Entity::fromParts(_listHead + 1, ETraits::Version::null); + _dense[_listHead] = ETraits::Ent::fromParts(_listHead + 1, ETraits::Version::null); --_counter; if constexpr (!Traits::flag) { @@ -206,8 +206,8 @@ bool POOL_CE::removeComponent(const EntityT entity) noexcept { return true; } -TEMPLATE_CE -POOL_CE::GetReference POOL_CE::get(const EntityT entity) noexcept { +TEMPLATE_C +POOL_C::GetReference POOL_C::get(const EntityT entity) noexcept { // assumed existence of component if constexpr (Traits::flag) { return contains(entity); @@ -222,13 +222,13 @@ POOL_CE::GetReference POOL_CE::get(const EntityT entity) noexcept { } } -TEMPLATE_CE -POOL_CE::ConstGetReference POOL_CE::get(const EntityT entity) const noexcept { - return const_cast(this)->get(entity); +TEMPLATE_C +POOL_C::ConstGetReference POOL_C::get(const EntityT entity) const noexcept { + return const_cast(this)->get(entity); } -TEMPLATE_CE -std::optional> POOL_CE::tryGet(const EntityT entity) noexcept requires(!TRAITS_CE::flag) +TEMPLATE_C +std::optional> POOL_C::tryGet(const EntityT entity) noexcept requires(!TRAITS_C::flag) { const size_t id = ETraits::Id::part(entity); @@ -242,75 +242,75 @@ std::optional> POOL_CE::tryGet(const EntityT entity) n return std::optional{ std::ref(_components[idx / Traits::pageSize][idx % Traits::pageSize]) }; } -TEMPLATE_CE -std::optional> POOL_CE::tryGet(const EntityT entity) const noexcept - requires(!TRAITS_CE::flag) +TEMPLATE_C +std::optional> POOL_C::tryGet(const EntityT entity) const noexcept + requires(!TRAITS_C::flag) { - return const_cast(this)->tryGet(entity); + return const_cast(this)->tryGet(entity); } -TEMPLATE_CE -POOL_CE::Iterator POOL_CE::begin() noexcept { +TEMPLATE_C +POOL_C::Iterator POOL_C::begin() noexcept { return Iterator(this, _findFirst()); } -TEMPLATE_CE -const POOL_CE::Iterator POOL_CE::begin() const noexcept { - return const_cast(this)->begin(); +TEMPLATE_C +const POOL_C::Iterator POOL_C::begin() const noexcept { + return const_cast(this)->begin(); } -TEMPLATE_CE -const POOL_CE::Iterator POOL_CE::cbegin() const noexcept { +TEMPLATE_C +const POOL_C::Iterator POOL_C::cbegin() const noexcept { return begin(); } -TEMPLATE_CE -POOL_CE::Iterator POOL_CE::end() noexcept { +TEMPLATE_C +POOL_C::Iterator POOL_C::end() noexcept { return Iterator(this, _findLast() + 1); } -TEMPLATE_CE -const POOL_CE::Iterator POOL_CE::end() const noexcept { - return const_cast(this)->end(); +TEMPLATE_C +const POOL_C::Iterator POOL_C::end() const noexcept { + return const_cast(this)->end(); } -TEMPLATE_CE -const POOL_CE::Iterator POOL_CE::cend() const noexcept { +TEMPLATE_C +const POOL_C::Iterator POOL_C::cend() const noexcept { return end(); } -TEMPLATE_CE -POOL_CE::ReverseIterator POOL_CE::rbegin() noexcept { +TEMPLATE_C +POOL_C::ReverseIterator POOL_C::rbegin() noexcept { return std::reverse_iterator(end()); } -TEMPLATE_CE -POOL_CE::ConstReverseIterator POOL_CE::rbegin() const noexcept { +TEMPLATE_C +POOL_C::ConstReverseIterator POOL_C::rbegin() const noexcept { return std::reverse_iterator(end()); } -TEMPLATE_CE -POOL_CE::ConstReverseIterator POOL_CE::crbegin() const noexcept { +TEMPLATE_C +POOL_C::ConstReverseIterator POOL_C::crbegin() const noexcept { return std::reverse_iterator(cend()); } -TEMPLATE_CE -POOL_CE::ReverseIterator POOL_CE::rend() noexcept { +TEMPLATE_C +POOL_C::ReverseIterator POOL_C::rend() noexcept { return std::reverse_iterator(begin()); } -TEMPLATE_CE -POOL_CE::ConstReverseIterator POOL_CE::rend() const noexcept { +TEMPLATE_C +POOL_C::ConstReverseIterator POOL_C::rend() const noexcept { return std::reverse_iterator(begin()); } -TEMPLATE_CE -POOL_CE::ConstReverseIterator POOL_CE::crend() const noexcept { +TEMPLATE_C +POOL_C::ConstReverseIterator POOL_C::crend() const noexcept { return std::reverse_iterator(cbegin()); } } // namespace arch::ecs -#undef TEMPLATE_CE -#undef POOL_CE -#undef TRAITS_CE +#undef TEMPLATE_C +#undef POOL_C +#undef TRAITS_C diff --git a/include/ecs/ComponentPoolIterator.h b/include/ecs/ComponentPoolIterator.h index 72dae57..4ad10f2 100644 --- a/include/ecs/ComponentPoolIterator.h +++ b/include/ecs/ComponentPoolIterator.h @@ -6,7 +6,7 @@ namespace arch::ecs { -template +template class ComponentPool; } @@ -17,18 +17,16 @@ namespace arch::ecs::_details { /// @details Models std::bidirectional_iterator /// @tparam C - component type /// @tparam E - entity type -template +template class ComponentPoolIterator { - using Traits = _details::ComponentTraits; + using Traits = _details::ComponentTraits; public: /// @brief Component type using ComponentT = C; - /// @brief Entity type - using EntityT = E; /// @brief Return value - using ValueType = std::pair>; + using ValueType = std::pair>; /// @brief Return value reference using Reference = ValueType&; /// @brief Return value Pointer @@ -74,16 +72,16 @@ class ComponentPoolIterator { std::strong_ordering operator<=>(const ComponentPoolIterator& other) const noexcept; private: - friend arch::ecs::ComponentPool; + friend arch::ecs::ComponentPool; // if iterator is valid bool _valid() const noexcept; void _update() noexcept; - using ETraits = _details::EntityTraits; + using ETraits = _details::EntityTraits; // used by ComponentPool - ComponentPoolIterator(ComponentPool* pool, size_t i = 0) noexcept; + ComponentPoolIterator(ComponentPool* pool, size_t i = 0) noexcept; Reference _pair() const noexcept; @@ -94,7 +92,7 @@ class ComponentPoolIterator { // iterating through pages with offset was tested to be the fastest C** _componentPage; size_t _offset; - std::vector* _dense; + std::vector* _dense; size_t _i; }; @@ -104,11 +102,11 @@ namespace std { /// @brief std::iterator_traits' specialization for ComponentPoolIterator /// @tparam C - component type /// @tparam E - entity type -template -struct iterator_traits> { +template +struct iterator_traits> { private: - using Iter = arch::ecs::_details::ComponentPoolIterator; + using Iter = arch::ecs::_details::ComponentPoolIterator; public: @@ -120,6 +118,4 @@ struct iterator_traits> { }; } // namespace std -static_assert(std::bidirectional_iterator>); - #include "ComponentPoolIterator.hpp" diff --git a/include/ecs/ComponentPoolIterator.hpp b/include/ecs/ComponentPoolIterator.hpp index bd64665..adc1239 100644 --- a/include/ecs/ComponentPoolIterator.hpp +++ b/include/ecs/ComponentPoolIterator.hpp @@ -1,13 +1,13 @@ #include "ComponentPool.h" #include "ComponentPoolIterator.h" -#define TEMPLATE_CE template -#define ITER_CE ComponentPoolIterator +#define TEMPLATE_C template +#define ITER_C ComponentPoolIterator namespace arch::ecs::_details { -TEMPLATE_CE -void ITER_CE::_update() noexcept { +TEMPLATE_C +void ITER_C::_update() noexcept { if (_valid()) { // update internal std::pair if constexpr (Traits::flag) { new (_value) ValueType((*_dense)[_i], true); @@ -17,8 +17,8 @@ void ITER_CE::_update() noexcept { } } -TEMPLATE_CE -ITER_CE::ComponentPoolIterator(ComponentPool* pool, size_t i) noexcept: +TEMPLATE_C +ITER_C::ComponentPoolIterator(ComponentPool* pool, size_t i) noexcept: _componentPage{ [&]() { if constexpr (Traits::flag) { return nullptr; @@ -38,12 +38,12 @@ ITER_CE::ComponentPoolIterator(ComponentPool* pool, size_t i) noexcept: _update(); } -TEMPLATE_CE -ITER_CE::Reference ITER_CE::_pair() const noexcept { - return *reinterpret_cast(&const_cast(this)->_value); +TEMPLATE_C +ITER_C::Reference ITER_C::_pair() const noexcept { + return *reinterpret_cast(&const_cast(this)->_value); } -TEMPLATE_CE void ITER_CE::swap(ITER_CE& other) noexcept { +TEMPLATE_C void ITER_C::swap(ITER_C& other) noexcept { std::swap(this->_componentPage, other._componentPage); std::swap(this->_offset, other._offset); std::swap(this->_dense, other._dense); @@ -51,13 +51,13 @@ TEMPLATE_CE void ITER_CE::swap(ITER_CE& other) noexcept { std::swap(this->_pair(), other._pair()); } -TEMPLATE_CE -bool ITER_CE::_valid() const noexcept { +TEMPLATE_C +bool ITER_C::_valid() const noexcept { return _i < _dense->size() && !ETraits::Version::hasNull((*_dense)[_i]); } -TEMPLATE_CE -ITER_CE& ITER_CE::operator++() noexcept { +TEMPLATE_C +ITER_C& ITER_C::operator++() noexcept { if constexpr (Traits::inPlace) { // need to search for next valid do { if constexpr (!Traits::flag) { @@ -81,15 +81,15 @@ ITER_CE& ITER_CE::operator++() noexcept { return *this; } -TEMPLATE_CE -ITER_CE ITER_CE::operator++(int) noexcept { +TEMPLATE_C +ITER_C ITER_C::operator++(int) noexcept { auto temp = *this; ++(*this); return temp; } -TEMPLATE_CE -ITER_CE& ITER_CE::operator--() noexcept { +TEMPLATE_C +ITER_C& ITER_C::operator--() noexcept { if constexpr (Traits::inPlace) { // need to search for next valid if (_i != 0 && _i != (size_t)-1) { do { @@ -121,34 +121,34 @@ ITER_CE& ITER_CE::operator--() noexcept { return *this; } -TEMPLATE_CE -ITER_CE ITER_CE::operator--(int) noexcept { +TEMPLATE_C +ITER_C ITER_C::operator--(int) noexcept { auto temp = *this; --(*this); return temp; } -TEMPLATE_CE -ITER_CE::Reference ITER_CE::operator*() const noexcept { +TEMPLATE_C +ITER_C::Reference ITER_C::operator*() const noexcept { return _pair(); } -TEMPLATE_CE -ITER_CE::Pointer ITER_CE::operator->() const noexcept { +TEMPLATE_C +ITER_C::Pointer ITER_C::operator->() const noexcept { return &(_pair()); } -TEMPLATE_CE -bool ITER_CE::operator==(const ITER_CE& other) const noexcept { +TEMPLATE_C +bool ITER_C::operator==(const ITER_C& other) const noexcept { return _i == other._i; } -TEMPLATE_CE -std::strong_ordering ITER_CE::operator<=>(const ITER_CE& other) const noexcept { +TEMPLATE_C +std::strong_ordering ITER_C::operator<=>(const ITER_C& other) const noexcept { return _i <=> other._i; } } // namespace arch::ecs::_details -#undef TEMPLATE_CE -#undef ITER_CE +#undef TEMPLATE_C +#undef ITER_C diff --git a/include/ecs/ComponentTraits.h b/include/ecs/ComponentTraits.h index 7842c69..337acc1 100644 --- a/include/ecs/ComponentTraits.h +++ b/include/ecs/ComponentTraits.h @@ -13,8 +13,7 @@ namespace arch::ecs::_details { // NOLINT /// @brief Contains info, constants and operations on components and their pages /// @details All data extracted from ComponentSpecs /// @tparam C - component type -/// @tparam E - entity type -template +template struct ComponentTraits { /// @brief Component Specifications using Specs = ComponentSpecs; @@ -46,7 +45,7 @@ struct ComponentTraits { /// @param pages - pointer to all pages /// @param pageNum - index of page in pages /// @param dense - dense vector of entities - static inline void deletePage(ComponentT** pages, size_t pageNum, const std::vector& dense) noexcept; + static inline void deletePage(ComponentT** pages, size_t pageNum, const std::vector& dense) noexcept; /// @brief Destroys component at given location /// @param component - component to destroy static inline void destroyAt(ComponentT* component) noexcept; diff --git a/include/ecs/ComponentTraits.hpp b/include/ecs/ComponentTraits.hpp index f314d1a..3cef928 100644 --- a/include/ecs/ComponentTraits.hpp +++ b/include/ecs/ComponentTraits.hpp @@ -1,19 +1,19 @@ #include "ComponentTraits.h" -#define TEMPLATE_CE template -#define TRAITS_CE ComponentTraits +#define TEMPLATE_C template +#define TRAITS_C ComponentTraits namespace arch::ecs::_details { -TEMPLATE_CE -typename TRAITS_CE::ComponentT* TRAITS_CE::newPage() noexcept { +TEMPLATE_C +typename TRAITS_C::ComponentT* TRAITS_C::newPage() noexcept { return (ComponentT*) operator new[](pageSize * sizeof(ComponentT), (std::align_val_t)alignof(C[pageSize]), std::nothrow); } -TEMPLATE_CE -void TRAITS_CE::deletePage(ComponentT** pages, size_t pageNum, const std::vector& dense) noexcept { - using ETraits = EntityTraits; +TEMPLATE_C +void TRAITS_C::deletePage(ComponentT** pages, size_t pageNum, const std::vector& dense) noexcept { + using ETraits = EntityTraits; ComponentT*& componentPage = pages[pageNum]; if (!componentPage) { @@ -32,18 +32,18 @@ void TRAITS_CE::deletePage(ComponentT** pages, size_t pageNum, const std::vector componentPage = nullptr; } -TEMPLATE_CE -void TRAITS_CE::destroyAt(ComponentT* component) noexcept { +TEMPLATE_C +void TRAITS_C::destroyAt(ComponentT* component) noexcept { component->~ComponentT(); } -TEMPLATE_CE +TEMPLATE_C template -TRAITS_CE::ComponentT& TRAITS_CE::constructAt(ComponentT* component, Args&&... args) noexcept { +TRAITS_C::ComponentT& TRAITS_C::constructAt(ComponentT* component, Args&&... args) noexcept { return *new (component) ComponentT(std::forward(args)...); } } // namespace arch::ecs::_details -#undef TEMPLATE_CE -#undef TRAITS_CE +#undef TEMPLATE_C +#undef TRAITS_C diff --git a/include/ecs/Domain.h b/include/ecs/Domain.h index 09e6f33..8a08c25 100644 --- a/include/ecs/Domain.h +++ b/include/ecs/Domain.h @@ -6,42 +6,37 @@ #include "ComponentPool.h" #include "EntityPool.h" #include "ExcludeT.h" -#include "View.h" #include "meta/Rtti.h" #include "tUtils/TypeList.h" namespace arch::ecs { /// @brief ECS' domain (main class) -/// @tparam E - entity type -template class Domain { public: - /// @brief EntityTraits of entity - using Traits = _details::EntityTraits; - /// @brief Entity type - using EntityT = typename Traits::EntityT; + /// @brief Entityraits of entity + using Traits = _details::EntityTraits; /// @brief Id type using IdT = typename Traits::IdT; /// @brief Version type using VersionT = typename Traits::VersionT; /// @brief Iterator type - using Iterator = typename EntityPool::Iterator; + using Iterator = typename EntityPool::Iterator; /// @brief Const iterator type - using ConstIterator = typename EntityPool::ConstIterator; + using ConstIterator = typename EntityPool::ConstIterator; /// @brief Reverse iterator type - using ReverseIterator = typename EntityPool::ReverseIterator; + using ReverseIterator = typename EntityPool::ReverseIterator; /// @brief Const reverse iterator type - using ConstReverseIterator = typename EntityPool::ConstReverseIterator; + using ConstReverseIterator = typename EntityPool::ConstReverseIterator; template - using GetReference = std::conditional_t<_details::ComponentTraits, E>::flag, bool, C&>; + using GetReference = std::conditional_t<_details::ComponentTraits>::flag, bool, C&>; template using ConstGetReference = std:: - conditional_t<_details::ComponentTraits, E>::flag, bool, const std::remove_const_t&>; + conditional_t<_details::ComponentTraits>::flag, bool, const std::remove_const_t&>; - static inline constexpr EntityT null = Traits::Entity::null; + static inline constexpr Entity null = Traits::Ent::null; /// @brief Default constructor Domain() noexcept = default; @@ -62,13 +57,13 @@ class Domain { /// @brief Checks if given entity is alive /// @param entity - entity to check - bool alive(const EntityT entity) const noexcept; + bool alive(const Entity entity) const noexcept; /// @brief Checks if given entity of given id is alive /// @param id - id of entity to check bool contains(const IdT id) const noexcept; /// @brief Returns version of entity with id of given entity /// @param entity - entity to check - VersionT version(const EntityT entity) const noexcept; + VersionT version(const Entity entity) const noexcept; /// @brief Returns version of entity with given id /// @param id - id of entity to check VersionT version(const IdT id) const noexcept; @@ -77,26 +72,26 @@ class Domain { /// @brief Creates new entity or recycles killed /// @return Created entity, null if achieved entity limit - EntityT newEntity() noexcept; + Entity newEntity() noexcept; /// @brief Recycles given entity, retaining its version /// @param entity - entity to recycle /// @return Recycled entity, null if id occupied - EntityT recycleEntity(const EntityT entity) noexcept; + Entity recycleEntity(const Entity entity) noexcept; /// @brief Recycles entity of given id /// @param id - id of entity to recycle /// @return Recycled entity, null if id occupied - EntityT recycleId(const IdT id) noexcept; + Entity recycleId(const IdT id) noexcept; /// @brief Kills given entity /// @param entity - entity to kill - void kill(const EntityT entity) noexcept; + void kill(const Entity entity) noexcept; /// @brief Kills entities in range /// @param first - beginning of range /// @param last - end of range void kill(std::input_iterator auto first, std::input_iterator auto last) noexcept; /// @brief Kills all entites in list /// @param entities - entities to kill - void kill(std::initializer_list entities) noexcept; + void kill(std::initializer_list entities) noexcept; /// @brief Returns readonly std::view of entities auto entities() const noexcept; @@ -107,65 +102,65 @@ class Domain { /// @param ...args - arguments to constructor /// @return Reference to new entity or old one template - GetReference> addComponent(const EntityT entity, Args&&... args) noexcept; + GetReference> addComponent(const Entity entity, Args&&... args) noexcept; /// @brief Adds component to entity or returns existing one /// @tparam C - component type /// @param entity - entity to add component to /// @param ...args - arguments to constructor /// @return Reference to new entity or old one template - GetReference> addComponent(const EntityT entity, C&& component) noexcept; + GetReference> addComponent(const Entity entity, C&& component) noexcept; /// @brief Obtains reference to existing component of given entity /// @details If entity does not contain component, behavior is undefined /// @tparam C - component type /// @param entity - entity to get component from template requires(!std::is_const_v) - GetReference getComponent(const EntityT entity) noexcept; + GetReference getComponent(const Entity entity) noexcept; /// @brief Obtains readonly reference to existing component of given entity /// @details If entity does not contain component, behavior is undefined /// @tparam C - component type /// @param entity - entity to get component from template - ConstGetReference> getComponent(const EntityT entity) const noexcept; + ConstGetReference> getComponent(const Entity entity) const noexcept; /// @brief Obtains readonly reference to existing component of given entity /// @details If entity does not contain component, behavior is undefined /// @tparam C - component type /// @param entity - entity to get component from template requires(std::is_const_v) - ConstGetReference> getComponent(const EntityT entity) const noexcept; + ConstGetReference> getComponent(const Entity entity) const noexcept; /// @brief Obtains optional with reference to component of given entity /// @tparam C - component type /// @param entity - entity to get component from template - requires(!_details::ComponentTraits::flag && !std::is_const_v) - std::optional> tryGetComponent(const EntityT entity) noexcept; + requires(!_details::ComponentTraits::flag && !std::is_const_v) + std::optional> tryGetComponent(const Entity entity) noexcept; /// @brief Obtains optional with readonly reference to component of given entity /// @tparam C - component type /// @param entity - entity to get component from template - requires(!_details::ComponentTraits, E>::flag) - std::optional>> tryGetComponent(const EntityT entity + requires(!_details::ComponentTraits>::flag) + std::optional>> tryGetComponent(const Entity entity ) const noexcept; /// @brief Removes component from given entity, if has one /// @param entity - entity to remove component from /// @return Whether component was removed template - bool removeComponent(const EntityT entity) noexcept; + bool removeComponent(const Entity entity) noexcept; /// @brief Removes and returns component from given entity, component must exist /// @details If component does not exist, the behavior is undefined /// @param entity - entity to remove component from /// @return Removed component template - requires(std::movable> && !_details::ComponentTraits, E>::flag) - std::remove_const_t removeComponent(const EntityT entity, MoveFlag) noexcept; + requires(std::movable> && !_details::ComponentTraits>::flag) + std::remove_const_t removeComponent(const Entity entity, MoveFlag) noexcept; /// @brief Checks if entity has component /// @tparam C - component type /// @param entity - entity to check template - bool hasComponent(const EntityT entity) const noexcept; + bool hasComponent(const Entity entity) const noexcept; /// @brief Returns count of given component type /// @tparam C - component to count template @@ -200,40 +195,37 @@ class Domain { private: - template + template friend class View; // ComponentPools mapped by type - using CPoolsT = std::unordered_map>; + using CPoolsT = std::unordered_map; // returns ComponentPool for given type, initializes if not exists template - ComponentPool, E>& _assureCPool() noexcept; + ComponentPool>& _assureCPool() noexcept; // returns ComponentPool for given type template requires(!std::is_const_v) - ComponentPool& _getCPool() noexcept; + ComponentPool& _getCPool() noexcept; template - const ComponentPool, E>& _getCPool() const noexcept; + const ComponentPool>& _getCPool() const noexcept; // same as ComponentPool, but nullptr if does not exist template requires(!std::is_const_v) - ComponentPool* _tryGetCPool() noexcept; + ComponentPool* _tryGetCPool() noexcept; template - const ComponentPool, E>* _tryGetCPool() const noexcept; + const ComponentPool>* _tryGetCPool() const noexcept; // destroying function for ComponentPool of given type template static inline void _destroyCPool(CPoolsT& cpools) noexcept; - EntityPool _entityPool; + EntityPool _entityPool; CPoolsT _componentPools; // mapped destroying functions std::unordered_map _cpoolDestroyers; }; -extern template class Domain; -extern template class Domain; - } // namespace arch::ecs #include "Domain.hpp" diff --git a/include/ecs/Domain.hpp b/include/ecs/Domain.hpp index c475bd4..3c1a9ad 100644 --- a/include/ecs/Domain.hpp +++ b/include/ecs/Domain.hpp @@ -2,232 +2,129 @@ #include "Domain.h" -#define TEMPLATE_E template -#define DOMAIN_E Domain - namespace arch::ecs { -TEMPLATE_E template -ComponentPool, E>& DOMAIN_E::_assureCPool() noexcept { +ComponentPool>& Domain::_assureCPool() noexcept { using Comp = std::remove_const_t; const auto type = staticTypedesc(Comp).wrap(); // custom RTTI gets a use auto found = _componentPools.find(type); if (found == _componentPools.end()) { _cpoolDestroyers[type] = _destroyCPool; - return *(new (_componentPools[type].storage.data()) ComponentPool); + return *(new (_componentPools[type].storage.data()) ComponentPool); } - return *reinterpret_cast*>(found->second.storage.data()); + return *reinterpret_cast*>(found->second.storage.data()); } -TEMPLATE_E template -void DOMAIN_E::_destroyCPool(CPoolsT& cpools) noexcept { +void Domain::_destroyCPool(CPoolsT& cpools) noexcept { using Comp = std::remove_const_t; - reinterpret_cast*>(cpools[staticTypedesc(Comp).wrap()].storage.data())->~ComponentPool(); + reinterpret_cast*>(cpools[staticTypedesc(Comp).wrap()].storage.data())->~ComponentPool(); } -TEMPLATE_E template requires(!std::is_const_v) -ComponentPool& DOMAIN_E::_getCPool() noexcept { - return *reinterpret_cast*>(_componentPools.find(staticTypedesc(C).wrap())->second.storage.data() - ); +ComponentPool& Domain::_getCPool() noexcept { + return *reinterpret_cast*>(_componentPools.find(staticTypedesc(C).wrap())->second.storage.data()); } -TEMPLATE_E template -const ComponentPool, E>& DOMAIN_E::_getCPool() const noexcept { +const ComponentPool>& Domain::_getCPool() const noexcept { using Comp = std::remove_const_t; - return *reinterpret_cast*>( + return *reinterpret_cast*>( _componentPools.find(staticTypedesc(Comp).wrap())->second.storage.data() ); } -TEMPLATE_E template requires(!std::is_const_v) -ComponentPool* DOMAIN_E::_tryGetCPool() noexcept { +ComponentPool* Domain::_tryGetCPool() noexcept { const auto found = _componentPools.find(staticTypedesc(C).wrap()); - return found != _componentPools.end() ? reinterpret_cast*>(found->second.storage.data()) : - nullptr; + return found != _componentPools.end() ? reinterpret_cast*>(found->second.storage.data()) : nullptr; } -TEMPLATE_E template -const ComponentPool, E>* DOMAIN_E::_tryGetCPool() const noexcept { +const ComponentPool>* Domain::_tryGetCPool() const noexcept { using Comp = std::remove_const_t; const auto found = _componentPools.find(staticTypedesc(Comp).wrap()); - return found != _componentPools.end() ? - reinterpret_cast*>(found->second.storage.data()) : - nullptr; -} - -TEMPLATE_E -DOMAIN_E::~Domain() noexcept { - for (const auto& [type, destroyer] : _cpoolDestroyers) { - destroyer(_componentPools); - } -} - -TEMPLATE_E -void DOMAIN_E::swap(DOMAIN_E& other) noexcept { - std::swap(_entityPool, other._entityPool); - std::swap(_componentPools, other._componentPools); - std::swap(_cpoolDestroyers, other._cpoolDestroyers); -} - -TEMPLATE_E -bool DOMAIN_E::alive(const EntityT entity) const noexcept { - return _entityPool.contains(entity); -} - -TEMPLATE_E -bool DOMAIN_E::contains(const IdT id) const noexcept { - return _entityPool.contains(id); -} - -TEMPLATE_E -DOMAIN_E::VersionT DOMAIN_E::version(const EntityT entity) const noexcept { - return _entityPool.version(entity); -} - -TEMPLATE_E -DOMAIN_E::VersionT DOMAIN_E::version(const IdT id) const noexcept { - return _entityPool.version(id); -} - -TEMPLATE_E -size_t DOMAIN_E::count() const noexcept { - return _entityPool.size(); -} - -TEMPLATE_E -DOMAIN_E::EntityT DOMAIN_E::newEntity() noexcept { - return _entityPool.newEntity(); -} - -TEMPLATE_E -DOMAIN_E::EntityT DOMAIN_E::recycleEntity(const EntityT entity) noexcept { - return _entityPool.recycleEntity(entity); -} - -TEMPLATE_E -DOMAIN_E::EntityT DOMAIN_E::recycleId(const IdT id) noexcept { - return _entityPool.recycleId(id); -} - -TEMPLATE_E -void DOMAIN_E::kill(const EntityT entity) noexcept { - for (auto&& [type, poolStorage] : _componentPools) { - reinterpret_cast<_details::CommonComponentPool*>(&poolStorage)->removeComponent(entity); - } - - _entityPool.kill(entity); -} - -TEMPLATE_E -void DOMAIN_E::kill(std::input_iterator auto first, std::input_iterator auto last) noexcept { - _entityPool.kill(first, last); -} - -TEMPLATE_E -void DOMAIN_E::kill(std::initializer_list entities) noexcept { - _entityPool.kill(entities); -} - -TEMPLATE_E -auto DOMAIN_E::entities() const noexcept { - return std::views::all(_entityPool); + return found != _componentPools.end() ? reinterpret_cast*>(found->second.storage.data()) : + nullptr; } -TEMPLATE_E template -DOMAIN_E::GetReference> DOMAIN_E::addComponent(const EntityT entity, Args&&... args) noexcept { +Domain::GetReference> Domain::addComponent(const Entity entity, Args&&... args) noexcept { return _assureCPool>().addComponent(entity, std::forward(args)...); } -TEMPLATE_E template -DOMAIN_E::GetReference> DOMAIN_E::addComponent(const EntityT entity, C&& component) noexcept { +Domain::GetReference> Domain::addComponent(const Entity entity, C&& component) noexcept { return _assureCPool>().addComponent(entity, std::forward(component)); } -TEMPLATE_E template requires(!std::is_const_v) -DOMAIN_E::GetReference DOMAIN_E::getComponent(const EntityT entity) noexcept { +Domain::GetReference Domain::getComponent(const Entity entity) noexcept { return _getCPool().get(entity); } -TEMPLATE_E template -DOMAIN_E::ConstGetReference> DOMAIN_E::getComponent(const EntityT entity) const noexcept { +Domain::ConstGetReference> Domain::getComponent(const Entity entity) const noexcept { return _getCPool>().get(entity); } -TEMPLATE_E template -requires(!_details::ComponentTraits::flag && !std::is_const_v) -std::optional> DOMAIN_E::tryGetComponent(const EntityT entity) noexcept { +requires(!_details::ComponentTraits::flag && !std::is_const_v) +std::optional> Domain::tryGetComponent(const Entity entity) noexcept { return _getCPool().tryGet(entity); } -TEMPLATE_E template -requires(!_details::ComponentTraits, E>::flag) -std::optional>> DOMAIN_E::tryGetComponent(const EntityT entity +requires(!_details::ComponentTraits>::flag) +std::optional>> Domain::tryGetComponent(const Entity entity ) const noexcept { return _getCPool>().tryGet(entity); } -TEMPLATE_E template -bool DOMAIN_E::removeComponent(const EntityT entity) noexcept { +bool Domain::removeComponent(const Entity entity) noexcept { return _assureCPool>().removeComponent(entity); } -TEMPLATE_E template -requires(std::movable> && !_details::ComponentTraits, E>::flag) -std::remove_const_t DOMAIN_E::removeComponent(const EntityT entity, MoveFlag) noexcept { +requires(std::movable> && !_details::ComponentTraits>::flag) +std::remove_const_t Domain::removeComponent(const Entity entity, MoveFlag) noexcept { return _assureCPool().removeComponent(entity, moveFlag); } -TEMPLATE_E template requires(!std::is_const_v) -auto DOMAIN_E::components() noexcept { +auto Domain::components() noexcept { return std::views::all(_assureCPool()); } -TEMPLATE_E template -auto DOMAIN_E::components() const noexcept { - static const ComponentPool empty; +auto Domain::components() const noexcept { + static const ComponentPool empty; auto poolPtr = _tryGetCPool(); return poolPtr ? std::views::all(*poolPtr) : std::views::all(empty); } -TEMPLATE_E template -bool DOMAIN_E::hasComponent(const EntityT entity) const noexcept { +bool Domain::hasComponent(const Entity entity) const noexcept { auto cpool = _tryGetCPool>(); return cpool ? cpool->contains(entity) : false; } -TEMPLATE_E template -size_t DOMAIN_E::count() const noexcept { +size_t Domain::count() const noexcept { auto cpool = _tryGetCPool>(); return cpool ? cpool->count() : 0; } -TEMPLATE_E template -auto DOMAIN_E::view(ExcludeT) noexcept { +auto Domain::view(ExcludeT) noexcept { if constexpr (sizeof...(Includes) != 0) { /*ARCH_ASSERT( std::ranges::none_of( @@ -238,15 +135,14 @@ auto DOMAIN_E::view(ExcludeT) noexcept { ), "One of requested ComponentPools does not exist" );*/ - return View, TypeList>(this); + return View, TypeList>(this); } else { - return View, TypeList>(this); + return View, TypeList>(this); } } -TEMPLATE_E template -auto DOMAIN_E::view(ExcludeT) const noexcept { +auto Domain::view(ExcludeT) const noexcept { if constexpr (sizeof...(Includes) != 0) { /*ARCH_ASSERT( std::ranges::none_of( @@ -257,19 +153,15 @@ auto DOMAIN_E::view(ExcludeT) const noexcept { ), "One of requested ComponentPools does not exist" );*/ - return View, TypeList>(this); + return View, TypeList>(this); } else { - return View, TypeList>(this); + return View, TypeList>(this); } } -TEMPLATE_E template -auto DOMAIN_E::readonlyView(ExcludeT) const noexcept { +auto Domain::readonlyView(ExcludeT) const noexcept { return view(exclude); } } // namespace arch::ecs - -#undef TEMPLATE_E -#undef DOMAIN_E diff --git a/include/ecs/Entity.h b/include/ecs/Entity.h index 762bd23..192d29a 100644 --- a/include/ecs/Entity.h +++ b/include/ecs/Entity.h @@ -5,16 +5,10 @@ namespace arch::ecs { -/// @brief 32-bit entity type -/// @details 24-bit id (16'777'215 ids) -/// @details 8-bit version (255) -/// @details page size: 1024 -enum class e32 : uint32_t; /* NOLINT */ - -/// @brief 64-bit entity type +/// @brief Entity type /// @details 48-bit id (281'474'976'710'655 ids) /// @details 16-bit version (65'535) /// @details page size: 1024 -enum class e64 : uint64_t; /* NOLINT */ +enum class Entity : uint64_t; /* NOLINT */ } // namespace arch::ecs diff --git a/include/ecs/EntityPool.h b/include/ecs/EntityPool.h index f778b29..2e38994 100644 --- a/include/ecs/EntityPool.h +++ b/include/ecs/EntityPool.h @@ -12,15 +12,14 @@ namespace arch::ecs { /// @brief Pool for creating and killing entities /// @details Uses sparse set data structure -/// @tparam E - entity type -template -class EntityPool: public _details::SparseSet { - using Base = _details::SparseSet; +/// @tparam Entity - entity type +class EntityPool: public _details::SparseSet { + using Base = _details::SparseSet; public: /// @brief EntityTraits of entity - using Traits = _details::EntityTraits; + using Traits = _details::EntityTraits; /// @brief Entity type using EntityT = typename Traits::EntityT; /// @brief Entity id type @@ -28,8 +27,6 @@ class EntityPool: public _details::SparseSet { /// @brief Entity version type using VersionT = typename Traits::VersionT; - static_assert(std::popcount(Traits::pageSize) == 1, "pageSize for entity must be a power of 2"); - /// @brief Iterator type using Iterator = typename Base::DenseContainer::iterator; /// @brief Const iterator type @@ -40,7 +37,7 @@ class EntityPool: public _details::SparseSet { using ConstReverseIterator = typename Base::DenseContainer::const_reverse_iterator; /// @brief Null entity - static inline constexpr EntityT null = Traits::Entity::null; + static inline constexpr EntityT null = Traits::Ent::null; /// @brief Swaps this pool with given pool /// @param other - pool to swap with @@ -75,8 +72,6 @@ class EntityPool: public _details::SparseSet { /// @param entities - entities to kill void kill(std::initializer_list ids) noexcept; - using Base::contains; - /// @brief Finds given entity /// @param entity - entity to find /// @return Iterator to found entity, end() otherwise @@ -94,8 +89,6 @@ class EntityPool: public _details::SparseSet { /// @return Iterator to found entity, end() otherwise ConstIterator find(IdT id) const noexcept; - using Base::version; - /// @brief Returns amount of entities alive size_t size() const noexcept; @@ -126,24 +119,10 @@ class EntityPool: public _details::SparseSet { private: - using Base::_sparseAssure; - using Base::_sparseAssurePage; - using Base::_sparseGet; - using Base::_sparseTryGet; - // for manual checks - std::tuple _debug() noexcept { - return { &_sparse, &_dense }; - } + std::tuple _debug() noexcept; - using Base::_dense; - using Base::_sparse; size_t _size = 0; }; -extern template class EntityPool; -extern template class EntityPool; - } // namespace arch::ecs - -#include "EntityPool.hpp" diff --git a/include/ecs/EntitySpecs.h b/include/ecs/EntitySpecs.h index e6ae14e..7f60983 100644 --- a/include/ecs/EntitySpecs.h +++ b/include/ecs/EntitySpecs.h @@ -7,42 +7,20 @@ namespace arch::ecs { -/// @brief Automatic entity specification -/// @tparam E - entity type -/// @tparam V - version type -/// @tparam IdLen - bit length of id -template -struct AutoEntitySpecs { +/// @brief Entity specification +struct EntitySpecs { /// @brief Type of entity - using EntityT = E; + using EntityT = Entity; /// @brief Type of id - using IdT = std::underlying_type_t; + using IdT = std::underlying_type_t; /// @brief Type of version - using VersionT = V; + using VersionT = uint16_t; /// @brief Bit length of id - static inline constexpr size_t idLength = IdLen; + static inline constexpr size_t idLength = 48; /// @brief Size of sparse pages - static inline constexpr size_t pageSize = PageSize; -}; - -/// @brief Contains specification of an entity, specialize template to create your own -/// @brief Must have the same members as AutoEntitySpecs (usage by inheritance is recommended) -/// @tparam E - entity type -template -struct EntitySpecs { - static_assert("EntitySpecs not specialized for given type"); + static inline constexpr size_t pageSize = 1'024; }; -/// @brief EntitySpecs specialization for 32-bit entity -/// @sa arch::ecs::e32 -template<> -struct EntitySpecs: AutoEntitySpecs {}; - -template<> -/// @brief EntitySpecs specialization for 64-bit entity -/// @sa arch::ecs::e64 -struct EntitySpecs: AutoEntitySpecs {}; - } // namespace arch::ecs diff --git a/include/ecs/EntityTraits.h b/include/ecs/EntityTraits.h index 746df43..c3e3113 100644 --- a/include/ecs/EntityTraits.h +++ b/include/ecs/EntityTraits.h @@ -11,29 +11,25 @@ namespace arch::ecs::_details { /* NOLINT */ /// @details All data extracted from EntitySpecs /// @details 'part' of entity - abstract value describing part of entity /// @details 'raw part' of entity - 'part' of entity as held in memory, in general 'part' != 'raw part' -/// @tparam E - entity type -template struct EntityTraits { - /// @brief Entity specifications - using Specs = EntitySpecs; - + // Entity specification + using Specs = EntitySpecs; /// @brief Entity type - using EntityT = typename Specs::EntityT; + using EntityT = typename ::arch::ecs::Entity; /// @brief Entity underlying type - using EntityRawT = std::underlying_type_t; + using EntityRawT = std::underlying_type_t; /// @brief Entity id type - using IdT = typename Specs::IdT; + using IdT = EntityRawT; /// @brief Entity version type - using VersionT = typename Specs::VersionT; + using VersionT = uint16_t; /// @brief Size of sparse pages - static inline constexpr size_t pageSize = Specs::pageSize; - static_assert(std::popcount(pageSize) == 1, "pageSize must be a power of 2"); + static inline constexpr size_t pageSize = 1'024; /// @brief Entity section - struct Entity { + struct Ent { /// @brief Entity type - using Type = EntityT; + using Type = Specs::EntityT; /// @brief Entity underlying type using RawT = EntityRawT; @@ -45,18 +41,18 @@ struct EntityTraits { /// @brief Constructs entity from its parts /// @param id - id of entity /// @param ver - version of entity - static inline Type fromParts(const IdT id, const VersionT ver) noexcept; + static Type fromParts(const IdT id, const VersionT ver) noexcept; /// @brief Constructs entity from its raw parts /// @param id - id part of entity /// @param ver - version part of entity - static inline Type fromRawParts(const RawT id, const RawT ver) noexcept; + static Type fromRawParts(const RawT id, const RawT ver) noexcept; /// @brief Constructs entity from others /// @param id - entity to extract id from /// @param ver - entity to extract version from - static inline Type fromOthers(const Type id, const Type ver) noexcept; + static Type fromOthers(const Type id, const Type ver) noexcept; /// @brief Obtains entity as underlying type /// @param entity - entity to convert - static inline RawT toRaw(const Type entity) noexcept; + static RawT toRaw(const Type entity) noexcept; }; /// @brief Id section @@ -78,19 +74,19 @@ struct EntityTraits { static inline constexpr EntityRawT nullRaw = mask; /// @brief Obtains id part from given entity - static inline Type part(const EntityT entity) noexcept; + static Type part(const EntityT entity) noexcept; /// @brief Obtains id raw part from given entity - static inline EntityRawT rawPart(const EntityT entity) noexcept; + static EntityRawT rawPart(const EntityT entity) noexcept; /// @brief Converts id part to raw part - static inline EntityRawT asRawPart(const Type id) noexcept; + static EntityRawT asRawPart(const Type id) noexcept; /// @brief Checks if entity has null id - static inline bool hasNull(const EntityT entity) noexcept; + static bool hasNull(const EntityT entity) noexcept; /// @brief Creates entity with null id and version of given entity - static inline EntityT withNull(const EntityT entity) noexcept; + static EntityT withNull(const EntityT entity) noexcept; /// @brief Swaps ids of two entities - static inline void swap(EntityT& entity1, EntityT& entity2) noexcept; + static void swap(EntityT& entity1, EntityT& entity2) noexcept; /// @brief Compares ids of 2 given entities - static inline bool equal(const EntityT entity1, const EntityT entity2) noexcept; + static bool equal(const EntityT entity1, const EntityT entity2) noexcept; }; /// @brief Version section @@ -99,45 +95,39 @@ struct EntityTraits { using Type = VersionT; /// @brief Bit length of version - static inline constexpr size_t length = Entity::length - Specs::idLength; + static inline constexpr size_t length = Ent::length - Specs::idLength; /// @brief Bit mask of version static inline constexpr EntityRawT mask = ~(std::numeric_limits::max() >> length); /// @brief Max value of version as raw part static inline constexpr EntityRawT maxRaw = ((mask - 1) & mask); /// @brief Max value of version as part - static inline constexpr Type max = (Type)(maxRaw >> (Entity::length - length)); + static inline constexpr Type max = (Type)(maxRaw >> (Ent::length - length)); /// @brief Null version (part) static inline constexpr Type null = max + 1; /// @brief Null version (raw part) static inline constexpr EntityRawT nullRaw = mask; /// @brief Obtains version part from given entity - static inline Type part(const EntityT entity) noexcept; + static Type part(const EntityT entity) noexcept; /// @brief Obtains version raw part from given entity - static inline EntityRawT asRawPart(const Type version) noexcept; + static EntityRawT asRawPart(const Type version) noexcept; /// @brief Converts version part to raw part - static inline EntityRawT rawPart(const EntityT entity) noexcept; + static EntityRawT rawPart(const EntityT entity) noexcept; /// @brief Checks if entity has null version - static inline bool hasNull(const EntityT entity) noexcept; + static bool hasNull(const EntityT entity) noexcept; /// @brief Checks if entity has != null version - static inline bool hasNotNull(const EntityT entity) noexcept; + static bool hasNotNull(const EntityT entity) noexcept; /// @brief Creates entity with next version and id of given entity - static inline EntityT withNext(const EntityT entity) noexcept; + static EntityT withNext(const EntityT entity) noexcept; /// @brief Gets next version of given entity - static inline Type next(const EntityT entity) noexcept; + static Type next(const EntityT entity) noexcept; /// @brief Creates entity with null version and id of given entity - static inline EntityT withNull(const EntityT entity) noexcept; + static EntityT withNull(const EntityT entity) noexcept; /// @brief Swaps versions of two entities - static inline void swap(EntityT& entity1, EntityT& entity2) noexcept; + static void swap(EntityT& entity1, EntityT& entity2) noexcept; /// @brief Compares versions of 2 given entities - static inline bool equal(const EntityT entity1, const EntityT entity2) noexcept; + static bool equal(const EntityT entity1, const EntityT entity2) noexcept; }; }; -extern template class EntityTraits; -extern template class EntityTraits; - } // namespace arch::ecs::_details - -#include "EntityTraits.hpp" - diff --git a/include/ecs/EntityTraits.hpp b/include/ecs/EntityTraits.hpp deleted file mode 100644 index f39971d..0000000 --- a/include/ecs/EntityTraits.hpp +++ /dev/null @@ -1,129 +0,0 @@ -#include "ArchMath.h" -#include "EntityTraits.h" - -#define TEMPLATE_E template -#define TRAITS_E EntityTraits -#define ENTITY TRAITS_E::Entity -#define ID TRAITS_E::Id -#define VERSION TRAITS_E::Version - -namespace arch::ecs::_details { - -TEMPLATE_E -ENTITY::Type ENTITY::fromParts(const IdT id, const VersionT ver) noexcept { - return fromRawParts(Id::asRawPart(id), Version::asRawPart(ver)); -} - -TEMPLATE_E -ENTITY::Type ENTITY::fromRawParts(const RawT id, const RawT ver) noexcept { - return Type(id | ver); -} - -TEMPLATE_E -ENTITY::Type ENTITY::fromOthers(const Type id, const Type ver) noexcept { - return fromRawParts(Id::rawPart(id), Version::rawPart(ver)); -} - -TEMPLATE_E -ENTITY::RawT ENTITY::toRaw(const Type entity) noexcept { - return (RawT)entity; -} - -TEMPLATE_E -ID::Type ID::part(const EntityT entity) noexcept { - return { rawPart(entity) }; -} - -TEMPLATE_E -TRAITS_E::EntityRawT ID::asRawPart(const Type id) noexcept { - return (EntityRawT)id & mask; -} - -TEMPLATE_E -TRAITS_E::EntityRawT ID::rawPart(const EntityT entity) noexcept { - return Entity::toRaw(entity) & mask; -} - -TEMPLATE_E -bool ID::hasNull(const EntityT entity) noexcept { - return (Entity::toRaw(entity) & mask) == nullRaw; -} - -TEMPLATE_E -TRAITS_E::EntityT ID::withNull(const EntityT entity) noexcept { - return Entity::fromRawParts(nullRaw, Version::rawPart(entity)); -} - -TEMPLATE_E -void ID::swap(EntityT& entity1, EntityT& entity2) noexcept { - const auto id1 = rawPart(entity1); - entity1 = Entity::fromRawParts(rawPart(entity2), Version::rawPart(entity1)); - entity2 = Entity::fromRawParts(id1, Version::rawPart(entity2)); -} - -TEMPLATE_E -bool ID::equal(const EntityT entity1, const EntityT entity2) noexcept { - return rawPart(entity1) == rawPart(entity2); -} - -TEMPLATE_E -VERSION::Type VERSION::part(const EntityT entity) noexcept { - return Type(rawPart(entity) >> Id::length); -} - -TEMPLATE_E -TRAITS_E::EntityRawT VERSION::asRawPart(const Type version) noexcept { - return (EntityRawT)version << Id::length; -} - -TEMPLATE_E -TRAITS_E::EntityRawT VERSION::rawPart(const EntityT entity) noexcept { - return Entity::toRaw(entity) & mask; -} - -TEMPLATE_E -bool VERSION::hasNull(const EntityT entity) noexcept { - return (Entity::toRaw(entity) & mask) == nullRaw; -} - -TEMPLATE_E -bool VERSION::hasNotNull(const EntityT entity) noexcept { - return (Entity::toRaw(entity) & mask) != nullRaw; -} - -TEMPLATE_E -TRAITS_E::EntityT VERSION::withNext(const EntityT entity) noexcept { - return Entity::fromParts(Id::part(entity), next(entity)); -} - -TEMPLATE_E -VERSION::Type VERSION::next(const EntityT entity) noexcept { - const auto newVersion = part(entity) + 1; - - return newVersion + (newVersion == null); -} - -TEMPLATE_E -TRAITS_E::EntityT VERSION::withNull(const EntityT entity) noexcept { - return Entity::fromRawParts(Id::rawPart(entity), nullRaw); -} - -TEMPLATE_E -void VERSION::swap(EntityT& entity1, EntityT& entity2) noexcept { - const auto ver1 = rawPart(entity1); - entity1 = Entity::fromRawParts(ID::rawPart(entity1), rawPart(entity2)); - entity2 = Entity::fromRawParts(ID::rawPart(entity2), ver1); -} - -TEMPLATE_E -bool VERSION::equal(const EntityT entity1, const EntityT entity2) noexcept { - return rawPart(entity1) == rawPart(entity2); -} - -} // namespace arch::ecs::_details - -#undef TEMPLATE_E -#undef TRAITS_E -#undef ENTITY -#undef ID -#undef VERSION diff --git a/include/ecs/SparseSet.h b/include/ecs/SparseSet.h index a6d684a..c243453 100644 --- a/include/ecs/SparseSet.h +++ b/include/ecs/SparseSet.h @@ -9,13 +9,12 @@ namespace arch::ecs::_details { /// @brief Sparse set data structure with find-like operations -/// @tparam E - entity type -template +/// @tparam Entity - entity type class SparseSet { public: /// @brief EntityTraits of entity - using Traits = EntityTraits; + using Traits = EntityTraits; /// @brief Entity type using EntityT = Traits::EntityT; /// @brief Id type @@ -48,8 +47,8 @@ class SparseSet { protected: - using SparseContainer = std::vector>>; - using DenseContainer = std::vector; + using SparseContainer = std::vector>>; + using DenseContainer = std::vector; // possibly inits sparse page EntityT* _sparseAssurePage(const size_t n) noexcept; @@ -66,9 +65,4 @@ class SparseSet { DenseContainer _dense; }; -extern template class SparseSet; -extern template class SparseSet; - } // namespace arch::ecs::_details - -#include "SparseSet.hpp" diff --git a/include/ecs/View.h b/include/ecs/View.h index 0b9c8b8..3593ead 100644 --- a/include/ecs/View.h +++ b/include/ecs/View.h @@ -8,85 +8,77 @@ namespace arch::ecs { -template class Domain; namespace _details { /// @brief Returns given component wrapped in std::tuple -/// @tparam E - entity type +/// @tparam Entity - entity type /// @tparam C - component type /// @param domain - domain to get component from /// @param entity - entity to get component from -template -auto getAsTuple(Domain& domain, const E entity) noexcept; +template +auto getAsTuple(Domain& domain, const Entity entity) noexcept; /// @brief Returns given readonly component wrapped in std::tuple -/// @tparam E - entity type +/// @tparam Entity - entity type /// @tparam C - component type /// @param domain - domain to get component from /// @param entity - entity to get component from -template -auto getAsTuple(const Domain& domain, const E entity) noexcept; +template +auto getAsTuple(const Domain& domain, const Entity entity) noexcept; /// @brief Returns std::tuple with specified components from given entity -/// @tparam E - entity type +/// @tparam Entity - entity type /// @tparam Includes - component types /// @param domain - domain to get components from /// @param entity - entity to get components from /// @param - TypeList instance to help in deduction -template -auto getByTL(Domain& domain, const E entity, TypeList) noexcept; +template +auto getByTL(Domain& domain, const Entity entity, TypeList) noexcept; /// @brief Returns std::tuple with specified components from given entity -/// @tparam E - entity type +/// @tparam Entity - entity type /// @tparam Includes - component types /// @param domain - domain to get components from /// @param entity - entity to get components from /// @param - TypeList instance to help in deduction -template -auto getByTL(const Domain& domain, const E entity, TypeList) noexcept; +template +auto getByTL(const Domain& domain, const Entity entity, TypeList) noexcept; -/// @brief Predicate wrapper for TypeList to filter out flag-components -/// @tparam E - entity type -template +/// @brief Predicate for TypeList to filter out flag-components +/// @tparam C - component type +template struct IsFlagComponent { - /// @brief Actual predicate - /// @tparam C - component type - template - struct Pred { - static inline constexpr bool value = _details::ComponentTraits::flag; - }; + static inline constexpr bool value = _details::ComponentTraits::flag; }; } // namespace _details /// @brief Domain view -/// @tparam E - entity type +/// @tparam Entity - entity type /// @tparam Const - whether view is readonly /// @tparam Include - TypeList with components included in view /// @tparam Exclude - TypeList with components excluded in view -template +template class View; /// @brief Domain's view -/// @tparam E - entity type +/// @tparam Entity - entity type /// @tparam Const - whether view is readonly /// @tparam Includes - components included in view /// @tparam Excludes - components excluded in view -template -class View, TypeList> { +template +class View, TypeList> { public: /// @brief Entity traits - using Traits = _details::EntityTraits; - /// @brief Entity type - using EntityT = Traits::EntityT; + using Traits = _details::EntityTraits; /// @brief Actual domain type used - using DomainT = std::conditional_t, Domain>; + using DomainT = std::conditional_t; /// @brief TypeList with components included in view using Include = TypeList; /// @brief TypeList with components excluded in view using Exclude = TypeList; /// @brief Include without flag-components - using NoFlags = typename Include::template eraseIf<_details::IsFlagComponent::template Pred>; + using NoFlags = typename Include::template eraseIf<_details::IsFlagComponent>; /// @brief Count of included component types static inline constexpr size_t includeCount = Include::length; @@ -97,7 +89,7 @@ class View, TypeList> { /// @brief Checks if entity is contained by view /// @param entity - entity to check - bool contains(const EntityT entity) const noexcept; + bool contains(const Entity entity) const noexcept; /// @brief Recomputes entities in view /// @brief Needed if at least one component is in-place @@ -105,26 +97,26 @@ class View, TypeList> { /// @brief Returns std::tuple with included components, excluding flag-components /// @param entity - entity to get components from - auto get(const EntityT entity) noexcept requires(!Const); + auto get(const Entity entity) noexcept requires(!Const); /// @brief Returns std::tuple with readonly included components, excluding flag-components /// @param entity - entity to get components from - auto get(const EntityT entity) const noexcept; + auto get(const Entity entity) const noexcept; /// @brief Returns std::tuple with included components, including flag-components /// @param entity - entity to get components from - auto getAll(const EntityT entity) noexcept requires(!Const); + auto getAll(const Entity entity) noexcept requires(!Const); /// @brief Returns std::tuple with readonly included components, including flag-components /// @param entity - entity to get components from - auto getAll(const EntityT entity) const noexcept; + auto getAll(const Entity entity) const noexcept; /// @brief Returns std::tuple with given components /// @tparam Cs - components to get /// @param entity - entity to get components from template - auto get(const EntityT entity) noexcept requires(!Const); + auto get(const Entity entity) noexcept requires(!Const); /// @brief Returns std::tuple with given readonly components /// @tparam Cs - components to get /// @param entity - entity to get components from template - auto get(const EntityT entity) const noexcept; + auto get(const Entity entity) const noexcept; /// @brief Returns std::view of std::tuples /// @brief First element is the entity @@ -189,30 +181,30 @@ class View, TypeList> { private: - friend Domain; + friend Domain; - using CCPoolPtr = const _details::CommonComponentPool*; - //, _details::CommonComponentPool* > ; + using CCPoolPtr = const _details::CommonComponentPool*; + //, _details::CommonComponentPool* > ; template using CPoolPtr = std::conditional_t< Const || std::is_const_v, - const ComponentPool, E>*, - ComponentPool, E>*>; + const ComponentPool>*, + ComponentPool>*>; size_t _minInclude() const noexcept; template - auto getAsTuple(const E entity) noexcept requires(!Const); + auto getAsTuple(const Entity entity) noexcept requires(!Const); template - auto getAsTuple(const E entity) const noexcept; + auto getAsTuple(const Entity entity) const noexcept; template - auto getByTL(const E entity, TypeList) noexcept requires(!Const); + auto getByTL(const Entity entity, TypeList) noexcept requires(!Const); template - auto getByTL(const E entity, TypeList) const noexcept; + auto getByTL(const Entity entity, TypeList) const noexcept; // expected type of entities view using EntitesViewT = decltype(std::views::filter( - std::declval>()._entitiesForView(), + std::declval()._entitiesForView(), std::bind(&View::contains, std::declval(), std::placeholders::_1) )); @@ -225,25 +217,22 @@ class View, TypeList> { }; /// @brief Domain's exclude-only view -/// @tparam E - entity type +/// @tparam Entity - entity type /// @tparam Const - whether view is readonly /// @tparam Excludes - components excluded in view -template -class View, TypeList> { +template +class View, TypeList> { public: /// @brief Entity traits - using Traits = _details::EntityTraits; - /// @brief Entity type - using EntityT = Traits::EntityT; - /// @brief Actual domain type used + using Traits = _details::EntityTraits; /// @brief TypeList with components included in view using Include = TypeList<>; /// @brief TypeList with components excluded in view using Exclude = TypeList; - /// @brief Include without flag-components - using DomainT = std::conditional_t, Domain>; + /// @brief Actual domain type used + using DomainT = std::conditional_t; /// @brief Count of included component types static inline constexpr size_t includeCount = TypeList<>::length; @@ -254,7 +243,7 @@ class View, TypeList> { /// @brief Checks if entity is contained by view /// @param entity - entity to check - bool contains(const EntityT entity) const noexcept; + bool contains(const Entity entity) const noexcept; /// @brief Recomputes entities in view /// @brief Needed if at least one component is in-place @@ -264,12 +253,12 @@ class View, TypeList> { /// @tparam Cs - components to get /// @param entity - entity to get components from template - auto get(const EntityT entity) noexcept requires(!Const); + auto get(const Entity entity) noexcept requires(!Const); /// @brief Returns std::tuple with given readonly components (may be out of view) /// @tparam Cs - components to get /// @param entity - entity to get components from template - auto get(const EntityT entity) const noexcept; + auto get(const Entity entity) const noexcept; /// @brief Executes given function with entities in view as arguments /// @tparam Fn - function type @@ -297,22 +286,21 @@ class View, TypeList> { private: - friend Domain; + friend Domain; // filtering function - bool _containsNoCheck(const E entity) const noexcept; + bool _containsNoCheck(const Entity entity) const noexcept; - using CCPoolPtr = - std::conditional_t*, _details::CommonComponentPool*>; + using CCPoolPtr = std::conditional_t; template using CPoolPtr = std::conditional_t< Const || std::is_const_v, - const ComponentPool, E>*, - ComponentPool, E>*>; + const ComponentPool>*, + ComponentPool>*>; // expected type of entities view using EntitesViewT = decltype(std::views::filter( - std::declval>().entities(), + std::declval().entities(), std::bind(&View::_containsNoCheck, std::declval(), std::placeholders::_1) )); diff --git a/include/ecs/View.hpp b/include/ecs/View.hpp index 8b65276..167c3c8 100644 --- a/include/ecs/View.hpp +++ b/include/ecs/View.hpp @@ -1,59 +1,54 @@ #include #include -#include "View.h" -// #include "Domain.h" +// +#include "View.h" #include "tUtils/Functions/IsApplicable.h" -#define TEMPLATE_ECIE template -#define TEMPLATE_ECE template -#define VIEW_ECIE View, TypeList> -#define VIEW_ECE View, TypeList> +#define TEMPLATE_CIE template +#define TEMPLATE_CE template +#define VIEW_CIE View, TypeList> +#define VIEW_CE View, TypeList> namespace arch::ecs { namespace _details { -template -auto getAsTuple(Domain& domain, const E entity) noexcept { +template +auto getAsTuple(Domain& domain, const Entity entity) noexcept { // make_tuple - by values, tie - by references - if constexpr (_details::ComponentTraits::flag) { + if constexpr (_details::ComponentTraits::flag) { return std::make_tuple(domain.template getComponent(entity)); } else { return std::tie(domain.template getComponent(entity)); } } -template -auto getAsTuple(const Domain& domain, const E entity) noexcept { +template +auto getAsTuple(const Domain& domain, const Entity entity) noexcept { // make_tuple - by values, tie - by references - if constexpr (_details::ComponentTraits::flag) { + if constexpr (_details::ComponentTraits::flag) { return std::make_tuple(domain.template getComponent(entity)); } else { return std::tie(domain.template getComponent(entity)); } } -template -auto getByTL(Domain& domain, const E entity, TypeList) noexcept { - return std::tuple_cat(getAsTuple(domain, entity)...); +template +auto getByTL(Domain& domain, const Entity entity, TypeList) noexcept { + return std::tuple_cat(getAsTuple(domain, entity)...); } -template -auto getByTL(const Domain& domain, const E entity, TypeList) noexcept { - return std::tuple_cat(getAsTuple(domain, entity)...); +template +auto getByTL(const Domain& domain, const Entity entity, TypeList) noexcept { + return std::tuple_cat(getAsTuple(domain, entity)...); } } // namespace _details -TEMPLATE_ECIE -size_t VIEW_ECIE::_minInclude() const noexcept { - /*ARCH_ASSERT( - std::ranges::none_of(_includedCPools, [](const auto ptr) { return ptr == nullptr; }), - "At least one ComponentPool does not exist" - );*/ - +TEMPLATE_CIE +size_t VIEW_CIE::_minInclude() const noexcept { if constexpr (includeCount != 1) { const auto min = std::ranges::min_element(_includedCPools, [](const auto lhs, const auto rks) { return !lhs ? true : (!rks ? true : lhs->count() < rks->count()); @@ -69,16 +64,16 @@ size_t VIEW_ECIE::_minInclude() const noexcept { } } -TEMPLATE_ECIE +TEMPLATE_CIE template -auto VIEW_ECIE::getAsTuple(const E entity) noexcept requires(!Const) +auto VIEW_CIE::getAsTuple(const Entity entity) noexcept requires(!Const) { constexpr auto idx = Include::template find; - if constexpr (_details::ComponentTraits::flag) { + if constexpr (_details::ComponentTraits::flag) { return std::make_tuple( dynamic_cast>( - const_cast, CCPoolPtr, _details::CommonComponentPool*>>( + const_cast, CCPoolPtr, _details::CommonComponentPool*>>( _includedCPools[idx] ) ) @@ -87,7 +82,7 @@ auto VIEW_ECIE::getAsTuple(const E entity) noexcept requires(!Const) } else { return std::tie( dynamic_cast>( - const_cast, CCPoolPtr, _details::CommonComponentPool*>>( + const_cast, CCPoolPtr, _details::CommonComponentPool*>>( _includedCPools[idx] ) ) @@ -96,45 +91,45 @@ auto VIEW_ECIE::getAsTuple(const E entity) noexcept requires(!Const) } } -TEMPLATE_ECIE +TEMPLATE_CIE template -auto VIEW_ECIE::getAsTuple(const E entity) const noexcept { +auto VIEW_CIE::getAsTuple(const Entity entity) const noexcept { constexpr auto idx = Include::template find; - if constexpr (_details::ComponentTraits::flag) { + if constexpr (_details::ComponentTraits::flag) { return std::make_tuple(dynamic_cast>(_includedCPools[idx])->get(entity)); } else { return std::tie(dynamic_cast>(_includedCPools[idx])->get(entity)); } } -TEMPLATE_ECIE +TEMPLATE_CIE template -auto VIEW_ECIE::getByTL(const E entity, TypeList) noexcept requires(!Const) +auto VIEW_CIE::getByTL(const Entity entity, TypeList) noexcept requires(!Const) { return std::tuple_cat(getAsTuple(entity)...); } -TEMPLATE_ECIE +TEMPLATE_CIE template -auto VIEW_ECIE::getByTL(const E entity, TypeList) const noexcept { +auto VIEW_CIE::getByTL(const Entity entity, TypeList) const noexcept { return std::tuple_cat(getAsTuple(entity)...); } -TEMPLATE_ECIE -VIEW_ECIE::View(DomainT* domain) noexcept: +TEMPLATE_CIE +VIEW_CIE::View(DomainT* domain) noexcept: _includedCPools{ dynamic_cast(domain->template _tryGetCPool())... }, _excludedCPools{ dynamic_cast(domain->template _tryGetCPool())... }, _minIdx{ _minInclude() }, // can't just call refresh(), _entities is not default_initializable _entities( - _minIdx == (size_t)-1 ? _details::CommonComponentPool::_emptyEntitiesForView() : + _minIdx == (size_t)-1 ? _details::CommonComponentPool::_emptyEntitiesForView() : _includedCPools[_minIdx]->_entitiesForView(), std::bind(&View::contains, (const View*)this, std::placeholders::_1) ) {} -TEMPLATE_ECE -VIEW_ECE::View(DomainT* domain) noexcept: +TEMPLATE_CE +VIEW_CE::View(DomainT* domain) noexcept: _excludedCPools{ dynamic_cast(domain->template _tryGetCPool())... }, // can't just call refresh(), _entities is not default_initializable _entities(domain->entities(), std::bind(&View::_containsNoCheck, this, std::placeholders::_1)), @@ -145,12 +140,13 @@ VIEW_ECE::View(DomainT* domain) noexcept: ); } -TEMPLATE_ECE -bool VIEW_ECE::_containsNoCheck(const E entity) const noexcept { +TEMPLATE_CE +bool VIEW_CE::_containsNoCheck(const Entity entity) const noexcept { return std::ranges::none_of(_excludedCPools, [entity](const auto cpool) { return cpool->contains(entity); }); } -TEMPLATE_ECIE bool VIEW_ECIE::contains(const EntityT entity) const noexcept { +TEMPLATE_CIE +bool VIEW_CIE::contains(const Entity entity) const noexcept { return std::all_of( _includedCPools.begin(), _includedCPools.begin() + _minIdx, @@ -164,12 +160,12 @@ TEMPLATE_ECIE bool VIEW_ECIE::contains(const EntityT entity) const noexcept { std::ranges::none_of(_excludedCPools, [entity](const auto cpool) { return cpool->contains(entity); }); } -TEMPLATE_ECE -bool VIEW_ECE::contains(const EntityT entity) const noexcept { +TEMPLATE_CE +bool VIEW_CE::contains(const Entity entity) const noexcept { return _domain.alive(entity) && _containsNoCheck(entity); } -TEMPLATE_ECIE VIEW_ECIE& VIEW_ECIE::refresh() noexcept { +TEMPLATE_CIE VIEW_CIE& VIEW_CIE::refresh() noexcept { // destroy _entities _entities.~EntitesViewT(); _minIdx = _minInclude(); @@ -182,28 +178,28 @@ TEMPLATE_ECIE VIEW_ECIE& VIEW_ECIE::refresh() noexcept { return *this; } -TEMPLATE_ECE -VIEW_ECE& VIEW_ECE::refresh() noexcept { +TEMPLATE_CE +VIEW_CE& VIEW_CE::refresh() noexcept { _entities.~EntitesViewT(); new (&_entities) EntitesViewT(_domain->entities(), std::bind(&View::_containsNoCheck, this, std::placeholders::_1)); return *this; } -TEMPLATE_ECIE -auto VIEW_ECIE::get(const EntityT entity) noexcept requires(!Const) +TEMPLATE_CIE +auto VIEW_CIE::get(const Entity entity) noexcept requires(!Const) { return getByTL(entity, NoFlags()); } -TEMPLATE_ECIE -auto VIEW_ECIE::get(const EntityT entity) const noexcept { +TEMPLATE_CIE +auto VIEW_CIE::get(const Entity entity) const noexcept { return getByTL(entity, NoFlags()); } -TEMPLATE_ECIE +TEMPLATE_CIE template -auto VIEW_ECIE::get(const EntityT entity) noexcept requires(!Const) +auto VIEW_CIE::get(const Entity entity) noexcept requires(!Const) { static_assert( TypeList::template containsAll, @@ -212,9 +208,9 @@ auto VIEW_ECIE::get(const EntityT entity) noexcept requires(!Const) return getByTL(entity, TypeList()); } -TEMPLATE_ECIE +TEMPLATE_CIE template -auto VIEW_ECIE::get(const EntityT entity) const noexcept { +auto VIEW_CIE::get(const Entity entity) const noexcept { static_assert( TypeList::template containsAll, "One or more specified components is not contained in this view" @@ -222,79 +218,79 @@ auto VIEW_ECIE::get(const EntityT entity) const noexcept { return getByTL(entity, TypeList()); } -TEMPLATE_ECIE -auto VIEW_ECIE::getAll(const EntityT entity) noexcept requires(!Const) +TEMPLATE_CIE +auto VIEW_CIE::getAll(const Entity entity) noexcept requires(!Const) { return get(entity); } -TEMPLATE_ECIE -auto VIEW_ECIE::getAll(const EntityT entity) const noexcept { +TEMPLATE_CIE +auto VIEW_CIE::getAll(const Entity entity) const noexcept { return get(entity); } -TEMPLATE_ECE +TEMPLATE_CE template -auto VIEW_ECE::get(const EntityT entity) noexcept requires(!Const) +auto VIEW_CE::get(const Entity entity) noexcept requires(!Const) { return _details::getByTL(*_domain, entity, TypeList()); } -TEMPLATE_ECE +TEMPLATE_CE template -auto VIEW_ECE::get(const EntityT entity) const noexcept { +auto VIEW_CE::get(const Entity entity) const noexcept { return _details::getByTL(*_domain, entity, TypeList()); } -TEMPLATE_ECIE -auto VIEW_ECIE::all() noexcept requires(!Const) +TEMPLATE_CIE +auto VIEW_CIE::all() noexcept requires(!Const) { - return std::views::transform(_entities, [this](const EntityT entity) { + return std::views::transform(_entities, [this](const Entity entity) { return std::tuple_cat(std::make_tuple(entity), get(entity)); }); } -TEMPLATE_ECIE -auto VIEW_ECIE::all() const noexcept { - return std::views::transform(_entities, [this](const EntityT entity) { +TEMPLATE_CIE +auto VIEW_CIE::all() const noexcept { + return std::views::transform(_entities, [this](const Entity entity) { return std::tuple_cat(std::make_tuple(entity), get(entity)); }); } -TEMPLATE_ECIE -auto VIEW_ECIE::components() noexcept requires(!Const) +TEMPLATE_CIE +auto VIEW_CIE::components() noexcept requires(!Const) { - return std::views::transform(_entities, [this](const EntityT entity) { return getAll(entity); }); + return std::views::transform(_entities, [this](const Entity entity) { return getAll(entity); }); } -TEMPLATE_ECIE -auto VIEW_ECIE::components() const noexcept { - return std::views::transform(_entities, [this](const EntityT entity) { return getAll(entity); }); +TEMPLATE_CIE +auto VIEW_CIE::components() const noexcept { + return std::views::transform(_entities, [this](const Entity entity) { return getAll(entity); }); } -TEMPLATE_ECIE +TEMPLATE_CIE template -auto VIEW_ECIE::components() noexcept requires(!Const) +auto VIEW_CIE::components() noexcept requires(!Const) { - return std::views::transform(_entities, [this](const EntityT entity) { return get(entity); }); + return std::views::transform(_entities, [this](const Entity entity) { return get(entity); }); } -TEMPLATE_ECIE +TEMPLATE_CIE template -auto VIEW_ECIE::components() const noexcept { - return std::views::transform(_entities, [this](const EntityT entity) { return get(entity); }); +auto VIEW_CIE::components() const noexcept { + return std::views::transform(_entities, [this](const Entity entity) { return get(entity); }); } -TEMPLATE_ECIE +TEMPLATE_CIE template -void VIEW_ECIE::forEach(Fn&& fn) noexcept requires(!Const) +void VIEW_CIE::forEach(Fn&& fn) noexcept requires(!Const) { // call modes - using EntityTuple = decltype(std::tuple()); - using EntityNoFlagsTuple = decltype(std::tuple_cat(std::tuple(), get(EntityT()))); - using EntityComponentsTuple = decltype(std::tuple_cat(std::tuple(), getAll(EntityT()))); - using NoFlagsTuple = decltype(get(EntityT())); - using ComponentsTuple = decltype(getAll(EntityT())); + using EntityTuple = decltype(std::tuple()); + using EntityNoFlagsTuple = decltype(std::tuple_cat(std::tuple(), get(Entity()))); + using EntityComponentsTuple = decltype(std::tuple_cat(std::tuple(), getAll(Entity()))); + using NoFlagsTuple = decltype(get(Entity())); + using ComponentsTuple = decltype(getAll(Entity())); for (const auto entity : _entities) { if constexpr (tUtils::isApplicableV) { @@ -303,10 +299,10 @@ void VIEW_ECIE::forEach(Fn&& fn) noexcept requires(!Const) } else if constexpr (tUtils::isApplicableV) { // [...](entity, comp1&, comp2&, ...){ ... } // non-flags ------ ^ ----- ^ ... - std::apply(std::forward(fn), std::tuple_cat(std::tuple(entity), get(entity))); + std::apply(std::forward(fn), std::tuple_cat(std::tuple(entity), get(entity))); } else if constexpr (tUtils::isApplicableV) { // [...](entity, comp1&, comp2&, ...){ ... } - std::apply(std::forward(fn), std::tuple_cat(std::tuple(entity), getAll(entity))); + std::apply(std::forward(fn), std::tuple_cat(std::tuple(entity), getAll(entity))); } else if constexpr (tUtils::isApplicableV) { // [...]( comp1&, comp2&, ...){ ... } // non-flags ---- ^ ----- ^ ... @@ -320,15 +316,15 @@ void VIEW_ECIE::forEach(Fn&& fn) noexcept requires(!Const) } } -TEMPLATE_ECIE +TEMPLATE_CIE template -void VIEW_ECIE::forEach(Fn&& fn) const noexcept { +void VIEW_CIE::forEach(Fn&& fn) const noexcept { // call modes - using EntityTuple = decltype(std::tuple()); - using EntityNoFlagsTuple = decltype(std::tuple_cat(std::tuple(), get(EntityT()))); - using EntityComponentsTuple = decltype(std::tuple_cat(std::tuple(), getAll(EntityT()))); - using NoFlagsTuple = decltype(get(EntityT())); - using ComponentsTuple = decltype(getAll(EntityT())); + using EntityTuple = decltype(std::tuple()); + using EntityNoFlagsTuple = decltype(std::tuple_cat(std::tuple(), get(Entity()))); + using EntityComponentsTuple = decltype(std::tuple_cat(std::tuple(), getAll(Entity()))); + using NoFlagsTuple = decltype(get(Entity())); + using ComponentsTuple = decltype(getAll(Entity())); for (const auto entity : _entities) { if constexpr (tUtils::isApplicableV) { @@ -337,10 +333,10 @@ void VIEW_ECIE::forEach(Fn&& fn) const noexcept { } else if constexpr (tUtils::isApplicableV) { // [...](entity, comp1&, comp2&, ...){ ... } // non-flags ------ ^ ----- ^ ... - std::apply(std::forward(fn), std::tuple_cat(std::tuple(entity), get(entity))); + std::apply(std::forward(fn), std::tuple_cat(std::tuple(entity), get(entity))); } else if constexpr (tUtils::isApplicableV) { // [...](entity, comp1&, comp2&, ...){ ... } - std::apply(std::forward(fn), std::tuple_cat(std::tuple(entity), getAll(entity))); + std::apply(std::forward(fn), std::tuple_cat(std::tuple(entity), getAll(entity))); } else if constexpr (tUtils::isApplicableV) { // [...]( comp1&, comp2&, ...){ ... } // non-flags ---- ^ ----- ^ ... @@ -354,12 +350,12 @@ void VIEW_ECIE::forEach(Fn&& fn) const noexcept { } } -TEMPLATE_ECE +TEMPLATE_CE template -void VIEW_ECE::forEach(Fn&& fn) noexcept requires(!Const) +void VIEW_CE::forEach(Fn&& fn) noexcept requires(!Const) { // only one call mode for excluding views - using EntityTuple = decltype(std::tuple()); + using EntityTuple = decltype(std::tuple()); for (const auto entity : _entities) { if constexpr (tUtils::isApplicableV) { @@ -371,11 +367,11 @@ void VIEW_ECE::forEach(Fn&& fn) noexcept requires(!Const) } } -TEMPLATE_ECE +TEMPLATE_CE template -void VIEW_ECE::forEach(Fn&& fn) const noexcept { +void VIEW_CE::forEach(Fn&& fn) const noexcept { // only one call mode for excluding views - using EntityTuple = decltype(std::tuple()); + using EntityTuple = decltype(std::tuple()); for (const auto entity : _entities) { if constexpr (tUtils::isApplicableV) { @@ -387,72 +383,72 @@ void VIEW_ECE::forEach(Fn&& fn) const noexcept { } } -TEMPLATE_ECE -auto VIEW_ECE::begin() noexcept requires(!Const) +TEMPLATE_CE +auto VIEW_CE::begin() noexcept requires(!Const) { return _entities.begin(); } -TEMPLATE_ECE -const auto VIEW_ECE::begin() const noexcept { +TEMPLATE_CE +const auto VIEW_CE::begin() const noexcept { return _entities.begin(); } -TEMPLATE_ECE -const auto VIEW_ECE::cbegin() const noexcept { +TEMPLATE_CE +const auto VIEW_CE::cbegin() const noexcept { return _entities.begin(); } -TEMPLATE_ECE -auto VIEW_ECE::end() noexcept requires(!Const) +TEMPLATE_CE +auto VIEW_CE::end() noexcept requires(!Const) { return _entities.end(); } -TEMPLATE_ECE -const auto VIEW_ECE::end() const noexcept { +TEMPLATE_CE +const auto VIEW_CE::end() const noexcept { return _entities.end(); } -TEMPLATE_ECE -const auto VIEW_ECE::cend() const noexcept { +TEMPLATE_CE +const auto VIEW_CE::cend() const noexcept { return _entities.end(); } -TEMPLATE_ECIE auto VIEW_ECIE::begin() noexcept requires(!Const) +TEMPLATE_CIE auto VIEW_CIE::begin() noexcept requires(!Const) { return _entities.begin(); } -TEMPLATE_ECIE -const auto VIEW_ECIE::begin() const noexcept { +TEMPLATE_CIE +const auto VIEW_CIE::begin() const noexcept { return _entities.begin(); } -TEMPLATE_ECIE -const auto VIEW_ECIE::cbegin() const noexcept { +TEMPLATE_CIE +const auto VIEW_CIE::cbegin() const noexcept { return _entities.begin(); } -TEMPLATE_ECIE -auto VIEW_ECIE::end() noexcept requires(!Const) +TEMPLATE_CIE +auto VIEW_CIE::end() noexcept requires(!Const) { return _entities.end(); } -TEMPLATE_ECIE -const auto VIEW_ECIE::end() const noexcept { +TEMPLATE_CIE +const auto VIEW_CIE::end() const noexcept { return _entities.end(); } -TEMPLATE_ECIE -const auto VIEW_ECIE::cend() const noexcept { +TEMPLATE_CIE +const auto VIEW_CIE::cend() const noexcept { return _entities.end(); } } // namespace arch::ecs -#undef TEMPLATE_ECIE -#undef TEMPLATE_ECE -#undef VIEW_ECIE -#undef VIEW_ECE +#undef TEMPLATE_CIE +#undef TEMPLATE_CE +#undef VIEW_CIE +#undef VIEW_CE diff --git a/include/utils/ReadonlyCounter.h b/include/utils/ReadonlyCounter.h index 4f18940..4a0576c 100644 --- a/include/utils/ReadonlyCounter.h +++ b/include/utils/ReadonlyCounter.h @@ -1,5 +1,7 @@ #pragma once +#include + namespace arch::utils { /// @brief Readonly counter of given type, derived class manages _counter field. @@ -13,6 +15,7 @@ class ReadonlyCounter { protected: T _counter{}; }; + extern template class ReadonlyCounter; } // namespace arch::utils diff --git a/src/ecs/CommonComponentPool.cpp b/src/ecs/CommonComponentPool.cpp new file mode 100644 index 0000000..3dd292b --- /dev/null +++ b/src/ecs/CommonComponentPool.cpp @@ -0,0 +1,17 @@ +#include + +#include + +namespace arch::ecs::_details { // NOLINT + +CommonComponentPool::EntitiesViewT CommonComponentPool::_entitiesForView() const noexcept { + // entites with version != null are valid + return std::views::filter(_dense, _details::EntityTraits::Version::hasNotNull); +} + +CommonComponentPool::EntitiesViewT CommonComponentPool::_emptyEntitiesForView() noexcept { + static const std::vector empty; + return std::views::filter(empty, _details::EntityTraits::Version::hasNotNull); +} + +} // namespace arch::ecs::_details diff --git a/src/ecs/Domain.cpp b/src/ecs/Domain.cpp new file mode 100644 index 0000000..8229e30 --- /dev/null +++ b/src/ecs/Domain.cpp @@ -0,0 +1,71 @@ +#include + +#include + +namespace arch::ecs { + +Domain::~Domain() noexcept { + for (const auto& [type, destroyer] : _cpoolDestroyers) { + destroyer(_componentPools); + } +} + +void Domain::swap(Domain& other) noexcept { + std::swap(_entityPool, other._entityPool); + std::swap(_componentPools, other._componentPools); + std::swap(_cpoolDestroyers, other._cpoolDestroyers); +} + +bool Domain::alive(const Entity entity) const noexcept { + return _entityPool.contains(entity); +} + +bool Domain::contains(const IdT id) const noexcept { + return _entityPool.contains(id); +} + +Domain::VersionT Domain::version(const Entity entity) const noexcept { + return _entityPool.version(entity); +} + +Domain::VersionT Domain::version(const IdT id) const noexcept { + return _entityPool.version(id); +} + +size_t Domain::count() const noexcept { + return _entityPool.size(); +} + +Entity Domain::newEntity() noexcept { + return _entityPool.newEntity(); +} + +Entity Domain::recycleEntity(const Entity entity) noexcept { + return _entityPool.recycleEntity(entity); +} + +Entity Domain::recycleId(const IdT id) noexcept { + return _entityPool.recycleId(id); +} + +void Domain::kill(const Entity entity) noexcept { + for (auto&& [type, poolStorage] : _componentPools) { + reinterpret_cast<_details::CommonComponentPool*>(&poolStorage)->removeComponent(entity); + } + + _entityPool.kill(entity); +} + +void Domain::kill(std::input_iterator auto first, std::input_iterator auto last) noexcept { + _entityPool.kill(first, last); +} + +void Domain::kill(std::initializer_list entities) noexcept { + _entityPool.kill(entities); +} + +auto Domain::entities() const noexcept { + return std::views::all(_entityPool); +} + +} // namespace arch::ecs diff --git a/include/ecs/EntityPool.hpp b/src/ecs/EntityPool.cpp similarity index 62% rename from include/ecs/EntityPool.hpp rename to src/ecs/EntityPool.cpp index 45d3bf0..168de88 100644 --- a/include/ecs/EntityPool.hpp +++ b/src/ecs/EntityPool.cpp @@ -1,89 +1,71 @@ -#pragma once - -#include "EntityPool.h" #include "utils/Assert.h" - -#define TEMPLATE_E template -#define POOL_E EntityPool +#include // https://miro.com/app/board/uXjVK4gF1DI=/?share_link_id=296698570044 // ^ picture explanations namespace arch::ecs { -TEMPLATE_E POOL_E::Iterator POOL_E::begin() noexcept { +EntityPool::Iterator EntityPool::begin() noexcept { return _dense.begin(); } -TEMPLATE_E -POOL_E::ConstIterator POOL_E::begin() const noexcept { +EntityPool::ConstIterator EntityPool::begin() const noexcept { return _dense.begin(); } -TEMPLATE_E -POOL_E::ConstIterator POOL_E::cbegin() const noexcept { +EntityPool::ConstIterator EntityPool::cbegin() const noexcept { return _dense.cbegin(); } -TEMPLATE_E -POOL_E::Iterator POOL_E::end() noexcept { +EntityPool::Iterator EntityPool::end() noexcept { return _dense.begin() + _size; } -TEMPLATE_E -POOL_E::ConstIterator POOL_E::end() const noexcept { +EntityPool::ConstIterator EntityPool::end() const noexcept { return _dense.begin() + _size; } -TEMPLATE_E -POOL_E::ConstIterator POOL_E::cend() const noexcept { +EntityPool::ConstIterator EntityPool::cend() const noexcept { return _dense.cbegin() + _size; } -TEMPLATE_E -POOL_E::ReverseIterator POOL_E::rbegin() noexcept { +EntityPool::ReverseIterator EntityPool::rbegin() noexcept { return std::make_reverse_iterator(end()); } -TEMPLATE_E -POOL_E::ConstReverseIterator POOL_E::rbegin() const noexcept { +EntityPool::ConstReverseIterator EntityPool::rbegin() const noexcept { return std::make_reverse_iterator(end()); } -TEMPLATE_E -POOL_E::ConstReverseIterator POOL_E::crbegin() const noexcept { +EntityPool::ConstReverseIterator EntityPool::crbegin() const noexcept { return std::make_reverse_iterator(cend()); } -TEMPLATE_E -POOL_E::ReverseIterator POOL_E::rend() noexcept { +EntityPool::ReverseIterator EntityPool::rend() noexcept { return std::make_reverse_iterator(begin()); } -TEMPLATE_E -POOL_E::ConstReverseIterator POOL_E::rend() const noexcept { +EntityPool::ConstReverseIterator EntityPool::rend() const noexcept { return std::make_reverse_iterator(begin()); } -TEMPLATE_E -POOL_E::ConstReverseIterator POOL_E::crend() const noexcept { +EntityPool::ConstReverseIterator EntityPool::crend() const noexcept { return std::make_reverse_iterator(cbegin()); } -TEMPLATE_E -void POOL_E::swap(POOL_E& other) noexcept { +void EntityPool::swap(EntityPool& other) noexcept { std::swap(_sparse, other._sparse); std::swap(_dense, other._dense); std::swap(_size, other._size); } -TEMPLATE_E -POOL_E::EntityT POOL_E::newEntity() noexcept { +EntityPool::EntityT EntityPool::newEntity() noexcept { if (_size == Traits::Id::max + 1) { // entity limit achieved return null; } if (_size == _dense.size()) { // new entity - const auto entity = Traits::Entity::fromParts(_size++, 0); + const auto entity = Traits::Ent::fromParts(_size++, 0); _dense.push_back(entity); _sparseAssure(Traits::Id::part(entity)) = entity; @@ -97,8 +79,7 @@ POOL_E::EntityT POOL_E::newEntity() noexcept { } } -TEMPLATE_E -POOL_E::EntityT POOL_E::recycleEntity(const EntityT entity) noexcept { +EntityPool::EntityT EntityPool::recycleEntity(const EntityT entity) noexcept { if (!contains(Traits::Id::part(entity)) /*&& _size <= Traits::Id::max*/) { auto& wantedSparse = _sparseGet(Traits::Id::part(entity)); auto& toSwapDense = _dense[_size++]; @@ -106,16 +87,15 @@ POOL_E::EntityT POOL_E::recycleEntity(const EntityT entity) noexcept { // std::swap(_dense[Traits::Id::part(wantedSparse)], toSwapDense); Traits::Id::swap(wantedSparse, _sparseGet(Traits::Id::part(toSwapDense))); - toSwapDense = Traits::Entity::fromOthers(toSwapDense, entity); - wantedSparse = Traits::Entity::fromOthers(wantedSparse, toSwapDense); + toSwapDense = Traits::Ent::fromOthers(toSwapDense, entity); + wantedSparse = Traits::Ent::fromOthers(wantedSparse, toSwapDense); return toSwapDense; } return null; } -TEMPLATE_E -POOL_E::EntityT POOL_E::recycleId(const IdT id) noexcept { +EntityPool::EntityT EntityPool::recycleId(const IdT id) noexcept { if (!contains(id)) { auto& wantedSparse = _sparseGet(id); auto& toSwapDense = _dense[_size++]; @@ -123,15 +103,14 @@ POOL_E::EntityT POOL_E::recycleId(const IdT id) noexcept { std::swap(_dense[Traits::Id::part(wantedSparse)], toSwapDense); Traits::Id::swap(wantedSparse, _sparseGet(Traits::Id::part(toSwapDense))); - wantedSparse = Traits::Entity::fromOthers(wantedSparse, toSwapDense); + wantedSparse = Traits::Ent::fromOthers(wantedSparse, toSwapDense); return toSwapDense; } return null; } -TEMPLATE_E -void POOL_E::kill(const EntityT entity) noexcept { +void EntityPool::kill(const EntityT entity) noexcept { if (contains(entity)) { auto& wantedSparse = _sparseGet(Traits::Id::part(entity)); auto& toSwapDense = _dense[--_size]; @@ -145,8 +124,7 @@ void POOL_E::kill(const EntityT entity) noexcept { } } -TEMPLATE_E -void POOL_E::kill(const IdT id) noexcept { +void EntityPool::kill(const IdT id) noexcept { if (contains(id)) { auto& wantedSparse = _sparseGet(id); auto& toSwapDense = _dense[--_size]; @@ -160,35 +138,29 @@ void POOL_E::kill(const IdT id) noexcept { } } -TEMPLATE_E -void POOL_E::kill(std::input_iterator auto first, std::input_iterator auto last) noexcept { +void EntityPool::kill(std::input_iterator auto first, std::input_iterator auto last) noexcept { for (; first != last; ++first) { kill(*first); } } -TEMPLATE_E -void POOL_E::kill(std::initializer_list entities) noexcept { +void EntityPool::kill(std::initializer_list entities) noexcept { return kill(entities.begin(), entities.end()); } -TEMPLATE_E -void POOL_E::kill(std::initializer_list ids) noexcept { +void EntityPool::kill(std::initializer_list ids) noexcept { return kill(ids.begin(), ids.end()); } -TEMPLATE_E -POOL_E::Iterator POOL_E::find(const EntityT entity) noexcept { +EntityPool::Iterator EntityPool::find(const EntityT entity) noexcept { return find(Traits::Id::part(entity)); } -TEMPLATE_E -POOL_E::ConstIterator POOL_E::find(const EntityT entity) const noexcept { +EntityPool::ConstIterator EntityPool::find(const EntityT entity) const noexcept { return find(Traits::Id::part(entity)); } -TEMPLATE_E -POOL_E::Iterator POOL_E::find(const IdT id) noexcept { +EntityPool::Iterator EntityPool::find(const IdT id) noexcept { if (id == Traits::Id::null) { return end(); } @@ -202,8 +174,7 @@ POOL_E::Iterator POOL_E::find(const IdT id) noexcept { end(); } -TEMPLATE_E -POOL_E::ConstIterator POOL_E::find(const IdT id) const noexcept { +EntityPool::ConstIterator EntityPool::find(const IdT id) const noexcept { if (id == Traits::Id::null) { return end(); } @@ -217,12 +188,12 @@ POOL_E::ConstIterator POOL_E::find(const IdT id) const noexcept { end(); } -TEMPLATE_E -size_t POOL_E::size() const noexcept { +size_t EntityPool::size() const noexcept { return _size; } -} // namespace arch::ecs +auto EntityPool::_debug() noexcept -> std::tuple { + return { &_sparse, &_dense }; +} -#undef TEMPLATE_E -#undef POOL_E +} // namespace arch::ecs diff --git a/src/ecs/EntityTraits.cpp b/src/ecs/EntityTraits.cpp new file mode 100644 index 0000000..0b2c0f8 --- /dev/null +++ b/src/ecs/EntityTraits.cpp @@ -0,0 +1,96 @@ +#include "ArchMath.h" +#include + +namespace arch::ecs::_details { + +EntityTraits::Ent::Type EntityTraits::Ent::fromParts(const IdT id, const VersionT ver) noexcept { + return fromRawParts(Id::asRawPart(id), Version::asRawPart(ver)); +} + +EntityTraits::Ent::Type EntityTraits::Ent::fromRawParts(const RawT id, const RawT ver) noexcept { + return Type(id | ver); +} + +EntityTraits::Ent::Type EntityTraits::Ent::fromOthers(const Type id, const Type ver) noexcept { + return fromRawParts(Id::rawPart(id), Version::rawPart(ver)); +} + +EntityTraits::Ent::RawT EntityTraits::Ent::toRaw(const Type entity) noexcept { + return (RawT)entity; +} + +EntityTraits::Id::Type EntityTraits::Id::part(const EntityT entity) noexcept { + return { rawPart(entity) }; +} + +EntityTraits::EntityRawT EntityTraits::Id::asRawPart(const Type id) noexcept { + return (EntityRawT)id & mask; +} + +EntityTraits::EntityRawT EntityTraits::Id::rawPart(const EntityT entity) noexcept { + return Ent::toRaw(entity) & mask; +} + +bool EntityTraits::Id::hasNull(const EntityT entity) noexcept { + return (Ent::toRaw(entity) & mask) == nullRaw; +} + +EntityTraits::EntityT EntityTraits::Id::withNull(const EntityT entity) noexcept { + return Ent::fromRawParts(nullRaw, Version::rawPart(entity)); +} + +void EntityTraits::Id::swap(EntityT& entity1, EntityT& entity2) noexcept { + const auto id1 = rawPart(entity1); + entity1 = Ent::fromRawParts(rawPart(entity2), Version::rawPart(entity1)); + entity2 = Ent::fromRawParts(id1, Version::rawPart(entity2)); +} + +bool EntityTraits::Id::equal(const EntityT entity1, const EntityT entity2) noexcept { + return rawPart(entity1) == rawPart(entity2); +} + +EntityTraits::Version::Type EntityTraits::Version::part(const EntityT entity) noexcept { + return Type(rawPart(entity) >> Id::length); +} + +EntityTraits::EntityRawT EntityTraits::Version::asRawPart(const Type version) noexcept { + return (EntityRawT)version << Id::length; +} + +EntityTraits::EntityRawT EntityTraits::Version::rawPart(const EntityT entity) noexcept { + return Ent::toRaw(entity) & mask; +} + +bool EntityTraits::Version::hasNull(const EntityT entity) noexcept { + return (Ent::toRaw(entity) & mask) == nullRaw; +} + +bool EntityTraits::Version::hasNotNull(const EntityT entity) noexcept { + return (Ent::toRaw(entity) & mask) != nullRaw; +} + +EntityTraits::EntityT EntityTraits::Version::withNext(const EntityT entity) noexcept { + return Ent::fromParts(Id::part(entity), next(entity)); +} + +EntityTraits::Version::Type EntityTraits::Version::next(const EntityT entity) noexcept { + const auto newVersion = part(entity) + 1; + + return newVersion + (newVersion == null); +} + +EntityTraits::EntityT EntityTraits::Version::withNull(const EntityT entity) noexcept { + return Ent::fromRawParts(Id::rawPart(entity), nullRaw); +} + +void EntityTraits::Version::swap(EntityT& entity1, EntityT& entity2) noexcept { + const auto ver1 = rawPart(entity1); + entity1 = Ent::fromRawParts(EntityTraits::Id::rawPart(entity1), rawPart(entity2)); + entity2 = Ent::fromRawParts(EntityTraits::Id::rawPart(entity2), ver1); +} + +bool EntityTraits::Version::equal(const EntityT entity1, const EntityT entity2) noexcept { + return rawPart(entity1) == rawPart(entity2); +} + +} // namespace arch::ecs::_details diff --git a/src/ecs/ExplicitInstantiation.cpp b/src/ecs/ExplicitInstantiation.cpp deleted file mode 100644 index 8aea233..0000000 --- a/src/ecs/ExplicitInstantiation.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include - -namespace arch::ecs { - -namespace _details { - -template class EntityTraits; -template class EntityTraits; -template class SparseSet; -template class SparseSet; -template class CommonComponentPool; -template class CommonComponentPool; - -} // namespace _details - -template class Domain; -template class Domain; -template class EntityPool; -template class EntityPool; - -} // namespace arch::ecs - -template class arch::utils::ReadonlyCounter; diff --git a/include/ecs/SparseSet.hpp b/src/ecs/SparseSet.cpp similarity index 60% rename from include/ecs/SparseSet.hpp rename to src/ecs/SparseSet.cpp index bfaffd0..b03fa60 100644 --- a/include/ecs/SparseSet.hpp +++ b/src/ecs/SparseSet.cpp @@ -1,15 +1,11 @@ #include -#include "SparseSet.h" #include "utils/Assert.h" - -#define TEMPLATE template -#define SET SparseSet +#include namespace arch::ecs::_details { -TEMPLATE -typename SET::EntityT* SET::_sparseAssurePage(const size_t n) noexcept { +typename SparseSet::EntityT* SparseSet::_sparseAssurePage(const size_t n) noexcept { // resize(n) only would make capacity == n (bad) if (_sparse.size() < n + 1) { _sparse.reserve(std::bit_ceil(n + 1)); @@ -19,19 +15,17 @@ typename SET::EntityT* SET::_sparseAssurePage(const size_t n) noexcept { auto& page = _sparse[n]; if (page == nullptr) { page = std::make_unique>(); - page->fill(Traits::Entity::null); + page->fill(Traits::Ent::null); } return page->data(); } -TEMPLATE -typename SET::EntityT& SET::_sparseAssure(const IdT id) noexcept { +typename SparseSet::EntityT& SparseSet::_sparseAssure(const IdT id) noexcept { return _sparseAssurePage(id / Traits::pageSize)[id % Traits::pageSize]; } -TEMPLATE -typename SET::EntityT& SET::_sparseGet(const IdT id) noexcept { +typename SparseSet::EntityT& SparseSet::_sparseGet(const IdT id) noexcept { ARCH_ASSERT( id / Traits::pageSize < _sparse.size() && _sparse[id / Traits::pageSize] != nullptr, "Page for given id does not exist" @@ -39,13 +33,11 @@ typename SET::EntityT& SET::_sparseGet(const IdT id) noexcept { return (*_sparse[id / Traits::pageSize])[id % Traits::pageSize]; } -TEMPLATE -const typename SET::EntityT& SET::_sparseGet(const IdT id) const noexcept { - return const_cast(this)->_sparseGet(id); +const typename SparseSet::EntityT& SparseSet::_sparseGet(const IdT id) const noexcept { + return const_cast(this)->_sparseGet(id); } -TEMPLATE -typename SET::EntityT* SET::_sparseTryGet(const IdT id) noexcept { +typename SparseSet::EntityT* SparseSet::_sparseTryGet(const IdT id) noexcept { const size_t pageNum = id / Traits::pageSize; if (_sparse.size() <= pageNum) { @@ -57,52 +49,42 @@ typename SET::EntityT* SET::_sparseTryGet(const IdT id) noexcept { return page ? page->data() + id % Traits::pageSize : nullptr; } -TEMPLATE -const typename SET::EntityT* SET::_sparseTryGet(const IdT id) const noexcept { - return const_cast(this)->_sparseTryGet(id); +const typename SparseSet::EntityT* SparseSet::_sparseTryGet(const IdT id) const noexcept { + return const_cast(this)->_sparseTryGet(id); } -TEMPLATE -size_t SET::find(const IdT id) const noexcept { +size_t SparseSet::find(const IdT id) const noexcept { const auto sparsePtr = _sparseTryGet(id); return sparsePtr && !Traits::Version::hasNull(*sparsePtr) ? Traits::Id::part(*sparsePtr) : (size_t)-1; } -TEMPLATE -size_t SET::find(const EntityT entity) const noexcept { +size_t SparseSet::find(const EntityT entity) const noexcept { const auto sparsePtr = _sparseTryGet(Traits::Id::part(entity)); return sparsePtr && Traits::Version::equal(*sparsePtr, entity) ? Traits::Id::part(*sparsePtr) : (size_t)-1; } -TEMPLATE -typename SET::VersionT SET::version(const IdT id) const noexcept { +typename SparseSet::VersionT SparseSet::version(const IdT id) const noexcept { const auto sparsePtr = _sparseTryGet(id); return sparsePtr ? Traits::Version::part(*sparsePtr) : Traits::Version::null; } -TEMPLATE -typename SET::VersionT SET::version(const EntityT entity) const noexcept { +typename SparseSet::VersionT SparseSet::version(const EntityT entity) const noexcept { return version(Traits::Id::part(entity)); } -TEMPLATE -bool SET::contains(const IdT id) const noexcept { +bool SparseSet::contains(const IdT id) const noexcept { const auto sparsePtr = _sparseTryGet(id); return sparsePtr && !Traits::Version::hasNull(*sparsePtr); } -TEMPLATE -bool SET::contains(const EntityT entity) const noexcept { +bool SparseSet::contains(const EntityT entity) const noexcept { const auto sparsePtr = _sparseTryGet(Traits::Id::part(entity)); return sparsePtr && Traits::Version::equal(*sparsePtr, entity); } } // namespace arch::ecs::_details - -#undef TEMPLATE -#undef SET diff --git a/src/utils/ReadonlyCounter.cpp b/src/utils/ReadonlyCounter.cpp new file mode 100644 index 0000000..9303554 --- /dev/null +++ b/src/utils/ReadonlyCounter.cpp @@ -0,0 +1,7 @@ +#include + +namespace arch::utils { + +template class ReadonlyCounter; + +} diff --git a/tests/ecs/Component.cpp b/tests/ecs/Component.cpp index 698d1f5..4bda5e9 100644 --- a/tests/ecs/Component.cpp +++ b/tests/ecs/Component.cpp @@ -25,7 +25,7 @@ TEST(ECS, ComponentSimple) { // the above components are the simplest that can be // plain structs like those model many crucial concepts (on them later) - ecs::Domain domain; + ecs::Domain domain; // components can be then added to entites: auto e0 = domain.newEntity(); @@ -124,7 +124,7 @@ struct EnemyFlag { // flag-components are never instantiated, therefore occupy less space TEST(ECS, ComponentFlag) { - ecs::Domain domain; + ecs::Domain domain; auto e0 = domain.newEntity(); auto e1 = domain.newEntity(); @@ -225,9 +225,9 @@ struct arch::ecs::ComponentSpecs { // let's now check if our settings acually work TEST(ECS, ComponentInPlaceCustomPageSize) { - ecs::Domain domain; + ecs::Domain domain; - std::array entities; + std::array entities; for (size_t i = 0; i != 32 * 3; ++i) { entities[i] = domain.newEntity(); diff --git a/tests/ecs/Entity.cpp b/tests/ecs/Entity.cpp index 89c93b9..044ab66 100644 --- a/tests/ecs/Entity.cpp +++ b/tests/ecs/Entity.cpp @@ -2,12 +2,12 @@ #include namespace ecs = arch::ecs; -using Traits = ecs::_details::EntityTraits; +using Traits = ecs::_details::EntityTraits; TEST(ECS, EntityCreationAlive) { // e64 - 64-bit entity handle // there also exists e32, with smaller range - ecs::Domain domain; + ecs::Domain domain; for (size_t i = 0; i != 100; ++i) { auto e = domain.newEntity(); @@ -22,8 +22,8 @@ TEST(ECS, EntityCreationAlive) { } TEST(ECS, EntityKillAlive) { - ecs::Domain domain; - std::array toDelete; + ecs::Domain domain; + std::array toDelete; for (size_t i = 0; i != 100; ++i) { auto e = domain.newEntity(); @@ -48,13 +48,13 @@ TEST(ECS, EntityKillAlive) { } // discouraged usage vvv - // EXPECT_FALSE(domain.alive(Traits::Entity::fromParts(10, 0))); + // EXPECT_FALSE(domain.alive(Traits::Ent::fromParts(10, 0))); // only handles aquired by interactions with domain are guaranteed to give valid results // but since its C++ you can do whatever you want } TEST(ECS, EntityRecycle) { - ecs::Domain domain; + ecs::Domain domain; // 10 entites for (size_t i = 0; i != 10; ++i) { @@ -62,7 +62,7 @@ TEST(ECS, EntityRecycle) { } // 10 entites, but marked for deletion - std::array toDelete; + std::array toDelete; for (size_t i = 0; i != 10; ++i) { toDelete[i] = domain.newEntity(); } @@ -111,7 +111,7 @@ TEST(ECS, EntityRecycle) { } TEST(ECS, EntityRecycleExisting) { - ecs::Domain domain; + ecs::Domain domain; // 10 entites for (size_t i = 0; i != 10; ++i) { @@ -119,7 +119,7 @@ TEST(ECS, EntityRecycleExisting) { } // 10 entites, but marked for deletion - std::array toRecycle; + std::array toRecycle; for (size_t i = 0; i != 10; ++i) { toRecycle[i] = domain.newEntity(); } @@ -140,47 +140,3 @@ TEST(ECS, EntityRecycleExisting) { EXPECT_EQ(recycled2, domain.null); } } - -// normally there are two entity types: e32 and e64 -// they are not special in themselves, just enum types -// ECS allows you to create your own entity types like this: - -// 16-bit entity -enum class Entity16Bit : uint16_t; - -// however, Entity16Bit is not usable yet -// lets create example configuration -struct Entity16BitConfiguration { - // type of entity - using EntityT = Entity16Bit; - // bit lenght of id part of handle, for example 12 - static inline constexpr size_t idLength = 12; - // type of id part of handle, must be big enough to store 12 bits - using IdT = uint16_t; - // type of version part of handle, must be big enough to store 4 bits - using VersionT = uint8_t; - // size of pages in sparse sets, default is 1024 - // ATTENTION! pageSize must be a power of 2 - // tip: (2^N) can be written as (1 << N) - static inline constexpr size_t pageSize = 1'024; -}; - -// to be visible to ECS you need to specialize arch::ecs::EntitySpecs -// you can use AutoEntitySpecs -template<> -struct arch::ecs::EntitySpecs: arch::ecs::AutoEntitySpecs {}; - -// or your previous configuration: Entity16BitConfiguration - -// and thats basically it, ECS will compute rest of the info by itself -// you can now proudly use you entities - -TEST(ECS, EntityCustom) { - ecs::Domain domain; - - auto e0 = domain.newEntity(); - - // ... - - EXPECT_EQ(typeid(e0), typeid(Entity16Bit)); -} diff --git a/tests/ecs/View.cpp b/tests/ecs/View.cpp index 48cf1b2..b6c4771 100644 --- a/tests/ecs/View.cpp +++ b/tests/ecs/View.cpp @@ -16,7 +16,7 @@ TEST(ECS, ViewsSimple) { float y; }; - ecs::Domain domain; + ecs::Domain domain; // let's create some entities with some components @@ -49,7 +49,7 @@ TEST(ECS, ViewsSimple) { // now you have to choose how you update all positions // option 1. manually obtain components - for (ecs::e32 entity : viewPosVel) { + for (ecs::Entity entity : viewPosVel) { { // get tuple with references auto posVelTuple = viewPosVel.get(entity); @@ -76,7 +76,7 @@ TEST(ECS, ViewsSimple) { } // option 3. use view.forEach(entity) - viewPosVel.forEach([&viewPosVel /* capture view */](ecs::e32 entity) { + viewPosVel.forEach([&viewPosVel /* capture view */](ecs::Entity entity) { // auto&& [pos, vel] = viewPosVel.get(entity); // pos.x += vel.x; @@ -84,7 +84,7 @@ TEST(ECS, ViewsSimple) { }); // option 4. use view.forEach(entity, &pos, const &vel) - viewPosVel.forEach([&viewPosVel /* capture view */](ecs::e32 entity, Pos& pos, const Vel& vel) { + viewPosVel.forEach([&viewPosVel /* capture view */](ecs::Entity entity, Pos& pos, const Vel& vel) { // pos.x += vel.x; // pos.y += vel.y; });