Helios Engine
A modular ECS based data-oriented C++23 game engine framework
Loading...
Searching...
No Matches
manager.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <helios/assert.hpp>
12#include <helios/ecs/details/profile.hpp>
15
16#include <algorithm>
17#include <array>
18#include <concepts>
19#include <cstddef>
20#include <deque>
21#include <functional>
22#include <span>
23#include <string_view>
24#include <type_traits>
25#include <unordered_map>
26#include <utility>
27#include <vector>
28
29#ifdef HELIOS_STL_FLAT_MAP_AVAILABLE
30#include <flat_map>
31#else
32#include <boost/container/flat_map.hpp>
33#endif
34
35namespace helios::ecs {
36
37/**
38 * @brief Runtime metadata for a registered component type.
39 * @details Stores runtime information required for type-erased component
40 * storage and migration between archetypes.
41 */
44 std::string_view name;
45 size_t size = 0;
46 size_t alignment = 0;
48 bool is_tag = false;
49
50 /// @brief Type-erased function to initialize a column with the correct
51 /// concrete type.
53
54 /// @brief Type-erased function to move one element from a source column row
55 /// into the back of a target column.
58 size_t src_row);
59
60 /// @brief Type-erased function to push a default-constructed element onto a
61 /// column.
63
67
68 /**
69 * @brief Creates metadata for a component type.
70 * @tparam T Component type
71 * @return Populated metadata
72 */
73 template <ComponentTrait T>
74 [[nodiscard]] static consteval ComponentMetadata From() noexcept;
75};
76
77template <ComponentTrait T>
79 using Decayed = std::remove_cvref_t<T>;
80
81 ComponentMetadata meta{};
84 meta.size = sizeof(Decayed);
85 meta.alignment = alignof(Decayed);
88
90 col.template ChangeType<Decayed>();
91 };
92
93 if constexpr (std::move_constructible<Decayed>) {
96 size_t src_row) {
97 auto& val = src.template At<Decayed>(src_row);
98 dst.template PushBack<Decayed>(std::move(val));
99 };
100 }
101
102 if constexpr (std::default_initializable<Decayed>) {
104 col.template EmplaceBack<Decayed>();
105 };
106 }
107
108 return meta;
109}
110
111/**
112 * @brief Central manager for all component storage (archetype and sparse-set).
113 * @details Maintains:
114 * - A registry of component metadata (type-erased info per component type)
115 * - An archetype graph for entities with archetype-stored components
116 * - Sparse-set storages for sparse-set-stored components
117 * - Entity-to-archetype mapping for O(1) archetype lookup by entity
118 *
119 * Archetype-stored components live in dense SoA columns inside `Archetype`
120 * instances. Adding/removing archetype components causes entity migration
121 * between archetypes.
122 *
123 * Sparse-set-stored components live in independent `SparseComponentStorage<T>`
124 * instances, stored inside `SparseStorageEntry` values within a `MultiTypeMap`.
125 *
126 * Archetypes are owned by a `std::deque<Archetype>` which provides
127 * pointer/reference stability on `push_back`, allowing all maps and edge caches
128 * to safely hold `std::reference_wrapper<Archetype>` without invalidation
129 * concerns.
130 *
131 * @note Not thread-safe.
132 */
134public:
135 using size_type = size_t;
136
137 ComponentManager() = default;
139 ComponentManager(ComponentManager&&) noexcept = default;
140 ~ComponentManager() = default;
141
142 ComponentManager& operator=(const ComponentManager&) = delete;
143 ComponentManager& operator=(ComponentManager&&) noexcept = default;
144
145 /// @brief Clears all component data, archetypes, and metadata.
146 void Clear();
147
148 /// @brief Clears all component data and archetypes but retains metadata
149 /// registrations.
150 void ClearData() noexcept;
151
152 /**
153 * @brief Registers a component type so the manager knows its metadata.
154 * @details Registration is optional for compile-time-typed APIs (they
155 * auto-register). Explicitly registering is useful for pre-warming or
156 * validation.
157 * @tparam T Component type
158 */
159 template <ComponentTrait T>
160 void Register();
161
162 /**
163 * @brief Registers multiple component types.
164 * @tparam Ts Component types
165 */
166 template <ComponentTrait... Ts>
167 requires(sizeof...(Ts) > 1)
168 void RegisterAll() {
169 (Register<Ts>(), ...);
170 }
171
172 /**
173 * @brief Initializes an entity in the component manager with an empty
174 * archetype.
175 * @details Must be called after entity creation but before any component
176 * operations.
177 * @warning Triggers assertion in next cases:
178 * - Entity is invalid.
179 * - Entity is already tracked.
180 * @param entity Entity to initialize
181 */
182 void InitEntity(Entity entity);
183
184 /**
185 * @brief Removes all component data for an entity.
186 * @details Removes the entity from its archetype and from all sparse-set
187 * storages.
188 * @warning Triggers assertion in next cases:
189 * - Entity is invalid.
190 * - Entity is not tracked.
191 * @param entity Entity to remove
192 */
193 void RemoveEntity(Entity entity);
194
195 /**
196 * @brief Tries to remove all component data for an entity.
197 * @details Removes the entity from its archetype and from all sparse-set
198 * storages.
199 * @warning Triggers assertion if entity is invalid.
200 * @param entity Entity to remove
201 * @return True if removed, false if entity was not tracked
202 */
203 bool TryRemoveEntity(Entity entity);
204
205 /**
206 * @brief Removes all archetype and sparse-set components from an entity.
207 * @details The entity is moved to the empty archetype.
208 * @warning Triggers assertion in next cases:
209 * - Entity is invalid.
210 * - Entity is not tracked.
211 * @param entity Entity to clear
212 */
213 void Clear(Entity entity);
214
215 /**
216 * @brief Adds multiple archetype-stored components to an entity.
217 * @warning Triggers assertion in next cases:
218 * - Entity is invalid.
219 * - Entity is not tracked.
220 * - Any component type is not part of this archetype.
221 * @tparam Ts Component types, must be unique and at least 1 type
222 * @param entity Entity
223 * @param components Component values
224 */
225 template <ArchetypeComponentTrait... Ts>
226 requires utils::UniqueTypes<Ts...> && (sizeof...(Ts) > 0)
227 void AddArchetypeComponents(Entity entity, Ts&&... components);
228
229 /**
230 * @brief Adds multiple archetype-stored components to an entity.
231 * @details Components are added in a single migration to minimize overhead.
232 * @warning Triggers assertion in next cases:
233 * - Entity is invalid.
234 * - Entity is not tracked.
235 * @tparam Ts Component types, must have archetype storage, be unique and at
236 * least 1 type
237 * @param entity Entity
238 * @param components Component values
239 */
240 template <ArchetypeComponentTrait... Ts>
241 requires utils::UniqueTypes<Ts...> && (sizeof...(Ts) > 0)
242 inline auto TryAddArchetypeComponents(Entity entity, Ts&&... components)
243 -> std::array<bool, sizeof...(Ts)>;
244
245 /**
246 * @brief Emplaces an archetype-stored component (construct in-place).
247 * @details Replaces if already present, otherwise migrates.
248 * @warning Triggers assertion in next cases:
249 * - Entity is invalid.
250 * - Entity is not tracked.
251 * @tparam T Component type (must have archetype storage)
252 * @tparam Args Constructor argument types
253 * @param entity Entity
254 * @param args Arguments forwarded to `T`'s constructor
255 */
256 template <ArchetypeComponentTrait T, typename... Args>
257 requires std::constructible_from<T, Args...>
258 void EmplaceArchetypeComponent(Entity entity, Args&&... args);
259
260 /**
261 * @brief Tries to emplace an archetype-stored component.
262 * @warning Triggers assertion in next cases:
263 * - Entity is invalid.
264 * - Entity is not tracked.
265 * @tparam T Component type (must have archetype storage)
266 * @tparam Args Constructor argument types
267 * @param entity Entity
268 * @param args Arguments forwarded to `T`'s constructor
269 * @return True if emplaced, false if entity already had the component
270 */
271 template <ArchetypeComponentTrait T, typename... Args>
272 requires std::constructible_from<T, Args...>
273 bool TryEmplaceArchetypeComponent(Entity entity, Args&&... args);
274
275 /**
276 * @brief Removes multiple archetype-stored components from an entity.
277 * @warning Triggers assertion in next cases:
278 * - Entity is invalid.
279 * - Entity is not tracked.
280 * @tparam Ts Component types, must have archetype storage, be unique and at
281 * least 1 type
282 * @param entity Entity
283 */
284 template <ArchetypeComponentTrait... Ts>
285 requires utils::UniqueTypes<Ts...> && (sizeof...(Ts) > 0)
286 void RemoveArchetypeComponents(Entity entity);
287
288 /**
289 * @brief Tries to remove multiple archetype-stored components.
290 * @warning Triggers assertion in next cases:
291 * - Entity is invalid.
292 * - Entity is not tracked.
293 * @tparam Ts Component types, must have archetype storage, be unique and at
294 * least 1 type
295 * @param entity Entity
296 * @return Array of bools indicating whether each component was removed if
297 * more than one type passed, or a single bool if only one type passed
298 */
299 template <ArchetypeComponentTrait... Ts>
300 requires utils::UniqueTypes<Ts...> && (sizeof...(Ts) > 0)
301 auto TryRemoveArchetypeComponents(Entity entity)
302 -> std::array<bool, sizeof...(Ts)>;
303
304 /**
305 * @brief Adds a sparse-set-stored component to an entity.
306 * @details Replaces if already present.
307 * @warning Triggers assertion in next cases:
308 * - Entity is invalid.
309 * - Entity is not tracked.
310 * @tparam T Component type (must have sparse-set storage)
311 * @param entity Entity
312 * @param component Component value
313 */
314 template <SparseComponentTrait T>
315 void AddSparseComponent(Entity entity, T&& component);
316
317 /**
318 * @brief Tries to add a sparse-set-stored component.
319 * @warning Triggers assertion in next cases:
320 * - Entity is invalid.
321 * - Entity is not tracked.
322 * @tparam T Component type (must have sparse-set storage)
323 * @param entity Entity
324 * @param component Component value
325 * @return True if added, false if entity already had the component
326 */
327 template <SparseComponentTrait T>
328 bool TryAddSparseComponent(Entity entity, T&& component);
329
330 /**
331 * @brief Emplaces a sparse-set-stored component.
332 * @details Replaces if already present.
333 * @warning Triggers assertion in next cases:
334 * - Entity is invalid.
335 * - Entity is not tracked.
336 * @tparam T Component type (must have sparse-set storage)
337 * @tparam Args Constructor argument types
338 * @param entity Entity
339 * @param args Arguments forwarded to `T`'s constructor
340 */
341 template <SparseComponentTrait T, typename... Args>
342 requires std::constructible_from<T, Args...>
343 void EmplaceSparseComponent(Entity entity, Args&&... args);
344
345 /**
346 * @brief Tries to emplace a sparse-set-stored component.
347 * @warning Triggers assertion in next cases:
348 * - Entity is invalid.
349 * - Entity is not tracked.
350 * @tparam T Component type
351 * @tparam Args Constructor argument types
352 * @param entity Entity
353 * @param args Arguments forwarded to `T`'s constructor
354 * @return True if emplaced, false if entity already had the component
355 */
356 template <SparseComponentTrait T, typename... Args>
357 requires std::constructible_from<T, Args...>
358 bool TryEmplaceSparseComponent(Entity entity, Args&&... args);
359
360 /**
361 * @brief Removes a sparse-set-stored component from an entity.
362 * @warning Triggers assertion in next cases:
363 * - Entity is invalid.
364 * - Entity is not tracked.
365 * - Sparse storage for the component type does not exist.
366 * - Entity does not have the component.
367 * @tparam T Component type (must have sparse-set storage)
368 * @param entity Entity
369 */
370 template <SparseComponentTrait T>
371 void RemoveSparseComponent(Entity entity);
372
373 /**
374 * @brief Tries to remove a sparse-set-stored component.
375 * @warning Triggers assertion in next cases:
376 * - Entity is invalid.
377 * - Entity is not tracked.
378 * @tparam T Component type (must have sparse-set storage)
379 * @param entity Entity
380 * @return True if removed, false if entity didn't have the component or
381 * storage doesn't exist
382 */
383 template <SparseComponentTrait T>
384 bool TryRemoveSparseComponent(Entity entity);
385
386 /**
387 * @brief Adds multiple components to an entity.
388 * @details Archetype components are batched into a single migration.
389 * Sparse components are added individually after the archetype
390 * migration.
391 * @warning Triggers assertion in next cases:
392 * - Entity is invalid.
393 * - Entity is not tracked.
394 * @tparam Ts Component types, must be unique and at least 1 type
395 * @param entity Entity
396 * @param components Component values
397 */
398 template <ComponentTrait... Ts>
399 requires utils::UniqueTypes<Ts...> && (sizeof...(Ts) > 0)
400 void Add(Entity entity, Ts&&... components);
401
402 /**
403 * @brief Tries to add multiple components.
404 * @details Archetype components are batched into a single migration.
405 * Sparse components are added individually after the archetype
406 * migration. Results are returned in the same order as the input types.
407 * @warning Triggers assertion in next cases:
408 * - Entity is invalid.
409 * - Entity is not tracked.
410 * @tparam Ts Component types, must be unique and at least 1 type
411 * @param entity Entity
412 * @param components Component values
413 * @return Array of bools indicating whether each component was added if
414 * multiple components were added, or a single bool if only one component was
415 * added
416 */
417 template <ComponentTrait... Ts>
418 requires utils::UniqueTypes<Ts...> && (sizeof...(Ts) > 0)
419 auto TryAdd(Entity entity, Ts&&... components)
420 -> std::conditional_t<sizeof...(Ts) == 1, bool,
421 std::array<bool, sizeof...(Ts)>>;
422
423 /**
424 * @brief Emplaces a component (construct in-place), dispatching by storage
425 * type.
426 * @details Replaces if already present.
427 * @warning Triggers assertion in next cases:
428 * - Entity is invalid.
429 * - Entity is not tracked.
430 * @tparam T Component type
431 * @tparam Args Constructor argument types
432 * @param entity Entity
433 * @param args Arguments forwarded to `T`'s constructor
434 */
435 template <ComponentTrait T, typename... Args>
436 requires std::constructible_from<T, Args...>
437 void Emplace(Entity entity, Args&&... args);
438
439 /**
440 * @brief Tries to emplace a component.
441 * @warning Triggers assertion in next cases:
442 * - Entity is invalid.
443 * - Entity is not tracked.
444 * @tparam T Component type
445 * @tparam Args Constructor argument types
446 * @param entity Entity
447 * @param args Arguments forwarded to `T`'s constructor
448 * @return True if emplaced, false if entity already had the component
449 */
450 template <ComponentTrait T, typename... Args>
451 requires std::constructible_from<T, Args...>
452 bool TryEmplace(Entity entity, Args&&... args);
453
454 /**
455 * @brief Removes multiple components from an entity.
456 * @details Archetype components are removed in a single migration.
457 * Sparse components are removed individually.
458 * @warning Triggers assertion in next cases:
459 * - Entity is invalid.
460 * - Entity is not tracked.
461 * - Entity does not have any of the components.
462 * @tparam Ts Component types, must be unique and at least 1 type
463 * @param entity Entity
464 */
465 template <ComponentTrait... Ts>
466 requires utils::UniqueTypes<Ts...> && (sizeof...(Ts) > 0)
467 void Remove(Entity entity);
468
469 /**
470 * @brief Tries to remove multiple components.
471 * @details Archetype components are removed in a single migration.
472 * Sparse components are removed individually.
473 * Results are returned in the same order as the input types.
474 * @warning Triggers assertion in next cases:
475 * - Entity is invalid.
476 * - Entity is not tracked.
477 * @tparam Ts Component types, must be unique and at least 1 type
478 * @param entity Entity
479 * @return Array of bools indicating whether each component was removed if
480 * more than one type was passed, or a single bool if only one type was
481 * passed
482 */
483 template <ComponentTrait... Ts>
484 requires utils::UniqueTypes<Ts...> && (sizeof...(Ts) > 0)
485 auto TryRemove(Entity entity)
486 -> std::conditional_t<sizeof...(Ts) == 1, bool,
487 std::array<bool, sizeof...(Ts)>>;
488
489 /**
490 * @brief Gets a mutable reference to a component.
491 * @warning Triggers assertion in next cases:
492 * - Entity is invalid.
493 * - Entity is not tracked (for archetype-stored components).
494 * - Sparse storage does not exist for the component type (for sparse-stored
495 * components).
496 * - Entity does not have the component.
497 * @tparam T Component type
498 * @param entity Entity
499 * @return Mutable reference to component
500 */
501 template <ComponentTrait T>
502 [[nodiscard]] T& Get(Entity entity);
503
504 /**
505 * @brief Gets a const reference to a component.
506 * @warning Triggers assertion in next cases:
507 * - Entity is invalid.
508 * - Entity is not tracked (for archetype-stored components).
509 * - Sparse storage does not exist for the component type (for sparse-stored
510 * components).
511 * - Entity does not have the component.
512 * @tparam T Component type
513 * @param entity Entity
514 * @return Const reference to component
515 */
516 template <ComponentTrait T>
517 [[nodiscard]] const T& Get(Entity entity) const;
518
519 /**
520 * @brief Tries to get a mutable pointer to a component.
521 * @warning Triggers assertion if entity is invalid.
522 * @tparam T Component type
523 * @param entity Entity
524 * @return Pointer to component or `nullptr` if not found
525 */
526 template <ComponentTrait T>
527 [[nodiscard]] T* TryGet(Entity entity);
528
529 /**
530 * @brief Tries to get a const pointer to a component.
531 * @warning Triggers assertion if entity is invalid.
532 * @tparam T Component type
533 * @param entity Entity
534 * @return Const pointer to component or `nullptr` if not found
535 */
536 template <ComponentTrait T>
537 [[nodiscard]] const T* TryGet(Entity entity) const;
538
539 /**
540 * @brief Checks if a component type is registered.
541 * @tparam T Component type
542 * @return True if registered
543 */
544 template <ComponentTrait T>
545 [[nodiscard]] bool Registered() const noexcept;
546
547 /**
548 * @brief Checks if an entity is tracked by the component manager.
549 * @warning Triggers assertion if entity is invalid.
550 * @param entity Entity to check
551 * @return True if the entity has been initialized
552 */
553 [[nodiscard]] bool Tracked(Entity entity) const noexcept;
554
555 /**
556 * @brief Checks if an entity has a component.
557 * @warning Triggers assertion if entity is invalid.
558 * @tparam T Component type
559 * @param entity Entity
560 * @return True if entity has the component
561 */
562 template <ComponentTrait T>
563 [[nodiscard]] bool Has(Entity entity) const;
564
565 /**
566 * @brief Checks if an entity has multiple components.
567 * @warning Triggers assertion if entity is invalid.
568 * @tparam Ts Component types
569 * @param entity Entity
570 * @return Array of bools indicating whether the entity has each component
571 */
572 template <ComponentTrait... Ts>
573 requires utils::UniqueTypes<Ts...> && (sizeof...(Ts) > 1)
574 [[nodiscard]] auto Has(Entity entity) const
575 -> std::array<bool, sizeof...(Ts)>;
576
577 /**
578 * @brief Gets the archetype that the entity currently belongs to.
579 * @warning Triggers assertion in next cases:
580 * - Entity is invalid.
581 * - Entity is not tracked.
582 * @param entity Entity
583 * @return Reference to the entity's archetype
584 */
585 [[nodiscard]] Archetype& EntityArchetype(Entity entity);
586
587 /**
588 * @brief Gets the archetype that the entity currently belongs to (const).
589 * @warning Triggers assertion in next cases:
590 * - Entity is invalid.
591 * - Entity is not tracked.
592 * @param entity Entity
593 * @return Const reference to the entity's archetype
594 */
595 [[nodiscard]] const Archetype& EntityArchetype(Entity entity) const;
596
597 /**
598 * @brief Gets the sparse-set storage for a component type, creating it if
599 * necessary.
600 * @tparam T Component type (must have sparse-set storage)
601 * @return Mutable reference to the sparse-set storage
602 */
603 template <SparseComponentTrait T>
604 [[nodiscard]] auto SparseStorage() -> SparseComponentStorage<T>& {
605 return EnsureSparseStorage<T>();
606 }
607
608 /**
609 * @brief Gets the sparse-set storage for a component type (const).
610 * @warning Triggers assertion if the storage for the component type does not
611 * exist.
612 * @tparam T Component type
613 * @return Const reference to the sparse-set storage
614 */
615 template <SparseComponentTrait T>
616 [[nodiscard]] auto SparseStorage() const -> const SparseComponentStorage<T>&;
617
618 /**
619 * @brief Gets metadata for a component type.
620 * @warning Triggers assertion if not registered.
621 * @tparam T Component type
622 * @return Const reference to metadata
623 */
624 template <ComponentTrait T>
625 [[nodiscard]] const ComponentMetadata& Metadata() const noexcept;
626
627 /**
628 * @brief Gets metadata for a component type by its runtime type index.
629 * @param type_index Component type index
630 * @return Pointer to metadata, or `nullptr` if not registered
631 */
632 [[nodiscard]] const ComponentMetadata* MetadataByIndex(
633 ComponentTypeIndex type_index) const noexcept;
634
635 struct SparseStorageEntry;
636
637 [[nodiscard]] const SparseStorageEntry* SparseEntry(
638 ComponentTypeIndex type_index) const noexcept {
639 return sparse_storages_.TryGet(type_index);
640 }
641
642 /**
643 * @brief Gets the number of entities currently tracked by the component
644 * manager.
645 * @return Number of tracked entities
646 */
647 [[nodiscard]] size_type TrackedEntityCount() const noexcept {
648 return entity_archetype_.size();
649 }
650
651 /**
652 * @brief Gets the number of archetypes currently in the manager.
653 * @return Number of archetypes
654 */
655 [[nodiscard]] size_type ArchetypeCount() const noexcept {
656 return archetype_list_.size();
657 }
658
659 /**
660 * @brief Gets all archetypes currently in the manager.
661 * @details Useful for query iteration. Callers can filter archetypes by their
662 * ArchetypeId.
663 * @return Span of reference wrappers to all archetypes
664 */
665 [[nodiscard]] auto Archetypes() noexcept
666 -> std::span<std::reference_wrapper<Archetype>> {
667 return archetype_list_;
668 }
669
670 /**
671 * @brief Gets all archetypes (const).
672 * @return Span of const reference wrappers to all archetypes
673 */
674 [[nodiscard]] auto Archetypes() const noexcept
675 -> std::span<const std::reference_wrapper<Archetype>> {
676 return archetype_list_;
677 }
678
679 /**
680 * @brief Gets the current structural version of the component manager.
681 * @details Incremented on every archetype creation or entity migration.
682 * @return Structural version counter
683 */
684 [[nodiscard]] size_type StructuralVersion() const noexcept {
685 return structural_version_;
686 }
687
688 /**
689 * @brief Type-erased entry for sparse-set component storage.
690 * @details Stores a `SparseComponentStorage<T>` inside a `TypedBuffer` along
691 * with type-erased function pointers for operations that need to be performed
692 * without knowing the concrete type (e.g., clearing all storages, removing an
693 * entity from all storages).
694 */
698 Entity entity);
700 Entity entity);
701 using SizeFn = size_t (*)(const container::TypedBuffer<>& storage);
702
704 ClearFn clear_fn = nullptr;
707 SizeFn size_fn = nullptr;
708
709 constexpr SparseStorageEntry() = default;
711 constexpr SparseStorageEntry(SparseStorageEntry&&) noexcept = default;
712 constexpr ~SparseStorageEntry() = default;
713
714 SparseStorageEntry& operator=(const SparseStorageEntry&) = delete;
715 constexpr SparseStorageEntry& operator=(SparseStorageEntry&&) noexcept =
716 default;
717
718 /**
719 * @brief Creates a `SparseStorageEntry` for a specific component type `T`.
720 * @tparam T Component type
721 * @return Initialized `SparseStorageEntry` with a
722 * `SparseComponentStorage<T>` and appropriate function pointers
723 */
724 template <ComponentTrait T>
725 [[nodiscard]] static SparseStorageEntry From() {
726 using StorageT = SparseComponentStorage<T>;
727
728 SparseStorageEntry entry;
729 entry.storage.template Set<StorageT>();
730
731 entry.clear_fn = [](container::TypedBuffer<>& buf) {
732 buf.template Value<StorageT>().Clear();
733 };
734
736 Entity entity) -> bool {
737 return buf.template Value<StorageT>().TryRemove(entity);
738 };
739
740 entry.contains_fn = [](const container::TypedBuffer<>& buf,
741 Entity entity) -> bool {
742 return buf.template Value<StorageT>().Contains(entity);
743 };
744
745 entry.size_fn = [](const container::TypedBuffer<>& buf) -> size_t {
746 return buf.template Value<StorageT>().Size();
747 };
748
749 return entry;
750 }
751
752 /// @brief Clears the underlying storage.
753 void Clear() noexcept {
754 if (clear_fn != nullptr) [[unlikely]] {
756 }
757 }
758
759 /**
760 * @brief Tries to remove the component for the given entity.
761 * @warning Triggers assertion if entity is invalid.
762 * @param entity Entity
763 * @return True if removed, false otherwise
764 */
765 bool TryRemove(Entity entity) {
766 HELIOS_ASSERT(entity.Valid(), "Entity '{}' is invalid!", entity);
767 if (try_remove_fn == nullptr) [[unlikely]] {
768 return false;
769 }
770 return try_remove_fn(storage, entity);
771 }
772
773 /**
774 * @brief Checks if the component for the given entity exists.
775 * @warning Triggers assertion if entity is invalid.
776 * @param entity Entity
777 * @return True if entity has the component, false otherwise
778 */
779 [[nodiscard]] bool Contains(Entity entity) const noexcept {
780 HELIOS_ASSERT(entity.Valid(), "Entity '{}' is invalid!", entity);
781 if (contains_fn == nullptr) [[unlikely]] {
782 return false;
783 }
784 return contains_fn(storage, entity);
785 }
786
787 /**
788 * @brief Gets the number of stored components.
789 * @return Number of components
790 */
791 [[nodiscard]] size_t Size() const noexcept {
792 if (size_fn == nullptr) [[unlikely]] {
793 return 0;
794 }
795 return size_fn(storage);
796 }
797
798 /**
799 * @brief Gets the typed storage.
800 * @warning Triggers assertion if the storage does not contain the expected
801 * type.
802 * @tparam T Component type
803 * @return Mutable reference to the typed storage
804 */
805 template <ComponentTrait T>
806 [[nodiscard]] auto As() noexcept -> SparseComponentStorage<T>& {
807 return storage.template Value<SparseComponentStorage<T>>();
808 }
809
810 /**
811 * @brief Gets the typed storage (const).
812 * @warning Triggers assertion if the storage does not contain the expected
813 * type.
814 * @tparam T Component type
815 * @return Const reference to the typed storage
816 */
817 template <ComponentTrait T>
818 [[nodiscard]] auto As() const noexcept -> const SparseComponentStorage<T>& {
819 return storage.template Value<SparseComponentStorage<T>>();
820 }
821 };
822
823private:
824 /// @brief Sparse-set storages keyed by component `TypeIndex`.
825 using SparseStorageMap = container::MultiTypeMap<SparseStorageEntry>;
826
827 /**
828 * @brief Stores per-archetype edge caches for fast archetype transitions.
829 * @details When a component is added or removed from an entity, the edge
830 * graph allows O(1) lookup of the target archetype without recomputing the
831 * archetype id. The archetype itself is owned by `archetype_storage_` (a
832 * `std::deque`); the record only holds a reference to it.
833 */
834 struct ArchetypeRecord {
835 std::reference_wrapper<Archetype> archetype;
836
837#ifdef HELIOS_STL_FLAT_MAP_AVAILABLE
838 using EdgeMap =
839 std::flat_map<ComponentTypeIndex, std::reference_wrapper<Archetype>>;
840#else
841 using EdgeMap = boost::container::flat_map<
842 ComponentTypeIndex, std::reference_wrapper<Archetype>, std::less<>>;
843#endif
844
845 EdgeMap add_edges; ///< Cache: component added -> target archetype.
846 EdgeMap remove_edges; ///< Cache: component removed -> target archetype.
847 };
848
849 // Strategy: a consteval lambda fills a compile-time std::array by iterating
850 // over a bool[] predicate result. A second alias unpacks that array back into
851 // a variadic index_sequence via std::make_index_sequence + another lambda.
852 // The whole filter compiles as two flat alias instantiations — no recursion.
853 template <template <typename> typename Pred, typename... Ts>
854 static consteval auto FilteredIndicesArray() noexcept;
855
856 // Convenience predicates.
857 template <typename T>
858 struct IsArchetype : std::bool_constant<ArchetypeComponentTrait<T>> {};
859 template <typename T>
860 struct IsSparse : std::bool_constant<SparseComponentTrait<T>> {};
861
862 /// Mixed-storage dispatch
863 template <size_t I, typename... Ts>
864 using TypeAt =
865 std::remove_cvref_t<std::tuple_element_t<I, std::tuple<Ts...>>>;
866
867 template <size_t I, typename... Ts>
868 using FwdTypeAt = std::tuple_element_t<I, std::tuple<Ts...>>;
869
870 // Unpack a constexpr std::array<size_t, N> into std::index_sequence<arr[0],
871 // arr[1], ...>
872 template <auto Arr, typename Seq>
873 struct ArrayToIndexSequence;
874
875 template <auto Arr, size_t... Js>
876 struct ArrayToIndexSequence<Arr, std::index_sequence<Js...>> {
877 using type = std::index_sequence<Arr[Js]...>;
878 };
879
880 template <template <typename> typename Pred, typename... Ts>
881 using FilteredIndices = typename ArrayToIndexSequence<
882 FilteredIndicesArray<Pred, Ts...>(),
883 std::make_index_sequence<FilteredIndicesArray<Pred, Ts...>().size()>>::
884 type;
885
886 // Batch-dispatch archetype components through the correct single/multi
887 // overload.
888 template <typename... Ts, size_t... ArchIs>
889 void DispatchAddArchetype(Entity entity, std::tuple<Ts...>& args,
890 std::index_sequence<ArchIs...> /*seq*/);
891
892 template <typename... Ts, size_t... ArchIs>
893 auto DispatchTryAddArchetype(Entity entity, std::tuple<Ts...>& args,
894 std::index_sequence<ArchIs...> /*seq*/)
895 -> std::array<bool, sizeof...(ArchIs)>;
896
897 template <typename... Ts, size_t... ArchIs>
898 void DispatchRemoveArchetype(Entity entity,
899 std::index_sequence<ArchIs...> /*seq*/);
900
901 template <typename... Ts, size_t... ArchIs>
902 auto DispatchTryRemoveArchetype(Entity entity,
903 std::index_sequence<ArchIs...> /*seq*/)
904 -> std::array<bool, sizeof...(ArchIs)>;
905
906 template <ComponentTrait T>
907 void EnsureRegistered();
908
909 template <SparseComponentTrait T>
910 SparseComponentStorage<T>& EnsureSparseStorage();
911
912 Archetype& GetOrCreateArchetype(const ArchetypeId& id);
913
914 void InitArchetypeColumns(Archetype& archetype);
915
916 void MigrateEntity(Entity entity, Archetype& src, Archetype& dst);
917
918 Archetype* TryGetAddEdge(Archetype& from, ComponentTypeIndex type);
919 void SetAddEdge(Archetype& from, ComponentTypeIndex type, Archetype& to) {
920 GetRecord(from).add_edges.emplace(type, std::ref(to));
921 }
922
923 Archetype* TryGetRemoveEdge(Archetype& from, ComponentTypeIndex type);
924 void SetRemoveEdge(Archetype& from, ComponentTypeIndex type, Archetype& to) {
925 GetRecord(from).remove_edges.emplace(type, std::ref(to));
926 }
927
928 ArchetypeRecord& GetRecord(Archetype& archetype);
929 const ArchetypeRecord& GetRecord(const Archetype& archetype) const;
930
931 /// Metadata per component type.
932#ifdef HELIOS_STL_FLAT_MAP_AVAILABLE
933 using MetadataMap = std::flat_map<ComponentTypeIndex, ComponentMetadata>;
934#else
935 using MetadataMap =
936 boost::container::flat_map<ComponentTypeIndex, ComponentMetadata,
937 std::less<>>;
938#endif
939 MetadataMap metadata_;
940
941 /// @brief Owns all archetypes. `std::deque` guarantees pointer/reference
942 /// stability on `push_back`.
943 std::deque<Archetype> archetype_storage_;
944
945 /// Archetype lookup. Key = ArchetypeId hash. Value = ArchetypeRecord
946 /// (references into `archetype_storage_`).
947 std::unordered_map<size_t, ArchetypeRecord> archetype_map_;
948
949 /// Flat list of all archetype references for fast iteration (query support).
950 std::vector<std::reference_wrapper<Archetype>> archetype_list_;
951
952 /// Maps entity index -> archetype the entity is in.
953 std::unordered_map<Entity::IndexType, std::reference_wrapper<Archetype>>
954 entity_archetype_;
955
956 SparseStorageMap sparse_storages_;
957
958 /// Empty archetype (entities with no archetype-stored components live here).
959 Archetype* empty_archetype_ = nullptr;
960
961 /// Structural version counter, incremented on archetype creation or entity
962 /// migration.
963 size_type structural_version_ = 0;
964};
965
967 entity_archetype_.clear();
968 archetype_map_.clear();
969 archetype_list_.clear();
970 archetype_storage_.clear();
971 sparse_storages_.ResetAll();
972 metadata_.clear();
973 empty_archetype_ = nullptr;
974 structural_version_ = 0;
975}
976
977inline void ComponentManager::ClearData() noexcept {
978 entity_archetype_.clear();
979 archetype_map_.clear();
980 archetype_list_.clear();
981 archetype_storage_.clear();
982 for (auto&& [type_index, entry] : sparse_storages_.Data()) {
983 entry.Clear();
984 }
985 empty_archetype_ = nullptr;
986 ++structural_version_;
987}
988
989template <ComponentTrait T>
991 constexpr auto type_index = ComponentTypeIndex::From<T>();
992 if (metadata_.find(type_index) != metadata_.end()) {
993 return;
994 }
995 metadata_.emplace(type_index, ComponentMetadata::From<T>());
996}
997
999 HELIOS_ASSERT(entity.Valid(), "Entity '{}' is invalid!", entity);
1000 HELIOS_ASSERT(!Tracked(entity), "Entity '{}' already tracked!", entity);
1001
1002 // Ensure empty archetype exists.
1003 if (empty_archetype_ == nullptr) {
1004 empty_archetype_ = &GetOrCreateArchetype(ArchetypeId{});
1005 }
1006
1007 // Place entity in empty archetype (no components to add, just allocate a
1008 // row).
1009 empty_archetype_->AllocateRow(entity);
1010 entity_archetype_.emplace(entity.Index(), std::ref(*empty_archetype_));
1011}
1012
1014 HELIOS_ASSERT(entity.Valid(), "Entity '{}' is invalid!", entity);
1015 const auto it = entity_archetype_.find(entity.Index());
1016 HELIOS_ASSERT(it != entity_archetype_.end(), "Entity '{}' not tracked!",
1017 entity);
1018
1019 Archetype& arch = it->second.get();
1020
1021 // Remove from archetype.
1022 Entity swapped = arch.Remove(entity);
1023 if (swapped.Valid()) {
1024 entity_archetype_.insert_or_assign(swapped.Index(), std::ref(arch));
1025 }
1026
1027 entity_archetype_.erase(it);
1028
1029 // Remove from all sparse storages.
1030 for (auto&& [type_index, entry] : sparse_storages_.Data()) {
1031 entry.TryRemove(entity);
1032 }
1033}
1034
1036 HELIOS_ASSERT(entity.Valid(), "Entity '{}' is invalid!", entity);
1037
1038 const auto it = entity_archetype_.find(entity.Index());
1039 if (it == entity_archetype_.end()) {
1040 return false;
1041 }
1042
1043 Archetype& arch = it->second.get();
1044 Entity swapped = arch.Remove(entity);
1045 if (swapped.Valid()) {
1046 entity_archetype_.insert_or_assign(swapped.Index(), std::ref(arch));
1047 }
1048 entity_archetype_.erase(it);
1049
1050 for (auto&& [type_index, entry] : sparse_storages_.Data()) {
1051 entry.TryRemove(entity);
1052 }
1053 return true;
1054}
1055
1056inline void ComponentManager::Clear(Entity entity) {
1057 HELIOS_ASSERT(entity.Valid(), "Entity '{}' is invalid!", entity);
1058 const auto it = entity_archetype_.find(entity.Index());
1059 HELIOS_ASSERT(it != entity_archetype_.end(), "Entity '{}' not tracked!",
1060 entity);
1061
1062 Archetype& current = it->second.get();
1063
1064 if (empty_archetype_ == nullptr) {
1065 empty_archetype_ = &GetOrCreateArchetype(ArchetypeId{});
1066 }
1067
1068 if (&current != empty_archetype_) {
1069 Entity swapped = current.Remove(entity);
1070 if (swapped.Valid()) {
1071 entity_archetype_.insert_or_assign(swapped.Index(), std::ref(current));
1072 }
1073 empty_archetype_->AllocateRow(entity);
1074 entity_archetype_.insert_or_assign(entity.Index(),
1075 std::ref(*empty_archetype_));
1076 ++structural_version_;
1077 }
1078
1079 // Remove from all sparse storages.
1080 for (auto&& [type_index, entry] : sparse_storages_.Data()) {
1081 entry.TryRemove(entity);
1082 }
1083}
1084
1085template <ArchetypeComponentTrait... Ts>
1086 requires utils::UniqueTypes<Ts...> && (sizeof...(Ts) > 0)
1088 Ts&&... components) {
1089 using AddedMask = std::array<bool, sizeof...(Ts)>;
1090
1091 HELIOS_ASSERT(entity.Valid(), "Entity '{}' is invalid!", entity);
1092
1093 (EnsureRegistered<std::remove_cvref_t<Ts>>(), ...);
1094
1095 const auto it = entity_archetype_.find(entity.Index());
1096 HELIOS_ASSERT(it != entity_archetype_.end(), "Entity '{}' not tracked!",
1097 entity);
1098
1099 Archetype& current = it->second.get();
1100 const AddedMask added = {(!current.HasColumn(
1101 ComponentTypeIndex::From<std::remove_cvref_t<Ts>>()))...};
1102
1103 auto target_id = current.Id();
1104 [&]<size_t... Is>(std::index_sequence<Is...>) {
1105 ((added[Is]
1106 ? static_cast<void>(
1107 target_id = target_id.template With<std::remove_cvref_t<Ts>>())
1108 : void()),
1109 ...);
1110 }(std::make_index_sequence<sizeof...(Ts)>{});
1111
1112 if (target_id != current.Id()) {
1113 Archetype& target = GetOrCreateArchetype(target_id);
1114 MigrateEntity(entity, current, target);
1115
1116 auto args = std::forward_as_tuple(std::forward<Ts>(components)...);
1117 [&]<size_t... Is>(std::index_sequence<Is...>) {
1118 ((added[Is] ? target.Set(entity, std::forward<FwdTypeAt<Is, Ts...>>(
1119 std::get<Is>(args)))
1120 : void()),
1121 ...);
1122 }(std::make_index_sequence<sizeof...(Ts)>{});
1123
1124 ++structural_version_;
1125 } else {
1126 (current.Set(entity, std::forward<Ts>(components)), ...);
1127 }
1128}
1129
1130template <ArchetypeComponentTrait... Ts>
1131 requires utils::UniqueTypes<Ts...> && (sizeof...(Ts) > 0)
1133 Ts&&... components)
1134 -> std::array<bool, sizeof...(Ts)> {
1135 using AddedMask = std::array<bool, sizeof...(Ts)>;
1136
1137 HELIOS_ASSERT(entity.Valid(), "Entity '{}' is invalid!", entity);
1138
1139 (EnsureRegistered<std::remove_cvref_t<Ts>>(), ...);
1140
1141 const auto it = entity_archetype_.find(entity.Index());
1142 HELIOS_ASSERT(it != entity_archetype_.end(), "Entity '{}' not tracked!",
1143 entity);
1144
1145 Archetype& current = it->second.get();
1146 const AddedMask results = {(!current.HasColumn(
1147 ComponentTypeIndex::From<std::remove_cvref_t<Ts>>()))...};
1148
1149 auto target_id = current.Id();
1150 [&]<size_t... Is>(std::index_sequence<Is...>) {
1151 ((results[Is]
1152 ? static_cast<void>(
1153 target_id = target_id.template With<std::remove_cvref_t<Ts>>())
1154 : void()),
1155 ...);
1156 }(std::make_index_sequence<sizeof...(Ts)>{});
1157
1158 if (target_id != current.Id()) {
1159 Archetype& target = GetOrCreateArchetype(target_id);
1160 MigrateEntity(entity, current, target);
1161
1162 auto args = std::forward_as_tuple(std::forward<Ts>(components)...);
1163 [&]<size_t... Is>(std::index_sequence<Is...>) {
1164 ((results[Is] ? target.Set(entity, std::forward<FwdTypeAt<Is, Ts...>>(
1165 std::get<Is>(args)))
1166 : void()),
1167 ...);
1168 }(std::make_index_sequence<sizeof...(Ts)>{});
1169
1170 ++structural_version_;
1171 }
1172
1173 return results;
1174}
1175
1176template <ArchetypeComponentTrait T, typename... Args>
1177 requires std::constructible_from<T, Args...>
1179 Args&&... args) {
1180 HELIOS_ASSERT(entity.Valid(), "Entity '{}' is invalid!", entity);
1181 AddArchetypeComponents<T>(entity, T(std::forward<Args>(args)...));
1182}
1183
1184template <ArchetypeComponentTrait T, typename... Args>
1185 requires std::constructible_from<T, Args...>
1187 Args&&... args) {
1188 HELIOS_ASSERT(entity.Valid(), "Entity '{}' is invalid!", entity);
1189 return TryAddArchetypeComponents<T>(entity, T(std::forward<Args>(args)...))
1190 .front();
1191}
1192
1193template <ArchetypeComponentTrait... Ts>
1194 requires utils::UniqueTypes<Ts...> && (sizeof...(Ts) > 0)
1196 HELIOS_ASSERT(entity.Valid(), "Entity '{}' is invalid!", entity);
1197
1198 const auto it = entity_archetype_.find(entity.Index());
1199 HELIOS_ASSERT(it != entity_archetype_.end(), "Entity '{}' not tracked!",
1200 entity);
1201
1202 Archetype& current = it->second.get();
1203 auto target_id = current.Id();
1204 ((target_id = target_id.template Without<Ts>()), ...);
1205
1206 if (target_id != current.Id()) {
1207 Archetype& target = GetOrCreateArchetype(target_id);
1208 MigrateEntity(entity, current, target);
1209 ++structural_version_;
1210 }
1211}
1212
1213template <ArchetypeComponentTrait... Ts>
1214 requires utils::UniqueTypes<Ts...> && (sizeof...(Ts) > 0)
1216 -> std::array<bool, sizeof...(Ts)> {
1217 HELIOS_ASSERT(entity.Valid(), "Entity '{}' is invalid!", entity);
1218
1219 const auto it = entity_archetype_.find(entity.Index());
1220 HELIOS_ASSERT(it != entity_archetype_.end(), "Entity '{}' not tracked!",
1221 entity);
1222
1223 Archetype& current = it->second.get();
1224 std::array<bool, sizeof...(Ts)> results = {(current.HasColumn(
1225 ComponentTypeIndex::From<std::remove_cvref_t<Ts>>()))...};
1226
1227 auto target_id = current.Id();
1228 ((target_id = target_id.template Without<Ts>()), ...);
1229
1230 if (target_id != current.Id()) {
1231 Archetype& target = GetOrCreateArchetype(target_id);
1232 MigrateEntity(entity, current, target);
1233 ++structural_version_;
1234 }
1235
1236 return results;
1237}
1238
1239template <SparseComponentTrait T>
1240inline void ComponentManager::AddSparseComponent(Entity entity, T&& component) {
1241 HELIOS_ASSERT(entity.Valid(), "Entity '{}' is invalid!", entity);
1242 HELIOS_ASSERT(Tracked(entity), "Entity '{}' not tracked!", entity);
1243
1244 auto& storage = EnsureSparseStorage<std::remove_cvref_t<T>>();
1245 storage.Set(entity, std::forward<T>(component));
1246}
1247
1248template <SparseComponentTrait T>
1250 T&& component) {
1251 HELIOS_ASSERT(entity.Valid(), "Entity '{}' is invalid!", entity);
1252 HELIOS_ASSERT(Tracked(entity), "Entity '{}' not tracked!", entity);
1253
1254 auto& storage = EnsureSparseStorage<std::remove_cvref_t<T>>();
1255 return storage.TrySet(entity, std::forward<T>(component));
1256}
1257
1258template <SparseComponentTrait T, typename... Args>
1259 requires std::constructible_from<T, Args...>
1261 Args&&... args) {
1262 HELIOS_ASSERT(entity.Valid(), "Entity '{}' is invalid!", entity);
1263 HELIOS_ASSERT(Tracked(entity), "Entity '{}' not tracked!", entity);
1264
1265 auto& storage = EnsureSparseStorage<T>();
1266 storage.Emplace(entity, std::forward<Args>(args)...);
1267}
1268
1269template <SparseComponentTrait T, typename... Args>
1270 requires std::constructible_from<T, Args...>
1272 Args&&... args) {
1273 HELIOS_ASSERT(entity.Valid(), "Entity '{}' is invalid!", entity);
1274 HELIOS_ASSERT(Tracked(entity), "Entity '{}' not tracked!", entity);
1275
1276 auto& storage = EnsureSparseStorage<T>();
1277 return storage.TryEmplace(entity, std::forward<Args>(args)...);
1278}
1279
1280template <SparseComponentTrait T>
1282 HELIOS_ASSERT(entity.Valid(), "Entity '{}' is invalid!", entity);
1283 HELIOS_ASSERT(Tracked(entity), "Entity '{}' not tracked!", entity);
1284
1285 auto* entry = sparse_storages_.template TryGet<T>();
1286 HELIOS_ASSERT(entry != nullptr,
1287 "Sparse storage for component '{}' does not exist!",
1289 entry->template As<T>().Remove(entity);
1290}
1291
1292template <SparseComponentTrait T>
1294 HELIOS_ASSERT(entity.Valid(), "Entity '{}' is invalid!", entity);
1295 HELIOS_ASSERT(Tracked(entity), "Entity '{}' not tracked!", entity);
1296
1297 auto* entry = sparse_storages_.template TryGet<T>();
1298 if (entry == nullptr) {
1299 return false;
1300 }
1301 return entry->template As<T>().TryRemove(entity);
1302}
1303
1304template <ComponentTrait... Ts>
1305 requires utils::UniqueTypes<Ts...> && (sizeof...(Ts) > 0)
1306inline void ComponentManager::Add(Entity entity, Ts&&... components) {
1307 HELIOS_ASSERT(entity.Valid(), "Entity '{}' is invalid!", entity);
1308 HELIOS_ASSERT(Tracked(entity), "Entity '{}' not tracked!", entity);
1309
1310 constexpr bool kAllSparse =
1312 constexpr bool kAllArchetype =
1314
1315 if constexpr (kAllSparse) {
1316 (AddSparseComponent(entity, std::forward<Ts>(components)), ...);
1317 } else if constexpr (kAllArchetype) {
1318 AddArchetypeComponents(entity, std::forward<Ts>(components)...);
1319 } else {
1320 using ArchIs = FilteredIndices<IsArchetype, Ts...>;
1321 using SparseIs = FilteredIndices<IsSparse, Ts...>;
1322 auto args = std::forward_as_tuple(std::forward<Ts>(components)...);
1323 DispatchAddArchetype(entity, args, ArchIs{});
1324 [this, entity, &args]<size_t... Is>(std::index_sequence<Is...>) {
1326 entity, std::forward<FwdTypeAt<Is, Ts...>>(std::get<Is>(args))),
1327 ...);
1328 }(SparseIs{});
1329 }
1330}
1331
1332template <ComponentTrait... Ts>
1333 requires utils::UniqueTypes<Ts...> && (sizeof...(Ts) > 0)
1334inline auto ComponentManager::TryAdd(Entity entity, Ts&&... components)
1335 -> std::conditional_t<sizeof...(Ts) == 1, bool,
1336 std::array<bool, sizeof...(Ts)>> {
1337 HELIOS_ASSERT(entity.Valid(), "Entity '{}' is invalid!", entity);
1338 HELIOS_ASSERT(Tracked(entity), "Entity '{}' not tracked!", entity);
1339
1340 constexpr bool kAllSparse =
1342 constexpr bool kAllArchetype =
1344
1345 if constexpr (kAllSparse) {
1346 auto results = std::to_array(
1347 {TryAddSparseComponent(entity, std::forward<Ts>(components))...});
1348 if constexpr (sizeof...(Ts) == 1) {
1349 return results.front();
1350 } else {
1351 return results;
1352 }
1353 } else if constexpr (kAllArchetype) {
1354 auto results =
1355 TryAddArchetypeComponents(entity, std::forward<Ts>(components)...);
1356 if constexpr (sizeof...(Ts) == 1) {
1357 return results.front();
1358 } else {
1359 return results;
1360 }
1361 } else {
1362 using ArchIs = FilteredIndices<IsArchetype, Ts...>;
1363 using SparseIs = FilteredIndices<IsSparse, Ts...>;
1364 auto args = std::forward_as_tuple(std::forward<Ts>(components)...);
1365 std::array<bool, sizeof...(Ts)> results = {};
1366
1367 // Batch archetype: sub-results map back to original positions via ArchIs.
1368 auto arch_results = DispatchTryAddArchetype(entity, args, ArchIs{});
1369 [this, entity, &args, &results,
1370 &arch_results]<size_t... ArchPos, size_t... SubIs>(
1371 std::index_sequence<ArchPos...>, std::index_sequence<SubIs...>) {
1372 ((results[ArchPos] = arch_results[SubIs]), ...);
1373 }(ArchIs{}, std::make_index_sequence<arch_results.size()>{});
1374
1375 // Sparse: one call each, result goes directly to the original position.
1376 [this, entity, &args, &results]<size_t... Is>(std::index_sequence<Is...>) {
1377 ((results[Is] = TryAddSparseComponent(
1378 entity, std::forward<FwdTypeAt<Is, Ts...>>(std::get<Is>(args)))),
1379 ...);
1380 }(SparseIs{});
1381
1382 if constexpr (sizeof...(Ts) == 1) {
1383 return results.front();
1384 } else {
1385 return results;
1386 }
1387 }
1388}
1389
1390template <ComponentTrait T, typename... Args>
1391 requires std::constructible_from<T, Args...>
1392inline void ComponentManager::Emplace(Entity entity, Args&&... args) {
1393 HELIOS_ASSERT(entity.Valid(), "Entity '{}' is invalid!", entity);
1394 HELIOS_ASSERT(Tracked(entity), "Entity '{}' not tracked!", entity);
1395
1396 if constexpr (SparseComponentTrait<T>) {
1397 EmplaceSparseComponent<T>(entity, std::forward<Args>(args)...);
1398 } else {
1399 EmplaceArchetypeComponent<T>(entity, std::forward<Args>(args)...);
1400 }
1401}
1402
1403template <ComponentTrait T, typename... Args>
1404 requires std::constructible_from<T, Args...>
1405inline bool ComponentManager::TryEmplace(Entity entity, Args&&... args) {
1406 HELIOS_ASSERT(entity.Valid(), "Entity '{}' is invalid!", entity);
1407 HELIOS_ASSERT(Tracked(entity), "Entity '{}' not tracked!", entity);
1408
1409 if constexpr (SparseComponentTrait<T>) {
1410 return TryEmplaceSparseComponent<T>(entity, std::forward<Args>(args)...);
1411 } else {
1412 return TryEmplaceArchetypeComponent<T>(entity, std::forward<Args>(args)...);
1413 }
1414}
1415
1416template <ComponentTrait... Ts>
1417 requires utils::UniqueTypes<Ts...> && (sizeof...(Ts) > 0)
1419 HELIOS_ASSERT(entity.Valid(), "Entity '{}' is invalid!", entity);
1420 HELIOS_ASSERT(Tracked(entity), "Entity '{}' not tracked!", entity);
1421
1422 constexpr bool kAllSparse =
1424 constexpr bool kAllArchetype =
1426
1427 if constexpr (kAllSparse) {
1428 (RemoveSparseComponent<Ts>(entity), ...);
1429 } else if constexpr (kAllArchetype) {
1430 RemoveArchetypeComponents<Ts...>(entity);
1431 } else {
1432 using ArchIs = FilteredIndices<IsArchetype, Ts...>;
1433 using SparseIs = FilteredIndices<IsSparse, Ts...>;
1434 DispatchRemoveArchetype<Ts...>(entity, ArchIs{});
1435 [this, entity]<size_t... Is>(std::index_sequence<Is...>) {
1436 (RemoveSparseComponent<TypeAt<Is, Ts...>>(entity), ...);
1437 }(SparseIs{});
1438 }
1439}
1440
1441template <ComponentTrait... Ts>
1442 requires utils::UniqueTypes<Ts...> && (sizeof...(Ts) > 0)
1444 -> std::conditional_t<sizeof...(Ts) == 1, bool,
1445 std::array<bool, sizeof...(Ts)>> {
1446 HELIOS_ASSERT(entity.Valid(), "Entity '{}' is invalid!", entity);
1447 HELIOS_ASSERT(Tracked(entity), "Entity '{}' not tracked!", entity);
1448
1449 constexpr bool kAllSparse =
1451 constexpr bool kAllArchetype =
1453
1454 if constexpr (kAllSparse) {
1455 auto results = std::to_array({TryRemoveSparseComponent<Ts>(entity)...});
1456 if constexpr (sizeof...(Ts) == 1) {
1457 return results.front();
1458 } else {
1459 return results;
1460 }
1461 } else if constexpr (kAllArchetype) {
1462 auto results = TryRemoveArchetypeComponents<Ts...>(entity);
1463 if constexpr (sizeof...(Ts) == 1) {
1464 return results.front();
1465 } else {
1466 return results;
1467 }
1468 } else {
1469 using ArchIs = FilteredIndices<IsArchetype, Ts...>;
1470 using SparseIs = FilteredIndices<IsSparse, Ts...>;
1471 std::array<bool, sizeof...(Ts)> results = {};
1472
1473 auto arch_results = DispatchTryRemoveArchetype<Ts...>(entity, ArchIs{});
1474 [&results,
1475 &arch_results]<size_t... ArchPos>(std::index_sequence<ArchPos...>) {
1476 ((results[ArchPos] = arch_results[ArchPos]), ...);
1477 }(ArchIs{});
1478
1479 [this, entity, &results]<size_t... Is>(std::index_sequence<Is...>) {
1480 ((results[Is] = TryRemoveSparseComponent<TypeAt<Is, Ts...>>(entity)),
1481 ...);
1482 }(SparseIs{});
1483
1484 if constexpr (sizeof...(Ts) == 1) {
1485 return results.front();
1486 } else {
1487 return results;
1488 }
1489 }
1490}
1491
1492template <ComponentTrait T>
1494 HELIOS_ASSERT(entity.Valid(), "Entity '{}' is invalid!", entity);
1495
1496 T* component = nullptr;
1497 if constexpr (SparseComponentTrait<T>) {
1498 auto* entry = sparse_storages_.template TryGet<T>();
1499 HELIOS_ASSERT(entry != nullptr,
1500 "Sparse storage for component '{}' does not exist!",
1502 component = entry->template As<T>().TryGet(entity);
1503 } else {
1504 const auto it = entity_archetype_.find(entity.Index());
1505 HELIOS_ASSERT(it != entity_archetype_.end(), "Entity '{}' not tracked!",
1506 entity);
1507 component = it->second.get().template TryGet<T>(entity);
1508 }
1509
1510 HELIOS_ASSERT(component != nullptr,
1511 "Entity '{}' does not have component '{}'!", entity,
1513 return *component;
1514}
1515
1516template <ComponentTrait T>
1517inline const T& ComponentManager::Get(Entity entity) const {
1518 HELIOS_ASSERT(entity.Valid(), "Entity '{}' is invalid!", entity);
1519
1520 const T* component = nullptr;
1521 if constexpr (SparseComponentTrait<T>) {
1522 auto* entry = sparse_storages_.template TryGet<T>();
1523 HELIOS_ASSERT(entry != nullptr,
1524 "Sparse storage for component '{}' does not exist!",
1526 component = entry->template As<T>().TryGet(entity);
1527 } else {
1528 const auto it = entity_archetype_.find(entity.Index());
1529 HELIOS_ASSERT(it != entity_archetype_.end(), "Entity '{}' not tracked!",
1530 entity);
1531 component = it->second.get().template TryGet<T>(entity);
1532 }
1533
1534 HELIOS_ASSERT(component != nullptr,
1535 "Entity '{}' does not have component '{}'!", entity,
1537 return *component;
1538}
1539
1540template <ComponentTrait T>
1542 HELIOS_ASSERT(entity.Valid(), "Entity '{}' is invalid!", entity);
1543
1544 if constexpr (SparseComponentTrait<T>) {
1545 auto* entry = sparse_storages_.template TryGet<T>();
1546 if (entry == nullptr) {
1547 return nullptr;
1548 }
1549 return entry->template As<T>().TryGet(entity);
1550 } else {
1551 const auto it = entity_archetype_.find(entity.Index());
1552 if (it == entity_archetype_.end()) {
1553 return nullptr;
1554 }
1555 return it->second.get().template TryGet<T>(entity);
1556 }
1557}
1558
1559template <ComponentTrait T>
1560inline const T* ComponentManager::TryGet(Entity entity) const {
1561 HELIOS_ASSERT(entity.Valid(), "Entity '{}' is invalid!", entity);
1562
1563 if constexpr (SparseComponentTrait<T>) {
1564 const auto* entry = sparse_storages_.template TryGet<T>();
1565 if (entry == nullptr) {
1566 return nullptr;
1567 }
1568 return entry->template As<T>().TryGet(entity);
1569 } else {
1570 const auto it = entity_archetype_.find(entity.Index());
1571 if (it == entity_archetype_.end()) {
1572 return nullptr;
1573 }
1574 return it->second.get().template TryGet<T>(entity);
1575 }
1576}
1577
1578template <ComponentTrait T>
1579inline bool ComponentManager::Registered() const noexcept {
1580 constexpr auto type_index = ComponentTypeIndex::From<T>();
1581 return metadata_.find(type_index) != metadata_.end();
1582}
1583
1584inline bool ComponentManager::Tracked(Entity entity) const noexcept {
1585 HELIOS_ASSERT(entity.Valid(), "Entity '{}' is invalid!", entity);
1586 return entity_archetype_.contains(entity.Index());
1587}
1588
1589template <ComponentTrait T>
1590inline bool ComponentManager::Has(Entity entity) const {
1591 HELIOS_ASSERT(entity.Valid(), "Entity '{}' is invalid!", entity);
1592
1593 if constexpr (SparseComponentTrait<T>) {
1594 const auto* entry = sparse_storages_.template TryGet<T>();
1595 if (entry == nullptr) {
1596 return false;
1597 }
1598 return entry->template As<T>().Contains(entity);
1599 } else {
1600 const auto it = entity_archetype_.find(entity.Index());
1601 if (it == entity_archetype_.end()) {
1602 return false;
1603 }
1604 return it->second.get().template HasColumn<T>();
1605 }
1606}
1607
1608template <ComponentTrait... Ts>
1609 requires utils::UniqueTypes<Ts...> && (sizeof...(Ts) > 1)
1610inline auto ComponentManager::Has(Entity entity) const
1611 -> std::array<bool, sizeof...(Ts)> {
1612 HELIOS_ASSERT(entity.Valid(), "Entity '{}' is invalid!", entity);
1613 return {Has<Ts>(entity)...};
1614}
1615
1617 HELIOS_ASSERT(entity.Valid(), "Entity '{}' is invalid!", entity);
1618 const auto it = entity_archetype_.find(entity.Index());
1619 HELIOS_ASSERT(it != entity_archetype_.end(), "Entity '{}' not tracked!",
1620 entity);
1621 return it->second.get();
1622}
1623
1625 HELIOS_ASSERT(entity.Valid(), "Entity '{}' is invalid!", entity);
1626 const auto it = entity_archetype_.find(entity.Index());
1627 HELIOS_ASSERT(it != entity_archetype_.end(), "Entity '{}' not tracked!",
1628 entity);
1629 return it->second.get();
1630}
1631
1632template <SparseComponentTrait T>
1634 -> const SparseComponentStorage<T>& {
1635 const auto* entry = sparse_storages_.template TryGet<T>();
1636 HELIOS_ASSERT(entry != nullptr, "Sparse storage for '{}' does not exist!",
1638 return entry->template As<T>();
1639}
1640
1641template <ComponentTrait T>
1642inline const ComponentMetadata& ComponentManager::Metadata() const noexcept {
1643 constexpr auto type_index = ComponentTypeIndex::From<T>();
1644 const auto it = metadata_.find(type_index);
1645 HELIOS_ASSERT(it != metadata_.end(), "Component '{}' is not registered!",
1647 return it->second;
1648}
1649
1651 ComponentTypeIndex type_index) const noexcept {
1652 const auto it = metadata_.find(type_index);
1653 if (it == metadata_.end()) {
1654 return nullptr;
1655 }
1656 return &it->second;
1657}
1658
1659template <ComponentTrait T>
1660inline void ComponentManager::EnsureRegistered() {
1661 constexpr auto type_index = ComponentTypeIndex::From<T>();
1662
1663 if (metadata_.find(type_index) == metadata_.end()) {
1664 metadata_.emplace(type_index, ComponentMetadata::From<T>());
1665 }
1666}
1667
1668template <template <typename> typename Pred, typename... Ts>
1669consteval auto ComponentManager::FilteredIndicesArray() noexcept {
1670 // Predicate results as a bool array.
1671 constexpr std::array mask = {Pred<std::remove_cvref_t<Ts>>::value...};
1672 constexpr size_t count =
1673 (0 + ... + (Pred<std::remove_cvref_t<Ts>>::value ? 1 : 0));
1674
1675 std::array<size_t, count> out = {};
1676 size_t j = 0;
1677 for (size_t i = 0; i < sizeof...(Ts); ++i) {
1678 if (mask[i]) {
1679 out[j++] = i;
1680 }
1681 }
1682 return out;
1683}
1684
1685template <typename... Ts, size_t... ArchIs>
1686inline void ComponentManager::DispatchAddArchetype(
1687 Entity entity, std::tuple<Ts...>& args,
1688 std::index_sequence<ArchIs...> /*seq*/) {
1689 AddArchetypeComponents(entity, std::forward<FwdTypeAt<ArchIs, Ts...>>(
1690 std::get<ArchIs>(args))...);
1691}
1692
1693template <typename... Ts, size_t... ArchIs>
1694inline auto ComponentManager::DispatchTryAddArchetype(
1695 Entity entity, std::tuple<Ts...>& args,
1696 std::index_sequence<ArchIs...> /*seq*/)
1697 -> std::array<bool, sizeof...(ArchIs)> {
1698 return TryAddArchetypeComponents(
1699 entity,
1700 std::forward<FwdTypeAt<ArchIs, Ts...>>(std::get<ArchIs>(args))...);
1701}
1702
1703template <typename... Ts, size_t... ArchIs>
1704inline void ComponentManager::DispatchRemoveArchetype(
1705 Entity entity, std::index_sequence<ArchIs...> /*seq*/) {
1706 RemoveArchetypeComponents<TypeAt<ArchIs, Ts...>...>(entity);
1707}
1708
1709template <typename... Ts, size_t... ArchIs>
1710inline auto ComponentManager::DispatchTryRemoveArchetype(
1711 Entity entity, std::index_sequence<ArchIs...> /*seq*/)
1712 -> std::array<bool, sizeof...(ArchIs)> {
1713 return TryRemoveArchetypeComponents<TypeAt<ArchIs, Ts...>...>(entity);
1714}
1715
1716template <SparseComponentTrait T>
1717inline auto ComponentManager::EnsureSparseStorage()
1718 -> SparseComponentStorage<T>& {
1719 EnsureRegistered<T>();
1720
1721 if (auto* entry = sparse_storages_.template TryGet<T>(); entry != nullptr) {
1722 return entry->template As<T>();
1723 }
1724
1725 // Create and insert new entry keyed by the component type index.
1726 auto& new_entry = sparse_storages_.template Ensure<T>();
1727 new_entry = SparseStorageEntry::From<T>();
1728 return new_entry.template As<T>();
1729}
1730
1731inline Archetype& ComponentManager::GetOrCreateArchetype(
1732 const ArchetypeId& id) {
1733 HELIOS_ECS_PROFILE_SCOPE_N(
1734 "helios::ecs::ComponentManager::GetOrCreateArchetype");
1735 HELIOS_ECS_PROFILE_ZONE_VALUE(id.Types().size());
1736
1737 const size_t hash = id.Hash();
1738 if (const auto it = archetype_map_.find(hash); it != archetype_map_.end()) {
1739 return it->second.archetype.get();
1740 }
1741
1742 // Create archetype in the stable deque storage.
1743 archetype_storage_.emplace_back(id);
1744 Archetype& archetype = archetype_storage_.back();
1745
1746 // Create record referencing the archetype.
1747 archetype_map_.emplace(hash, ArchetypeRecord{.archetype = std::ref(archetype),
1748 .add_edges = {},
1749 .remove_edges = {}});
1750
1751 // Initialize columns with type info from metadata.
1752 InitArchetypeColumns(archetype);
1753 archetype_list_.emplace_back(archetype);
1754 ++structural_version_;
1755 return archetype;
1756}
1757
1758inline void ComponentManager::InitArchetypeColumns(Archetype& archetype) {
1759 for (const auto type_index : archetype.Id().Types()) {
1760 const auto meta_it = metadata_.find(type_index);
1761 if (meta_it == metadata_.end()) {
1762 continue;
1763 }
1764
1765 auto* col = archetype.TryColumn(type_index);
1766 if (col != nullptr && meta_it->second.init_column != nullptr) {
1767 meta_it->second.init_column(*col);
1768 }
1769 }
1770}
1771
1772inline void ComponentManager::MigrateEntity(Entity entity, Archetype& src,
1773 Archetype& dst) {
1774 HELIOS_ECS_PROFILE_SCOPE_N("helios::ecs::ComponentManager::MigrateEntity");
1775 HELIOS_ECS_PROFILE_ZONE_VALUE(src.EntityCount());
1776 HELIOS_ECS_PROFILE_ZONE_VALUE(dst.Id().Types().size());
1777
1778 HELIOS_ASSERT(entity.Valid(), "Entity '{}' is invalid!", entity);
1779
1780 if (&src == &dst) [[unlikely]] {
1781 return;
1782 }
1783
1784 const auto src_row = src.Row(entity);
1785
1786 // Phase 1: Allocate a row in the destination archetype for this entity.
1787 [[maybe_unused]] const auto dst_row = dst.AllocateRow(entity);
1788
1789 // Phase 2: For each column in dst, either move data from src or push a
1790 // default placeholder.
1791 for (const auto type_index : dst.Id().Types()) {
1792 auto* dst_col = dst.TryColumn(type_index);
1793 if (dst_col == nullptr) {
1794 continue;
1795 }
1796
1797 const auto meta_it = metadata_.find(type_index);
1798 HELIOS_ASSERT(meta_it != metadata_.end(),
1799 "Component metadata not registered for a column in target "
1800 "archetype!");
1801
1802 if (src.HasColumn(type_index)) {
1803 // Shared column: move the element from src[src_row] into dst (appended
1804 // at back).
1805 auto* src_col = src.TryColumn(type_index);
1806 if (src_col != nullptr && !src_col->Empty() &&
1807 meta_it->second.move_column_element != nullptr) {
1808 meta_it->second.move_column_element(*dst_col, *src_col,
1809 static_cast<size_t>(src_row));
1810 }
1811 } else {
1812 // New column in dst that src doesn't have.
1813 // Push a default-constructed placeholder; the caller will overwrite it
1814 // via Set.
1815 if (meta_it->second.default_push != nullptr) {
1816 meta_it->second.default_push(*dst_col);
1817 }
1818 }
1819 }
1820
1821 // Phase 3: Remove the entity from the source archetype using swap-and-pop.
1822 const Entity swapped = src.Remove(entity);
1823
1824 // Phase 4: Update entity-to-archetype mapping.
1825 entity_archetype_.insert_or_assign(entity.Index(), std::ref(dst));
1826 if (swapped.Valid()) {
1827 entity_archetype_.insert_or_assign(swapped.Index(), std::ref(src));
1828 }
1829
1830 ++structural_version_;
1831}
1832
1833inline Archetype* ComponentManager::TryGetAddEdge(Archetype& from,
1834 ComponentTypeIndex type) {
1835 auto& record = GetRecord(from);
1836 const auto it = record.add_edges.find(type);
1837 return (it != record.add_edges.end()) ? &it->second.get() : nullptr;
1838}
1839
1840inline Archetype* ComponentManager::TryGetRemoveEdge(Archetype& from,
1841 ComponentTypeIndex type) {
1842 auto& record = GetRecord(from);
1843 const auto it = record.remove_edges.find(type);
1844 return (it != record.remove_edges.end()) ? &it->second.get() : nullptr;
1845}
1846
1847inline auto ComponentManager::GetRecord(Archetype& archetype)
1848 -> ArchetypeRecord& {
1849 const size_t hash = archetype.Id().Hash();
1850 const auto it = archetype_map_.find(hash);
1851 HELIOS_ASSERT(it != archetype_map_.end(), "Archetype record not found!");
1852 return it->second;
1853}
1854
1855inline auto ComponentManager::GetRecord(const Archetype& archetype) const
1856 -> const ArchetypeRecord& {
1857 const size_t hash = archetype.Id().Hash();
1858 const auto it = archetype_map_.find(hash);
1859 HELIOS_ASSERT(it != archetype_map_.end(), "Archetype record not found!");
1860 return it->second;
1861}
1862
1863} // namespace helios::ecs
#define HELIOS_ASSERT(condition,...)
Assertion macro that aborts execution in debug builds.
Definition assert.hpp:259
Generic type-indexed map that stores one Storage instance per registered type key.
Type-erased sequential byte storage for a single type (array variant).
Type-erased single-instance byte storage.
Unique identifier for an archetype, represented as a sorted set of component type indices.
Stores entities and their component data in a Structure-of-Arrays layout for a specific archetype.
Definition archetype.hpp:50
void Set(Entity entity, T &&component)
Sets (overwrites) a component value for an existing entity.
const ArchetypeId & Id() const noexcept
Gets the archetype id.
bool HasColumn() const noexcept
Checks if this archetype has a column for the given component type.
Entity Remove(Entity entity)
Removes an entity from this archetype using swap-and-pop.
T * TryGet(Entity entity)
Tries to get a mutable pointer to a component.
Definition manager.hpp:1541
bool TryRemoveSparseComponent(Entity entity)
Tries to remove a sparse-set-stored component.
Definition manager.hpp:1293
ComponentManager(const ComponentManager &)=delete
auto TryRemoveArchetypeComponents(Entity entity) -> std::array< bool, sizeof...(Ts)>
Tries to remove multiple archetype-stored components.
Definition manager.hpp:1215
void InitEntity(Entity entity)
Initializes an entity in the component manager with an empty archetype.
Definition manager.hpp:998
auto Archetypes() const noexcept -> std::span< const std::reference_wrapper< Archetype > >
Gets all archetypes (const).
Definition manager.hpp:674
bool Has(Entity entity) const
Checks if an entity has a component.
Definition manager.hpp:1590
void EmplaceSparseComponent(Entity entity, Args &&... args)
Emplaces a sparse-set-stored component.
Definition manager.hpp:1260
size_type TrackedEntityCount() const noexcept
Gets the number of entities currently tracked by the component manager.
Definition manager.hpp:647
void RemoveArchetypeComponents(Entity entity)
Removes multiple archetype-stored components from an entity.
Definition manager.hpp:1195
bool Registered() const noexcept
Checks if a component type is registered.
Definition manager.hpp:1579
const SparseStorageEntry * SparseEntry(ComponentTypeIndex type_index) const noexcept
Definition manager.hpp:637
void Clear()
Clears all component data, archetypes, and metadata.
Definition manager.hpp:966
T & Get(Entity entity)
Gets a mutable reference to a component.
Definition manager.hpp:1493
bool TryEmplaceArchetypeComponent(Entity entity, Args &&... args)
Tries to emplace an archetype-stored component.
Definition manager.hpp:1186
auto TryRemove(Entity entity) -> std::conditional_t< sizeof...(Ts)==1, bool, std::array< bool, sizeof...(Ts)> >
Tries to remove multiple components.
Definition manager.hpp:1443
Archetype & EntityArchetype(Entity entity)
Gets the archetype that the entity currently belongs to.
Definition manager.hpp:1616
void Register()
Registers a component type so the manager knows its metadata.
Definition manager.hpp:990
auto TryAddArchetypeComponents(Entity entity, Ts &&... components) -> std::array< bool, sizeof...(Ts)>
Adds multiple archetype-stored components to an entity.
Definition manager.hpp:1132
bool TryAddSparseComponent(Entity entity, T &&component)
Tries to add a sparse-set-stored component.
Definition manager.hpp:1249
auto TryAdd(Entity entity, Ts &&... components) -> std::conditional_t< sizeof...(Ts)==1, bool, std::array< bool, sizeof...(Ts)> >
Tries to add multiple components.
Definition manager.hpp:1334
const ComponentMetadata * MetadataByIndex(ComponentTypeIndex type_index) const noexcept
Gets metadata for a component type by its runtime type index.
Definition manager.hpp:1650
void Add(Entity entity, Ts &&... components)
Adds multiple components to an entity.
Definition manager.hpp:1306
void AddSparseComponent(Entity entity, T &&component)
Adds a sparse-set-stored component to an entity.
Definition manager.hpp:1240
const ComponentMetadata & Metadata() const noexcept
Gets metadata for a component type.
Definition manager.hpp:1642
bool TryEmplace(Entity entity, Args &&... args)
Tries to emplace a component.
Definition manager.hpp:1405
auto SparseStorage() -> SparseComponentStorage< T > &
Gets the sparse-set storage for a component type, creating it if necessary.
Definition manager.hpp:604
void Emplace(Entity entity, Args &&... args)
Emplaces a component (construct in-place), dispatching by storage type.
Definition manager.hpp:1392
auto Archetypes() noexcept -> std::span< std::reference_wrapper< Archetype > >
Gets all archetypes currently in the manager.
Definition manager.hpp:665
size_type ArchetypeCount() const noexcept
Gets the number of archetypes currently in the manager.
Definition manager.hpp:655
bool Tracked(Entity entity) const noexcept
Checks if an entity is tracked by the component manager.
Definition manager.hpp:1584
bool TryEmplaceSparseComponent(Entity entity, Args &&... args)
Tries to emplace a sparse-set-stored component.
Definition manager.hpp:1271
ComponentManager(ComponentManager &&) noexcept=default
void EmplaceArchetypeComponent(Entity entity, Args &&... args)
Emplaces an archetype-stored component (construct in-place).
Definition manager.hpp:1178
void RemoveSparseComponent(Entity entity)
Removes a sparse-set-stored component from an entity.
Definition manager.hpp:1281
void RegisterAll()
Registers multiple component types.
Definition manager.hpp:168
void Remove(Entity entity)
Removes multiple components from an entity.
Definition manager.hpp:1418
size_type StructuralVersion() const noexcept
Gets the current structural version of the component manager.
Definition manager.hpp:684
void RemoveEntity(Entity entity)
Removes all component data for an entity.
Definition manager.hpp:1013
bool TryRemoveEntity(Entity entity)
Tries to remove all component data for an entity.
Definition manager.hpp:1035
void AddArchetypeComponents(Entity entity, Ts &&... components)
Adds multiple archetype-stored components to an entity.
Definition manager.hpp:1087
void ClearData() noexcept
Clears all component data and archetypes but retains metadata registrations.
Definition manager.hpp:977
Unique identifier for entities with generation counter to handle recycling.
Definition entity.hpp:22
constexpr bool Valid() const noexcept
Checks if the entity is valid.
Definition entity.hpp:64
constexpr IndexType Index() const noexcept
Gets the index component of the entity.
Definition entity.hpp:82
Concrete sparse-set storage for a single component type (non-polymorphic).
static constexpr TypeIndex From() noexcept
Constructs a TypeIndex from a type T.
Concept for components that are stored in an archetype.
Concept to check if a type can be used as a component.
Definition component.hpp:31
Concept for components that are stored in a sparse set.
Concept to check if the type is considered a tag component.
Definition component.hpp:43
Concept that checks if all types in a pack are unique (after removing cv/ref qualifiers).
ComponentStorageType
Storage type for components.
Definition component.hpp:20
@ kArchetype
Component is stored in an archetype.
Definition component.hpp:21
consteval ComponentStorageType ComponentStorageTypeOf() noexcept
Component storage type.
constexpr std::string_view ComponentNameOf() noexcept
Component name for debugging and serialization.
Definition component.hpp:76
utils::TypeIndex ComponentTypeIndex
Type index for components.
Definition component.hpp:14
STL namespace.
Type-erased entry for sparse-set component storage.
Definition manager.hpp:695
auto As() const noexcept -> const SparseComponentStorage< T > &
Gets the typed storage (const).
Definition manager.hpp:818
bool Contains(Entity entity) const noexcept
Checks if the component for the given entity exists.
Definition manager.hpp:779
static SparseStorageEntry From()
Creates a SparseStorageEntry for a specific component type T.
Definition manager.hpp:725
auto As() noexcept -> SparseComponentStorage< T > &
Gets the typed storage.
Definition manager.hpp:806
size_t Size() const noexcept
Gets the number of stored components.
Definition manager.hpp:791
void Clear() noexcept
Clears the underlying storage.
Definition manager.hpp:753
bool(*)(const container::TypedBuffer<> &storage, Entity entity) ContainsFn
Definition manager.hpp:699
bool TryRemove(Entity entity)
Tries to remove the component for the given entity.
Definition manager.hpp:765
SparseStorageEntry(const SparseStorageEntry &)=delete
void(*)(container::TypedBuffer<> &storage) ClearFn
Definition manager.hpp:696
constexpr SparseStorageEntry(SparseStorageEntry &&) noexcept=default
bool(*)(container::TypedBuffer<> &storage, Entity entity) TryRemoveFn
Definition manager.hpp:697
size_t(*)(const container::TypedBuffer<> &storage) SizeFn
Definition manager.hpp:701
Runtime metadata for a registered component type.
Definition manager.hpp:42
ComponentTypeIndex type_index
Definition manager.hpp:43
MoveColumnElementFn move_column_element
Definition manager.hpp:65
static consteval ComponentMetadata From() noexcept
Creates metadata for a component type.
Definition manager.hpp:78
void(*)(container::TypedBufferArray<> &) InitColumnFn
Type-erased function to initialize a column with the correct concrete type.
Definition manager.hpp:52
void(*)(container::TypedBufferArray<> &dst, container::TypedBufferArray<> &src, size_t src_row) MoveColumnElementFn
Type-erased function to move one element from a source column row into the back of a target column.
Definition manager.hpp:56
void(*)(container::TypedBufferArray<> &) DefaultPushFn
Type-erased function to push a default-constructed element onto a column.
Definition manager.hpp:62
ComponentStorageType storage_type
Definition manager.hpp:47