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}