anput/
universe.rs

1use crate::{
2    bundle::Bundle,
3    commands::CommandBuffer,
4    component::{Component, ComponentRef, ComponentRefMut},
5    entity::Entity,
6    processor::WorldProcessor,
7    query::{Lookup, Query, TypedLookupFetch, TypedQueryFetch},
8    resources::Resources,
9    systems::{System, SystemContext, Systems},
10    world::World,
11};
12use intuicio_core::{context::Context, registry::Registry};
13use intuicio_framework_serde::SerializationRegistry;
14use std::{error::Error, marker::PhantomData};
15
16pub trait UniverseFetch<'a> {
17    type Value;
18
19    fn fetch(universe: &'a Universe, system: Entity) -> Result<Self::Value, Box<dyn Error>>;
20}
21
22impl UniverseFetch<'_> for Entity {
23    type Value = Entity;
24
25    fn fetch(_: &Universe, entity: Entity) -> Result<Self::Value, Box<dyn Error>> {
26        Ok(entity)
27    }
28}
29
30impl<'a> UniverseFetch<'a> for &'a Universe {
31    type Value = &'a Universe;
32
33    fn fetch(universe: &'a Universe, _: Entity) -> Result<Self::Value, Box<dyn Error>> {
34        Ok(universe)
35    }
36}
37
38impl<'a> UniverseFetch<'a> for &'a World {
39    type Value = &'a World;
40
41    fn fetch(universe: &'a Universe, _: Entity) -> Result<Self::Value, Box<dyn Error>> {
42        Ok(&universe.simulation)
43    }
44}
45
46impl<'a> UniverseFetch<'a> for &'a Resources {
47    type Value = &'a Resources;
48
49    fn fetch(universe: &'a Universe, _: Entity) -> Result<Self::Value, Box<dyn Error>> {
50        Ok(&universe.resources)
51    }
52}
53
54impl<'a> UniverseFetch<'a> for &'a Systems {
55    type Value = &'a Systems;
56
57    fn fetch(universe: &'a Universe, _: Entity) -> Result<Self::Value, Box<dyn Error>> {
58        Ok(&universe.systems)
59    }
60}
61
62pub struct Res<const LOCKING: bool, T>(PhantomData<fn() -> T>);
63
64impl<'a, const LOCKING: bool, T: Component> UniverseFetch<'a> for Res<LOCKING, &'a T> {
65    type Value = ComponentRef<'a, LOCKING, T>;
66
67    fn fetch(universe: &'a Universe, _: Entity) -> Result<Self::Value, Box<dyn Error>> {
68        universe.resources.get()
69    }
70}
71
72impl<'a, const LOCKING: bool, T: Component> UniverseFetch<'a> for Res<LOCKING, &'a mut T> {
73    type Value = ComponentRefMut<'a, LOCKING, T>;
74
75    fn fetch(universe: &'a Universe, _: Entity) -> Result<Self::Value, Box<dyn Error>> {
76        universe.resources.get_mut()
77    }
78}
79
80impl<'a, const LOCKING: bool, T: Component> UniverseFetch<'a> for Res<LOCKING, Option<&'a T>> {
81    type Value = Option<ComponentRef<'a, LOCKING, T>>;
82
83    fn fetch(universe: &'a Universe, _: Entity) -> Result<Self::Value, Box<dyn Error>> {
84        Ok(universe.resources.get().ok())
85    }
86}
87
88impl<'a, const LOCKING: bool, T: Component> UniverseFetch<'a> for Res<LOCKING, Option<&'a mut T>> {
89    type Value = Option<ComponentRefMut<'a, LOCKING, T>>;
90
91    fn fetch(universe: &'a Universe, _: Entity) -> Result<Self::Value, Box<dyn Error>> {
92        Ok(universe.resources.get_mut().ok())
93    }
94}
95
96pub struct Local<const LOCKING: bool, T>(PhantomData<fn() -> T>);
97
98impl<'a, const LOCKING: bool, T: Component> UniverseFetch<'a> for Local<LOCKING, &'a T> {
99    type Value = ComponentRef<'a, LOCKING, T>;
100
101    fn fetch(universe: &'a Universe, system: Entity) -> Result<Self::Value, Box<dyn Error>> {
102        Ok(universe.systems.component(system)?)
103    }
104}
105
106impl<'a, const LOCKING: bool, T: Component> UniverseFetch<'a> for Local<LOCKING, &'a mut T> {
107    type Value = ComponentRefMut<'a, LOCKING, T>;
108
109    fn fetch(universe: &'a Universe, system: Entity) -> Result<Self::Value, Box<dyn Error>> {
110        Ok(universe.systems.component_mut(system)?)
111    }
112}
113
114impl<'a, const LOCKING: bool, T: Component> UniverseFetch<'a> for Local<LOCKING, Option<&'a T>> {
115    type Value = Option<ComponentRef<'a, LOCKING, T>>;
116
117    fn fetch(universe: &'a Universe, system: Entity) -> Result<Self::Value, Box<dyn Error>> {
118        Ok(universe.systems.component(system).ok())
119    }
120}
121
122impl<'a, const LOCKING: bool, T: Component> UniverseFetch<'a>
123    for Local<LOCKING, Option<&'a mut T>>
124{
125    type Value = Option<ComponentRefMut<'a, LOCKING, T>>;
126
127    fn fetch(universe: &'a Universe, system: Entity) -> Result<Self::Value, Box<dyn Error>> {
128        Ok(universe.systems.component_mut(system).ok())
129    }
130}
131
132impl<'a, const LOCKING: bool, Fetch: TypedQueryFetch<'a, LOCKING>> UniverseFetch<'a>
133    for Query<'a, LOCKING, Fetch>
134{
135    type Value = Query<'a, LOCKING, Fetch>;
136
137    fn fetch(_: &Universe, _: Entity) -> Result<Self::Value, Box<dyn Error>> {
138        Ok(Query::<LOCKING, Fetch>::default())
139    }
140}
141
142impl<'a, const LOCKING: bool, Fetch: TypedLookupFetch<'a, LOCKING>> UniverseFetch<'a>
143    for Lookup<'a, LOCKING, Fetch>
144{
145    type Value = Lookup<'a, LOCKING, Fetch>;
146
147    fn fetch(_: &Universe, _: Entity) -> Result<Self::Value, Box<dyn Error>> {
148        Ok(Lookup::<LOCKING, Fetch>::default())
149    }
150}
151
152macro_rules! impl_universe_fetch_tuple {
153    ($($type:ident),+) => {
154        impl<'a, $($type: UniverseFetch<'a>),+> UniverseFetch<'a> for ($($type,)+) {
155            type Value = ($($type::Value,)+);
156
157            fn fetch(universe: &'a Universe, entity: Entity) -> Result<Self::Value, Box<dyn Error>> {
158                Ok(($($type::fetch(universe, entity)?,)+))
159            }
160        }
161    };
162}
163
164impl_universe_fetch_tuple!(A);
165impl_universe_fetch_tuple!(A, B);
166impl_universe_fetch_tuple!(A, B, C);
167impl_universe_fetch_tuple!(A, B, C, D);
168impl_universe_fetch_tuple!(A, B, C, D, E);
169impl_universe_fetch_tuple!(A, B, C, D, E, F);
170impl_universe_fetch_tuple!(A, B, C, D, E, F, G);
171impl_universe_fetch_tuple!(A, B, C, D, E, F, G, H);
172impl_universe_fetch_tuple!(A, B, C, D, E, F, G, H, I);
173impl_universe_fetch_tuple!(A, B, C, D, E, F, G, H, I, J);
174impl_universe_fetch_tuple!(A, B, C, D, E, F, G, H, I, J, K);
175impl_universe_fetch_tuple!(A, B, C, D, E, F, G, H, I, J, K, L);
176impl_universe_fetch_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M);
177impl_universe_fetch_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
178impl_universe_fetch_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
179impl_universe_fetch_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
180
181pub trait UniverseCondition {
182    fn evaluate(context: SystemContext) -> bool;
183}
184
185pub struct NegateUniverseCondition<T: UniverseCondition>(PhantomData<fn() -> T>);
186
187impl<T: UniverseCondition> UniverseCondition for NegateUniverseCondition<T> {
188    fn evaluate(context: SystemContext) -> bool {
189        !T::evaluate(context)
190    }
191}
192
193pub struct ResourceDidChanged<T: Component>(PhantomData<fn() -> T>);
194
195impl<T: Component> UniverseCondition for ResourceDidChanged<T> {
196    fn evaluate(context: SystemContext) -> bool {
197        context.universe.resources.did_changed::<T>()
198    }
199}
200
201pub struct ResourceAdded<T: Component>(PhantomData<fn() -> T>);
202
203impl<T: Component> UniverseCondition for ResourceAdded<T> {
204    fn evaluate(context: SystemContext) -> bool {
205        context.universe.resources.added().has_component::<T>()
206    }
207}
208
209pub struct ResourceRemoved<T: Component>(PhantomData<fn() -> T>);
210
211impl<T: Component> UniverseCondition for ResourceRemoved<T> {
212    fn evaluate(context: SystemContext) -> bool {
213        context.universe.resources.removed().has_component::<T>()
214    }
215}
216
217pub struct ResourceUpdated<T: Component>(PhantomData<fn() -> T>);
218
219impl<T: Component> UniverseCondition for ResourceUpdated<T> {
220    fn evaluate(context: SystemContext) -> bool {
221        context
222            .universe
223            .resources
224            .updated()
225            .map(|changes| changes.has_component::<T>())
226            .unwrap_or_default()
227    }
228}
229
230pub struct ComponentDidChanged<T: Component>(PhantomData<fn() -> T>);
231
232impl<T: Component> UniverseCondition for ComponentDidChanged<T> {
233    fn evaluate(context: SystemContext) -> bool {
234        context.universe.simulation.component_did_changed::<T>()
235    }
236}
237
238pub struct ComponentAdded<T: Component>(PhantomData<fn() -> T>);
239
240impl<T: Component> UniverseCondition for ComponentAdded<T> {
241    fn evaluate(context: SystemContext) -> bool {
242        context.universe.simulation.added().has_component::<T>()
243    }
244}
245
246pub struct ComponentRemoved<T: Component>(PhantomData<fn() -> T>);
247
248impl<T: Component> UniverseCondition for ComponentRemoved<T> {
249    fn evaluate(context: SystemContext) -> bool {
250        context.universe.simulation.removed().has_component::<T>()
251    }
252}
253
254pub struct ComponentUpdated<T: Component>(PhantomData<fn() -> T>);
255
256impl<T: Component> UniverseCondition for ComponentUpdated<T> {
257    fn evaluate(context: SystemContext) -> bool {
258        context
259            .universe
260            .simulation
261            .updated()
262            .map(|changes| changes.has_component::<T>())
263            .unwrap_or_default()
264    }
265}
266
267pub struct SystemLocalDidChanged<T: Component>(PhantomData<fn() -> T>);
268
269impl<T: Component> UniverseCondition for SystemLocalDidChanged<T> {
270    fn evaluate(context: SystemContext) -> bool {
271        context
272            .universe
273            .systems
274            .entity_component_did_changed::<T>(context.entity())
275    }
276}
277
278pub struct SystemLocalAdded<T: Component>(PhantomData<fn() -> T>);
279
280impl<T: Component> UniverseCondition for SystemLocalAdded<T> {
281    fn evaluate(context: SystemContext) -> bool {
282        context
283            .universe
284            .systems
285            .added()
286            .has_entity_component::<T>(context.entity())
287    }
288}
289
290pub struct SystemLocalRemoved<T: Component>(PhantomData<fn() -> T>);
291
292impl<T: Component> UniverseCondition for SystemLocalRemoved<T> {
293    fn evaluate(context: SystemContext) -> bool {
294        context
295            .universe
296            .systems
297            .removed()
298            .has_entity_component::<T>(context.entity())
299    }
300}
301
302pub struct SystemLocalUpdated<T: Component>(PhantomData<fn() -> T>);
303
304impl<T: Component> UniverseCondition for SystemLocalUpdated<T> {
305    fn evaluate(context: SystemContext) -> bool {
306        context
307            .universe
308            .systems
309            .updated()
310            .map(|changes| changes.has_entity_component::<T>(context.entity()))
311            .unwrap_or_default()
312    }
313}
314
315macro_rules! impl_universe_condition_tuple {
316    ($($type:ident),+) => {
317        impl<$($type: UniverseCondition),+> UniverseCondition for ($($type,)+) {
318            fn evaluate(context: SystemContext) -> bool {
319                $($type::evaluate(context))&&+
320            }
321        }
322    };
323}
324
325impl_universe_condition_tuple!(A);
326impl_universe_condition_tuple!(A, B);
327impl_universe_condition_tuple!(A, B, C);
328impl_universe_condition_tuple!(A, B, C, D);
329impl_universe_condition_tuple!(A, B, C, D, E);
330impl_universe_condition_tuple!(A, B, C, D, E, F);
331impl_universe_condition_tuple!(A, B, C, D, E, F, G);
332impl_universe_condition_tuple!(A, B, C, D, E, F, G, H);
333impl_universe_condition_tuple!(A, B, C, D, E, F, G, H, I);
334impl_universe_condition_tuple!(A, B, C, D, E, F, G, H, I, J);
335impl_universe_condition_tuple!(A, B, C, D, E, F, G, H, I, J, K);
336impl_universe_condition_tuple!(A, B, C, D, E, F, G, H, I, J, K, L);
337impl_universe_condition_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M);
338impl_universe_condition_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
339impl_universe_condition_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
340impl_universe_condition_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
341
342#[derive(Default)]
343pub struct Universe {
344    pub simulation: World,
345    pub systems: Systems,
346    pub resources: Resources,
347}
348
349impl Universe {
350    pub fn new(simulation: World) -> Self {
351        Self {
352            simulation,
353            resources: Default::default(),
354            systems: Default::default(),
355        }
356    }
357
358    pub fn with_plugin<T: Plugin + 'static>(mut self, plugin: T) -> Self {
359        plugin.install(&mut self.simulation, &mut self.systems, &mut self.resources);
360        self
361    }
362
363    pub fn with_basics(
364        self,
365        stack_capacity: usize,
366        registers_capacity: usize,
367    ) -> Result<Self, Box<dyn Error>> {
368        self.with_resource(CommandBuffer::default())?
369            .with_resource(Registry::default().with_basic_types())?
370            .with_resource(Context::new(stack_capacity, registers_capacity))?
371            .with_resource(WorldProcessor::default())?
372            .with_resource(SerializationRegistry::default().with_basic_types())
373    }
374
375    pub fn with_resource(mut self, resource: impl Component) -> Result<Self, Box<dyn Error>> {
376        self.resources.add((resource,))?;
377        Ok(self)
378    }
379
380    pub fn with_system(
381        mut self,
382        system: impl System,
383        locals: impl Bundle,
384    ) -> Result<Self, Box<dyn Error>> {
385        self.systems.add(system, locals)?;
386        Ok(self)
387    }
388
389    pub fn clear_changes(&mut self) {
390        self.simulation.clear_changes();
391        self.resources.clear_changes();
392        self.systems.clear_changes();
393    }
394
395    pub fn execute_commands<const LOCKING: bool>(&mut self) {
396        for commands in self.resources.query::<LOCKING, &mut CommandBuffer>() {
397            commands.execute(&mut self.simulation);
398        }
399        for commands in self.systems.query::<LOCKING, &mut CommandBuffer>() {
400            commands.execute(&mut self.simulation);
401        }
402    }
403}
404
405pub trait Plugin: Send + Sync {
406    fn install(self, simulation: &mut World, systems: &mut Systems, resources: &mut Resources);
407}
408
409#[cfg(test)]
410mod tests {
411    use super::*;
412    use crate::scheduler::{GraphScheduler, GraphSchedulerPlugin, SystemParallelize};
413    use moirai::jobs::Jobs;
414
415    #[test]
416    fn test_universe_parallelized_scheduler() {
417        struct A(f32);
418        struct B(f32);
419        struct C(f32);
420        struct D(f32);
421        struct E(f32);
422
423        fn ab(context: SystemContext) -> Result<(), Box<dyn Error>> {
424            let (world, query) = context.fetch::<(&World, Query<true, (&mut A, &mut B)>)>()?;
425
426            for (a, b) in query.query(world) {
427                std::mem::swap(&mut a.0, &mut b.0);
428            }
429
430            Ok(())
431        }
432
433        fn cd(context: SystemContext) -> Result<(), Box<dyn Error>> {
434            let (world, query) = context.fetch::<(&World, Query<true, (&mut C, &mut D)>)>()?;
435
436            for (c, d) in query.query(world) {
437                std::mem::swap(&mut c.0, &mut d.0);
438            }
439
440            Ok(())
441        }
442
443        fn ce(context: SystemContext) -> Result<(), Box<dyn Error>> {
444            let (world, query) = context.fetch::<(&World, Query<true, (&mut C, &mut E)>)>()?;
445
446            for (c, e) in query.query(world) {
447                std::mem::swap(&mut c.0, &mut e.0);
448            }
449
450            Ok(())
451        }
452
453        let mut universe = Universe::default().with_plugin(
454            GraphSchedulerPlugin::<true>::default()
455                .plugin_setup(|plugin| {
456                    plugin
457                        .name("root")
458                        .system_setup(ab, |system| {
459                            system.name("ab").local(SystemParallelize::AnyWorker)
460                        })
461                        .system_setup(cd, |system| {
462                            system.name("cd").local(SystemParallelize::AnyWorker)
463                        })
464                })
465                .system_setup(ce, |system| {
466                    system.name("ce").local(SystemParallelize::AnyWorker)
467                }),
468        );
469
470        for _ in 0..10 {
471            universe.simulation.spawn((A(0.0), B(0.0))).unwrap();
472        }
473        for _ in 0..10 {
474            universe.simulation.spawn((A(0.0), B(0.0), C(0.0))).unwrap();
475        }
476        for _ in 0..10 {
477            universe
478                .simulation
479                .spawn((A(0.0), B(0.0), C(0.0), D(0.0)))
480                .unwrap();
481        }
482        for _ in 0..10 {
483            universe
484                .simulation
485                .spawn((A(0.0), B(0.0), C(0.0), E(0.0)))
486                .unwrap();
487        }
488
489        let jobs = Jobs::default();
490        GraphScheduler::<true>.run(&jobs, &mut universe).unwrap();
491    }
492}