oxygengine_core/ecs/
mod.rs

1pub mod commands;
2pub mod components;
3pub mod hierarchy;
4pub mod life_cycle;
5pub mod pipeline;
6
7use crate::{
8    app::AppLifeCycle,
9    ecs::{
10        commands::UniverseCommands,
11        components::NonPersistent,
12        life_cycle::EntityChanges,
13        pipeline::{PipelineEngine, PipelineId},
14    },
15    state::{State, StateChange, StateToken},
16};
17pub use hecs::*;
18use std::{
19    any::{type_name, Any, TypeId},
20    collections::{HashMap, HashSet},
21    marker::PhantomData,
22    ops::{Deref, DerefMut},
23    sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard},
24};
25use typid::ID;
26
27pub type System = fn(&mut Universe);
28pub type UniverseId = ID<Universe>;
29pub type Resource = dyn Any + Send + Sync;
30
31pub struct WorldRef;
32pub struct Comp<T>(PhantomData<fn() -> T>);
33
34#[derive(Default)]
35pub struct Multiverse {
36    pub parallel: bool,
37    universes: HashMap<UniverseId, Universe>,
38    pipelines: HashMap<PipelineId, Box<dyn PipelineEngine + Send + Sync>>,
39    bindings: HashMap<UniverseId, PipelineId>,
40    default_universe: Option<UniverseId>,
41}
42
43impl Multiverse {
44    pub fn new<T, S>(pipeline: T, state: S) -> Self
45    where
46        T: PipelineEngine + 'static + Send + Sync,
47        S: State + 'static,
48    {
49        let mut result = Self::default();
50        let universe = result.create_universe(state);
51        let pipeline = result.insert_pipeline(pipeline);
52        result.bind(universe, pipeline);
53        result.set_default_universe_id(Some(universe));
54        result
55    }
56
57    pub fn with_parallel(mut self, mode: bool) -> Self {
58        self.parallel = mode;
59        self
60    }
61
62    pub fn create_universe<S>(&mut self, state: S) -> UniverseId
63    where
64        S: State + 'static,
65    {
66        let id = UniverseId::new();
67        self.universes.insert(id, Universe::new(state));
68        id
69    }
70
71    pub fn delete_universe(&mut self, id: UniverseId) -> Option<Universe> {
72        if let Some(uni) = self.default_universe {
73            if uni == id {
74                self.default_universe = None;
75            }
76        }
77        self.bindings.remove(&id);
78        self.universes.remove(&id)
79    }
80
81    pub fn default_universe_id(&self) -> Option<UniverseId> {
82        self.default_universe
83    }
84
85    pub fn set_default_universe_id(&mut self, id: Option<UniverseId>) {
86        self.default_universe = id;
87    }
88
89    pub fn default_universe(&self) -> Option<&Universe> {
90        if let Some(id) = self.default_universe {
91            self.universe(id)
92        } else {
93            None
94        }
95    }
96
97    pub fn default_universe_mut(&mut self) -> Option<&mut Universe> {
98        if let Some(id) = self.default_universe {
99            self.universe_mut(id)
100        } else {
101            None
102        }
103    }
104
105    pub fn universe(&self, id: UniverseId) -> Option<&Universe> {
106        self.universes.get(&id)
107    }
108
109    pub fn universe_mut(&mut self, id: UniverseId) -> Option<&mut Universe> {
110        self.universes.get_mut(&id)
111    }
112
113    pub fn universe_ids(&self) -> impl Iterator<Item = UniverseId> + '_ {
114        self.universes.keys().cloned()
115    }
116
117    pub fn universes(&self) -> impl Iterator<Item = &Universe> {
118        self.universes.values()
119    }
120
121    pub fn universes_mut(&mut self) -> impl Iterator<Item = &mut Universe> {
122        self.universes.values_mut()
123    }
124
125    pub fn universes_with_ids(&self) -> impl Iterator<Item = (UniverseId, &Universe)> {
126        self.universes.iter().map(|(id, u)| (*id, u))
127    }
128
129    pub fn universes_with_ids_mut(&mut self) -> impl Iterator<Item = (UniverseId, &mut Universe)> {
130        self.universes.iter_mut().map(|(id, u)| (*id, u))
131    }
132
133    pub fn insert_pipeline<T>(&mut self, pipeline: T) -> PipelineId
134    where
135        T: PipelineEngine + 'static + Send + Sync,
136    {
137        let id = PipelineId::new();
138        self.pipelines.insert(id, Box::new(pipeline));
139        id
140    }
141
142    pub fn remove_pipeline(&mut self, id: PipelineId) {
143        self.bindings.retain(|_, p| p != &id);
144        self.pipelines.remove(&id);
145    }
146
147    pub fn pipeline_ids(&self) -> impl Iterator<Item = PipelineId> + '_ {
148        self.pipelines.keys().cloned()
149    }
150
151    pub fn bind(&mut self, universe: UniverseId, pipeline: PipelineId) {
152        self.bindings.insert(universe, pipeline);
153    }
154
155    pub fn unbind(&mut self, universe: UniverseId) {
156        self.bindings.remove(&universe);
157    }
158
159    pub fn unbind_all(&mut self) {
160        self.bindings.clear();
161    }
162
163    pub fn is_running(&self) -> bool {
164        self.bindings.keys().any(|id| {
165            self.universes
166                .get(id)
167                .map(|u| u.is_running())
168                .unwrap_or_default()
169        })
170    }
171
172    pub fn process(&mut self) {
173        #[cfg(not(feature = "parallel"))]
174        {
175            for (universe, pipeline) in &self.bindings {
176                if let (Some(universe), Some(pipeline)) = (
177                    self.universes.get_mut(universe),
178                    self.pipelines.get(pipeline),
179                ) {
180                    if !universe.paused_systems_execution {
181                        pipeline.run(universe);
182                    }
183                }
184            }
185            for universe in self.universes.values_mut() {
186                universe.maintain();
187            }
188        }
189        #[cfg(feature = "parallel")]
190        {
191            if self.parallel && self.bindings.len() > 1 {
192                use rayon::prelude::*;
193                let bindings = self
194                    .bindings
195                    .iter()
196                    .map(|(u, p)| (*u, *p))
197                    .collect::<Vec<_>>();
198                bindings.into_par_iter().for_each(|(universe, pipeline)| {
199                    if let (Some(universe), Some(pipeline)) =
200                        (self.universes.get(&universe), self.pipelines.get(&pipeline))
201                    {
202                        if !universe.paused_systems_execution {
203                            #[allow(mutable_transmutes)]
204                            #[allow(clippy::transmute_ptr_to_ptr)]
205                            pipeline.run(unsafe { std::mem::transmute(universe) });
206                        }
207                    }
208                });
209                self.universes
210                    .par_iter_mut()
211                    .for_each(|(_, universe)| universe.maintain());
212            } else {
213                for (universe, pipeline) in &self.bindings {
214                    if let (Some(universe), Some(pipeline)) = (
215                        self.universes.get_mut(universe),
216                        self.pipelines.get(pipeline),
217                    ) {
218                        if !universe.paused_systems_execution {
219                            pipeline.run(universe);
220                        }
221                    }
222                }
223                for universe in self.universes.values_mut() {
224                    universe.maintain();
225                }
226            }
227        }
228    }
229}
230
231pub struct Universe {
232    resources: HashMap<TypeId, Arc<RwLock<Box<Resource>>>>,
233    states: Vec<Box<dyn State>>,
234    startup: bool,
235    pub paused_systems_execution: bool,
236    world: Arc<RwLock<World>>,
237}
238
239impl Default for Universe {
240    fn default() -> Self {
241        Self {
242            resources: Default::default(),
243            states: vec![],
244            startup: true,
245            paused_systems_execution: false,
246            world: Default::default(),
247        }
248    }
249}
250
251impl Universe {
252    pub fn new<S>(state: S) -> Self
253    where
254        S: State + 'static,
255    {
256        Self {
257            resources: Default::default(),
258            states: vec![Box::new(state)],
259            startup: true,
260            paused_systems_execution: false,
261            world: Default::default(),
262        }
263    }
264
265    pub fn world(&self) -> RwLockReadGuard<World> {
266        self.world
267            .try_read()
268            .unwrap_or_else(|error| panic!("{}: {}", std::any::type_name::<World>(), error))
269    }
270
271    pub fn world_mut(&self) -> RwLockWriteGuard<World> {
272        self.world
273            .try_write()
274            .unwrap_or_else(|error| panic!("{}: {}", std::any::type_name::<World>(), error))
275    }
276
277    pub fn try_world(&self) -> Option<RwLockReadGuard<World>> {
278        self.world.try_read().ok()
279    }
280
281    pub fn try_world_mut(&self) -> Option<RwLockWriteGuard<World>> {
282        self.world.try_write().ok()
283    }
284
285    pub fn insert_resource<T>(&mut self, resource: T)
286    where
287        T: 'static + Send + Sync,
288    {
289        self.resources
290            .insert(TypeId::of::<T>(), Arc::new(RwLock::new(Box::new(resource))));
291    }
292
293    /// # Safety
294    /// This function assume that `as_type` matches exactly the type of `resource`, you can call it
295    /// for example if you want to move already prepared resources from another place to this
296    /// universe (in this case we can be sure type IDs matches the types of resources).
297    pub unsafe fn insert_resource_raw(&mut self, as_type: TypeId, resource: Box<Resource>) {
298        self.resources
299            .insert(as_type, Arc::new(RwLock::new(resource)));
300    }
301
302    pub fn remove_resource<T>(&mut self)
303    where
304        T: 'static,
305    {
306        self.resources.remove(&TypeId::of::<T>());
307    }
308
309    pub fn has_resource<T>(&self) -> bool
310    where
311        T: 'static,
312    {
313        self.resources.contains_key(&TypeId::of::<T>())
314    }
315
316    pub fn resource<T>(&self) -> Option<ResRead<T>>
317    where
318        T: 'static,
319    {
320        if let Some(res) = self.resources.get(&TypeId::of::<T>()) {
321            return Some(ResRead {
322                inner: unsafe {
323                    std::mem::transmute(res.try_read().unwrap_or_else(|error| {
324                        panic!("{}: {}", std::any::type_name::<T>(), error)
325                    }))
326                },
327                _phantom: PhantomData,
328            });
329        }
330        None
331    }
332
333    pub fn resource_mut<T>(&self) -> Option<ResWrite<T>>
334    where
335        T: 'static,
336    {
337        if let Some(res) = self.resources.get(&TypeId::of::<T>()) {
338            return Some(ResWrite {
339                inner: unsafe {
340                    std::mem::transmute(res.try_write().unwrap_or_else(|error| {
341                        panic!("{}: {}", std::any::type_name::<T>(), error)
342                    }))
343                },
344                _phantom: PhantomData,
345            });
346        }
347        None
348    }
349
350    pub fn expect_resource<T>(&self) -> ResRead<T>
351    where
352        T: 'static,
353    {
354        self.resource::<T>()
355            .unwrap_or_else(|| panic!("Resource not found: {}", type_name::<T>()))
356    }
357
358    pub fn expect_resource_mut<T>(&self) -> ResWrite<T>
359    where
360        T: 'static,
361    {
362        self.resource_mut::<T>()
363            .unwrap_or_else(|| panic!("Resource not found: {}", type_name::<T>()))
364    }
365
366    pub fn query_resources<T>(&self) -> T::Fetch
367    where
368        T: ResQuery,
369    {
370        T::fetch(self)
371    }
372
373    pub fn is_running(&self) -> bool {
374        !self.states.is_empty() && self.expect_resource::<AppLifeCycle>().running
375    }
376
377    pub fn maintain(&mut self) {
378        if self.states.is_empty() {
379            return;
380        }
381        self.expect_resource_mut::<EntityChanges>().clear();
382        let mut commands = self.expect_resource_mut::<UniverseCommands>();
383        let executor = commands.execute();
384        drop(commands);
385        executor.execute(self);
386        self.expect_resource_mut::<EntityChanges>()
387            .entities
388            .extend(self.world().iter().map(|entity_ref| entity_ref.entity()));
389        let mut states = std::mem::take(&mut self.states);
390        if self.startup {
391            states.last_mut().unwrap().on_enter(self);
392            self.startup = false;
393        }
394        let count = states.len() - 1;
395        for state in states.iter_mut().take(count) {
396            state.on_process_background(self);
397        }
398        let change = states.last_mut().unwrap().on_process(self);
399        match &change {
400            StateChange::Pop | StateChange::Swap(_) => {
401                let token = self.expect_resource::<AppLifeCycle>().current_state_token();
402                let to_delete = self
403                    .world()
404                    .query::<&NonPersistent>()
405                    .iter()
406                    .filter_map(|(entity, pers)| if pers.0 == token { Some(entity) } else { None })
407                    .collect::<Vec<_>>();
408                for entity in to_delete {
409                    let _ = self.world_mut().despawn(entity);
410                }
411            }
412            StateChange::Quit => {
413                let to_delete = self
414                    .world()
415                    .query::<&NonPersistent>()
416                    .iter()
417                    .map(|(entity, _)| entity)
418                    .collect::<Vec<_>>();
419                for entity in to_delete {
420                    let _ = self.world_mut().despawn(entity);
421                }
422            }
423            _ => {}
424        }
425        match change {
426            StateChange::Push(mut state) => {
427                states.last_mut().unwrap().on_pause(self);
428                self.expect_resource_mut::<AppLifeCycle>()
429                    .states_tokens
430                    .push(StateToken::new());
431                state.on_enter(self);
432                states.push(state);
433            }
434            StateChange::Pop => {
435                states.pop().unwrap().on_exit(self);
436                self.expect_resource_mut::<AppLifeCycle>()
437                    .states_tokens
438                    .pop();
439                if let Some(state) = states.last_mut() {
440                    state.on_resume(self);
441                }
442            }
443            StateChange::Swap(mut state) => {
444                states.pop().unwrap().on_exit(self);
445                let mut lifecycle = self.expect_resource_mut::<AppLifeCycle>();
446                lifecycle.states_tokens.pop();
447                lifecycle.states_tokens.push(StateToken::new());
448                drop(lifecycle);
449                state.on_enter(self);
450                states.push(state);
451            }
452            StateChange::Quit => {
453                while let Some(mut state) = states.pop() {
454                    state.on_exit(self);
455                    self.expect_resource_mut::<AppLifeCycle>()
456                        .states_tokens
457                        .pop();
458                }
459            }
460            _ => {}
461        }
462        self.expect_resource_mut::<AppLifeCycle>().timer.tick();
463
464        let _ = std::mem::replace(&mut self.states, states);
465    }
466}
467
468pub struct UnsafeScope;
469
470impl UnsafeScope {
471    /// # Safety
472    /// Extending lifetimes is unsafe and when done wrongly can cause undefined behaviour.
473    /// Make sure lifetime can be extended to the scope where data behind reference won't be moved.
474    pub unsafe fn lifetime_ref<'a>(&self) -> &'a Self {
475        std::mem::transmute(self)
476    }
477
478    /// # Safety
479    /// Extending lifetimes is unsafe and when done wrongly can cause undefined behaviour.
480    /// Make sure lifetime can be extended to the scope where data behind reference won't be moved.
481    pub unsafe fn lifetime_mut<'a>(&mut self) -> &'a mut Self {
482        std::mem::transmute(self)
483    }
484}
485
486pub struct UnsafeRef<'a, T>(&'a UnsafeScope, &'a T);
487
488impl<'a, T> UnsafeRef<'a, T> {
489    /// # Safety
490    /// Extending lifetimes is unsafe and when done wrongly can cause undefined behaviour.
491    /// Make sure lifetime can be extended to the scope where data behind reference won't be moved.
492    pub unsafe fn upgrade(scope: &'a UnsafeScope, v: &T) -> Self {
493        Self(scope, std::mem::transmute(v))
494    }
495
496    /// # Safety
497    /// Extending lifetimes is unsafe and when done wrongly can cause undefined behaviour.
498    /// Make sure lifetime can be extended to the scope where data behind reference won't be moved.
499    pub unsafe fn read(&self) -> &T {
500        self.1
501    }
502}
503
504pub struct UnsafeMut<'a, T>(&'a UnsafeScope, &'a mut T);
505
506impl<'a, T> UnsafeMut<'a, T> {
507    /// # Safety
508    /// Extending lifetimes is unsafe and when done wrongly can cause undefined behaviour.
509    /// Make sure lifetime can be extended to the scope where data behind reference won't be moved.
510    pub unsafe fn upgrade(scope: &'a UnsafeScope, v: &mut T) -> Self {
511        Self(scope, std::mem::transmute(v))
512    }
513
514    /// # Safety
515    /// Extending lifetimes is unsafe and when done wrongly can cause undefined behaviour.
516    /// Make sure lifetime can be extended to the scope where data behind reference won't be moved.
517    pub unsafe fn read(&self) -> &T {
518        self.1
519    }
520
521    /// # Safety
522    /// Extending lifetimes is unsafe and when done wrongly can cause undefined behaviour.
523    /// Make sure lifetime can be extended to the scope where data behind reference won't be moved.
524    pub unsafe fn write(&mut self) -> &mut T {
525        self.1
526    }
527}
528
529pub trait ResAccess {}
530
531impl ResAccess for () {}
532
533pub type ResQueryItem<Q> = <Q as ResQuery>::Fetch;
534
535pub trait ResQuery {
536    type Fetch: ResAccess;
537
538    fn fetch(universe: &Universe) -> Self::Fetch;
539}
540
541pub struct ResRead<T> {
542    inner: RwLockReadGuard<'static, Box<Resource>>,
543    _phantom: PhantomData<fn() -> T>,
544}
545
546impl<T> ResAccess for ResRead<T> {}
547
548impl<T> Deref for ResRead<T>
549where
550    T: 'static,
551{
552    type Target = T;
553
554    fn deref(&self) -> &Self::Target {
555        self.inner.downcast_ref::<Self::Target>().unwrap()
556    }
557}
558
559pub struct RefRead<T>(RwLockReadGuard<'static, T>)
560where
561    T: 'static;
562
563impl<T> ResAccess for RefRead<T> {}
564
565impl<T> Deref for RefRead<T> {
566    type Target = T;
567
568    fn deref(&self) -> &Self::Target {
569        &self.0
570    }
571}
572
573pub struct ResWrite<T> {
574    inner: RwLockWriteGuard<'static, Box<Resource>>,
575    _phantom: PhantomData<fn() -> T>,
576}
577
578impl<T> ResAccess for ResWrite<T> {}
579
580impl<T> Deref for ResWrite<T>
581where
582    T: 'static,
583{
584    type Target = T;
585
586    fn deref(&self) -> &Self::Target {
587        self.inner.downcast_ref::<Self::Target>().unwrap()
588    }
589}
590
591impl<T> DerefMut for ResWrite<T>
592where
593    T: 'static,
594{
595    fn deref_mut(&mut self) -> &mut Self::Target {
596        self.inner.downcast_mut::<Self::Target>().unwrap()
597    }
598}
599
600pub struct RefWrite<T>(RwLockWriteGuard<'static, T>)
601where
602    T: 'static;
603
604impl<T> ResAccess for RefWrite<T> {}
605
606impl<T> Deref for RefWrite<T> {
607    type Target = T;
608
609    fn deref(&self) -> &Self::Target {
610        &self.0
611    }
612}
613
614impl<T> DerefMut for RefWrite<T> {
615    fn deref_mut(&mut self) -> &mut Self::Target {
616        &mut self.0
617    }
618}
619
620impl<T> ResAccess for Option<T> where T: ResAccess {}
621
622impl ResQuery for WorldRef {
623    type Fetch = RefRead<World>;
624
625    fn fetch(universe: &Universe) -> Self::Fetch {
626        RefRead(unsafe { std::mem::transmute(universe.world()) })
627    }
628}
629
630impl ResQuery for () {
631    type Fetch = ();
632
633    fn fetch(_: &Universe) -> Self::Fetch {}
634}
635
636impl<T> ResQuery for Comp<T> {
637    type Fetch = ();
638
639    fn fetch(_: &Universe) -> Self::Fetch {}
640}
641
642impl<T> ResQuery for &T
643where
644    T: 'static,
645{
646    type Fetch = ResRead<T>;
647
648    fn fetch(universe: &Universe) -> Self::Fetch {
649        universe.expect_resource::<T>()
650    }
651}
652
653impl<T> ResQuery for &mut T
654where
655    T: 'static,
656{
657    type Fetch = ResWrite<T>;
658
659    fn fetch(universe: &Universe) -> Self::Fetch {
660        universe.expect_resource_mut::<T>()
661    }
662}
663
664impl<T> ResQuery for Option<&T>
665where
666    T: 'static,
667{
668    type Fetch = Option<ResRead<T>>;
669
670    fn fetch(universe: &Universe) -> Self::Fetch {
671        universe.resource::<T>()
672    }
673}
674
675impl<T> ResQuery for Option<&mut T>
676where
677    T: 'static,
678{
679    type Fetch = Option<ResWrite<T>>;
680
681    fn fetch(universe: &Universe) -> Self::Fetch {
682        universe.resource_mut::<T>()
683    }
684}
685
686macro_rules! impl_res_query {
687    ( $( $ty:ident ),+ ) => {
688        impl<$( $ty ),+> ResAccess for ( $( $ty, )+ ) where $( $ty: ResAccess ),+ {}
689
690        impl<$( $ty ),+> ResQuery for ( $( $ty, )+ ) where $( $ty: ResQuery ),+ {
691            type Fetch = ( $( $ty::Fetch, )+ );
692
693            fn fetch(universe: &Universe) -> Self::Fetch {
694                ( $( $ty::fetch(universe), )+ )
695            }
696        }
697    }
698}
699
700impl_res_query!(A);
701impl_res_query!(A, B);
702impl_res_query!(A, B, C);
703impl_res_query!(A, B, C, D);
704impl_res_query!(A, B, C, D, E);
705impl_res_query!(A, B, C, D, E, F);
706impl_res_query!(A, B, C, D, E, F, G);
707impl_res_query!(A, B, C, D, E, F, G, H);
708impl_res_query!(A, B, C, D, E, F, G, H, I);
709impl_res_query!(A, B, C, D, E, F, G, H, I, J);
710impl_res_query!(A, B, C, D, E, F, G, H, I, J, K);
711impl_res_query!(A, B, C, D, E, F, G, H, I, J, K, L);
712impl_res_query!(A, B, C, D, E, F, G, H, I, J, K, L, M);
713impl_res_query!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
714impl_res_query!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
715
716pub trait AccessType {
717    fn feed_types(_reads: &mut HashSet<TypeId>, _writes: &mut HashSet<TypeId>) {}
718
719    /// ([reads], [writes])
720    fn get_types() -> (HashSet<TypeId>, HashSet<TypeId>) {
721        let mut reads = HashSet::new();
722        let mut writes = HashSet::new();
723        Self::feed_types(&mut reads, &mut writes);
724        (reads, writes)
725    }
726}
727
728impl AccessType for () {}
729
730impl AccessType for WorldRef {
731    fn feed_types(reads: &mut HashSet<TypeId>, _: &mut HashSet<TypeId>) {
732        reads.insert(TypeId::of::<World>());
733    }
734}
735
736impl<T> AccessType for Comp<T>
737where
738    T: AccessType,
739{
740    fn feed_types(reads: &mut HashSet<TypeId>, writes: &mut HashSet<TypeId>) {
741        T::feed_types(reads, writes);
742    }
743}
744
745impl<T> AccessType for &T
746where
747    T: 'static,
748{
749    fn feed_types(reads: &mut HashSet<TypeId>, _: &mut HashSet<TypeId>) {
750        reads.insert(TypeId::of::<T>());
751    }
752}
753
754impl<T> AccessType for &mut T
755where
756    T: 'static,
757{
758    fn feed_types(_: &mut HashSet<TypeId>, writes: &mut HashSet<TypeId>) {
759        writes.insert(TypeId::of::<T>());
760    }
761}
762
763macro_rules! impl_access_type {
764    ( $( $ty:ident ),+ ) => {
765        impl<$( $ty ),+> AccessType for ( $( $ty, )+ ) where $( $ty: AccessType ),+ {
766            fn feed_types(reads: &mut HashSet<TypeId>,writes: &mut HashSet<TypeId>) {
767                $( $ty::feed_types(reads, writes); )+
768            }
769        }
770    }
771}
772
773impl_access_type!(A);
774impl_access_type!(A, B);
775impl_access_type!(A, B, C);
776impl_access_type!(A, B, C, D);
777impl_access_type!(A, B, C, D, E);
778impl_access_type!(A, B, C, D, E, F);
779impl_access_type!(A, B, C, D, E, F, G);
780impl_access_type!(A, B, C, D, E, F, G, H);
781impl_access_type!(A, B, C, D, E, F, G, H, I);
782impl_access_type!(A, B, C, D, E, F, G, H, I, J);
783impl_access_type!(A, B, C, D, E, F, G, H, I, J, K);
784impl_access_type!(A, B, C, D, E, F, G, H, I, J, K, L);
785impl_access_type!(A, B, C, D, E, F, G, H, I, J, K, L, M);
786impl_access_type!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
787impl_access_type!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
788
789#[cfg(test)]
790mod tests {
791    use super::*;
792
793    #[test]
794    fn test_send_sync() {
795        fn foo<T: Send + Sync>() {
796            println!("{} is Send + Sync", std::any::type_name::<T>());
797        }
798
799        foo::<Universe>();
800        foo::<Multiverse>();
801    }
802}