1use crate::{
2 bundle::Bundle,
3 commands::CommandBuffer,
4 entity::Entity,
5 processor::WorldProcessor,
6 query::{Lookup, Query, TypedLookupFetch, TypedQueryFetch},
7 resources::Resources,
8 systems::{System, SystemContext, Systems},
9 world::World,
10 Component, ComponentRef, ComponentRefMut,
11};
12use intuicio_core::{context::Context, registry::Registry};
13use intuicio_data::type_hash::TypeHash;
14use intuicio_framework_serde::SerializationRegistry;
15use std::{
16 collections::{HashMap, HashSet},
17 error::Error,
18 marker::PhantomData,
19 sync::Mutex,
20};
21
22pub trait UniverseFetch<'a> {
23 type Value;
24
25 fn fetch(universe: &'a Universe, system: Entity) -> Result<Self::Value, Box<dyn Error>>;
26}
27
28impl<'a> UniverseFetch<'a> for &'a World {
29 type Value = &'a World;
30
31 fn fetch(universe: &'a Universe, _: Entity) -> Result<Self::Value, Box<dyn Error>> {
32 Ok(&universe.simulation)
33 }
34}
35
36pub struct Res<const LOCKING: bool, T>(PhantomData<fn() -> T>);
37
38impl<'a, const LOCKING: bool, T: Component> UniverseFetch<'a> for Res<LOCKING, &'a T> {
39 type Value = ComponentRef<'a, LOCKING, T>;
40
41 fn fetch(universe: &'a Universe, _: Entity) -> Result<Self::Value, Box<dyn Error>> {
42 universe.resources.get()
43 }
44}
45
46impl<'a, const LOCKING: bool, T: Component> UniverseFetch<'a> for Res<LOCKING, &'a mut T> {
47 type Value = ComponentRefMut<'a, LOCKING, T>;
48
49 fn fetch(universe: &'a Universe, _: Entity) -> Result<Self::Value, Box<dyn Error>> {
50 universe.resources.get_mut()
51 }
52}
53
54pub struct Local<const LOCKING: bool, T>(PhantomData<fn() -> T>);
55
56impl<'a, const LOCKING: bool, T: Component> UniverseFetch<'a> for Local<LOCKING, &'a T> {
57 type Value = ComponentRef<'a, LOCKING, T>;
58
59 fn fetch(universe: &'a Universe, system: Entity) -> Result<Self::Value, Box<dyn Error>> {
60 Ok(universe.systems.component(system)?)
61 }
62}
63
64impl<'a, const LOCKING: bool, T: Component> UniverseFetch<'a> for Local<LOCKING, &'a mut T> {
65 type Value = ComponentRefMut<'a, LOCKING, T>;
66
67 fn fetch(universe: &'a Universe, system: Entity) -> Result<Self::Value, Box<dyn Error>> {
68 Ok(universe.systems.component_mut(system)?)
69 }
70}
71
72impl<'a, const LOCKING: bool, Fetch: TypedQueryFetch<'a, LOCKING>> UniverseFetch<'a>
73 for Query<'a, LOCKING, Fetch>
74{
75 type Value = Query<'a, LOCKING, Fetch>;
76
77 fn fetch(_: &Universe, _: Entity) -> Result<Self::Value, Box<dyn Error>> {
78 Ok(Query::<LOCKING, Fetch>::default())
79 }
80}
81
82impl<'a, const LOCKING: bool, Fetch: TypedLookupFetch<'a, LOCKING>> UniverseFetch<'a>
83 for Lookup<'a, LOCKING, Fetch>
84{
85 type Value = Lookup<'a, LOCKING, Fetch>;
86
87 fn fetch(_: &Universe, _: Entity) -> Result<Self::Value, Box<dyn Error>> {
88 Ok(Lookup::<LOCKING, Fetch>::default())
89 }
90}
91
92macro_rules! impl_universe_fetch_tuple {
93 ($($type:ident),+) => {
94 impl<'a, $($type: UniverseFetch<'a>),+> UniverseFetch<'a> for ($($type,)+) {
95 type Value = ($($type::Value,)+);
96
97 fn fetch(universe: &'a Universe, entity: Entity) -> Result<Self::Value, Box<dyn Error>> {
98 Ok(($($type::fetch(universe, entity)?,)+))
99 }
100 }
101 };
102}
103
104impl_universe_fetch_tuple!(A);
105impl_universe_fetch_tuple!(A, B);
106impl_universe_fetch_tuple!(A, B, C);
107impl_universe_fetch_tuple!(A, B, C, D);
108impl_universe_fetch_tuple!(A, B, C, D, E);
109impl_universe_fetch_tuple!(A, B, C, D, E, F);
110impl_universe_fetch_tuple!(A, B, C, D, E, F, G);
111impl_universe_fetch_tuple!(A, B, C, D, E, F, G, H);
112impl_universe_fetch_tuple!(A, B, C, D, E, F, G, H, I);
113impl_universe_fetch_tuple!(A, B, C, D, E, F, G, H, I, J);
114impl_universe_fetch_tuple!(A, B, C, D, E, F, G, H, I, J, K);
115impl_universe_fetch_tuple!(A, B, C, D, E, F, G, H, I, J, K, L);
116impl_universe_fetch_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M);
117impl_universe_fetch_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
118impl_universe_fetch_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
119impl_universe_fetch_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
120
121pub trait UniverseCondition {
122 fn evaluate(context: SystemContext) -> bool;
123}
124
125pub struct ResourceDidChanged<T: Component>(PhantomData<fn() -> T>);
126
127impl<T: Component> UniverseCondition for ResourceDidChanged<T> {
128 fn evaluate(context: SystemContext) -> bool {
129 context.universe.resources.did_changed::<T>()
130 }
131}
132
133pub struct ResourceAdded<T: Component>(PhantomData<fn() -> T>);
134
135impl<T: Component> UniverseCondition for ResourceAdded<T> {
136 fn evaluate(context: SystemContext) -> bool {
137 context.universe.resources.added().has_component::<T>()
138 }
139}
140
141pub struct ResourceRemoved<T: Component>(PhantomData<fn() -> T>);
142
143impl<T: Component> UniverseCondition for ResourceRemoved<T> {
144 fn evaluate(context: SystemContext) -> bool {
145 context.universe.resources.removed().has_component::<T>()
146 }
147}
148
149pub struct ResourceUpdated<T: Component>(PhantomData<fn() -> T>);
150
151impl<T: Component> UniverseCondition for ResourceUpdated<T> {
152 fn evaluate(context: SystemContext) -> bool {
153 context
154 .universe
155 .resources
156 .updated()
157 .map(|changes| changes.has_component::<T>())
158 .unwrap_or_default()
159 }
160}
161
162pub struct ComponentDidChanged<T: Component>(PhantomData<fn() -> T>);
163
164impl<T: Component> UniverseCondition for ComponentDidChanged<T> {
165 fn evaluate(context: SystemContext) -> bool {
166 context.universe.simulation.component_did_changed::<T>()
167 }
168}
169
170pub struct ComponentAdded<T: Component>(PhantomData<fn() -> T>);
171
172impl<T: Component> UniverseCondition for ComponentAdded<T> {
173 fn evaluate(context: SystemContext) -> bool {
174 context.universe.simulation.added().has_component::<T>()
175 }
176}
177
178pub struct ComponentRemoved<T: Component>(PhantomData<fn() -> T>);
179
180impl<T: Component> UniverseCondition for ComponentRemoved<T> {
181 fn evaluate(context: SystemContext) -> bool {
182 context.universe.simulation.removed().has_component::<T>()
183 }
184}
185
186pub struct ComponentUpdated<T: Component>(PhantomData<fn() -> T>);
187
188impl<T: Component> UniverseCondition for ComponentUpdated<T> {
189 fn evaluate(context: SystemContext) -> bool {
190 context
191 .universe
192 .simulation
193 .updated()
194 .map(|changes| changes.has_component::<T>())
195 .unwrap_or_default()
196 }
197}
198
199pub struct SystemLocalDidChanged<T: Component>(PhantomData<fn() -> T>);
200
201impl<T: Component> UniverseCondition for SystemLocalDidChanged<T> {
202 fn evaluate(context: SystemContext) -> bool {
203 context
204 .universe
205 .systems
206 .entity_component_did_changed::<T>(context.entity())
207 }
208}
209
210pub struct SystemLocalAdded<T: Component>(PhantomData<fn() -> T>);
211
212impl<T: Component> UniverseCondition for SystemLocalAdded<T> {
213 fn evaluate(context: SystemContext) -> bool {
214 context
215 .universe
216 .systems
217 .added()
218 .has_entity_component::<T>(context.entity())
219 }
220}
221
222pub struct SystemLocalRemoved<T: Component>(PhantomData<fn() -> T>);
223
224impl<T: Component> UniverseCondition for SystemLocalRemoved<T> {
225 fn evaluate(context: SystemContext) -> bool {
226 context
227 .universe
228 .systems
229 .removed()
230 .has_entity_component::<T>(context.entity())
231 }
232}
233
234pub struct SystemLocalUpdated<T: Component>(PhantomData<fn() -> T>);
235
236impl<T: Component> UniverseCondition for SystemLocalUpdated<T> {
237 fn evaluate(context: SystemContext) -> bool {
238 context
239 .universe
240 .systems
241 .updated()
242 .map(|changes| changes.has_entity_component::<T>(context.entity()))
243 .unwrap_or_default()
244 }
245}
246
247macro_rules! impl_universe_condition_tuple {
248 ($($type:ident),+) => {
249 impl<$($type: UniverseCondition),+> UniverseCondition for ($($type,)+) {
250 fn evaluate(context: SystemContext) -> bool {
251 $($type::evaluate(context))&&+
252 }
253 }
254 };
255}
256
257impl_universe_condition_tuple!(A);
258impl_universe_condition_tuple!(A, B);
259impl_universe_condition_tuple!(A, B, C);
260impl_universe_condition_tuple!(A, B, C, D);
261impl_universe_condition_tuple!(A, B, C, D, E);
262impl_universe_condition_tuple!(A, B, C, D, E, F);
263impl_universe_condition_tuple!(A, B, C, D, E, F, G);
264impl_universe_condition_tuple!(A, B, C, D, E, F, G, H);
265impl_universe_condition_tuple!(A, B, C, D, E, F, G, H, I);
266impl_universe_condition_tuple!(A, B, C, D, E, F, G, H, I, J);
267impl_universe_condition_tuple!(A, B, C, D, E, F, G, H, I, J, K);
268impl_universe_condition_tuple!(A, B, C, D, E, F, G, H, I, J, K, L);
269impl_universe_condition_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M);
270impl_universe_condition_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
271impl_universe_condition_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
272impl_universe_condition_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
273
274#[derive(Default)]
275pub struct Universe {
276 pub simulation: World,
277 pub systems: Systems,
278 pub resources: Resources,
279 plugins: HashMap<TypeHash, Box<dyn Plugin>>,
280 plugins_to_register: Mutex<HashMap<TypeHash, Box<dyn Plugin>>>,
281 plugins_to_unregister: Mutex<HashSet<TypeHash>>,
282}
283
284impl Drop for Universe {
285 fn drop(&mut self) {
286 self.clear_plugins();
287 }
288}
289
290impl Universe {
291 pub fn new(simulation: World) -> Self {
292 Self {
293 simulation,
294 resources: Default::default(),
295 systems: Default::default(),
296 plugins: Default::default(),
297 plugins_to_register: Default::default(),
298 plugins_to_unregister: Default::default(),
299 }
300 }
301
302 pub fn with_basics(self, stack_capacity: usize, registers_capacity: usize) -> Self {
303 struct BasicsPlugin;
304 self.with_plugin(
305 QuickPlugin::<BasicsPlugin>::default()
306 .resource(CommandBuffer::default())
307 .resource(Registry::default())
308 .resource(Context::new(stack_capacity, registers_capacity))
309 .resource(WorldProcessor::default())
310 .resource(SerializationRegistry::default()),
311 )
312 }
313
314 pub fn clear_changes(&mut self) {
315 self.simulation.clear_changes();
316 self.resources.clear_changes();
317 self.systems.clear_changes();
318 }
319
320 pub fn execute_commands<const LOCKING: bool>(&mut self) {
321 for commands in self.resources.query::<LOCKING, &mut CommandBuffer>() {
322 commands.execute(&mut self.simulation);
323 }
324 for commands in self.systems.query::<LOCKING, &mut CommandBuffer>() {
325 commands.execute(&mut self.simulation);
326 }
327 }
328
329 pub fn with_plugin<T: Plugin + 'static>(mut self, plugin: T) -> Self {
330 self.add_plugin(plugin);
331 self.maintain_plugins();
332 self
333 }
334
335 pub fn add_plugin<T: Plugin + 'static>(&self, plugin: T) {
336 if let Ok(mut to_register) = self.plugins_to_register.lock() {
337 to_register.insert(TypeHash::of::<T>(), Box::new(plugin));
338 }
339 }
340
341 pub fn remove_plugin<T: Plugin + 'static>(&self) {
342 if let Ok(mut to_unregister) = self.plugins_to_unregister.lock() {
343 to_unregister.insert(TypeHash::of::<T>());
344 }
345 }
346
347 pub fn remove_plugin_raw(&self, type_hash: TypeHash) {
348 if let Ok(mut to_unregister) = self.plugins_to_unregister.lock() {
349 to_unregister.insert(type_hash);
350 }
351 }
352
353 pub fn clear_plugins(&mut self) {
354 for (_, mut plugin) in std::mem::take(&mut self.plugins) {
355 plugin.on_unregister(&mut self.simulation, &mut self.systems, &mut self.resources);
356 }
357 }
358
359 pub fn maintain_plugins(&mut self) {
360 if let Ok(mut to_unregister) = self.plugins_to_unregister.try_lock() {
361 if !to_unregister.is_empty() {
362 for type_hash in to_unregister.drain() {
363 if let Some(mut plugin) = self.plugins.remove(&type_hash) {
364 plugin.on_unregister(
365 &mut self.simulation,
366 &mut self.systems,
367 &mut self.resources,
368 );
369 }
370 }
371 }
372 }
373 if let Ok(mut to_register) = self.plugins_to_register.try_lock() {
374 if !to_register.is_empty() {
375 for (type_hash, mut plugin) in std::mem::take(&mut *to_register).drain() {
376 if let Some(mut plugin) = self.plugins.remove(&type_hash) {
377 plugin.on_unregister(
378 &mut self.simulation,
379 &mut self.systems,
380 &mut self.resources,
381 );
382 }
383 if plugin
384 .dependencies()
385 .into_iter()
386 .all(|type_hash| self.plugins.contains_key(&type_hash))
387 {
388 plugin.on_register(
389 &mut self.simulation,
390 &mut self.systems,
391 &mut self.resources,
392 );
393 self.plugins.insert(type_hash, plugin);
394 } else {
395 to_register.insert(type_hash, plugin);
396 }
397 }
398 }
399 }
400 }
401}
402
403pub trait Plugin: Send + Sync {
404 fn on_register(
405 &mut self,
406 simulation: &mut World,
407 systems: &mut Systems,
408 resources: &mut Resources,
409 );
410 fn on_unregister(
411 &mut self,
412 simulation: &mut World,
413 systems: &mut Systems,
414 resources: &mut Resources,
415 );
416
417 fn dependencies(&self) -> Vec<TypeHash> {
418 vec![]
419 }
420}
421
422#[derive(Default)]
423pub struct PluginsPackage {
424 plugins: Vec<Box<dyn Plugin>>,
425}
426
427impl PluginsPackage {
428 pub fn plugin(mut self, plugin: impl Plugin + 'static) -> Self {
429 self.plugins.push(Box::new(plugin));
430 self
431 }
432}
433
434impl Plugin for PluginsPackage {
435 fn on_register(
436 &mut self,
437 simulation: &mut World,
438 systems: &mut Systems,
439 resources: &mut Resources,
440 ) {
441 for plugin in &mut self.plugins {
442 plugin.on_register(simulation, systems, resources);
443 }
444 }
445
446 fn on_unregister(
447 &mut self,
448 simulation: &mut World,
449 systems: &mut Systems,
450 resources: &mut Resources,
451 ) {
452 for plugin in &mut self.plugins {
453 plugin.on_unregister(simulation, systems, resources);
454 }
455 }
456}
457
458pub struct QuickPlugin<Tag: Send + Sync> {
459 #[allow(clippy::type_complexity)]
460 simulation_register:
461 Vec<Box<dyn FnOnce(&mut World) -> Box<dyn FnOnce(&mut World) + Send + Sync> + Send + Sync>>,
462 #[allow(clippy::type_complexity)]
463 simulation_unregister: Vec<Box<dyn FnOnce(&mut World) + Send + Sync>>,
464 #[allow(clippy::type_complexity)]
465 systems_register: Vec<
466 Box<dyn FnOnce(&mut Systems) -> Box<dyn FnOnce(&mut Systems) + Send + Sync> + Send + Sync>,
467 >,
468 #[allow(clippy::type_complexity)]
469 systems_unregister: Vec<Box<dyn FnOnce(&mut Systems) + Send + Sync>>,
470 #[allow(clippy::type_complexity)]
471 resources_register: Vec<
472 Box<
473 dyn FnOnce(&mut Resources) -> Box<dyn FnOnce(&mut Resources) + Send + Sync>
474 + Send
475 + Sync,
476 >,
477 >,
478 #[allow(clippy::type_complexity)]
479 resources_unregister: Vec<Box<dyn FnOnce(&mut Resources) + Send + Sync>>,
480 _phantom: PhantomData<fn() -> Tag>,
481}
482
483impl<Tag: Send + Sync> Default for QuickPlugin<Tag> {
484 fn default() -> Self {
485 Self {
486 simulation_register: Default::default(),
487 simulation_unregister: Default::default(),
488 systems_register: Default::default(),
489 systems_unregister: Default::default(),
490 resources_register: Default::default(),
491 resources_unregister: Default::default(),
492 _phantom: Default::default(),
493 }
494 }
495}
496
497impl<Tag: Send + Sync> QuickPlugin<Tag> {
498 pub fn entity(mut self, bundle: impl Bundle + Send + Sync + 'static) -> Self {
499 self.simulation_register.push(Box::new(|world| {
500 let entity = world.spawn(bundle).unwrap();
501 Box::new(move |world| {
502 let _ = world.despawn(entity);
503 })
504 }));
505 self
506 }
507
508 pub fn entity_relation<const LOCKING: bool, R: Component, E: Component + PartialEq>(
509 mut self,
510 from: E,
511 payload: R,
512 to: E,
513 ) -> Self {
514 self.simulation_register.push(Box::new(move |world| {
515 let from = world.find_by::<LOCKING, E>(&from).unwrap();
516 let to = world.find_by::<LOCKING, E>(&to).unwrap();
517 world.relate::<LOCKING, R>(payload, from, to).unwrap();
518 Box::new(move |world| {
519 let _ = world.unrelate::<LOCKING, R>(from, to);
520 })
521 }));
522 self
523 }
524
525 pub fn system(
526 mut self,
527 system: impl System,
528 locals: impl Bundle + Send + Sync + 'static,
529 ) -> Self {
530 self.systems_register.push(Box::new(|systems| {
531 let entity = systems.add(system, locals).unwrap();
532 Box::new(move |systems| {
533 let _ = systems.despawn(entity);
534 })
535 }));
536 self
537 }
538
539 pub fn system_meta(mut self, locals: impl Bundle + Send + Sync + 'static) -> Self {
540 self.systems_register.push(Box::new(|systems| {
541 let entity = systems.spawn(locals).unwrap();
542 Box::new(move |systems| {
543 let _ = systems.despawn(entity);
544 })
545 }));
546 self
547 }
548
549 pub fn system_relation<const LOCKING: bool, R: Component, E: Component + PartialEq>(
550 mut self,
551 from: E,
552 payload: R,
553 to: E,
554 ) -> Self {
555 self.systems_register.push(Box::new(move |systems| {
556 let from = systems.find_by::<LOCKING, E>(&from).unwrap();
557 let to = systems.find_by::<LOCKING, E>(&to).unwrap();
558 systems.relate::<LOCKING, R>(payload, from, to).unwrap();
559 Box::new(move |systems| {
560 let _ = systems.unrelate::<LOCKING, R>(from, to);
561 })
562 }));
563 self
564 }
565
566 pub fn resource<T: Component>(mut self, resource: T) -> Self {
567 self.resources_register.push(Box::new(|resources| {
568 resources.add((resource,)).unwrap();
569 Box::new(|resources| {
570 let _ = resources.remove::<(T,)>();
571 })
572 }));
573 self
574 }
575}
576
577impl<T: Send + Sync> Plugin for QuickPlugin<T> {
578 fn on_register(
579 &mut self,
580 simulation: &mut World,
581 systems: &mut Systems,
582 resources: &mut Resources,
583 ) {
584 for execute in self.simulation_register.drain(..) {
585 self.simulation_unregister.push(execute(simulation));
586 }
587 for execute in self.systems_register.drain(..) {
588 self.systems_unregister.push(execute(systems));
589 }
590 for execute in self.resources_register.drain(..) {
591 self.resources_unregister.push(execute(resources));
592 }
593 }
594
595 fn on_unregister(
596 &mut self,
597 simulation: &mut World,
598 systems: &mut Systems,
599 resources: &mut Resources,
600 ) {
601 for execute in self.simulation_unregister.drain(..) {
602 execute(simulation);
603 }
604 for execute in self.systems_unregister.drain(..) {
605 execute(systems);
606 }
607 for execute in self.resources_unregister.drain(..) {
608 execute(resources);
609 }
610 }
611}