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 ResourceDidChanged<T: Component>(PhantomData<fn() -> T>);
186
187impl<T: Component> UniverseCondition for ResourceDidChanged<T> {
188 fn evaluate(context: SystemContext) -> bool {
189 context.universe.resources.did_changed::<T>()
190 }
191}
192
193pub struct ResourceAdded<T: Component>(PhantomData<fn() -> T>);
194
195impl<T: Component> UniverseCondition for ResourceAdded<T> {
196 fn evaluate(context: SystemContext) -> bool {
197 context.universe.resources.added().has_component::<T>()
198 }
199}
200
201pub struct ResourceRemoved<T: Component>(PhantomData<fn() -> T>);
202
203impl<T: Component> UniverseCondition for ResourceRemoved<T> {
204 fn evaluate(context: SystemContext) -> bool {
205 context.universe.resources.removed().has_component::<T>()
206 }
207}
208
209pub struct ResourceUpdated<T: Component>(PhantomData<fn() -> T>);
210
211impl<T: Component> UniverseCondition for ResourceUpdated<T> {
212 fn evaluate(context: SystemContext) -> bool {
213 context
214 .universe
215 .resources
216 .updated()
217 .map(|changes| changes.has_component::<T>())
218 .unwrap_or_default()
219 }
220}
221
222pub struct ComponentDidChanged<T: Component>(PhantomData<fn() -> T>);
223
224impl<T: Component> UniverseCondition for ComponentDidChanged<T> {
225 fn evaluate(context: SystemContext) -> bool {
226 context.universe.simulation.component_did_changed::<T>()
227 }
228}
229
230pub struct ComponentAdded<T: Component>(PhantomData<fn() -> T>);
231
232impl<T: Component> UniverseCondition for ComponentAdded<T> {
233 fn evaluate(context: SystemContext) -> bool {
234 context.universe.simulation.added().has_component::<T>()
235 }
236}
237
238pub struct ComponentRemoved<T: Component>(PhantomData<fn() -> T>);
239
240impl<T: Component> UniverseCondition for ComponentRemoved<T> {
241 fn evaluate(context: SystemContext) -> bool {
242 context.universe.simulation.removed().has_component::<T>()
243 }
244}
245
246pub struct ComponentUpdated<T: Component>(PhantomData<fn() -> T>);
247
248impl<T: Component> UniverseCondition for ComponentUpdated<T> {
249 fn evaluate(context: SystemContext) -> bool {
250 context
251 .universe
252 .simulation
253 .updated()
254 .map(|changes| changes.has_component::<T>())
255 .unwrap_or_default()
256 }
257}
258
259pub struct SystemLocalDidChanged<T: Component>(PhantomData<fn() -> T>);
260
261impl<T: Component> UniverseCondition for SystemLocalDidChanged<T> {
262 fn evaluate(context: SystemContext) -> bool {
263 context
264 .universe
265 .systems
266 .entity_component_did_changed::<T>(context.entity())
267 }
268}
269
270pub struct SystemLocalAdded<T: Component>(PhantomData<fn() -> T>);
271
272impl<T: Component> UniverseCondition for SystemLocalAdded<T> {
273 fn evaluate(context: SystemContext) -> bool {
274 context
275 .universe
276 .systems
277 .added()
278 .has_entity_component::<T>(context.entity())
279 }
280}
281
282pub struct SystemLocalRemoved<T: Component>(PhantomData<fn() -> T>);
283
284impl<T: Component> UniverseCondition for SystemLocalRemoved<T> {
285 fn evaluate(context: SystemContext) -> bool {
286 context
287 .universe
288 .systems
289 .removed()
290 .has_entity_component::<T>(context.entity())
291 }
292}
293
294pub struct SystemLocalUpdated<T: Component>(PhantomData<fn() -> T>);
295
296impl<T: Component> UniverseCondition for SystemLocalUpdated<T> {
297 fn evaluate(context: SystemContext) -> bool {
298 context
299 .universe
300 .systems
301 .updated()
302 .map(|changes| changes.has_entity_component::<T>(context.entity()))
303 .unwrap_or_default()
304 }
305}
306
307macro_rules! impl_universe_condition_tuple {
308 ($($type:ident),+) => {
309 impl<$($type: UniverseCondition),+> UniverseCondition for ($($type,)+) {
310 fn evaluate(context: SystemContext) -> bool {
311 $($type::evaluate(context))&&+
312 }
313 }
314 };
315}
316
317impl_universe_condition_tuple!(A);
318impl_universe_condition_tuple!(A, B);
319impl_universe_condition_tuple!(A, B, C);
320impl_universe_condition_tuple!(A, B, C, D);
321impl_universe_condition_tuple!(A, B, C, D, E);
322impl_universe_condition_tuple!(A, B, C, D, E, F);
323impl_universe_condition_tuple!(A, B, C, D, E, F, G);
324impl_universe_condition_tuple!(A, B, C, D, E, F, G, H);
325impl_universe_condition_tuple!(A, B, C, D, E, F, G, H, I);
326impl_universe_condition_tuple!(A, B, C, D, E, F, G, H, I, J);
327impl_universe_condition_tuple!(A, B, C, D, E, F, G, H, I, J, K);
328impl_universe_condition_tuple!(A, B, C, D, E, F, G, H, I, J, K, L);
329impl_universe_condition_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M);
330impl_universe_condition_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
331impl_universe_condition_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
332impl_universe_condition_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
333
334#[derive(Default)]
335pub struct Universe {
336 pub simulation: World,
337 pub systems: Systems,
338 pub resources: Resources,
339}
340
341impl Universe {
342 pub fn new(simulation: World) -> Self {
343 Self {
344 simulation,
345 resources: Default::default(),
346 systems: Default::default(),
347 }
348 }
349
350 pub fn with_plugin<T: Plugin + 'static>(mut self, plugin: T) -> Self {
351 plugin.install(&mut self.simulation, &mut self.systems, &mut self.resources);
352 self
353 }
354
355 pub fn with_basics(
356 self,
357 stack_capacity: usize,
358 registers_capacity: usize,
359 ) -> Result<Self, Box<dyn Error>> {
360 self.with_resource(CommandBuffer::default())?
361 .with_resource(Registry::default().with_basic_types())?
362 .with_resource(Context::new(stack_capacity, registers_capacity))?
363 .with_resource(WorldProcessor::default())?
364 .with_resource(SerializationRegistry::default().with_basic_types())
365 }
366
367 pub fn with_resource(mut self, resource: impl Component) -> Result<Self, Box<dyn Error>> {
368 self.resources.add((resource,))?;
369 Ok(self)
370 }
371
372 pub fn with_system(
373 mut self,
374 system: impl System,
375 locals: impl Bundle,
376 ) -> Result<Self, Box<dyn Error>> {
377 self.systems.add(system, locals)?;
378 Ok(self)
379 }
380
381 pub fn clear_changes(&mut self) {
382 self.simulation.clear_changes();
383 self.resources.clear_changes();
384 self.systems.clear_changes();
385 }
386
387 pub fn execute_commands<const LOCKING: bool>(&mut self) {
388 for commands in self.resources.query::<LOCKING, &mut CommandBuffer>() {
389 commands.execute(&mut self.simulation);
390 }
391 for commands in self.systems.query::<LOCKING, &mut CommandBuffer>() {
392 commands.execute(&mut self.simulation);
393 }
394 }
395}
396
397pub trait Plugin: Send + Sync {
398 fn install(self, simulation: &mut World, systems: &mut Systems, resources: &mut Resources);
399}