2#ifndef LIBSBX_REGISTRY_HPP_
3#define LIBSBX_REGISTRY_HPP_
7#include <unordered_map>
8#include <unordered_set>
15#include <libsbx/utility/type_id.hpp>
16#include <libsbx/utility/concepts.hpp>
17#include <libsbx/utility/algorithm.hpp>
18#include <libsbx/utility/logger.hpp>
19#include <libsbx/utility/hashed_string.hpp>
20#include <libsbx/utility/assert.hpp>
22#include <libsbx/memory/concepts.hpp>
23#include <libsbx/memory/observer_ptr.hpp>
24#include <libsbx/memory/tracking_allocator.hpp>
26#include <libsbx/containers/dense_map.hpp>
28#include <libsbx/ecs/entity.hpp>
30#include <libsbx/ecs/storage.hpp>
31#include <libsbx/ecs/view.hpp>
33#include <libsbx/ecs/detail/registry_storage_iterator.hpp>
48template<
typename Type>
51template<
typename Type,
typename Entity = entity, memory::allocator_for<Type> Allocator = std::allocator<Type>>
56template<
typename... Args>
57using storage_type_t =
typename storage_type<Args...>::type;
59template<
typename Type,
typename Entity = entity, memory::allocator_for<std::remove_const_t<Type>> Allocator = std::allocator<std::remove_const_t<Type>>>
61 using type = utility::constness_as_t<storage_type_t<std::remove_const_t<Type>, Entity, Allocator>, Type>;
64template<
typename... Args>
65using storage_for_t =
typename storage_for<Args...>::type;
67template<
typename Entity, memory::allocator_for<Entity> Allocator = std::allocator<Entity>>
71 using allocator_traits = std::allocator_traits<Allocator>;
76 template<
typename Type>
77 using storage_for_type = storage_for_t<Type, Entity, memory::rebound_allocator_t<Allocator, std::remove_const_t<Type>>>;
81 using allocator_type = Allocator;
82 using entity_type = entity_traits::value_type;
83 using version_type = entity_traits::version_type;
84 using size_type = std::size_t;
85 using difference_type = std::ptrdiff_t;
102 basic_registry(
const size_type count,
const allocator_type &allocator = allocator_type{})
104 _entities{allocator} {
105 _pools.reserve(count);
111 : _pools{std::move(other._pools)},
112 _entities{std::move(other._entities)} { }
125 swap(_pools, other._pools);
126 swap(_entities, other._entities);
129 [[nodiscard]]
constexpr auto get_allocator()
const noexcept -> allocator_type {
130 return _entities.get_allocator();
133 [[nodiscard]]
auto is_valid(
const entity_type entity)
const ->
bool {
134 return static_cast<size_type
>(_entities.find(entity).index()) < _entities.free_list();
137 auto create() -> entity_type {
138 return _entities.generate();
141 auto destroy(
const entity_type entity) -> version_type {
142 for (
auto position = _pools.size(); position != 0u; --position) {
143 _pools.begin()[
static_cast<typename pool_container_type::difference_type
>(position - 1u)].second->remove(entity);
146 _entities.erase(entity);
147 return _entities.current(entity);
150 template<
typename Type,
typename... Args>
151 requires (std::is_constructible_v<Type, Args...>)
152 auto emplace(
const entity_type entity, Args&&... args) ->
decltype(
auto) {
153 utility::assert_that(is_valid(entity),
"Invalid entity");
154 return _assure<Type>().emplace(entity, std::forward<Args>(args)...);
157 template<
typename Type,
typename... Other>
158 auto remove(
const entity_type entity) -> size_type {
159 return (_assure<Type>().remove(entity) + ... + _assure<Other>().remove(entity));
162 template<
typename... Type>
163 [[nodiscard]]
auto all_of([[maybe_unused]]
const entity_type entity)
const ->
bool {
164 if constexpr(
sizeof...(Type) == 1u) {
165 auto* pool = _assure<std::remove_const_t<Type>...>();
166 return pool && pool->contains(entity);
168 return (all_of<Type>(entity) && ...);
172 template<
typename... Type>
173 [[nodiscard]]
auto any_of([[maybe_unused]]
const entity_type entity)
const ->
bool {
174 return (all_of<Type>(entity) || ...);
177 template<
typename... Type>
178 [[nodiscard]]
auto get([[maybe_unused]]
const entity_type entity)
const ->
decltype(
auto) {
179 if constexpr (
sizeof...(Type) == 1u) {
180 return (_assure<std::remove_const_t<Type>>()->get(entity), ...);
182 return std::forward_as_tuple(get<Type>(entity)...);
186 template<
typename... Type>
187 [[nodiscard]]
auto get([[maybe_unused]]
const entity_type entity) ->
decltype(
auto) {
188 if constexpr (
sizeof...(Type) == 1u) {
189 return (
static_cast<storage_for_type<Type>&
>(_assure<std::remove_const_t<Type>>()).get(entity), ...);
191 return std::forward_as_tuple(get<Type>(entity)...);
195 template<
typename Type,
typename... Args>
196 requires (std::is_constructible_v<Type, Args...>)
197 [[nodiscard]]
auto get_or_emplace(
const entity_type entity, Args&&... args) ->
decltype(
auto) {
198 auto& pool = _assure<Type>();
199 utility::assert_that(is_valid(entity),
"Invalid entity");
200 return pool.contains(entity) ? pool.get(entity) : pool.emplace(entity, std::forward<Args>(args)...);
203 template<
typename... Type>
204 [[nodiscard]]
auto try_get([[maybe_unused]]
const entity_type entity)
const ->
decltype(
auto) {
205 if constexpr (
sizeof...(Type) == 1u) {
206 const auto* pool = _assure<std::remove_const_t<Type>...>();
207 return (pool && pool->contains(entity)) ? std::addressof(pool->get(entity)) :
nullptr;
209 return std::make_tuple(try_get<Type>(entity)...);
213 template<
typename... Type>
214 [[nodiscard]]
auto try_get([[maybe_unused]]
const entity_type entity) ->
decltype(
auto) {
215 if constexpr (
sizeof...(Type) == 1u) {
216 return (
const_cast<Type*
>(std::as_const(*this).template try_get<Type>(entity)), ...);
218 return std::make_tuple(try_get<Type>(entity)...);
222 template<
typename... Type>
223 auto clear() ->
void {
224 if constexpr (
sizeof...(Type) == 0u) {
225 for (
auto position = _pools.size(); position; --position) {
226 _pools.begin()[
static_cast<typename pool_container_type::difference_type
>(position - 1u)].second->clear();
229 const auto element = _entities.each();
230 _entities.erase(element.begin().base(), element.end().base());
232 (_assure<Type>().clear(), ...);
236 template<
typename Type,
typename... Other,
typename... Exclude>
239 [&view](
const auto* ...current) { ((current ? view.set_storage(*current) : void()), ...); }(_assure<std::remove_const_t<Exclude>>()..., _assure<std::remove_const_t<Other>>()..., _assure<std::remove_const_t<Type>>());
243 template<
typename Type,
typename... Other,
typename... Exclude>
248 template<
typename Type,
typename Compare,
typename Sort =
utility::std_sort,
typename... Args>
249 auto sort(Compare compare, Sort sort = Sort{}, Args&&... args) ->
void {
251 auto& pool = _assure<Type>();
253 if constexpr(std::is_invocable_v<Compare,
decltype(pool.get(std::declval<entity_type>())),
decltype(pool.get(std::declval<entity_type>()))>) {
254 auto component_compare = [&pool, compare = std::move(compare)](
const auto lhs,
const auto rhs) {
return compare(std::as_const(pool.get(lhs)), std::as_const(pool.get(rhs))); };
255 pool.sort(std::move(component_compare), std::move(sort), std::forward<Args>(args)...);
257 pool.sort(std::move(compare), std::move(sort), std::forward<Args>(args)...);
261 [[nodiscard]]
auto storage()
noexcept ->
iterable {
262 return iterable{_pools.begin(), _pools.end()};
269 [[nodiscard]]
auto begin()
const ->
decltype(
auto) {
270 return _entities.begin();
273 [[nodiscard]]
auto end()
const ->
decltype(
auto) {
274 return _entities.end();
277 template<
typename Callable>
279 for (
const auto entity : _entities | ranges::views::filter(std::forward<Callable>(callable))) {
280 for (
auto&& [type, storage] : storage()) {
281 storage.invoke(tag, entity);
288 template<
typename Type>
289 requires (std::is_same_v<Type, std::decay_t<Type>>)
290 [[nodiscard]]
auto _assure([[maybe_unused]]
const std::uint32_t
id =
type_id<Type>::value()) -> storage_for_type<Type>& {
291 if constexpr(std::is_same_v<Type, entity_type>) {
297 if (
auto iterator = _pools.find(
id); iterator != _pools.cend()) {
301 using storage_allocator_type =
typename storage_type::allocator_type;
302 using pool_type =
typename pool_container_type::mapped_type;
304 auto pool = pool_type{};
306 if constexpr (std::is_void_v<Type> && !std::is_constructible_v<storage_allocator_type, allocator_type>) {
307 pool = std::allocate_shared<storage_type>(get_allocator(), storage_allocator_type{});
309 pool = std::allocate_shared<storage_type>(get_allocator(), get_allocator());
312 _pools.emplace(
id, pool);
318 template<
typename Type>
319 requires (std::is_same_v<Type, std::decay_t<Type>>)
320 [[nodiscard]]
auto _assure([[maybe_unused]]
const std::uint32_t
id =
type_id<Type>::value())
const ->
const storage_for_type<Type>* {
321 if constexpr(std::is_same_v<Type, entity_type>) {
325 if (
const auto iterator = _pools.find(
id); iterator != _pools.cend()) {
326 return static_cast<const storage_for_type<Type>*
>(iterator->second.get());
329 return static_cast<const storage_for_type<Type>*
>(
nullptr);
334 storage_for_type<entity_type> _entities;
Definition: dense_map.hpp:233
Definition: registry.hpp:68
Definition: storage.hpp:150
Definition: storage.hpp:27
Definition: iterable_adaptor.hpp:11
Definition: hashed_string.hpp:17
Sparse set container for ECS entity storage.
Definition: registry.hpp:39
Entity traits.
Definition: entity.hpp:73
Definition: registry.hpp:60
Definition: registry.hpp:52
A scoped type ID generator. Allows for generating unique IDs for types within a specific scope.
Definition: type_id.hpp:31
Definition: algorithm.hpp:13