diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index fa50ae8efd22a..838583a942a56 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -29,8 +29,9 @@ pub mod prelude { Schedule, Stage, StageLabel, State, SystemLabel, SystemSet, SystemStage, }, system::{ - Commands, ConfigurableSystem, In, IntoChainSystem, IntoExclusiveSystem, IntoSystem, - Local, NonSend, NonSendMut, Query, QuerySet, RemovedComponents, Res, ResMut, System, + Commands, ConfigurableSystem, ExclusiveSystem, In, IntoChainSystem, + IntoExclusiveSystem, IntoSystem, Local, NonSend, NonSendMut, Query, QuerySet, + RemovedComponents, Res, ResMut, System, }, world::{FromWorld, Mut, World}, }; diff --git a/crates/bevy_ecs/src/system/exclusive_system.rs b/crates/bevy_ecs/src/system/exclusive_system.rs index 7050355b6b8fe..c0801c8a93b3a 100644 --- a/crates/bevy_ecs/src/system/exclusive_system.rs +++ b/crates/bevy_ecs/src/system/exclusive_system.rs @@ -10,8 +10,38 @@ pub trait ExclusiveSystem: Send + Sync + 'static { fn id(&self) -> SystemId; + /// Runs the exclusive system in the world. + /// + /// Use [`run_direct`] instead if you are manually running a system outside of a schedule + fn run(&mut self, world: &mut World); + /// Runs the exclusive system directly on the world, correctly initializing its state first + /// + /// # Example + /// + /// ```rust + /// use bevy_ecs::prelude::*; + /// + /// struct Counter(u8); + /// let mut world = World::new(); + /// + /// fn count_up(world: &mut World){ + /// let mut counter = world.get_resource_mut::().unwrap(); + /// counter.0 += 1; + /// } + /// + /// world.insert_resource::(Counter(0)); + /// count_up.exclusive_system().run_direct(&mut world); + /// let counter = world.get_resource::().unwrap(); + /// assert_eq!(counter.0, 1); + ///``` + fn run_direct(&mut self, world: &mut World) { + self.initialize(world); + self.run(world); + } + + /// Initialize the World, so that the system can safely run fn initialize(&mut self, world: &mut World); fn check_change_tick(&mut self, change_tick: u32); @@ -48,6 +78,11 @@ impl ExclusiveSystem for ExclusiveSystemFn { world.last_change_tick = saved_last_tick; } + fn run_direct(&mut self, world: &mut World) { + self.initialize(world); + self.run(world); + } + fn initialize(&mut self, _: &mut World) {} fn check_change_tick(&mut self, change_tick: u32) { diff --git a/crates/bevy_ecs/src/system/function_system.rs b/crates/bevy_ecs/src/system/function_system.rs index 09790f742cbe0..a341655fa67a3 100644 --- a/crates/bevy_ecs/src/system/function_system.rs +++ b/crates/bevy_ecs/src/system/function_system.rs @@ -221,10 +221,10 @@ impl> IntoSystem(pub In); pub struct InputMarker; -/// The [`System`] counter part of an ordinary function. +/// The [`System`] counterpart 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 +/// [`SystemParam`]s. The output of the system becomes the function's return type, while the input /// becomes the functions [`In`] tagged parameter or `()` if no such paramater exists. pub struct FunctionSystem where @@ -299,6 +299,52 @@ where } } +/// Allows end users to call system-running methods from the [`System`](crate::system::System) trait without .system(). +pub trait RunnableSystem: + IntoSystem +{ + fn apply_buffers(self, world: &mut World); + + fn initialize(self, _world: &mut World); + + fn run(self, input: In, world: &mut World) -> Out; + + fn run_direct(self, input: In, world: &mut World) -> Out; +} + +impl RunnableSystem for F +where + In: 'static, + Out: 'static, + Param: SystemParam + 'static, + Marker: 'static, + F: SystemParamFunction + + IntoSystem< + In, + Out, + (IsFunctionSystem, Param, Marker), + System = FunctionSystem, + > + Send + + Sync + + 'static, +{ + fn apply_buffers(self, world: &mut World) { + self.system().apply_buffers(world); + } + + fn initialize(self, world: &mut World) { + self.system().initialize(world); + } + + fn run(self, input: In, world: &mut World) -> Out { + self.system().run(input, world) + } + + fn run_direct(self, input: In, world: &mut World) -> Out { + self.system().run_direct(input, world) + } +} + pub struct IsFunctionSystem; impl IntoSystem for F @@ -366,13 +412,17 @@ where #[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( + // Trait disambiguation required here to disambiguate .run call + // the .run found in the `RunnableSystem` trait + let out = >::run( + &mut self.func, input, self.param_state.as_mut().unwrap(), &self.system_meta, world, change_tick, ); + self.system_meta.last_change_tick = change_tick; out } diff --git a/crates/bevy_ecs/src/system/system.rs b/crates/bevy_ecs/src/system/system.rs index eabf476730340..bd94aee8495b9 100644 --- a/crates/bevy_ecs/src/system/system.rs +++ b/crates/bevy_ecs/src/system/system.rs @@ -61,12 +61,46 @@ pub trait System: Send + Sync + 'static { /// [`System::archetype_component_access()`]. unsafe fn run_unsafe(&mut self, input: Self::In, world: &World) -> Self::Out; /// Runs the system with the given input in the world. + /// + /// Use [`run_direct`] instead if you are manually running a system outside of a schedule fn run(&mut self, input: Self::In, world: &mut World) -> Self::Out { // SAFE: world and resources are exclusively borrowed unsafe { self.run_unsafe(input, world) } } + /// Runs the system directly on the world, initializing the world correctly; + /// immediately applying buffers (such as `Commands`) modified by its system parameters + /// + /// Use () as the `input` parameter for systems which do not take any chained input. + /// + /// Only one system will run at a time when executed in this way; + /// use a [`Schedule`] (or a custom abstraction created with [`run_unsafe`]) + /// when system parallelism is desired. + /// + /// # Examples + /// ```rust + /// use bevy_ecs::prelude::*; + /// + /// struct Counter(u8); + /// let mut world = World::new(); + /// + /// fn count_up(mut counter: ResMut){ + /// counter.0 += 1; + /// } + /// + /// world.insert_resource::(Counter(0)); + /// count_up.run_direct((), &mut world); + /// let counter = world.get_resource::().unwrap(); + /// assert_eq!(counter.0, 1); + /// ``` + fn run_direct(&mut self, input: Self::In, world: &mut World) -> Self::Out { + self.initialize(world); + let output = self.run(input, world); + self.apply_buffers(world); + output + } + /// Applies any buffers (such as `Commands`) created by this system's parameters to the world fn apply_buffers(&mut self, world: &mut World); - /// Initialize the system. + /// Initializes the system from the world, so that it may be run successfully fn initialize(&mut self, _world: &mut World); fn check_change_tick(&mut self, change_tick: u32); }