1use 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
145#[expect(clippy :: allow_attributes, reason =
"This is within a macro, and as such, the below lints may not always apply.")]
#[allow(non_snake_case, reason =
"Certain variable names are provided by the caller, not by us.")]
#[allow(unused_variables, reason =
"Zero-length tuples won't use any of the parameters.")]
#[allow(clippy :: unused_unit, reason = "Zero length tuple is unit.")]
#[doc(hidden)]
impl<P0: ExclusiveSystemParam, P1: ExclusiveSystemParam,
P2: ExclusiveSystemParam, P3: ExclusiveSystemParam,
P4: ExclusiveSystemParam, P5: ExclusiveSystemParam,
P6: ExclusiveSystemParam, P7: ExclusiveSystemParam,
P8: ExclusiveSystemParam, P9: ExclusiveSystemParam,
P10: ExclusiveSystemParam, P11: ExclusiveSystemParam,
P12: ExclusiveSystemParam, P13: ExclusiveSystemParam,
P14: ExclusiveSystemParam, P15: ExclusiveSystemParam> ExclusiveSystemParam
for (P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15)
{
type State =
(P0::State, P1::State, P2::State, P3::State, P4::State, P5::State,
P6::State, P7::State, P8::State, P9::State, P10::State, P11::State,
P12::State, P13::State, P14::State, P15::State);
type Item<'s> =
(P0::Item<'s>, P1::Item<'s>, P2::Item<'s>, P3::Item<'s>, P4::Item<'s>,
P5::Item<'s>, P6::Item<'s>, P7::Item<'s>, P8::Item<'s>, P9::Item<'s>,
P10::Item<'s>, P11::Item<'s>, P12::Item<'s>, P13::Item<'s>,
P14::Item<'s>, P15::Item<'s>);
#[inline]
fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self::State {
(P0::init(world, system_meta), P1::init(world, system_meta),
P2::init(world, system_meta), P3::init(world, system_meta),
P4::init(world, system_meta), P5::init(world, system_meta),
P6::init(world, system_meta), P7::init(world, system_meta),
P8::init(world, system_meta), P9::init(world, system_meta),
P10::init(world, system_meta), P11::init(world, system_meta),
P12::init(world, system_meta), P13::init(world, system_meta),
P14::init(world, system_meta), P15::init(world, system_meta))
}
#[inline]
fn get_param<'s>(state: &'s mut Self::State, system_meta: &SystemMeta)
-> Result<Self::Item<'s>, SystemParamValidationError> {
let (P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14,
P15) = state;
#[allow(clippy :: unused_unit, reason =
"Zero-length tuples won't have any params to get.")]
Ok((P0::get_param(P0, system_meta)?, P1::get_param(P1, system_meta)?,
P2::get_param(P2, system_meta)?,
P3::get_param(P3, system_meta)?,
P4::get_param(P4, system_meta)?,
P5::get_param(P5, system_meta)?,
P6::get_param(P6, system_meta)?,
P7::get_param(P7, system_meta)?,
P8::get_param(P8, system_meta)?,
P9::get_param(P9, system_meta)?,
P10::get_param(P10, system_meta)?,
P11::get_param(P11, system_meta)?,
P12::get_param(P12, system_meta)?,
P13::get_param(P13, system_meta)?,
P14::get_param(P14, system_meta)?,
P15::get_param(P15, system_meta)?))
}
}all_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}