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
184pub 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}