hyperion/commands.rs
1use crate::component_storage::Component;
2use crate::entity::Entity;
3use crate::world::World;
4
5/// Represents a deferred ECS operation that is applied later by a `CommandBuffer`.
6///
7/// These commands encapsulate world mutations (creating/destroying entities, adding/removing
8/// components) and are executed in order by [`CommandBuffer::execute`].
9///
10/// # Variants
11/// - `CreateEntity`: Create a new entity when executed.
12/// - `DestroyEntity(Entity)`: Destroy the specified entity when executed.
13/// - `AddComponent(Box<dyn FnOnce(&mut World)>)`: Invoke the closure to add a component to an entity.
14/// - `RemoveComponent(Box<dyn FnOnce(&mut World)>)`: Invoke the closure to remove a component from an entity.
15/// - `CreateEntityAndAddComponent(Box<dyn FnOnce(&mut World) -> Entity>)`: Invoke the closure which creates
16/// an entity and attaches a component, returning the new `Entity` to the executor.
17///
18/// # Safety
19/// - Commands are executed sequentially via [`CommandBuffer::execute`] with an exclusive `&mut World`,
20/// preventing aliasing violations during application.
21enum Command {
22 /// Creates a new entity in the world.
23 CreateEntity,
24
25 /// Destroys the specified entity and removes all of its components.
26 ///
27 /// # Parameters
28 /// - `Entity`: The entity to destroy when the command is executed.
29 DestroyEntity(Entity),
30
31 /// Defers adding a component to an entity by invoking the stored closure at execution time.
32 ///
33 /// # Parameters
34 /// - `Box<dyn FnOnce(&mut World)>`: A closure that, when called, performs the addition
35 /// (typically `world.add_component(entity, component)`).
36 AddComponent(Box<dyn FnOnce(&mut World)>),
37
38 /// Defers removing a component of some type from an entity by invoking the stored closure.
39 ///
40 /// # Parameters
41 /// - `Box<dyn FnOnce(&mut World)>`: A closure that, when called, performs the removal
42 /// (typically `world.get_storage_mut::<T>().map(|s| s.remove(entity))`).
43 RemoveComponent(Box<dyn FnOnce(&mut World)>),
44 /// Creates a new entity and immediately adds a component via a deferred closure.
45 ///
46 /// # Returns
47 /// - `Entity`: The newly created entity returned to the executor when the command runs.
48 ///
49 /// # Parameters
50 /// - `Box<dyn FnOnce(&mut World) -> Entity>`: Closure that creates the entity and adds its component.
51 CreateEntityAndAddComponent(Box<dyn FnOnce(&mut World) -> Entity>),
52}
53
54/// A buffer of deferred ECS commands such as entity creation, component addition/removal, and destruction.
55///
56/// This is useful for queuing up changes during system execution to avoid mutably borrowing the world
57/// while it's being accessed elsewhere. Changes can be applied all at once using [`CommandBuffer::execute`].
58///
59/// # Fields
60/// - `commands`: The queued operations to apply.
61///
62/// # Safety
63/// - Commands are applied with exclusive access to the `World` to ensure memory safety.
64#[derive(Default)]
65pub struct CommandBuffer {
66 commands: Vec<Command>,
67}
68
69impl CommandBuffer {
70 /// Clears the buffer
71 pub fn clear(&mut self) {
72 self.commands.clear();
73 }
74
75 /// Queues a command to create a new entity in the world.
76 /// The entity will be returned by `execute` when the command is applied.
77 pub fn create_entity(&mut self) {
78 self.commands.push(Command::CreateEntity);
79 }
80
81 /// Queues a command to destroy an entity.
82 /// This will remove all of its components and make its ID reusable.
83 ///
84 /// # Parameters
85 /// - `entity`: The entity to be destroyed.
86 pub fn destroy_entity(&mut self, entity: Entity) {
87 self.commands.push(Command::DestroyEntity(entity));
88 }
89
90 /// Queues a command to add a component to an entity.
91 /// The command is deferred to avoid borrowing issues during system execution.
92 ///
93 /// # Type Parameters
94 /// - `T`: The component type.
95 ///
96 /// # Parameters
97 /// - `entity`: The entity to receive the component.
98 /// - `component`: The component instance to be added.
99 pub fn add_component<T: Component + Send + 'static>(&mut self, entity: Entity, component: T) {
100 self.commands
101 .push(Command::AddComponent(Box::new(move |world| {
102 world.add_component(entity, component);
103 })));
104 }
105
106 /// Queues a command to remove a component of type `T` from an entity.
107 ///
108 /// # Type Parameters
109 /// - `T`: The component type to remove.
110 ///
111 /// # Parameters
112 /// - `entity`: The entity from which to remove the component.
113 pub fn remove_component<T: Component + 'static>(&mut self, entity: Entity) {
114 self.commands
115 .push(Command::RemoveComponent(Box::new(move |world| {
116 if let Some(storage) = world.get_storage_mut::<T>() {
117 storage.remove(entity);
118 }
119 })));
120 }
121
122 /// Queues a command to create a new entity and immediately add a component to it.
123 ///
124 /// # Type Parameters
125 /// - `T`: The component type.
126 ///
127 /// # Parameters
128 /// - `component`: The component instance to be added to the new entity.
129 pub fn create_entity_and_add_component<T: Component + Send + 'static>(&mut self, component: T) {
130 self.commands
131 .push(Command::CreateEntityAndAddComponent(Box::new(
132 move |world| world.create_entity_and_add_component(component),
133 )));
134 }
135
136 /// Executes all queued commands in order, applying them to the world.
137 ///
138 /// Returns a vector of entities created via `create_entity` for further tracking.
139 ///
140 /// # Parameters
141 /// - `world`: The world to apply the commands to.
142 pub fn execute(&mut self, world: &mut World) -> Vec<Entity> {
143 let mut created_entities = Vec::new();
144
145 for cmd in self.commands.drain(..) {
146 match cmd {
147 Command::CreateEntity => {
148 let e = world.create_entity();
149 created_entities.push(e);
150 }
151 Command::DestroyEntity(e) => {
152 world.destroy_entity(e);
153 }
154 Command::AddComponent(f) => {
155 f(world);
156 }
157 Command::RemoveComponent(f) => {
158 f(world);
159 }
160 Command::CreateEntityAndAddComponent(f) => {
161 let e = f(world);
162 created_entities.push(e);
163 }
164 }
165 }
166
167 created_entities
168 }
169}