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... Args>
158 auto emplace_or_replace(
const entity_type entity, Args&&... args) ->
decltype(
auto) {
159 auto& pool = _assure<Type>();
161 utility::assert_that(is_valid(entity),
"Invalid entity");
163 return pool.contains(entity) ? pool.patch(entity, [&args...](
auto&... current) { ((current = Type{std::forward<Args>(args)...}), ...); }) : pool.emplace(entity, std::forward<Args>(args)...);
166 template<
typename Type,
typename... Other>
167 auto remove(
const entity_type entity) -> size_type {
168 return (_assure<Type>().remove(entity) + ... + _assure<Other>().remove(entity));
171 template<
typename... Type>
172 [[nodiscard]]
auto all_of([[maybe_unused]]
const entity_type entity)
const ->
bool {
173 if constexpr(
sizeof...(Type) == 1u) {
174 auto* pool = _assure<std::remove_const_t<Type>...>();
175 return pool && pool->contains(entity);
177 return (all_of<Type>(entity) && ...);
181 template<
typename... Type>
182 [[nodiscard]]
auto any_of([[maybe_unused]]
const entity_type entity)
const ->
bool {
183 return (all_of<Type>(entity) || ...);
186 template<
typename... Type>
187 [[nodiscard]]
auto get([[maybe_unused]]
const entity_type entity)
const ->
decltype(
auto) {
188 if constexpr (
sizeof...(Type) == 1u) {
189 return (_assure<std::remove_const_t<Type>>()->get(entity), ...);
191 return std::forward_as_tuple(get<Type>(entity)...);
195 template<
typename... Type>
196 [[nodiscard]]
auto get([[maybe_unused]]
const entity_type entity) ->
decltype(
auto) {
197 if constexpr (
sizeof...(Type) == 1u) {
198 return (
static_cast<storage_for_type<Type>&
>(_assure<std::remove_const_t<Type>>()).get(entity), ...);
200 return std::forward_as_tuple(get<Type>(entity)...);
204 template<
typename Type,
typename... Args>
205 requires (std::is_constructible_v<Type, Args...>)
206 [[nodiscard]]
auto get_or_emplace(
const entity_type entity, Args&&... args) ->
decltype(
auto) {
207 auto& pool = _assure<Type>();
208 utility::assert_that(is_valid(entity),
"Invalid entity");
209 return pool.contains(entity) ? pool.get(entity) : pool.emplace(entity, std::forward<Args>(args)...);
212 template<
typename... Type>
213 [[nodiscard]]
auto try_get([[maybe_unused]]
const entity_type entity)
const ->
decltype(
auto) {
214 if constexpr (
sizeof...(Type) == 1u) {
215 const auto* pool = _assure<std::remove_const_t<Type>...>();
216 return (pool && pool->contains(entity)) ? std::addressof(pool->get(entity)) :
nullptr;
218 return std::make_tuple(try_get<Type>(entity)...);
222 template<
typename... Type>
223 [[nodiscard]]
auto try_get([[maybe_unused]]
const entity_type entity) ->
decltype(
auto) {
224 if constexpr (
sizeof...(Type) == 1u) {
225 return (
const_cast<Type*
>(std::as_const(*this).template try_get<Type>(entity)), ...);
227 return std::make_tuple(try_get<Type>(entity)...);
231 template<
typename... Type>
232 auto clear() ->
void {
233 if constexpr (
sizeof...(Type) == 0u) {
234 for (
auto position = _pools.size(); position; --position) {
235 _pools.begin()[
static_cast<typename pool_container_type::difference_type
>(position - 1u)].second->clear();
238 const auto element = _entities.each();
239 _entities.erase(element.begin().base(), element.end().base());
241 (_assure<Type>().clear(), ...);
245 template<
typename Type,
typename... Other,
typename... Exclude>
246 [[nodiscard]]
auto view(exclude_t<Exclude...> = exclude_t{})
const -> basic_view<get_t<storage_for_type<const Type>, storage_for_type<const Other>...>, exclude_t<storage_for_type<const Exclude>...>> {
247 auto view = basic_view<get_t<storage_for_type<const Type>, storage_for_type<const Other>...>, exclude_t<storage_for_type<const Exclude>...>>{};
248 [&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>>());
252 template<
typename Type,
typename... Other,
typename... Exclude>
253 [[nodiscard]]
auto view(exclude_t<Exclude...> = exclude_t{}) -> basic_view<get_t<storage_for_type<Type>, storage_for_type<Other>...>, exclude_t<storage_for_type<Exclude>...>> {
254 return basic_view<get_t<storage_for_type<Type>, storage_for_type<Other>...>, exclude_t<storage_for_type<Exclude>...>>{_assure<std::remove_const_t<Type>>(), _assure<std::remove_const_t<Other>>()..., _assure<std::remove_const_t<Exclude>>()...};
257 template<
typename Type,
typename Compare,
typename Sort = utility::std_sort,
typename... Args>
258 auto sort(Compare compare, Sort sort = Sort{}, Args&&... args) ->
void {
260 auto& pool = _assure<Type>();
262 if constexpr(std::is_invocable_v<Compare,
decltype(pool.get(std::declval<entity_type>())),
decltype(pool.get(std::declval<entity_type>()))>) {
263 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))); };
264 pool.sort(std::move(component_compare), std::move(sort), std::forward<Args>(args)...);
266 pool.sort(std::move(compare), std::move(sort), std::forward<Args>(args)...);
270 [[nodiscard]]
auto storage() noexcept -> iterable {
271 return iterable{_pools.begin(), _pools.end()};
274 [[nodiscard]]
auto storage() const noexcept -> const_iterable {
275 return const_iterable{_pools.cbegin(), _pools.cend()};
278 [[nodiscard]]
auto begin() const -> decltype(auto) {
279 return _entities.begin();
282 [[nodiscard]]
auto end() const -> decltype(auto) {
283 return _entities.end();
286 template<
typename Callable>
287 auto invoke(
const utility::hashed_string& tag, Callable&& callable) ->
void {
288 for (
const auto entity : _entities | ranges::views::filter(std::forward<Callable>(callable))) {
289 for (
auto&& [type, storage] : storage()) {
290 storage.invoke(tag, entity);
297 template<
typename Type>
298 requires (std::is_same_v<Type, std::decay_t<Type>>)
299 [[nodiscard]]
auto _assure([[maybe_unused]]
const std::uint32_t
id =
type_id<Type>::value()) -> storage_for_type<Type>& {
300 if constexpr(std::is_same_v<Type, entity_type>) {
304 using storage_type = storage_for_type<Type>;
306 if (
auto iterator = _pools.find(
id); iterator != _pools.cend()) {
307 return static_cast<storage_type&
>(*iterator->second);
310 using storage_allocator_type =
typename storage_type::allocator_type;
311 using pool_type =
typename pool_container_type::mapped_type;
313 auto pool = pool_type{};
315 if constexpr (std::is_void_v<Type> && !std::is_constructible_v<storage_allocator_type, allocator_type>) {
316 pool = std::allocate_shared<storage_type>(get_allocator(), storage_allocator_type{});
318 pool = std::allocate_shared<storage_type>(get_allocator(), get_allocator());
321 _pools.emplace(
id, pool);
323 return static_cast<storage_type&
>(*pool);
327 template<
typename Type>
328 requires (std::is_same_v<Type, std::decay_t<Type>>)
329 [[nodiscard]]
auto _assure([[maybe_unused]]
const std::uint32_t
id =
type_id<Type>::value())
const ->
const storage_for_type<Type>* {
330 if constexpr(std::is_same_v<Type, entity_type>) {
334 if (
const auto iterator = _pools.find(
id); iterator != _pools.cend()) {
335 return static_cast<const storage_for_type<Type>*
>(iterator->second.get());
338 return static_cast<const storage_for_type<Type>*
>(
nullptr);
342 pool_container_type _pools;
343 storage_for_type<entity_type> _entities;
347using registry = basic_registry<entity>;
Definition: dense_map.hpp:233
Definition: registry.hpp:68
Definition: storage.hpp:150
Definition: storage.hpp:27
Definition: iterable_adaptor.hpp:11
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
static auto value() noexcept -> std::uint32_t
Generates a unique ID for the type.
Definition: type_id.hpp:41