use crate::{ archetype::ArchetypeComponentId, component::{ComponentId, Tick}, query::Access, schedule::{InternedSystemSet, SystemSet}, system::{ check_system_change_tick, ExclusiveSystemParam, ExclusiveSystemParamItem, IntoSystem, System, SystemIn, SystemInput, SystemMeta, }, world::{unsafe_world_cell::UnsafeWorldCell, World}, }; use alloc::borrow::Cow; use core::marker::PhantomData; use variadics_please::all_tuples; /// A function system that runs with exclusive [`World`] access. /// /// You get this by calling [`IntoSystem::into_system`] on a function that only accepts /// [`ExclusiveSystemParam`]s. /// /// [`ExclusiveFunctionSystem`] must be `.initialized` before they can be run. pub struct ExclusiveFunctionSystem where F: ExclusiveSystemParamFunction, { func: F, param_state: Option<::State>, system_meta: SystemMeta, // NOTE: PhantomData T> gives this safe Send/Sync impls marker: PhantomData Marker>, } impl ExclusiveFunctionSystem where F: ExclusiveSystemParamFunction, { /// Return this system with a new name. /// /// Useful to give closure systems more readable and unique names for debugging and tracing. pub fn with_name(mut self, new_name: impl Into>) -> Self { self.system_meta.set_name(new_name.into()); self } } /// A marker type used to distinguish exclusive function systems from regular function systems. #[doc(hidden)] pub struct IsExclusiveFunctionSystem; impl IntoSystem for F where Marker: 'static, F: ExclusiveSystemParamFunction, { type System = ExclusiveFunctionSystem; fn into_system(func: Self) -> Self::System { ExclusiveFunctionSystem { func, param_state: None, system_meta: SystemMeta::new::(), marker: PhantomData, } } } const PARAM_MESSAGE: &str = "System's param_state was not found. Did you forget to initialize this system before running it?"; impl System for ExclusiveFunctionSystem where Marker: 'static, F: ExclusiveSystemParamFunction, { type In = F::In; type Out = F::Out; #[inline] fn name(&self) -> Cow<'static, str> { self.system_meta.name.clone() } #[inline] fn component_access(&self) -> &Access { self.system_meta.component_access_set.combined_access() } #[inline] fn archetype_component_access(&self) -> &Access { &self.system_meta.archetype_component_access } #[inline] fn is_send(&self) -> bool { // exclusive systems should have access to non-send resources // the executor runs exclusive systems on the main thread, so this // field reflects that constraint false } #[inline] fn is_exclusive(&self) -> bool { true } #[inline] fn has_deferred(&self) -> bool { // exclusive systems have no deferred system params false } #[inline] unsafe fn run_unsafe( &mut self, _input: SystemIn<'_, Self>, _world: UnsafeWorldCell, ) -> Self::Out { panic!("Cannot run exclusive systems with a shared World reference"); } fn run(&mut self, input: SystemIn<'_, Self>, world: &mut World) -> Self::Out { world.last_change_tick_scope(self.system_meta.last_run, |world| { #[cfg(feature = "trace")] let _span_guard = self.system_meta.system_span.enter(); let params = F::Param::get_param( self.param_state.as_mut().expect(PARAM_MESSAGE), &self.system_meta, ); let out = self.func.run(world, input, params); world.flush(); self.system_meta.last_run = world.increment_change_tick(); out }) } #[inline] fn apply_deferred(&mut self, _world: &mut World) { // "pure" exclusive systems do not have any buffers to apply. // Systems made by piping a normal system with an exclusive system // might have buffers to apply, but this is handled by `PipeSystem`. } #[inline] fn queue_deferred(&mut self, _world: crate::world::DeferredWorld) { // "pure" exclusive systems do not have any buffers to apply. // Systems made by piping a normal system with an exclusive system // might have buffers to apply, but this is handled by `PipeSystem`. } #[inline] unsafe fn validate_param_unsafe(&mut self, _world: UnsafeWorldCell) -> bool { // All exclusive system params are always available. true } #[inline] fn initialize(&mut self, world: &mut World) { self.system_meta.last_run = world.change_tick().relative_to(Tick::MAX); self.param_state = Some(F::Param::init(world, &mut self.system_meta)); } fn update_archetype_component_access(&mut self, _world: UnsafeWorldCell) {} #[inline] fn check_change_tick(&mut self, change_tick: Tick) { check_system_change_tick( &mut self.system_meta.last_run, change_tick, self.system_meta.name.as_ref(), ); } fn default_system_sets(&self) -> Vec { let set = crate::schedule::SystemTypeSet::::new(); vec![set.intern()] } fn get_last_run(&self) -> Tick { self.system_meta.last_run } fn set_last_run(&mut self, last_run: Tick) { self.system_meta.last_run = last_run; } } /// A trait implemented for all exclusive system functions that can be used as [`System`]s. /// /// This trait can be useful for making your own systems which accept other systems, /// sometimes called higher order systems. #[diagnostic::on_unimplemented( message = "`{Self}` is not an exclusive system", label = "invalid system" )] pub trait ExclusiveSystemParamFunction: Send + Sync + 'static { /// The input type to this system. See [`System::In`]. type In: SystemInput; /// The return type of this system. See [`System::Out`]. type Out; /// The [`ExclusiveSystemParam`]'s defined by this system's `fn` parameters. type Param: ExclusiveSystemParam; /// Executes this system once. See [`System::run`]. fn run( &mut self, world: &mut World, input: ::Inner<'_>, param_value: ExclusiveSystemParamItem, ) -> Self::Out; } /// A marker type used to distinguish exclusive function systems with and without input. #[doc(hidden)] pub struct HasExclusiveSystemInput; macro_rules! impl_exclusive_system_function { ($($param: ident),*) => { #[allow(non_snake_case)] impl ExclusiveSystemParamFunction Out> for Func where Func: Send + Sync + 'static, for <'a> &'a mut Func: FnMut(&mut World, $($param),*) -> Out + FnMut(&mut World, $(ExclusiveSystemParamItem<$param>),*) -> Out, Out: 'static, { type In = (); type Out = Out; type Param = ($($param,)*); #[inline] fn run(&mut self, world: &mut World, _in: (), param_value: ExclusiveSystemParamItem< ($($param,)*)>) -> Out { // Yes, this is strange, but `rustc` fails to compile this impl // without using this function. It fails to recognize that `func` // is a function, potentially because of the multiple impls of `FnMut` #[allow(clippy::too_many_arguments)] fn call_inner( mut f: impl FnMut(&mut World, $($param,)*) -> Out, world: &mut World, $($param: $param,)* ) -> Out { f(world, $($param,)*) } let ($($param,)*) = param_value; call_inner(self, world, $($param),*) } } #[allow(non_snake_case)] impl ExclusiveSystemParamFunction<(HasExclusiveSystemInput, fn(In, $($param,)*) -> Out)> for Func where Func: Send + Sync + 'static, for <'a> &'a mut Func: FnMut(In, &mut World, $($param),*) -> Out + FnMut(In::Param<'_>, &mut World, $(ExclusiveSystemParamItem<$param>),*) -> Out, In: SystemInput + 'static, Out: 'static, { type In = In; type Out = Out; type Param = ($($param,)*); #[inline] fn run(&mut self, world: &mut World, input: In::Inner<'_>, param_value: ExclusiveSystemParamItem< ($($param,)*)>) -> Out { // Yes, this is strange, but `rustc` fails to compile this impl // without using this function. It fails to recognize that `func` // is a function, potentially because of the multiple impls of `FnMut` #[allow(clippy::too_many_arguments)] fn call_inner( mut f: impl FnMut(In::Param<'_>, &mut World, $($param,)*) -> Out, input: In::Inner<'_>, world: &mut World, $($param: $param,)* ) -> Out { f(In::wrap(input), world, $($param,)*) } let ($($param,)*) = param_value; call_inner(self, input, world, $($param),*) } } }; } // Note that we rely on the highest impl to be <= the highest order of the tuple impls // of `SystemParam` created. all_tuples!(impl_exclusive_system_function, 0, 16, F); #[cfg(test)] mod tests { use crate::system::input::SystemInput; use super::*; #[test] fn into_system_type_id_consistency() { fn test(function: T) where T: IntoSystem + Copy, { fn reference_system(_world: &mut World) {} use core::any::TypeId; let system = IntoSystem::into_system(function); assert_eq!( system.type_id(), function.system_type_id(), "System::type_id should be consistent with IntoSystem::system_type_id" ); assert_eq!( system.type_id(), TypeId::of::(), "System::type_id should be consistent with TypeId::of::()" ); assert_ne!( system.type_id(), IntoSystem::into_system(reference_system).type_id(), "Different systems should have different TypeIds" ); } fn exclusive_function_system(_world: &mut World) {} test(exclusive_function_system); } }