bevy_ecs/system/
exclusive_system_param.rs

1use crate::{
2    prelude::{FromWorld, QueryState},
3    query::{QueryData, QueryFilter},
4    system::{Local, SystemMeta, SystemParam, SystemState},
5    world::World,
6};
7use bevy_utils::{all_tuples, synccell::SyncCell};
8use core::marker::PhantomData;
9
10/// A parameter that can be used in an exclusive system (a system with an `&mut World` parameter).
11/// Any parameters implementing this trait must come after the `&mut World` parameter.
12#[diagnostic::on_unimplemented(
13    message = "`{Self}` can not be used as a parameter for an exclusive system",
14    label = "invalid system parameter"
15)]
16pub trait ExclusiveSystemParam: Sized {
17    /// Used to store data which persists across invocations of a system.
18    type State: Send + Sync + 'static;
19    /// The item type returned when constructing this system param.
20    /// See [`SystemParam::Item`].
21    type Item<'s>: ExclusiveSystemParam<State = Self::State>;
22
23    /// Creates a new instance of this param's [`State`](Self::State).
24    fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self::State;
25
26    /// Creates a parameter to be passed into an [`ExclusiveSystemParamFunction`].
27    ///
28    /// [`ExclusiveSystemParamFunction`]: super::ExclusiveSystemParamFunction
29    fn get_param<'s>(state: &'s mut Self::State, system_meta: &SystemMeta) -> Self::Item<'s>;
30}
31
32/// Shorthand way of accessing the associated type [`ExclusiveSystemParam::Item`]
33/// for a given [`ExclusiveSystemParam`].
34pub type ExclusiveSystemParamItem<'s, P> = <P as ExclusiveSystemParam>::Item<'s>;
35
36impl<'a, D: QueryData + 'static, F: QueryFilter + 'static> ExclusiveSystemParam
37    for &'a mut QueryState<D, F>
38{
39    type State = QueryState<D, F>;
40    type Item<'s> = &'s mut QueryState<D, F>;
41
42    fn init(world: &mut World, _system_meta: &mut SystemMeta) -> Self::State {
43        QueryState::new(world)
44    }
45
46    fn get_param<'s>(state: &'s mut Self::State, _system_meta: &SystemMeta) -> Self::Item<'s> {
47        state
48    }
49}
50
51impl<'a, P: SystemParam + 'static> ExclusiveSystemParam for &'a mut SystemState<P> {
52    type State = SystemState<P>;
53    type Item<'s> = &'s mut SystemState<P>;
54
55    fn init(world: &mut World, _system_meta: &mut SystemMeta) -> Self::State {
56        SystemState::new(world)
57    }
58
59    fn get_param<'s>(state: &'s mut Self::State, _system_meta: &SystemMeta) -> Self::Item<'s> {
60        state
61    }
62}
63
64impl<'_s, T: FromWorld + Send + 'static> ExclusiveSystemParam for Local<'_s, T> {
65    type State = SyncCell<T>;
66    type Item<'s> = Local<'s, T>;
67
68    fn init(world: &mut World, _system_meta: &mut SystemMeta) -> Self::State {
69        SyncCell::new(T::from_world(world))
70    }
71
72    fn get_param<'s>(state: &'s mut Self::State, _system_meta: &SystemMeta) -> Self::Item<'s> {
73        Local(state.get())
74    }
75}
76
77impl<S: ?Sized> ExclusiveSystemParam for PhantomData<S> {
78    type State = ();
79    type Item<'s> = PhantomData<S>;
80
81    fn init(_world: &mut World, _system_meta: &mut SystemMeta) -> Self::State {}
82
83    fn get_param<'s>(_state: &'s mut Self::State, _system_meta: &SystemMeta) -> Self::Item<'s> {
84        PhantomData
85    }
86}
87
88macro_rules! impl_exclusive_system_param_tuple {
89    ($(#[$meta:meta])* $($param: ident),*) => {
90        #[allow(unused_variables)]
91        #[allow(non_snake_case)]
92        $(#[$meta])*
93        impl<$($param: ExclusiveSystemParam),*> ExclusiveSystemParam for ($($param,)*) {
94            type State = ($($param::State,)*);
95            type Item<'s> = ($($param::Item<'s>,)*);
96
97            #[inline]
98            fn init(_world: &mut World, _system_meta: &mut SystemMeta) -> Self::State {
99                (($($param::init(_world, _system_meta),)*))
100            }
101
102            #[inline]
103            #[allow(clippy::unused_unit)]
104            fn get_param<'s>(
105                state: &'s mut Self::State,
106                system_meta: &SystemMeta,
107            ) -> Self::Item<'s> {
108
109                let ($($param,)*) = state;
110                ($($param::get_param($param, system_meta),)*)
111            }
112        }
113    };
114}
115
116all_tuples!(
117    #[doc(fake_variadic)]
118    impl_exclusive_system_param_tuple,
119    0,
120    16,
121    P
122);
123
124#[cfg(test)]
125mod tests {
126    use crate as bevy_ecs;
127    use crate::{schedule::Schedule, system::Local, world::World};
128    use bevy_ecs_macros::Resource;
129    use core::marker::PhantomData;
130
131    #[test]
132    fn test_exclusive_system_params() {
133        #[derive(Resource, Default)]
134        struct Res {
135            test_value: u32,
136        }
137
138        fn my_system(world: &mut World, mut local: Local<u32>, _phantom: PhantomData<Vec<u32>>) {
139            assert_eq!(world.resource::<Res>().test_value, *local);
140            *local += 1;
141            world.resource_mut::<Res>().test_value += 1;
142        }
143
144        let mut schedule = Schedule::default();
145        schedule.add_systems(my_system);
146
147        let mut world = World::default();
148        world.init_resource::<Res>();
149
150        schedule.run(&mut world);
151        schedule.run(&mut world);
152
153        assert_eq!(2, world.get_resource::<Res>().unwrap().test_value);
154    }
155}