world_dispatcher/
system.rs

1use crate::*;
2
3/// Struct used to run a system function using the world.
4/// This struct is also used internally by the `Dispatcher` to create a coherent
5/// execution sequence.
6pub struct System {
7    pub(crate) initialize: Box<dyn Fn(&mut World) + Send>,
8    pub(crate) lock:
9        Box<dyn Fn(*const World, *mut Vec<Box<dyn RefLifetime>>) -> SystemResult + Send>,
10    pub(crate) run_fn: Box<dyn FnMut(&World) -> SystemResult + Send>,
11}
12
13impl System {
14    /// Initializes the resources required to run this system inside of the
15    /// provided `World`, if those resources don't already exist.
16    ///
17    /// This is called automatically if you use a `Dispatcher`, so in most
18    /// cases it is not required to call it manually.
19    pub fn initialize(&self, world: &mut World) {
20        (self.initialize)(world)
21    }
22    /// Runs the system's function using the provided `World`'s resources.
23    pub fn run(&mut self, world: &World) -> SystemResult {
24        (self.run_fn)(world)
25    }
26}
27
28/// Converts a function into a `System`. It is required to execute a function
29/// automatically from `World`'s resources.
30/// This trait is automatically implemented for functions taking 12 arguments (22 if using the
31/// `big_systems` feature)
32/// or less where:
33/// - All arguments are immutable or mutable references.
34/// - All immutable references are placed *before* all mutable references.
35/// - All arguments implement `Default`.
36/// - Does not use the same type twice.
37/// - Returns a `SystemResult` (usually just `Ok(())`).
38pub trait IntoSystem<R> {
39    fn system(self) -> System;
40}
41
42macro_rules! impl_system {
43    ($($id:ident,)* $(&mut $idmut:ident,)*) => {
44        impl<$($id: Send + Sync,)* $($idmut: Send + Sync,)* F> IntoSystem<($(&$id,)* $(&mut $idmut,)*)> for F
45        where
46            $($id: Default+'static,)*
47            $($idmut: Default+'static,)*
48            F: Fn($(&$id,)* $(&mut $idmut,)*) -> SystemResult + 'static + Send,
49        {
50            fn system(self) -> System {
51                System {
52                    initialize: Box::new(|_world: &mut World| {
53                        $(_world.initialize::<$id>();)*
54                        $(_world.initialize::<$idmut>();)*
55                    }),
56                    lock: Box::new(|_world: *const World, _locked: *mut Vec<Box<dyn RefLifetime>>| {
57                        // Unsafe: used to extend the lifetime because we need to store the
58                        // reference of a value that is inside a RefCell to keep the counter
59                        // incremented.
60                        $(unsafe {(&mut *_locked).push(Box::new((*_world).get::<$id>()?))};)*
61                        $(unsafe {(&mut *_locked).push(Box::new((*_world).get_mut::<$idmut>()?))};)*
62                        Ok(())
63                    }),
64                    run_fn: Box::new(move |_world: &World| {
65                        self($(&*_world.get::<$id>()?,)* $(&mut *_world.get_mut::<$idmut>()?),*)
66                    }),
67                }
68            }
69        }
70    }
71}
72
73macro_rules! impl_system_muts {
74    ($($processed:ident),*$(,)?;) => {
75        impl_system!($(&mut $processed,)*);
76    };
77    ($($processed:ident),*$(,)?; $head:ident, $($tail:ident,)*) => {
78        impl_system!($($tail,)* $head, $(&mut $processed,)*);
79        impl_system_muts!($($processed,)* $head; $($tail,)*);
80    }
81}
82macro_rules! impl_systems {
83    // base case
84    () => {};
85    ($head:ident, $($idents:ident,)*) => {
86        // recursive call
87        impl_system_muts!(; $head, $($idents,)*);
88        impl_systems!($($idents,)*);
89    }
90}
91
92impl_system!();
93#[cfg(not(feature="big_systems"))]
94impl_systems!(A, B, C, D, E, G, H, I, J, K, L, M,);
95// Sometimes I just hate rust. This compiles *very* slowly.
96#[cfg(feature="big_systems")]
97// 16
98//impl_systems!(A, B, C, D, E, G, H, I, J, K, L, M, O, P, Q, R,);
99// 26, 17s build time
100//impl_systems!(A, B, C, D, E, G, H, I, J, K, L, M, O, P, Q, R, S, T, U, V, W, X, Y, Z, AA,);
101// 22, 10s build time
102impl_systems!(A, B, C, D, E, G, H, I, J, K, L, M, O, P, Q, R, S, T, U, V, W,);
103
104#[cfg(test)]
105mod tests {
106    use crate::*;
107    use wasm_bindgen_test::*;
108
109    #[test]
110    #[wasm_bindgen_test]
111    fn convert_system() {
112        let _ = generic::<u32>.system();
113        fn tmp(_var1: &u32, _var2: &u64, _var3: &mut i32, _var4: &mut i64) -> SystemResult {
114            Ok(())
115        }
116        // Technically reusing the same type is incorrect and causes a runtime panic.
117        // However, there doesn't seem to be a clean way to handle type inequality in generics.
118        fn tmp2(
119            _var1: &u32,
120            _var2: &u64,
121            _var3: &mut i32,
122            _var4: &mut i64,
123            _var5: &mut i64,
124            _var6: &mut i64,
125            _var7: &mut i64,
126            _var8: &mut i64,
127            _var9: &mut i64,
128            _var10: &mut i64,
129            _var11: &mut i64,
130            _var12: &mut i64,
131        ) -> SystemResult {
132            Ok(())
133        }
134        let _ = tmp.system();
135        let _ = tmp2.system();
136    }
137
138    #[test]
139    #[wasm_bindgen_test]
140    fn system_is_send() {
141        let x = 6;
142        send(
143            (move |_var1: &u32| {
144                let _y = x;
145                Ok(())
146            })
147            .system(),
148        );
149        send((|| Ok(())).system());
150        send(sys.system());
151    }
152
153    fn sys(_var1: &u32) -> SystemResult {
154        Ok(())
155    }
156    fn generic<T>(_t: &T) -> SystemResult {
157        Ok(())
158    }
159    fn send<T: Send>(_t: T) {}
160
161    #[test]
162    #[wasm_bindgen_test]
163    fn manual_system_run() {
164        let mut world = World::default();
165        world.initialize::<u32>();
166        generic::<u32>.system().run(&world).unwrap();
167    }
168
169    #[test]
170    #[wasm_bindgen_test]
171    fn system_replace_resource() {
172        #[derive(Default)]
173        pub struct A;
174        #[derive(Default)]
175        pub struct B {
176            x: u32,
177        }
178        let mut world = World::default();
179        let mut my_system = (|_a: &A, b: &mut B| {
180            let b2 = B { x: 45 };
181            *b = b2;
182            Ok(())
183        })
184        .system();
185        my_system.initialize(&mut world);
186        my_system.run(&world).unwrap();
187        assert_eq!(world.get::<B>().unwrap().x, 45);
188    }
189}