diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index f729731ea327a..b756b7962346c 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -299,6 +299,34 @@ pub trait IntoSystem: Sized { WithInputFromWrapper::new(self) } + /// Passes a clone of `value` as input to the system each run, turning it into + /// a system that takes no input. + /// + /// `Self` can have any [`SystemInput`] type that takes an owned value of `T`, + /// such as [`In`]. + /// + /// # Example + /// + /// ``` + /// # use bevy_ecs::prelude::*; + /// # + /// + /// fn my_system(In(data): In) { + /// println!("Value is {}!", data); + /// } + /// + /// # let mut schedule = Schedule::default(); + /// schedule.add_systems(my_system.with_cloned_input(10)); + /// # bevy_ecs::system::assert_is_system(my_system.with_cloned_input::(10)); + /// ``` + fn with_cloned_input(self, value: T) -> WithClonedInputWrapper + where + for<'i> In: SystemInput = T>, + T: FromWorld + Send + Sync + 'static, + { + WithClonedInputWrapper::new(self, value) + } + /// Get the [`TypeId`] of the [`System`] produced after calling [`into_system`](`IntoSystem::into_system`). #[inline] fn system_type_id(&self) -> TypeId { diff --git a/crates/bevy_ecs/src/system/schedule_system.rs b/crates/bevy_ecs/src/system/schedule_system.rs index 8116f6c06b400..256089f1e6c96 100644 --- a/crates/bevy_ecs/src/system/schedule_system.rs +++ b/crates/bevy_ecs/src/system/schedule_system.rs @@ -211,5 +211,100 @@ where } } +/// Constructed in [`IntoSystem::with_cloned_input`]. +pub struct WithClonedInputWrapper { + system: S, + value: T, +} + +impl WithClonedInputWrapper +where + for<'i> S: System = T>>, + T: Send + Sync + 'static, +{ + /// Wraps the given system. + pub fn new(system: impl IntoSystem, value: T) -> Self { + Self { + system: IntoSystem::into_system(system), + value, + } + } + + /// Retrieves a reference to the contained value. + pub fn value(&self) -> &T { + &self.value + } + + /// Retrieves a mutable reference to the contained value. + pub fn value_mut(&mut self) -> &mut T { + &mut self.value + } +} + +impl System for WithClonedInputWrapper +where + for<'i> S: System = T>>, + T: Clone + Send + Sync + 'static, +{ + type In = (); + type Out = S::Out; + + fn name(&self) -> DebugName { + self.system.name() + } + + #[inline] + fn flags(&self) -> SystemStateFlags { + self.system.flags() + } + + unsafe fn run_unsafe( + &mut self, + _input: SystemIn<'_, Self>, + world: UnsafeWorldCell, + ) -> Result { + // SAFETY: Upheld by caller + unsafe { self.system.run_unsafe(self.value.clone(), world) } + } + + #[cfg(feature = "hotpatching")] + #[inline] + fn refresh_hotpatch(&mut self) { + self.system.refresh_hotpatch(); + } + + fn apply_deferred(&mut self, world: &mut World) { + self.system.apply_deferred(world); + } + + fn queue_deferred(&mut self, world: DeferredWorld) { + self.system.queue_deferred(world); + } + + unsafe fn validate_param_unsafe( + &mut self, + world: UnsafeWorldCell, + ) -> Result<(), SystemParamValidationError> { + // SAFETY: Upheld by caller + unsafe { self.system.validate_param_unsafe(world) } + } + + fn initialize(&mut self, world: &mut World) -> FilteredAccessSet { + self.system.initialize(world) + } + + fn check_change_tick(&mut self, check: CheckChangeTicks) { + self.system.check_change_tick(check); + } + + fn get_last_run(&self) -> Tick { + self.system.get_last_run() + } + + fn set_last_run(&mut self, last_run: Tick) { + self.system.set_last_run(last_run); + } +} + /// Type alias for a `BoxedSystem` that a `Schedule` can store. pub type ScheduleSystem = BoxedSystem<(), ()>;