use crate::{ archetype::{Archetype, ArchetypeComponentId}, component::ComponentId, query::{Access, FilteredAccessSet}, system::{ check_system_change_tick, System, SystemId, SystemParam, SystemParamFetch, SystemParamState, }, world::World, }; use bevy_ecs_macros::all_tuples; use std::{borrow::Cow, marker::PhantomData}; /// The state of a [`System`]. pub struct SystemState { pub(crate) id: SystemId, pub(crate) name: Cow<'static, str>, pub(crate) component_access_set: FilteredAccessSet, pub(crate) archetype_component_access: Access, // NOTE: this must be kept private. making a SystemState non-send is irreversible to prevent // SystemParams from overriding each other is_send: bool, pub(crate) last_change_tick: u32, } impl SystemState { fn new() -> Self { Self { name: std::any::type_name::().into(), archetype_component_access: Access::default(), component_access_set: FilteredAccessSet::default(), is_send: true, id: SystemId::new(), last_change_tick: 0, } } /// Returns true if the system is [`Send`]. #[inline] pub fn is_send(&self) -> bool { self.is_send } /// Sets the system to be not [`Send`]. /// /// This is irreversible. #[inline] pub fn set_non_send(&mut self) { self.is_send = false; } } /// Conversion trait to turn something into a [`System`]. /// /// Use this to get a system from a function. Also note that every system implements this trait as /// well. /// /// # Examples /// /// ``` /// use bevy_ecs::system::IntoSystem; /// use bevy_ecs::system::Res; /// /// fn my_system_function(an_usize_resource: Res) {} /// /// let system = my_system_function.system(); /// ``` pub trait IntoSystem { /// Turns this value into its corresponding [`System`]. fn system(self) -> SystemType; } // Systems implicitly implement IntoSystem impl IntoSystem<(), Sys> for Sys { fn system(self) -> Sys { self } } /// Wrapper type to mark a [`SystemParam`] as an input. /// /// [`System`]s may take an optional input which they require to be passed to them when they /// are being [`run`](System::run). For [`FunctionSystems`](FunctionSystem) the input may be marked /// with this `In` type, but only the first param of a function may be tagged as an input. This also /// means a system can only have one or zero input paramaters. /// /// # Examples /// /// Here is a simple example of a system that takes a [`usize`] returning the square of it. /// /// ``` /// use bevy_ecs::prelude::*; /// /// fn main() { /// let mut square_system = square.system(); /// /// let mut world = World::default(); /// square_system.initialize(&mut world); /// assert_eq!(square_system.run(12, &mut world), 144); /// } /// /// fn square(In(input): In) -> usize { /// input * input /// } /// ``` pub struct In(pub In); pub struct InputMarker; /// The [`System`] counter part of an ordinary function. /// /// You get this by calling [`IntoSystem::system`] on a function that only accepts /// [`SystemParam`]s. The output of the system becomes the functions return type, while the input /// becomes the functions [`In`] tagged parameter or `()` if no such paramater exists. pub struct FunctionSystem where Param: SystemParam, { func: F, param_state: Option, system_state: SystemState, config: Option<::Config>, // NOTE: PhantomData T> gives this safe Send/Sync impls #[allow(clippy::type_complexity)] marker: PhantomData (In, Out, Marker)>, } impl FunctionSystem { /// Gives mutable access to the systems config via a callback. This is useful to set up system /// [`Local`](crate::system::Local)s. /// /// # Examples /// /// ``` /// # use bevy_ecs::prelude::*; /// # let world = &mut World::default(); /// fn local_is_42(local: Local) { /// assert_eq!(*local, 42); /// } /// let mut system = local_is_42.system().config(|config| config.0 = Some(42)); /// system.initialize(world); /// system.run((), world); /// ``` pub fn config( mut self, f: impl FnOnce(&mut ::Config), ) -> Self { f(self.config.as_mut().unwrap()); self } } impl IntoSystem> for F where In: 'static, Out: 'static, Param: SystemParam + 'static, Marker: 'static, F: SystemParamFunction + Send + Sync + 'static, { fn system(self) -> FunctionSystem { FunctionSystem { func: self, param_state: None, config: Some(::default_config()), system_state: SystemState::new::(), marker: PhantomData, } } } impl System for FunctionSystem where In: 'static, Out: 'static, Param: SystemParam + 'static, Marker: 'static, F: SystemParamFunction + Send + Sync + 'static, { type In = In; type Out = Out; #[inline] fn name(&self) -> Cow<'static, str> { self.system_state.name.clone() } #[inline] fn id(&self) -> SystemId { self.system_state.id } #[inline] fn new_archetype(&mut self, archetype: &Archetype) { let param_state = self.param_state.as_mut().unwrap(); param_state.new_archetype(archetype, &mut self.system_state); } #[inline] fn component_access(&self) -> &Access { &self.system_state.component_access_set.combined_access() } #[inline] fn archetype_component_access(&self) -> &Access { &self.system_state.archetype_component_access } #[inline] fn is_send(&self) -> bool { self.system_state.is_send } #[inline] unsafe fn run_unsafe(&mut self, input: Self::In, world: &World) -> Self::Out { let change_tick = world.increment_change_tick(); let out = self.func.run( input, self.param_state.as_mut().unwrap(), &self.system_state, world, change_tick, ); self.system_state.last_change_tick = change_tick; out } #[inline] fn apply_buffers(&mut self, world: &mut World) { let param_state = self.param_state.as_mut().unwrap(); param_state.apply(world); } #[inline] fn initialize(&mut self, world: &mut World) { self.param_state = Some(::init( world, &mut self.system_state, self.config.take().unwrap(), )); } #[inline] fn check_change_tick(&mut self, change_tick: u32) { check_system_change_tick( &mut self.system_state.last_change_tick, change_tick, self.system_state.name.as_ref(), ); } } /// A trait implemented for all functions that can be used as [`System`]s. pub trait SystemParamFunction: Send + Sync + 'static { fn run( &mut self, input: In, state: &mut Param::Fetch, system_state: &SystemState, world: &World, change_tick: u32, ) -> Out; } macro_rules! impl_system_function { ($($param: ident),*) => { #[allow(non_snake_case)] impl SystemParamFunction<(), Out, ($($param,)*), ()> for Func where Func: FnMut($($param),*) -> Out + FnMut($(<<$param as SystemParam>::Fetch as SystemParamFetch>::Item),*) -> Out + Send + Sync + 'static, Out: 'static { #[inline] fn run(&mut self, _input: (), state: &mut <($($param,)*) as SystemParam>::Fetch, system_state: &SystemState, world: &World, change_tick: u32) -> Out { unsafe { let ($($param,)*) = <<($($param,)*) as SystemParam>::Fetch as SystemParamFetch>::get_param(state, system_state, world, change_tick); self($($param),*) } } } #[allow(non_snake_case)] impl SystemParamFunction for Func where Func: FnMut(In, $($param),*) -> Out + FnMut(In, $(<<$param as SystemParam>::Fetch as SystemParamFetch>::Item),*) -> Out + Send + Sync + 'static, Out: 'static { #[inline] fn run(&mut self, input: Input, state: &mut <($($param,)*) as SystemParam>::Fetch, system_state: &SystemState, world: &World, change_tick: u32) -> Out { unsafe { let ($($param,)*) = <<($($param,)*) as SystemParam>::Fetch as SystemParamFetch>::get_param(state, system_state, world, change_tick); self(In(input), $($param),*) } } } }; } all_tuples!(impl_system_function, 0, 16, F);