zengine_ecs/system/
mod.rs

1//! Tools for controlling behavior in an ECS application
2//!
3//! Systems define how an ECS application behaves.
4//! A system is added to a `Stage` to be able to run.
5//! A function that use only system parameters can be converted into a system.
6//! Usually this conversion is done automatically.
7//!
8//! System functions can query and mutate the ZENgine state using its parameters.
9//! Only types that implement [SystemParam]
10//! can be used, automatically fetching data from the World.
11//!
12//! # Example
13//! ```
14//! use zengine_macro::{Component, Resource};
15//! use zengine_ecs::{
16//!     Entity,
17//!     system::ResMut,
18//!     query::{Query, QueryIterMut}
19//! };
20//!
21//! #[derive(Component, Debug)]
22//! struct HeathPoints(u32);
23//!
24//! #[derive(Component, Debug)]
25//! struct Life(u32);
26//!
27//! #[derive(Resource, Default, Debug)]
28//! struct DeathEntities(Vec<Entity>);
29//!
30//! fn custom_system(
31//!     mut query: Query<(Entity, &HeathPoints, &mut Life)>,
32//!     mut death: ResMut<DeathEntities>,
33//! ) {
34//!     for (entity, hp, life) in query.iter_mut() {
35//!         if hp.0 == 0 {
36//!             life.0 -= 1;
37//!             if life.0 == 0 {
38//!                 death.0.push(*entity);
39//!             }
40//!         }
41//!     }
42//! }
43//! ```
44//!
45//! # System ordering
46//! Systems inside a Stage are executed sequentially based on the insertion order
47//!
48//! # System Parameters
49//! Following is the complete list of accepted types as system parameters:
50//! - [Query](crate::query::Query) to query over entities and components
51//! - [Res] and `Option<Res>` to get immutable access to a resource (or an optional resource)
52//! - [ResMut] and `Option<ResMut>` to get mutable access to a resource (or an optional resource)
53//! - [UnsendableRes] and `Option<UnsendableRes>` to get immutable access to
54//! an unsendable resource (or an optional unsendable resource)
55//! - [UnsendableResMut] and `Option<UnsendableResMut>` to get mutable access to
56//! an unsendable resource (or an optional unsendable resource)
57//! - [Event] to get access to an event without subscribing
58//! - [EventStream] to get access to an event with a subscription
59//! - [EventPublisher] to publish an event
60//! - [Commands] to send command to the [World]
61//! - [Local] to get access to data owned by the system
62
63use zengine_macro::all_tuples;
64
65use crate::world::World;
66
67mod system_parameter;
68pub use system_parameter::*;
69
70/// A trait implemented for all functions that can be used as a [System]
71pub trait SystemFunction<P: SystemParam> {
72    fn run_function(&self, parameter: SystemParamItem<P>);
73}
74
75/// Conversion trait to turn something into a [System]
76pub trait IntoSystem<P: SystemParam> {
77    type System: SystemFunction<P>;
78
79    fn into_system(self) -> SystemWrapper<Self::System, P>;
80}
81
82impl<Param: SystemParam, F> IntoSystem<Param> for F
83where
84    F: SystemFunction<Param>,
85{
86    type System = F;
87    fn into_system(self) -> SystemWrapper<Self::System, Param> {
88        SystemWrapper {
89            _marker: std::marker::PhantomData::default(),
90            function: self,
91            param_state: Param::Fetch::default(),
92        }
93    }
94}
95
96/// Wraps a function that implements the [SystemFunction] trait
97pub struct SystemWrapper<F: SystemFunction<P>, P: SystemParam> {
98    _marker: std::marker::PhantomData<P>,
99    function: F,
100    param_state: P::Fetch,
101}
102
103/// System trait
104pub trait System {
105    fn init(&mut self, world: &mut World);
106
107    fn run(&mut self, world: &World);
108
109    fn apply(&mut self, world: &mut World);
110}
111
112impl<F: SystemFunction<P>, P: SystemParam> System for SystemWrapper<F, P> {
113    fn init(&mut self, world: &mut World) {
114        self.param_state.init(world);
115    }
116
117    fn run(&mut self, world: &World) {
118        let data: <<P as SystemParam>::Fetch as SystemParamFetch>::Item =
119            <P as SystemParam>::Fetch::fetch(&mut self.param_state, world);
120        self.function.run_function(data);
121    }
122
123    fn apply(&mut self, world: &mut World) {
124        self.param_state.apply(world);
125    }
126}
127
128macro_rules! impl_system_function {
129    () => {
130        #[allow(non_snake_case)]
131        impl<'a> SystemParamFetch<'a> for () {
132            type Item = ();
133
134            fn fetch(&mut self, _world: &'a World) -> Self::Item {}
135        }
136
137        impl SystemParam for () {
138            type Fetch = ();
139        }
140
141        #[allow(non_snake_case)]
142        impl<Sys> SystemFunction<()> for Sys
143        where
144            for<'a> &'a Sys: Fn(),
145        {
146            fn run_function(&self, _parameter: SystemParamItem<()>) {
147                #[allow(clippy::too_many_arguments)]
148                fn call_inner(f: impl Fn()) {
149                    f()
150                }
151
152                call_inner(self)
153            }
154        }
155    };
156    ($($param: ident),+) => {
157        #[allow(non_snake_case)]
158        impl<'a, $($param: SystemParamFetch<'a>),*> SystemParamFetch<'a> for ($($param,)*) {
159            type Item = ($($param::Item,)*);
160
161            fn init(&mut self, world: &mut World) {
162                let ($($param,)*) = self;
163
164                ($($param::init($param, world),)*);
165            }
166
167            fn fetch(&'a mut self, world: &'a World) -> Self::Item {
168                let ($($param,)*) = self;
169
170                ($($param::fetch($param, world),)*)
171            }
172
173            fn apply(&mut self, world: &mut World) {
174                let ($($param,)*) = self;
175
176                ($($param::apply($param, world),)*);
177            }
178        }
179
180        impl<$($param: SystemParam),*> SystemParam for ($($param,)*) {
181            type Fetch = ($($param::Fetch,)*);
182        }
183
184        #[allow(non_snake_case)]
185        impl<$($param: SystemParam),*, Sys> SystemFunction<($($param,)*)> for Sys
186        where
187            for<'a> &'a Sys: Fn( $($param),*)
188                + Fn(
189                    $(<<$param as SystemParam>::Fetch as SystemParamFetch>::Item,)*
190                ),
191        {
192            fn run_function(&self, parameter: SystemParamItem<($($param,)*)>) {
193                #[allow(clippy::too_many_arguments)]
194                fn call_inner<$($param),*>(f: impl Fn($($param,)*), $($param: $param,)*) {
195                    f($($param,)*)
196                }
197
198                let ($($param,)*) = parameter;
199                call_inner(self, $($param),*)
200            }
201        }
202    }
203}
204all_tuples!(impl_system_function, 0, 12, F);
205
206#[cfg(test)]
207mod tests {
208
209    use crate::{query::Query, world::World, Component, Resource};
210
211    use super::{
212        system_parameter::{Local, Res},
213        IntoSystem, System, SystemParam,
214    };
215
216    #[derive(Default)]
217    struct Executor {
218        world: World,
219        systems: Vec<Box<dyn System>>,
220    }
221
222    impl Executor {
223        fn add_system<Params: SystemParam + 'static>(
224            mut self,
225            system: impl IntoSystem<Params> + 'static,
226        ) -> Self {
227            self.systems.push(Box::new(system.into_system()));
228
229            self
230        }
231    }
232
233    impl Executor {
234        pub fn run(mut self) {
235            for s in self.systems.iter_mut() {
236                s.init(&mut self.world);
237            }
238
239            for s in self.systems.iter_mut() {
240                s.run(&self.world);
241            }
242        }
243    }
244
245    #[derive(PartialEq, Debug)]
246    struct CacheTest {
247        data: u32,
248    }
249    impl Default for CacheTest {
250        fn default() -> Self {
251            Self { data: 6 }
252        }
253    }
254
255    #[derive(Debug, Default)]
256    struct Resource1 {
257        _data: u32,
258    }
259    impl Resource for Resource1 {}
260
261    #[derive(Debug)]
262    struct Component1 {
263        _data: u32,
264    }
265    impl Component for Component1 {}
266
267    fn test() {
268        println!("hello")
269    }
270
271    fn test1(_query: Query<(&Component1,)>) {}
272    fn test2(_res: Res<Resource1>) {}
273    fn test3(_res: Res<Resource1>, _query: Query<(&Component1,)>) {}
274    fn test4(_local: Local<Resource1>, _query: Query<(&Component1,)>) {}
275
276    #[test]
277    fn test_executor() {
278        Executor::default()
279            .add_system(test)
280            .add_system(test1)
281            .add_system(test2)
282            .add_system(test3)
283            .add_system(test4)
284            .run();
285    }
286}