1use crate::{
2 archetype::ArchetypeComponentId,
3 component::{ComponentId, Tick},
4 query::Access,
5 schedule::{InternedSystemSet, SystemSet},
6 system::{
7 check_system_change_tick, ExclusiveSystemParam, ExclusiveSystemParamItem, In, IntoSystem,
8 System, SystemMeta,
9 },
10 world::{unsafe_world_cell::UnsafeWorldCell, World},
11};
12
13use bevy_utils::all_tuples;
14use std::{borrow::Cow, marker::PhantomData};
15
16pub struct ExclusiveFunctionSystem<Marker, F>
23where
24 F: ExclusiveSystemParamFunction<Marker>,
25{
26 func: F,
27 param_state: Option<<F::Param as ExclusiveSystemParam>::State>,
28 system_meta: SystemMeta,
29 marker: PhantomData<fn() -> Marker>,
31}
32
33#[doc(hidden)]
35pub struct IsExclusiveFunctionSystem;
36
37impl<Marker, F> IntoSystem<F::In, F::Out, (IsExclusiveFunctionSystem, Marker)> for F
38where
39 Marker: 'static,
40 F: ExclusiveSystemParamFunction<Marker>,
41{
42 type System = ExclusiveFunctionSystem<Marker, F>;
43 fn into_system(func: Self) -> Self::System {
44 ExclusiveFunctionSystem {
45 func,
46 param_state: None,
47 system_meta: SystemMeta::new::<F>(),
48 marker: PhantomData,
49 }
50 }
51}
52
53const PARAM_MESSAGE: &str = "System's param_state was not found. Did you forget to initialize this system before running it?";
54
55impl<Marker, F> System for ExclusiveFunctionSystem<Marker, F>
56where
57 Marker: 'static,
58 F: ExclusiveSystemParamFunction<Marker>,
59{
60 type In = F::In;
61 type Out = F::Out;
62
63 #[inline]
64 fn name(&self) -> Cow<'static, str> {
65 self.system_meta.name.clone()
66 }
67
68 #[inline]
69 fn component_access(&self) -> &Access<ComponentId> {
70 self.system_meta.component_access_set.combined_access()
71 }
72
73 #[inline]
74 fn archetype_component_access(&self) -> &Access<ArchetypeComponentId> {
75 &self.system_meta.archetype_component_access
76 }
77
78 #[inline]
79 fn is_send(&self) -> bool {
80 false
84 }
85
86 #[inline]
87 fn is_exclusive(&self) -> bool {
88 true
89 }
90
91 #[inline]
92 fn has_deferred(&self) -> bool {
93 false
95 }
96
97 #[inline]
98 unsafe fn run_unsafe(&mut self, _input: Self::In, _world: UnsafeWorldCell) -> Self::Out {
99 panic!("Cannot run exclusive systems with a shared World reference");
100 }
101
102 fn run(&mut self, input: Self::In, world: &mut World) -> Self::Out {
103 world.last_change_tick_scope(self.system_meta.last_run, |world| {
104 #[cfg(feature = "trace")]
105 let _span_guard = self.system_meta.system_span.enter();
106
107 let params = F::Param::get_param(
108 self.param_state.as_mut().expect(PARAM_MESSAGE),
109 &self.system_meta,
110 );
111 let out = self.func.run(world, input, params);
112
113 world.flush();
114 let change_tick = world.change_tick.get_mut();
115 self.system_meta.last_run.set(*change_tick);
116 *change_tick = change_tick.wrapping_add(1);
117
118 out
119 })
120 }
121
122 #[inline]
123 fn apply_deferred(&mut self, _world: &mut World) {
124 }
128
129 #[inline]
130 fn queue_deferred(&mut self, _world: crate::world::DeferredWorld) {
131 }
135
136 #[inline]
137 fn initialize(&mut self, world: &mut World) {
138 self.system_meta.last_run = world.change_tick().relative_to(Tick::MAX);
139 self.param_state = Some(F::Param::init(world, &mut self.system_meta));
140 }
141
142 fn update_archetype_component_access(&mut self, _world: UnsafeWorldCell) {}
143
144 #[inline]
145 fn check_change_tick(&mut self, change_tick: Tick) {
146 check_system_change_tick(
147 &mut self.system_meta.last_run,
148 change_tick,
149 self.system_meta.name.as_ref(),
150 );
151 }
152
153 fn default_system_sets(&self) -> Vec<InternedSystemSet> {
154 let set = crate::schedule::SystemTypeSet::<Self>::new();
155 vec![set.intern()]
156 }
157
158 fn get_last_run(&self) -> Tick {
159 self.system_meta.last_run
160 }
161
162 fn set_last_run(&mut self, last_run: Tick) {
163 self.system_meta.last_run = last_run;
164 }
165}
166
167#[diagnostic::on_unimplemented(
172 message = "`{Self}` is not an exclusive system",
173 label = "invalid system"
174)]
175pub trait ExclusiveSystemParamFunction<Marker>: Send + Sync + 'static {
176 type In;
178
179 type Out;
181
182 type Param: ExclusiveSystemParam;
184
185 fn run(
187 &mut self,
188 world: &mut World,
189 input: Self::In,
190 param_value: ExclusiveSystemParamItem<Self::Param>,
191 ) -> Self::Out;
192}
193
194macro_rules! impl_exclusive_system_function {
195 ($($param: ident),*) => {
196 #[allow(non_snake_case)]
197 impl<Out, Func: Send + Sync + 'static, $($param: ExclusiveSystemParam),*> ExclusiveSystemParamFunction<fn($($param,)*) -> Out> for Func
198 where
199 for <'a> &'a mut Func:
200 FnMut(&mut World, $($param),*) -> Out +
201 FnMut(&mut World, $(ExclusiveSystemParamItem<$param>),*) -> Out,
202 Out: 'static,
203 {
204 type In = ();
205 type Out = Out;
206 type Param = ($($param,)*);
207 #[inline]
208 fn run(&mut self, world: &mut World, _in: (), param_value: ExclusiveSystemParamItem< ($($param,)*)>) -> Out {
209 #[allow(clippy::too_many_arguments)]
213 fn call_inner<Out, $($param,)*>(
214 mut f: impl FnMut(&mut World, $($param,)*) -> Out,
215 world: &mut World,
216 $($param: $param,)*
217 ) -> Out {
218 f(world, $($param,)*)
219 }
220 let ($($param,)*) = param_value;
221 call_inner(self, world, $($param),*)
222 }
223 }
224 #[allow(non_snake_case)]
225 impl<Input, Out, Func: Send + Sync + 'static, $($param: ExclusiveSystemParam),*> ExclusiveSystemParamFunction<fn(In<Input>, $($param,)*) -> Out> for Func
226 where
227 for <'a> &'a mut Func:
228 FnMut(In<Input>, &mut World, $($param),*) -> Out +
229 FnMut(In<Input>, &mut World, $(ExclusiveSystemParamItem<$param>),*) -> Out,
230 Out: 'static,
231 {
232 type In = Input;
233 type Out = Out;
234 type Param = ($($param,)*);
235 #[inline]
236 fn run(&mut self, world: &mut World, input: Input, param_value: ExclusiveSystemParamItem< ($($param,)*)>) -> Out {
237 #[allow(clippy::too_many_arguments)]
241 fn call_inner<Input, Out, $($param,)*>(
242 mut f: impl FnMut(In<Input>, &mut World, $($param,)*) -> Out,
243 input: Input,
244 world: &mut World,
245 $($param: $param,)*
246 ) -> Out {
247 f(In(input), world, $($param,)*)
248 }
249 let ($($param,)*) = param_value;
250 call_inner(self, input, world, $($param),*)
251 }
252 }
253 };
254}
255all_tuples!(impl_exclusive_system_function, 0, 16, F);
258
259#[cfg(test)]
260mod tests {
261 use super::*;
262
263 #[test]
264 fn into_system_type_id_consistency() {
265 fn test<T, In, Out, Marker>(function: T)
266 where
267 T: IntoSystem<In, Out, Marker> + Copy,
268 {
269 fn reference_system(_world: &mut World) {}
270
271 use std::any::TypeId;
272
273 let system = IntoSystem::into_system(function);
274
275 assert_eq!(
276 system.type_id(),
277 function.system_type_id(),
278 "System::type_id should be consistent with IntoSystem::system_type_id"
279 );
280
281 assert_eq!(
282 system.type_id(),
283 TypeId::of::<T::System>(),
284 "System::type_id should be consistent with TypeId::of::<T::System>()"
285 );
286
287 assert_ne!(
288 system.type_id(),
289 IntoSystem::into_system(reference_system).type_id(),
290 "Different systems should have different TypeIds"
291 );
292 }
293
294 fn exclusive_function_system(_world: &mut World) {}
295
296 test(exclusive_function_system);
297 }
298}