bevy_ecs/system/
exclusive_function_system.rs

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
17/// A function system that runs with exclusive [`World`] access.
18///
19/// You get this by calling [`IntoSystem::into_system`]  on a function that only accepts
20/// [`ExclusiveSystemParam`]s.
21///
22/// [`ExclusiveFunctionSystem`] must be `.initialized` before they can be run.
23pub 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    // NOTE: PhantomData<fn()-> T> gives this safe Send/Sync impls
31    marker: PhantomData<fn() -> Marker>,
32}
33
34impl<Marker, F> ExclusiveFunctionSystem<Marker, F>
35where
36    F: ExclusiveSystemParamFunction<Marker>,
37{
38    /// Return this system with a new name.
39    ///
40    /// Useful to give closure systems more readable and unique names for debugging and tracing.
41    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/// A marker type used to distinguish exclusive function systems from regular function systems.
48#[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        // exclusive systems should have access to non-send resources
95        // the executor runs exclusive systems on the main thread, so this
96        // field reflects that constraint
97        false
98    }
99
100    #[inline]
101    fn is_exclusive(&self) -> bool {
102        true
103    }
104
105    #[inline]
106    fn has_deferred(&self) -> bool {
107        // exclusive systems have no deferred system params
108        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        // "pure" exclusive systems do not have any buffers to apply.
141        // Systems made by piping a normal system with an exclusive system
142        // might have buffers to apply, but this is handled by `PipeSystem`.
143    }
144
145    #[inline]
146    fn queue_deferred(&mut self, _world: crate::world::DeferredWorld) {
147        // "pure" exclusive systems do not have any buffers to apply.
148        // Systems made by piping a normal system with an exclusive system
149        // might have buffers to apply, but this is handled by `PipeSystem`.
150    }
151
152    #[inline]
153    unsafe fn validate_param_unsafe(&mut self, _world: UnsafeWorldCell) -> bool {
154        // All exclusive system params are always available.
155        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/// A trait implemented for all exclusive system functions that can be used as [`System`]s.
190///
191/// This trait can be useful for making your own systems which accept other systems,
192/// sometimes called higher order systems.
193#[diagnostic::on_unimplemented(
194    message = "`{Self}` is not an exclusive system",
195    label = "invalid system"
196)]
197pub trait ExclusiveSystemParamFunction<Marker>: Send + Sync + 'static {
198    /// The input type to this system. See [`System::In`].
199    type In: SystemInput;
200
201    /// The return type of this system. See [`System::Out`].
202    type Out;
203
204    /// The [`ExclusiveSystemParam`]'s defined by this system's `fn` parameters.
205    type Param: ExclusiveSystemParam;
206
207    /// Executes this system once. See [`System::run`].
208    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/// A marker type used to distinguish exclusive function systems with and without input.
217#[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                // Yes, this is strange, but `rustc` fails to compile this impl
237                // without using this function. It fails to recognize that `func`
238                // is a function, potentially because of the multiple impls of `FnMut`
239                #[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                // Yes, this is strange, but `rustc` fails to compile this impl
268                // without using this function. It fails to recognize that `func`
269                // is a function, potentially because of the multiple impls of `FnMut`
270                #[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}
285// Note that we rely on the highest impl to be <= the highest order of the tuple impls
286// of `SystemParam` created.
287all_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}