bevy_ecs/system/
exclusive_system_param.rs1use 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#[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 type State: Send + Sync + 'static;
19 type Item<'s>: ExclusiveSystemParam<State = Self::State>;
22
23 fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self::State;
25
26 fn get_param<'s>(state: &'s mut Self::State, system_meta: &SystemMeta) -> Self::Item<'s>;
30}
31
32pub 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}