bevy_ecs/system/
exclusive_system_param.rs1use crate::{
2 prelude::{FromWorld, QueryState},
3 query::{QueryData, QueryFilter},
4 system::{Local, SystemMeta, SystemParam, SystemParamValidationError, SystemState},
5 world::World,
6};
7use bevy_platform::cell::SyncCell;
8use core::marker::PhantomData;
9use variadics_please::all_tuples;
10
11#[diagnostic::on_unimplemented(
14 message = "`{Self}` can not be used as a parameter for an exclusive system",
15 label = "invalid system parameter"
16)]
17pub trait ExclusiveSystemParam: Sized {
18 type State: Send + Sync + 'static;
20 type Item<'s>: ExclusiveSystemParam<State = Self::State>;
23
24 fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self::State;
26
27 fn get_param<'s>(
31 state: &'s mut Self::State,
32 system_meta: &SystemMeta,
33 ) -> Result<Self::Item<'s>, SystemParamValidationError>;
34}
35
36pub type ExclusiveSystemParamItem<'s, P> = <P as ExclusiveSystemParam>::Item<'s>;
39
40impl<'a, D: QueryData + 'static, F: QueryFilter + 'static> ExclusiveSystemParam
41 for &'a mut QueryState<D, F>
42{
43 type State = QueryState<D, F>;
44 type Item<'s> = &'s mut QueryState<D, F>;
45
46 fn init(world: &mut World, _system_meta: &mut SystemMeta) -> Self::State {
47 QueryState::new(world)
48 }
49
50 fn get_param<'s>(
51 state: &'s mut Self::State,
52 _system_meta: &SystemMeta,
53 ) -> Result<Self::Item<'s>, SystemParamValidationError> {
54 Ok(state)
55 }
56}
57
58impl<'a, P: SystemParam + 'static> ExclusiveSystemParam for &'a mut SystemState<P> {
59 type State = SystemState<P>;
60 type Item<'s> = &'s mut SystemState<P>;
61
62 fn init(world: &mut World, _system_meta: &mut SystemMeta) -> Self::State {
63 SystemState::new(world)
64 }
65
66 fn get_param<'s>(
67 state: &'s mut Self::State,
68 _system_meta: &SystemMeta,
69 ) -> Result<Self::Item<'s>, SystemParamValidationError> {
70 Ok(state)
71 }
72}
73
74impl<'_s, T: FromWorld + Send + 'static> ExclusiveSystemParam for Local<'_s, T> {
75 type State = SyncCell<T>;
76 type Item<'s> = Local<'s, T>;
77
78 fn init(world: &mut World, _system_meta: &mut SystemMeta) -> Self::State {
79 SyncCell::new(T::from_world(world))
80 }
81
82 fn get_param<'s>(
83 state: &'s mut Self::State,
84 _system_meta: &SystemMeta,
85 ) -> Result<Self::Item<'s>, SystemParamValidationError> {
86 Ok(Local(state.get()))
87 }
88}
89
90impl<S: ?Sized> ExclusiveSystemParam for PhantomData<S> {
91 type State = ();
92 type Item<'s> = PhantomData<S>;
93
94 fn init(_world: &mut World, _system_meta: &mut SystemMeta) -> Self::State {}
95
96 fn get_param<'s>(
97 _state: &'s mut Self::State,
98 _system_meta: &SystemMeta,
99 ) -> Result<Self::Item<'s>, SystemParamValidationError> {
100 Ok(PhantomData)
101 }
102}
103
104macro_rules! impl_exclusive_system_param_tuple {
105 ($(#[$meta:meta])* $($param: ident),*) => {
106 #[expect(
107 clippy::allow_attributes,
108 reason = "This is within a macro, and as such, the below lints may not always apply."
109 )]
110 #[allow(
111 non_snake_case,
112 reason = "Certain variable names are provided by the caller, not by us."
113 )]
114 #[allow(
115 unused_variables,
116 reason = "Zero-length tuples won't use any of the parameters."
117 )]
118 #[allow(clippy::unused_unit, reason = "Zero length tuple is unit.")]
119 $(#[$meta])*
120 impl<$($param: ExclusiveSystemParam),*> ExclusiveSystemParam for ($($param,)*) {
121 type State = ($($param::State,)*);
122 type Item<'s> = ($($param::Item<'s>,)*);
123
124 #[inline]
125 fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self::State {
126 ($($param::init(world, system_meta),)*)
127 }
128
129 #[inline]
130 fn get_param<'s>(
131 state: &'s mut Self::State,
132 system_meta: &SystemMeta,
133 ) -> Result<Self::Item<'s>, SystemParamValidationError> {
134 let ($($param,)*) = state;
135 #[allow(
136 clippy::unused_unit,
137 reason = "Zero-length tuples won't have any params to get."
138 )]
139 Ok(($($param::get_param($param, system_meta)?,)*))
140 }
141 }
142 };
143}
144
145all_tuples!(
146 #[doc(fake_variadic)]
147 impl_exclusive_system_param_tuple,
148 0,
149 16,
150 P
151);
152
153#[cfg(test)]
154mod tests {
155 use crate::{schedule::Schedule, system::Local, world::World};
156 use alloc::vec::Vec;
157 use bevy_ecs_macros::Resource;
158 use core::marker::PhantomData;
159
160 #[test]
161 fn test_exclusive_system_params() {
162 #[derive(Resource, Default)]
163 struct Res {
164 test_value: u32,
165 }
166
167 fn my_system(world: &mut World, mut local: Local<u32>, _phantom: PhantomData<Vec<u32>>) {
168 assert_eq!(world.resource::<Res>().test_value, *local);
169 *local += 1;
170 world.resource_mut::<Res>().test_value += 1;
171 }
172
173 let mut schedule = Schedule::default();
174 schedule.add_systems(my_system);
175
176 let mut world = World::default();
177 world.init_resource::<Res>();
178
179 schedule.run(&mut world);
180 schedule.run(&mut world);
181
182 assert_eq!(2, world.get_resource::<Res>().unwrap().test_value);
183 }
184}