oxygengine_core/ecs/
commands.rs

1use crate::{
2    app::AppLifeCycle,
3    ecs::{life_cycle::EntityChanges, Universe},
4    prefab::PrefabManager,
5};
6pub use hecs::*;
7use std::{any::TypeId, collections::HashMap, marker::PhantomData};
8
9pub trait UniverseCommand: Send + Sync {
10    fn run(&mut self, universe: &mut Universe);
11}
12
13struct ClosureUniverseCommand(Box<dyn FnMut(&mut Universe) + Send + Sync>);
14
15impl UniverseCommand for ClosureUniverseCommand {
16    fn run(&mut self, universe: &mut Universe) {
17        (self.0)(universe);
18    }
19}
20
21pub struct SpawnEntity {
22    pub entity_builder: EntityBuilder,
23    #[allow(clippy::type_complexity)]
24    on_complete: Option<Box<dyn FnOnce(&mut Universe, Entity) + Send + Sync>>,
25}
26
27impl SpawnEntity {
28    pub fn new(entity_builder: EntityBuilder) -> Self {
29        Self {
30            entity_builder,
31            on_complete: None,
32        }
33    }
34
35    pub fn from_bundle<B>(bundle: B) -> Self
36    where
37        B: DynamicBundle,
38    {
39        let mut entity_builder = EntityBuilder::new();
40        entity_builder.add_bundle(bundle);
41        Self::new(entity_builder)
42    }
43
44    pub fn on_complete<F>(mut self, f: F) -> Self
45    where
46        F: FnOnce(&mut Universe, Entity) + Send + Sync + 'static,
47    {
48        self.on_complete = Some(Box::new(f));
49        self
50    }
51
52    pub fn execute(&mut self, universe: &mut Universe) -> Entity {
53        let entity = universe.world_mut().spawn(self.entity_builder.build());
54        let mut changes = universe.expect_resource_mut::<EntityChanges>();
55        changes.spawned.insert(entity);
56        let components = changes.added_components.entry(entity).or_default();
57        components.extend(self.entity_builder.component_types());
58        if let Some(on_complete) = self.on_complete.take() {
59            (on_complete)(universe, entity);
60        }
61        entity
62    }
63}
64
65impl UniverseCommand for SpawnEntity {
66    fn run(&mut self, universe: &mut Universe) {
67        self.execute(universe);
68    }
69}
70
71pub struct DespawnEntity(pub Entity);
72
73impl UniverseCommand for DespawnEntity {
74    fn run(&mut self, universe: &mut Universe) {
75        if universe.world_mut().despawn(self.0).is_ok() {
76            universe
77                .expect_resource_mut::<EntityChanges>()
78                .despawned
79                .insert(self.0);
80        }
81    }
82}
83
84pub struct EntityAddComponent<C>
85where
86    C: Component + Send + Sync,
87{
88    pub entity: Entity,
89    component: Option<C>,
90    #[allow(clippy::type_complexity)]
91    on_complete: Option<Box<dyn FnMut(&mut Universe, Entity) + Send + Sync>>,
92}
93
94impl<C> EntityAddComponent<C>
95where
96    C: Component + Send + Sync,
97{
98    pub fn new(entity: Entity, component: C) -> Self {
99        Self {
100            entity,
101            component: Some(component),
102            on_complete: None,
103        }
104    }
105
106    pub fn on_complete<F>(mut self, f: F) -> Self
107    where
108        F: FnMut(&mut Universe, Entity) + Send + Sync + 'static,
109    {
110        self.on_complete = Some(Box::new(f));
111        self
112    }
113}
114
115impl<C> UniverseCommand for EntityAddComponent<C>
116where
117    C: Component + Send + Sync + 'static,
118{
119    fn run(&mut self, universe: &mut Universe) {
120        if let Some(component) = self.component.take() {
121            if universe
122                .world_mut()
123                .insert_one(self.entity, component)
124                .is_ok()
125            {
126                let mut changes = universe.expect_resource_mut::<EntityChanges>();
127                let components = changes.added_components.entry(self.entity).or_default();
128                components.insert(TypeId::of::<C>());
129                if let Some(mut on_complete) = self.on_complete.take() {
130                    (on_complete)(universe, self.entity);
131                }
132            }
133        }
134    }
135}
136
137pub struct EntityRemoveComponent<C>
138where
139    C: Component,
140{
141    pub entity: Entity,
142    #[allow(clippy::type_complexity)]
143    on_complete: Option<Box<dyn FnMut(&mut Universe, Entity) + Send + Sync>>,
144    _phantom: PhantomData<fn() -> C>,
145}
146
147impl<C> EntityRemoveComponent<C>
148where
149    C: Component,
150{
151    pub fn new(entity: Entity) -> Self {
152        Self {
153            entity,
154            on_complete: None,
155            _phantom: Default::default(),
156        }
157    }
158
159    pub fn on_complete<F>(mut self, f: F) -> Self
160    where
161        F: FnMut(&mut Universe, Entity) + Send + Sync + 'static,
162    {
163        self.on_complete = Some(Box::new(f));
164        self
165    }
166}
167
168impl<C> UniverseCommand for EntityRemoveComponent<C>
169where
170    C: Component,
171{
172    fn run(&mut self, universe: &mut Universe) {
173        if universe.world_mut().remove_one::<C>(self.entity).is_ok() {
174            let mut changes = universe.expect_resource_mut::<EntityChanges>();
175            let components = changes.removed_components.entry(self.entity).or_default();
176            components.insert(TypeId::of::<C>());
177            if let Some(mut on_complete) = self.on_complete.take() {
178                (on_complete)(universe, self.entity);
179            }
180        }
181    }
182}
183
184/// (template name, add/override components)
185pub struct InstantiatePrefab {
186    pub name: String,
187    pub components: HashMap<usize, EntityBuilder>,
188    #[allow(clippy::type_complexity)]
189    on_complete: Option<Box<dyn FnMut(&mut Universe, &[Entity]) + Send + Sync>>,
190}
191
192impl InstantiatePrefab {
193    pub fn new(name: impl ToString) -> Self {
194        Self {
195            name: name.to_string(),
196            components: Default::default(),
197            on_complete: None,
198        }
199    }
200
201    pub fn components(mut self, index: usize, entity_builder: EntityBuilder) -> Self {
202        self.components.insert(index, entity_builder);
203        self
204    }
205
206    pub fn components_from_bundle<B>(mut self, index: usize, bundle: B) -> Self
207    where
208        B: DynamicBundle,
209    {
210        let mut entity_builder = EntityBuilder::new();
211        entity_builder.add_bundle(bundle);
212        self.components.insert(index, entity_builder);
213        self
214    }
215
216    pub fn on_complete<F>(mut self, f: F) -> Self
217    where
218        F: FnMut(&mut Universe, &[Entity]) + Send + Sync + 'static,
219    {
220        self.on_complete = Some(Box::new(f));
221        self
222    }
223
224    pub fn execute(&mut self, universe: &mut Universe) -> Option<Vec<Entity>> {
225        if let Some(mut prefabs) = universe.resource_mut::<PrefabManager>() {
226            let mut world = universe.world_mut();
227            let mut changes = universe.expect_resource_mut::<EntityChanges>();
228            let state_token = universe
229                .expect_resource::<AppLifeCycle>()
230                .current_state_token();
231            let entities = if let Ok(entities) =
232                prefabs.instantiate_direct(&self.name, &mut world, &mut changes, state_token)
233            {
234                for (index, entity_builder) in &mut self.components {
235                    if let Some(entity) = entities.get(*index) {
236                        let _ = world.insert(*entity, entity_builder.build());
237                    }
238                }
239                entities
240            } else {
241                return None;
242            };
243            drop(world);
244            if let Some(mut on_complete) = self.on_complete.take() {
245                (on_complete)(universe, &entities);
246            }
247            Some(entities)
248        } else {
249            None
250        }
251    }
252}
253
254impl UniverseCommand for InstantiatePrefab {
255    fn run(&mut self, universe: &mut Universe) {
256        self.execute(universe);
257    }
258}
259
260pub struct UniverseCommands {
261    queue: Vec<Box<dyn UniverseCommand>>,
262    resize: usize,
263}
264
265impl Default for UniverseCommands {
266    fn default() -> Self {
267        Self {
268            queue: Default::default(),
269            resize: 1024,
270        }
271    }
272}
273
274impl UniverseCommands {
275    pub fn schedule<T>(&mut self, command: T)
276    where
277        T: UniverseCommand + 'static,
278    {
279        self.schedule_raw(Box::new(command));
280    }
281
282    pub fn schedule_raw(&mut self, command: Box<dyn UniverseCommand>) {
283        if self.resize > 0 && self.queue.len() == self.queue.capacity() {
284            self.queue.reserve(self.resize);
285        }
286        self.queue.push(command);
287    }
288
289    pub fn schedule_fn<F>(&mut self, f: F)
290    where
291        F: FnMut(&mut Universe) + Send + Sync + 'static,
292    {
293        self.schedule(ClosureUniverseCommand(Box::new(f)));
294    }
295
296    pub fn execute(&mut self) -> UniverseCommandsExecutor {
297        let commands = Vec::with_capacity(self.queue.len());
298        UniverseCommandsExecutor(std::mem::replace(&mut self.queue, commands))
299    }
300}
301
302pub struct UniverseCommandsExecutor(Vec<Box<dyn UniverseCommand>>);
303
304impl UniverseCommandsExecutor {
305    pub fn execute(self, universe: &mut Universe) {
306        for mut command in self.0 {
307            command.run(universe);
308        }
309    }
310}