diff --git a/crates/bevy_ecs/src/system/exclusive_function_system.rs b/crates/bevy_ecs/src/system/exclusive_function_system.rs index 272b9bce17eff..8c60e22e20290 100644 --- a/crates/bevy_ecs/src/system/exclusive_function_system.rs +++ b/crates/bevy_ecs/src/system/exclusive_function_system.rs @@ -6,7 +6,7 @@ use crate::{ schedule::{SystemLabel, SystemLabelId}, system::{ check_system_change_tick, AsSystemLabel, ExclusiveSystemParam, ExclusiveSystemParamItem, - IntoSystem, System, SystemMeta, SystemTypeIdLabel, + In, InputMarker, IntoSystem, System, SystemMeta, SystemTypeIdLabel, }, world::{World, WorldId}, }; @@ -19,7 +19,7 @@ use std::{borrow::Cow, marker::PhantomData}; /// [`ExclusiveSystemParam`]s. /// /// [`ExclusiveFunctionSystem`] must be `.initialized` before they can be run. -pub struct ExclusiveFunctionSystem +pub struct ExclusiveFunctionSystem where Param: ExclusiveSystemParam, { @@ -28,18 +28,21 @@ where system_meta: SystemMeta, world_id: Option, // NOTE: PhantomData T> gives this safe Send/Sync impls - marker: PhantomData Marker>, + marker: PhantomData (Out, Marker)>, } pub struct IsExclusiveFunctionSystem; -impl IntoSystem<(), (), (IsExclusiveFunctionSystem, Param, Marker)> for F +impl IntoSystem + for F where + In: 'static, + Out: 'static, Param: ExclusiveSystemParam + 'static, Marker: 'static, - F: ExclusiveSystemParamFunction + Send + Sync + 'static, + F: ExclusiveSystemParamFunction + Send + Sync + 'static, { - type System = ExclusiveFunctionSystem; + type System = ExclusiveFunctionSystem; fn into_system(func: Self) -> Self::System { ExclusiveFunctionSystem { func, @@ -53,14 +56,16 @@ where 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 +impl System for ExclusiveFunctionSystem where + In: 'static, + Out: 'static, Param: ExclusiveSystemParam + 'static, Marker: 'static, - F: ExclusiveSystemParamFunction + Send + Sync + 'static, + F: ExclusiveSystemParamFunction + Send + Sync + 'static, { - type In = (); - type Out = (); + type In = In; + type Out = Out; #[inline] fn name(&self) -> Cow<'static, str> { @@ -90,7 +95,7 @@ where panic!("Cannot run exclusive systems with a shared World reference"); } - fn run(&mut self, _input: Self::In, world: &mut World) -> Self::Out { + fn run(&mut self, input: Self::In, world: &mut World) -> Self::Out { let saved_last_tick = world.last_change_tick; world.last_change_tick = self.system_meta.last_change_tick; @@ -98,12 +103,14 @@ where self.param_state.as_mut().expect(PARAM_MESSAGE), &self.system_meta, ); - self.func.run(world, params); + let out = self.func.run(world, input, params); let change_tick = world.change_tick.get_mut(); self.system_meta.last_change_tick = *change_tick; *change_tick += 1; world.last_change_tick = saved_last_tick; + + out } #[inline] @@ -147,8 +154,11 @@ where } } -impl> - AsSystemLabel<(Param, Marker, IsExclusiveFunctionSystem)> for T +impl AsSystemLabel<(In, Out, Param, Marker, IsExclusiveFunctionSystem)> + for T +where + Param: ExclusiveSystemParam, + T: ExclusiveSystemParamFunction, { #[inline] fn as_system_label(&self) -> SystemLabelId { @@ -160,38 +170,70 @@ impl: +pub trait ExclusiveSystemParamFunction: Send + Sync + 'static { - fn run(&mut self, world: &mut World, param_value: ExclusiveSystemParamItem); + fn run( + &mut self, + world: &mut World, + input: In, + param_value: ExclusiveSystemParamItem, + ) -> Out; } macro_rules! impl_exclusive_system_function { ($($param: ident),*) => { #[allow(non_snake_case)] - impl ExclusiveSystemParamFunction<($($param,)*), ()> for Func + impl ExclusiveSystemParamFunction<(), Out, ($($param,)*), ()> for Func where for <'a> &'a mut Func: - FnMut(&mut World, $($param),*) + - FnMut(&mut World, $(ExclusiveSystemParamItem<$param>),*) + FnMut(&mut World, $($param),*) -> Out + + FnMut(&mut World, $(ExclusiveSystemParamItem<$param>),*) -> Out, + Out: 'static, { #[inline] - fn run(&mut self, world: &mut World, param_value: ExclusiveSystemParamItem< ($($param,)*)>) { + 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 recognise that `func` // is a function, potentially because of the multiple impls of `FnMut` #[allow(clippy::too_many_arguments)] - fn call_inner<$($param,)*>( - mut f: impl FnMut(&mut World, $($param,)*), + 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 for Func + where + for <'a> &'a mut Func: + FnMut(In, &mut World, $($param),*) -> Out + + FnMut(In, &mut World, $(ExclusiveSystemParamItem<$param>),*) -> Out, + Out: 'static, + { + #[inline] + fn run(&mut self, world: &mut World, input: Input, param_value: ExclusiveSystemParamItem< ($($param,)*)>) -> Out { + // Yes, this is strange, but `rustc` fails to compile this impl + // without using this function. It fails to recognise 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, &mut World, $($param,)*) -> Out, + input: Input, + world: &mut World, + $($param: $param,)* + ) -> Out { + f(In(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 diff --git a/crates/bevy_ecs/src/system/function_system.rs b/crates/bevy_ecs/src/system/function_system.rs index f99ae67b15a4a..050edf39b31b8 100644 --- a/crates/bevy_ecs/src/system/function_system.rs +++ b/crates/bevy_ecs/src/system/function_system.rs @@ -313,7 +313,7 @@ where world_id: Option, archetype_generation: ArchetypeGeneration, // NOTE: PhantomData T> gives this safe Send/Sync impls - marker: PhantomData (In, Out, Marker)>, + marker: PhantomData (Out, Marker)>, } pub struct IsFunctionSystem; diff --git a/crates/bevy_ecs/src/system/system_piping.rs b/crates/bevy_ecs/src/system/system_piping.rs index dd343461b9e5f..0af8caa5eed6e 100644 --- a/crates/bevy_ecs/src/system/system_piping.rs +++ b/crates/bevy_ecs/src/system/system_piping.rs @@ -433,8 +433,15 @@ pub mod adapter { unimplemented!() } + /// Mocks an exclusive system that takes an input and returns an output. + fn exclusive_in_out(_: In, _: &mut World) -> B { + unimplemented!() + } + assert_is_system(returning::>.pipe(unwrap)); assert_is_system(returning::>.pipe(ignore)); assert_is_system(returning::<&str>.pipe(new(u64::from_str)).pipe(unwrap)); + assert_is_system(exclusive_in_out::<(), Result<(), std::io::Error>>.pipe(error)); + assert_is_system(returning::.pipe(exclusive_in_out::)); } }