Skip to main content

engine/ecs/EntityRegistry.ixx File

Defines the EntityRegistry class for managing entity lifecycles. More...

Included Headers

#include <span> #include <vector> #include <memory> #include <helios.engine.common.types.VersionId> #include <helios.engine.ecs.types> #include <helios.engine.ecs.EntityHandle>

Namespaces Index

namespacehelios
namespaceengine

Main engine module aggregating core infrastructure and game systems. More...

namespaceecs

Core Entity-Component-System architecture. More...

Classes Index

classEntityRegistry

Central registry for creating and managing entity handles. More...

Description

Defines the EntityRegistry class for managing entity lifecycles.

File Listing

The file content with the documentation metadata removed is:

1/**
2 * @file EntityRegistry.ixx
3 * @brief Defines the EntityRegistry class for managing entity lifecycles.
4 */
5
6module;
7
8#include <span>
9#include <vector>
10#include <memory>
11
12
13export module helios.engine.ecs.EntityRegistry;
14
15import helios.engine.ecs.EntityHandle;
16import helios.engine.ecs.types;
17import helios.engine.common.types.VersionId;
18
19export namespace helios::engine::ecs {
20
21
22 /**
23 * @brief The initial version assigned to newly created entities.
24 *
25 * Versions start at 1 to distinguish valid handles from default-initialized handles
26 * that may have a version of 0
27 */
29
30 /**
31 * @brief Central registry for creating and managing entity handles.
32 *
33 * The `EntityRegistry` serves as the authoritative source for entity lifecycle
34 * management in the ECS architecture. It is responsible for:
35 *
36 * - **Handle Creation:** Generates unique `EntityHandle` instances with versioned IDs.
37 * - **Validation:** Determines whether a given handle refers to a currently alive entity.
38 * - **Destruction:** Marks entities as destroyed by incrementing their version and
39 * recycling the index for future use.
40 *
41 * ## Versioning
42 *
43 * Each entity slot maintains a version number. When an entity is destroyed, its
44 * version is incremented. This allows the registry to detect **stale handles** -
45 * handles that reference an entity that has been destroyed and potentially replaced
46 * by a new entity at the same index.
47 *
48 * ## Index Recycling
49 *
50 * Destroyed entity indices are added to a free list and reused when creating new
51 * entities. This keeps the dense version array compact and minimizes memory growth.
52 *
53 * ## Usage Example
54 *
55 * ```cpp
56 * helios::engine::ecs::EntityRegistry registry;
57 *
58 * // Create a new entity
59 * auto handle = registry.create();
60 *
61 * // Check if the handle is valid
62 * if (registry.isValid(handle)) {
63 * // Entity is alive, safe to use
64 * }
65 *
66 * // Destroy the entity
67 * registry.destroy(handle);
68 *
69 * // The handle is now stale
70 * assert(!registry.isValid(handle));
71 * ```
72 *
73 * @see EntityHandle
74 * @see helios::engine::ecs::types::EntityId
75 * @see helios::engine::common::types::VersionId
76 */
78
79 /**
80 * @brief Free list of recycled entity indices available for reuse.
81 */
82 std::vector<helios::engine::ecs::types::EntityId> freeList_;
83
84 /**
85 * @brief Version numbers for each entity slot.
86 *
87 * The version is incremented when an entity at that index is destroyed,
88 * allowing detection of stale handles.
89 */
90 std::vector<helios::engine::common::types::VersionId> versions_;
91
92 public:
93
94 /**
95 * @brief Default constructor. Creates an empty registry.
96 */
97 EntityRegistry() = default;
98
99 /**
100 * @brief Constructs an EntityRegistry with pre-allocated capacity.
101 *
102 * Reserving capacity upfront can improve performance when the approximate
103 * number of entities is known in advance.
104 *
105 * @param capacity The initial capacity to reserve for the version vector.
106 */
107 explicit EntityRegistry(const size_t capacity) {
108 versions_.reserve(capacity);
109 }
110
111 /**
112 * @brief Creates a new entity and returns its handle.
113 *
114 * If recycled indices are available in the free list, one is reused.
115 * Otherwise, a new index is allocated at the end of the version vector.
116 *
117 * @return A valid `EntityHandle` for the newly created entity.
118 */
120
123
124 if (freeList_.empty()) {
125 idx = versions_.size();
127
128 versions_.push_back(version);
129
130 } else {
131 idx = freeList_.back();
132 freeList_.pop_back();
133
134 // version was already incremented in destroy
135 version = versions_[idx];
136 }
137
138
139 return {idx, version};
140
141 }
142
143 /**
144 * @brief looks up the version for an EntityId.
145 *
146 * This method takes an EntityId as the argument and looks up the corresponding
147 * version. If the EntityId is not part of the registry, 0 will be returned.
148 *
149 * @param entityId The entity to retrieve the version for.
150 *
151 * @return The version for the EntityId, or 0 if not found.
152 */
154 if (entityId >= versions_.size()) {
155 return 0;
156 }
157 return versions_[entityId];
158 }
159
160 /**
161 * @brief Checks whether the given handle refers to a currently alive entity.
162 *
163 * A handle is valid if its index is within bounds and its version matches
164 * the current version stored in the registry.
165 *
166 * @param handle The handle to validate.
167 *
168 * @return `true` if the handle is valid and the entity is alive,
169 * `false` otherwise.
170 */
171 [[nodiscard]] bool isValid(const EntityHandle handle) const noexcept {
172
173 const auto index = handle.entityId;
174
175 if (index >= versions_.size()) {
176 return false;
177 }
178
179 return handle.versionId == versions_[index];
180 }
181
182 /**
183 * @brief Destroys the entity referenced by the given handle.
184 *
185 * If the handle is valid, the entity's version is incremented (invalidating
186 * all existing handles to it) and its index is added to the free list for
187 * recycling.
188 *
189 * @param handle The handle of the entity to destroy.
190 *
191 * @return `true` if the entity was successfully destroyed,
192 * `false` if the handle was already invalid.
193 */
194 bool destroy(const EntityHandle handle) {
195
196 if (!isValid(handle)) {
197 return false;
198 }
199
200 const auto index = handle.entityId;
201
202 versions_[index] += 1;
203
204 freeList_.push_back(index);
205
206 return true;
207 }
208
209 /**
210 * @brief Returns a view of the version vector.
211 *
212 * Provides read-write access to the internal version data. Useful for
213 * debugging or advanced use cases where direct version inspection is needed.
214 *
215 * @return A span over the version vector.
216 */
217 std::span<helios::engine::common::types::VersionId> version() noexcept {
218 return versions_;
219 }
220
221 };
222
223
224
225}

Generated via doxygen2docusaurus 2.0.0 by Doxygen 1.15.0.