Helios Engine 0.1.0
A modular ECS based data-oriented C++23 game engine
 
Loading...
Searching...
No Matches
entity_command_buffer.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <helios/core_pch.hpp>
4
13
14#include <concepts>
15#include <memory>
16#include <type_traits>
17#include <utility>
18#include <vector>
19
20namespace helios::ecs {
21
22// Forward declaration
23class World;
24
25/**
26 * @brief Command buffer to record entity operations for deferred execution.
27 * @details Provides a convenient interface for recording operations on a specific entity
28 * that will be executed during World::Update(). All operations are queued locally and
29 * flushed to SystemLocalStorage on destruction or explicit Flush() call.
30 *
31 * Supports custom allocators for internal storage, enabling use of frame allocators
32 * for zero-overhead temporary allocations within systems.
33 *
34 * @note Not thread-safe. Created per system.
35 * @tparam Allocator Allocator type for internal command pointer storage
36 *
37 * @example
38 * @code
39 * void MySystem(app::SystemContext& ctx) {
40 * auto cmd = ctx.EntityCommands(entity); // Uses frame allocator by default
41 * cmd.AddComponent(Position{1.0F, 2.0F, 3.0F});
42 * cmd.RemoveComponent<Velocity>();
43 * // Commands are flushed automatically when cmd goes out of scope
44 * }
45 * @endcode
46 */
47template <typename Allocator = std::allocator<std::unique_ptr<Command>>>
49public:
50 using allocator_type = Allocator;
51
52 /**
53 * @brief Creates command buffer for a new reserved entity from a World.
54 * @details Reserves an entity ID and creates a command buffer for operations on it.
55 * The entity will be created when commands are flushed during World::Update().
56 * @param world World to reserve entity in
57 * @param local_storage Reference to system local storage
58 * @param alloc Allocator instance for internal storage
59 * @return EntityCmdBuffer for the reserved entity
60 *
61 * @example
62 * @code
63 * auto cmd = EntityCmdBuffer<>::FromWorld(world, local_storage);
64 * cmd.AddComponent(Position{1.0F, 2.0F, 3.0F});
65 * @endcode
66 */
68 Allocator alloc = Allocator{});
69
70 /**
71 * @brief Creates command buffer for entity operations on existing entity.
72 * @details Creates command buffer for operations on the specified entity.
73 * @warning Triggers assertion if entity is invalid.
74 * @param entity Entity to operate on
75 * @param local_storage Reference to system local storage
76 * @param alloc Allocator instance for internal storage
77 * @return EntityCmdBuffer for the existing entity
78 *
79 * @example
80 * @code
81 * Entity entity = world.CreateEntity();
82 * auto cmd = EntityCmdBuffer<>::FromEntity(entity, local_storage);
83 * cmd.AddComponent(Position{1.0F, 2.0F, 3.0F});
84 * @endcode
85 */
87 Allocator alloc = Allocator{});
88
89 /**
90 * @brief Creates command buffer for a new reserved entity.
91 * @details Reserves an entity ID and creates a command buffer for operations on it.
92 * The entity will be created when commands are flushed during World::Update().
93 * @param world World to reserve entity in
94 * @param local_storage Reference to system local storage
95 * @param alloc Allocator instance for internal storage
96 */
97 EntityCmdBuffer(World& world, details::SystemLocalStorage& local_storage, Allocator alloc = Allocator{});
98
99 /**
100 * @brief Creates command buffer for entity operations on existing entity.
101 * @details Creates command buffer for operations on the specified entity.
102 * @warning Triggers assertion if entity is invalid.
103 * @param entity Entity to operate on
104 * @param local_storage Reference to system local storage
105 * @param alloc Allocator instance for internal storage
106 */
107 EntityCmdBuffer(Entity entity, details::SystemLocalStorage& local_storage, Allocator alloc = Allocator{});
110
111 /**
112 * @brief Destructor that automatically flushes pending commands.
113 */
115
118
119 /**
120 * @brief Flushes all pending commands to the system local storage.
121 * @details Moves all locally buffered commands to SystemLocalStorage for later execution.
122 * Called automatically by destructor. Safe to call multiple times.
123 */
124 void Flush() noexcept;
125
126 /**
127 * @brief Destroys entity and removes it from the world.
128 * @details Queues command to destroy the entity and remove all its components.
129 * @warning Triggers assertion during command execution if entity does not exist.
130 * After execution of this command, the entity will become invalid.
131 */
132 void Destroy() { commands_.push_back(std::make_unique<details::DestroyEntityCmd>(entity_)); }
133
134 /**
135 * @brief Tries to destroy entity if it exists.
136 * @details Queues command to destroy the entity only if it currently exists in the world.
137 * If entity does not exist during command execution, the command is ignored.
138 * After execution of this command, the entity will become invalid if it existed.
139 */
140 void TryDestroy() { commands_.push_back(std::make_unique<details::TryDestroyEntityCmd>(entity_)); }
141
142 /**
143 * @brief Adds component to the entity.
144 * @details Queues command to add component to entity.
145 * If entity already has this component type, it will be replaced during command execution.
146 * @warning Triggers assertion during command execution if entity does not exist.
147 * @tparam T Component type to add
148 * @param component Component to add
149 */
150 template <ComponentTrait T>
152 commands_.push_back(
153 std::make_unique<details::AddComponentCmd<std::remove_cvref_t<T>>>(entity_, std::forward<T>(component)));
154 }
155
156 /**
157 * @brief Adds components to the entity.
158 * @details Queues command to add all specified components to entity in a single operation.
159 * If entity already has this component type, it will be replaced during command execution.
160 * More efficient than multiple AddComponent calls.
161 * @warning Triggers assertion during command execution if entity does not exist.
162 * @tparam Ts Component types to add (must be unique)
163 * @param components Components to add
164 */
165 template <ComponentTrait... Ts>
166 requires utils::UniqueTypes<Ts...>
168 commands_.push_back(std::make_unique<details::AddComponentsCmd<std::remove_cvref_t<Ts>...>>(
169 entity_, std::forward<Ts>(components)...));
170 }
171
172 /**
173 * @brief Tries to add a component to the entity.
174 * @details Queues command that will add the component only if the entity does not
175 * already have one during command execution.
176 * @warning Triggers assertion during command execution if entity does not exist.
177 * @tparam T Component type to add
178 * @param component Component instance to add
179 */
180 template <ComponentTrait T>
182 commands_.push_back(
183 std::make_unique<details::TryAddComponentCmd<std::remove_cvref_t<T>>>(entity_, std::forward<T>(component)));
184 }
185
186 /**
187 * @brief Tries to add multiple components to the entity.
188 * @details Queues command that attempts to add all provided components.
189 * Any component types already present are ignored during command execution.
190 * No per-component success flags are returned—inspection after World::Update() should be done via HasComponent().
191 * @warning Triggers assertion during command execution if entity does not exist.
192 * @tparam Ts Component types to add (must be unique)
193 * @param components Component instances to add
194 */
195 template <ComponentTrait... Ts>
196 requires utils::UniqueTypes<Ts...>
198 commands_.push_back(std::make_unique<details::TryAddComponentsCmd<std::remove_cvref_t<Ts>...>>(
199 entity_, std::forward<Ts>(components)...));
200 }
201
202 /**
203 * @brief Emplaces and adds component to the entity.
204 * @details Queues command that will construct and add the component to the entity.
205 * @warning Triggers assertion during command execution if entity does not exist.
206 * @tparam T Component type to construct and add
207 * @tparam Args Argument types for T's constructor
208 * @param args Arguments perfectly forwarded to the component constructor
209 */
210 template <ComponentTrait T, typename... Args>
211 requires std::constructible_from<T, Args...>
212 void EmplaceComponent(Args&&... args) {
213 commands_.push_back(std::make_unique<details::AddComponentCmd<T>>(entity_, T{std::forward<Args>(args)...}));
214 }
215
216 /**
217 * @brief Tries to emplace and add component to the entity.
218 * @details Queues command that will construct and add the component to the entity.
219 * @warning Triggers assertion during command execution if entity does not exist.
220 * @tparam T Component type to construct and add
221 * @tparam Args Argument types for T's constructor
222 * @param args Arguments to forward to component constructor
223 */
224 template <ComponentTrait T, typename... Args>
225 requires std::constructible_from<T, Args...>
226 void TryEmplaceComponent(Args&&... args) {
227 commands_.push_back(std::make_unique<details::TryAddComponentCmd<T>>(entity_, T{std::forward<Args>(args)...}));
228 }
229
230 /**
231 * @brief Removes component from the entity.
232 * @details Queues command to remove the specified component type from entity.
233 * @warning Triggers assertion during command execution in next cases:
234 * - Entity does not exist.
235 * - Entity does not have the specified component.
236 * @tparam T Component type to remove
237 */
238 template <ComponentTrait T>
240 commands_.push_back(std::make_unique<details::RemoveComponentCmd<T>>(entity_));
241 }
242
243 /**
244 * @brief Removes components from the entity.
245 * @details Queues command to remove all specified component types from entity.
246 * More efficient than multiple RemoveComponent calls.
247 * @warning Triggers assertion during command execution in next cases:
248 * - Entity does not exist.
249 * - Entity lacks any of the specified components.
250 * @tparam Ts Component types to remove (must be unique)
251 */
252 template <ComponentTrait... Ts>
253 requires utils::UniqueTypes<Ts...>
255 commands_.push_back(std::make_unique<details::RemoveComponentsCmd<Ts...>>(entity_));
256 }
257
258 /**
259 * @brief Tries to remove a component from the entity.
260 * @details Queues command that attempts to remove the specified component type.
261 * If the entity lacks the component during command execution the command does nothing.
262 * @warning Triggers assertion during command execution if entity does not exist.
263 * @tparam T Component type to remove
264 */
265 template <ComponentTrait T>
267 commands_.push_back(std::make_unique<details::TryRemoveComponentCmd<T>>(entity_));
268 }
269
270 /**
271 * @brief Tries to remove multiple components from the entity (idempotent).
272 * @details Queues a deferred command that attempts to remove all specified component types.
273 * Any components not present are ignored silently.
274 * @warning Triggers assertion during command execution if entity does not exist.
275 * @tparam Ts Component types to remove (must be unique)
276 */
277 template <ComponentTrait... Ts>
278 requires utils::UniqueTypes<Ts...>
280 commands_.push_back(std::make_unique<details::TryRemoveComponentsCmd<Ts...>>(entity_));
281 }
282
283 /**
284 * @brief Removes all components from the entity.
285 * @details Queues command to remove all components from entity, leaving it with no components.
286 * The entity itself will remain valid unless explicitly destroyed.
287 * @warning Triggers assertion during command execution if entity does not exist.
288 */
289 void ClearComponents() { commands_.push_back(std::make_unique<details::ClearComponentsCmd>(entity_)); }
290
291 /**
292 * @brief Checks if there are no pending commands.
293 * @return True if no commands are buffered, false otherwise
294 */
295 [[nodiscard]] bool Empty() const noexcept { return commands_.empty(); }
296
297 /**
298 * @brief Gets the number of pending commands.
299 * @return Number of commands in the buffer
300 */
301 [[nodiscard]] size_t Size() const noexcept { return commands_.size(); }
302
303 /**
304 * @brief Gets the entity this command buffer operates on.
305 * @return Entity that this command buffer targets
306 */
307 [[nodiscard]] Entity GetEntity() const noexcept { return entity_; }
308
309 /**
310 * @brief Gets the allocator used by this buffer.
311 * @return Copy of the allocator
312 */
314
315private:
316 Entity entity_; ///< Entity this command buffer operates on
317 details::SystemLocalStorage& local_storage_; ///< Reference to system local storage
318 std::vector<std::unique_ptr<Command>, Allocator> commands_; ///< Local command buffer
319 [[no_unique_address]] Allocator alloc_; ///< Allocator instance
320};
321
322template <typename Allocator>
324 Allocator alloc) -> EntityCmdBuffer {
325 return EntityCmdBuffer(world, local_storage, std::move(alloc));
326}
327
328template <typename Allocator>
330 Allocator alloc) -> EntityCmdBuffer {
331 return EntityCmdBuffer(entity, local_storage, std::move(alloc));
332}
333
334template <typename Allocator>
336 Allocator alloc)
337 : entity_(world.ReserveEntity()), local_storage_(local_storage), commands_(alloc), alloc_(alloc) {}
338
339template <typename Allocator>
341 Allocator alloc)
342 : entity_(entity), local_storage_(local_storage), commands_(alloc), alloc_(alloc) {
343 HELIOS_ASSERT(entity.Valid(), "Failed to construct entity command buffer: Entity is invalid!");
344 // Note: We don't check world.Exists(entity) here because this constructor is also used
345 // for reserved entities (via ReserveEntity()), which don't exist in the world yet.
346 // The entity will be created when commands are flushed during World::Update().
347}
348
349template <typename Allocator>
351 for (auto& cmd : commands_) {
352 if (cmd != nullptr) {
353 local_storage_.AddCommand(std::move(cmd));
354 }
355 }
356 commands_.clear();
357}
358
359} // namespace helios::ecs
#define HELIOS_ASSERT(condition,...)
Assertion macro that aborts execution in debug builds.
Definition assert.hpp:140
void TryAddComponent(T &&component)
Tries to add a component to the entity.
void TryAddComponents(Ts &&... components)
Tries to add multiple components to the entity.
void Destroy()
Destroys entity and removes it from the world.
EntityCmdBuffer(EntityCmdBuffer &&) noexcept=default
void TryDestroy()
Tries to destroy entity if it exists.
void RemoveComponents()
Removes components from the entity.
void RemoveComponent()
Removes component from the entity.
void AddComponents(Ts &&... components)
Adds components to the entity.
allocator_type get_allocator() const noexcept
Gets the allocator used by this buffer.
static EntityCmdBuffer FromWorld(World &world, details::SystemLocalStorage &local_storage, Allocator alloc=Allocator{})
bool Empty() const noexcept
Checks if there are no pending commands.
void ClearComponents()
Removes all components from the entity.
void EmplaceComponent(Args &&... args)
Emplaces and adds component to the entity.
EntityCmdBuffer & operator=(const EntityCmdBuffer &)=delete
size_t Size() const noexcept
Gets the number of pending commands.
Entity GetEntity() const noexcept
Gets the entity this command buffer operates on.
EntityCmdBuffer(World &world, details::SystemLocalStorage &local_storage, Allocator alloc=Allocator{})
Creates command buffer for a new reserved entity.
void AddComponent(T &&component)
Adds component to the entity.
void TryRemoveComponent()
Tries to remove a component from the entity.
EntityCmdBuffer(const EntityCmdBuffer &)=delete
void Flush() noexcept
Flushes all pending commands to the system local storage.
void TryEmplaceComponent(Args &&... args)
Tries to emplace and add component to the entity.
EntityCmdBuffer & operator=(EntityCmdBuffer &&) noexcept=default
static EntityCmdBuffer FromEntity(Entity entity, details::SystemLocalStorage &local_storage, Allocator alloc=Allocator{})
void TryRemoveComponents()
Tries to remove multiple components from the entity (idempotent).
Unique identifier for entities with generation counter to handle recycling.
Definition entity.hpp:21
constexpr bool Valid() const noexcept
Checks if the entity is valid.
Definition entity.hpp:58
The World class manages entities with their components and systems.
Definition world.hpp:53
Command to add a component to an entity.
Definition commands.hpp:225
Command to add multiple components to an entity.
Definition commands.hpp:283
Command to remove a component from an entity.
Definition commands.hpp:443
Command to remove multiple components from an entity.
Definition commands.hpp:481
Local storage for system-specific data (commands, events, and temporary allocations).
Command to try add a component (only if missing).
Definition commands.hpp:331
Command to try add multiple components (only missing ones).
Definition commands.hpp:388
Command to try remove a single component (only if present).
Definition commands.hpp:518
Command to try remove multiple components (only those present).
Definition commands.hpp:555
Concept to check if a type can be used as a component.
Definition component.hpp:39
BasicQuery< World, Allocator, Components... > Query
Type alias for query with mutable world access.
Definition query.hpp:2481