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, IntoSystem,
8 System, SystemIn, SystemInput, SystemMeta,
9 },
10 world::{unsafe_world_cell::UnsafeWorldCell, World},
11};
12
13use alloc::borrow::Cow;
14use bevy_utils::all_tuples;
15use core::marker::PhantomData;
16
17pub struct ExclusiveFunctionSystem<Marker, F>
24where
25 F: ExclusiveSystemParamFunction<Marker>,
26{
27 func: F,
28 param_state: Option<<F::Param as ExclusiveSystemParam>::State>,
29 system_meta: SystemMeta,
30 marker: PhantomData<fn() -> Marker>,
32}
33
34impl<Marker, F> ExclusiveFunctionSystem<Marker, F>
35where
36 F: ExclusiveSystemParamFunction<Marker>,
37{
38 pub fn with_name(mut self, new_name: impl Into<Cow<'static, str>>) -> Self {
42 self.system_meta.set_name(new_name.into());
43 self
44 }
45}
46
47#[doc(hidden)]
49pub struct IsExclusiveFunctionSystem;
50
51impl<Marker, F> IntoSystem<F::In, F::Out, (IsExclusiveFunctionSystem, Marker)> for F
52where
53 Marker: 'static,
54 F: ExclusiveSystemParamFunction<Marker>,
55{
56 type System = ExclusiveFunctionSystem<Marker, F>;
57 fn into_system(func: Self) -> Self::System {
58 ExclusiveFunctionSystem {
59 func,
60 param_state: None,
61 system_meta: SystemMeta::new::<F>(),
62 marker: PhantomData,
63 }
64 }
65}
66
67const PARAM_MESSAGE: &str = "System's param_state was not found. Did you forget to initialize this system before running it?";
68
69impl<Marker, F> System for ExclusiveFunctionSystem<Marker, F>
70where
71 Marker: 'static,
72 F: ExclusiveSystemParamFunction<Marker>,
73{
74 type In = F::In;
75 type Out = F::Out;
76
77 #[inline]
78 fn name(&self) -> Cow<'static, str> {
79 self.system_meta.name.clone()
80 }
81
82 #[inline]
83 fn component_access(&self) -> &Access<ComponentId> {
84 self.system_meta.component_access_set.combined_access()
85 }
86
87 #[inline]
88 fn archetype_component_access(&self) -> &Access<ArchetypeComponentId> {
89 &self.system_meta.archetype_component_access
90 }
91
92 #[inline]
93 fn is_send(&self) -> bool {
94 false
98 }
99
100 #[inline]
101 fn is_exclusive(&self) -> bool {
102 true
103 }
104
105 #[inline]
106 fn has_deferred(&self) -> bool {
107 false
109 }
110
111 #[inline]
112 unsafe fn run_unsafe(
113 &mut self,
114 _input: SystemIn<'_, Self>,
115 _world: UnsafeWorldCell,
116 ) -> Self::Out {
117 panic!("Cannot run exclusive systems with a shared World reference");
118 }
119
120 fn run(&mut self, input: SystemIn<'_, Self>, world: &mut World) -> Self::Out {
121 world.last_change_tick_scope(self.system_meta.last_run, |world| {
122 #[cfg(feature = "trace")]
123 let _span_guard = self.system_meta.system_span.enter();
124
125 let params = F::Param::get_param(
126 self.param_state.as_mut().expect(PARAM_MESSAGE),
127 &self.system_meta,
128 );
129 let out = self.func.run(world, input, params);
130
131 world.flush();
132 self.system_meta.last_run = world.increment_change_tick();
133
134 out
135 })
136 }
137
138 #[inline]
139 fn apply_deferred(&mut self, _world: &mut World) {
140 }
144
145 #[inline]
146 fn queue_deferred(&mut self, _world: crate::world::DeferredWorld) {
147 }
151
152 #[inline]
153 unsafe fn validate_param_unsafe(&mut self, _world: UnsafeWorldCell) -> bool {
154 true
156 }
157
158 #[inline]
159 fn initialize(&mut self, world: &mut World) {
160 self.system_meta.last_run = world.change_tick().relative_to(Tick::MAX);
161 self.param_state = Some(F::Param::init(world, &mut self.system_meta));
162 }
163
164 fn update_archetype_component_access(&mut self, _world: UnsafeWorldCell) {}
165
166 #[inline]
167 fn check_change_tick(&mut self, change_tick: Tick) {
168 check_system_change_tick(
169 &mut self.system_meta.last_run,
170 change_tick,
171 self.system_meta.name.as_ref(),
172 );
173 }
174
175 fn default_system_sets(&self) -> Vec<InternedSystemSet> {
176 let set = crate::schedule::SystemTypeSet::<Self>::new();
177 vec![set.intern()]
178 }
179
180 fn get_last_run(&self) -> Tick {
181 self.system_meta.last_run
182 }
183
184 fn set_last_run(&mut self, last_run: Tick) {
185 self.system_meta.last_run = last_run;
186 }
187}
188
189#[diagnostic::on_unimplemented(
194 message = "`{Self}` is not an exclusive system",
195 label = "invalid system"
196)]
197pub trait ExclusiveSystemParamFunction<Marker>: Send + Sync + 'static {
198 type In: SystemInput;
200
201 type Out;
203
204 type Param: ExclusiveSystemParam;
206
207 fn run(
209 &mut self,
210 world: &mut World,
211 input: <Self::In as SystemInput>::Inner<'_>,
212 param_value: ExclusiveSystemParamItem<Self::Param>,
213 ) -> Self::Out;
214}
215
216#[doc(hidden)]
218pub struct HasExclusiveSystemInput;
219
220macro_rules! impl_exclusive_system_function {
221 ($($param: ident),*) => {
222 #[allow(non_snake_case)]
223 impl<Out, Func, $($param: ExclusiveSystemParam),*> ExclusiveSystemParamFunction<fn($($param,)*) -> Out> for Func
224 where
225 Func: Send + Sync + 'static,
226 for <'a> &'a mut Func:
227 FnMut(&mut World, $($param),*) -> Out +
228 FnMut(&mut World, $(ExclusiveSystemParamItem<$param>),*) -> Out,
229 Out: 'static,
230 {
231 type In = ();
232 type Out = Out;
233 type Param = ($($param,)*);
234 #[inline]
235 fn run(&mut self, world: &mut World, _in: (), param_value: ExclusiveSystemParamItem< ($($param,)*)>) -> Out {
236 #[allow(clippy::too_many_arguments)]
240 fn call_inner<Out, $($param,)*>(
241 mut f: impl FnMut(&mut World, $($param,)*) -> Out,
242 world: &mut World,
243 $($param: $param,)*
244 ) -> Out {
245 f(world, $($param,)*)
246 }
247 let ($($param,)*) = param_value;
248 call_inner(self, world, $($param),*)
249 }
250 }
251
252 #[allow(non_snake_case)]
253 impl<In, Out, Func, $($param: ExclusiveSystemParam),*> ExclusiveSystemParamFunction<(HasExclusiveSystemInput, fn(In, $($param,)*) -> Out)> for Func
254 where
255 Func: Send + Sync + 'static,
256 for <'a> &'a mut Func:
257 FnMut(In, &mut World, $($param),*) -> Out +
258 FnMut(In::Param<'_>, &mut World, $(ExclusiveSystemParamItem<$param>),*) -> Out,
259 In: SystemInput + 'static,
260 Out: 'static,
261 {
262 type In = In;
263 type Out = Out;
264 type Param = ($($param,)*);
265 #[inline]
266 fn run(&mut self, world: &mut World, input: In::Inner<'_>, param_value: ExclusiveSystemParamItem< ($($param,)*)>) -> Out {
267 #[allow(clippy::too_many_arguments)]
271 fn call_inner<In: SystemInput, Out, $($param,)*>(
272 mut f: impl FnMut(In::Param<'_>, &mut World, $($param,)*) -> Out,
273 input: In::Inner<'_>,
274 world: &mut World,
275 $($param: $param,)*
276 ) -> Out {
277 f(In::wrap(input), world, $($param,)*)
278 }
279 let ($($param,)*) = param_value;
280 call_inner(self, input, world, $($param),*)
281 }
282 }
283 };
284}
285all_tuples!(impl_exclusive_system_function, 0, 16, F);
288
289#[cfg(test)]
290mod tests {
291 use crate::system::input::SystemInput;
292
293 use super::*;
294
295 #[test]
296 fn into_system_type_id_consistency() {
297 fn test<T, In: SystemInput, Out, Marker>(function: T)
298 where
299 T: IntoSystem<In, Out, Marker> + Copy,
300 {
301 fn reference_system(_world: &mut World) {}
302
303 use core::any::TypeId;
304
305 let system = IntoSystem::into_system(function);
306
307 assert_eq!(
308 system.type_id(),
309 function.system_type_id(),
310 "System::type_id should be consistent with IntoSystem::system_type_id"
311 );
312
313 assert_eq!(
314 system.type_id(),
315 TypeId::of::<T::System>(),
316 "System::type_id should be consistent with TypeId::of::<T::System>()"
317 );
318
319 assert_ne!(
320 system.type_id(),
321 IntoSystem::into_system(reference_system).type_id(),
322 "Different systems should have different TypeIds"
323 );
324 }
325
326 fn exclusive_function_system(_world: &mut World) {}
327
328 test(exclusive_function_system);
329 }
330}