diff --git a/crates/bevy_ecs/src/system/system_registry.rs b/crates/bevy_ecs/src/system/system_registry.rs index 6dabb9a4569f86..2ae047f3c306b1 100644 --- a/crates/bevy_ecs/src/system/system_registry.rs +++ b/crates/bevy_ecs/src/system/system_registry.rs @@ -7,18 +7,18 @@ use thiserror::Error; /// A small wrapper for [`BoxedSystem`] that also keeps track whether or not the system has been initialized. #[derive(Component)] -struct RegisteredSystem { +struct RegisteredSystem { initialized: bool, - system: BoxedSystem, + system: BoxedSystem, } /// A system that has been removed from the registry. /// It contains the system and whether or not it has been initialized. /// /// This struct is returned by [`World::remove_system`]. -pub struct RemovedSystem { +pub struct RemovedSystem { initialized: bool, - system: BoxedSystem, + system: BoxedSystem, } impl RemovedSystem { @@ -39,29 +39,29 @@ impl RemovedSystem { /// These are opaque identifiers, keyed to a specific [`World`], /// and are created via [`World::register_system`]. #[derive(Eq)] -pub struct SystemId(Entity, std::marker::PhantomData); +pub struct SystemId(Entity, std::marker::PhantomData O>); -// A manual impl is used because the trait bounds should ignore the `I` phantom parameter. -impl Copy for SystemId {} -// A manual impl is used because the trait bounds should ignore the `I` phantom parameter. -impl Clone for SystemId { +// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters. +impl Copy for SystemId {} +// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters. +impl Clone for SystemId { fn clone(&self) -> Self { *self } } -// A manual impl is used because the trait bounds should ignore the `I` phantom parameter. -impl PartialEq for SystemId { +// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters. +impl PartialEq for SystemId { fn eq(&self, other: &Self) -> bool { self.0 == other.0 && self.1 == other.1 } } -// A manual impl is used because the trait bounds should ignore the `I` phantom parameter. -impl std::hash::Hash for SystemId { +// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters. +impl std::hash::Hash for SystemId { fn hash(&self, state: &mut H) { self.0.hash(state); } } -impl std::fmt::Debug for SystemId { +impl std::fmt::Debug for SystemId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { // The PhantomData field is omitted for simplicity. f.debug_tuple("SystemId").field(&self.0).finish() @@ -78,10 +78,10 @@ impl World { /// This allows for running systems in a pushed-based fashion. /// Using a [`Schedule`](crate::schedule::Schedule) is still preferred for most cases /// due to its better performance and abillity to run non-conflicting systems simultaneously. - pub fn register_system + 'static>( + pub fn register_system + 'static>( &mut self, system: S, - ) -> SystemId { + ) -> SystemId { SystemId( self.spawn(RegisteredSystem { initialized: false, @@ -98,14 +98,14 @@ impl World { /// /// If no system corresponds to the given [`SystemId`], this method returns an error. /// Systems are also not allowed to remove themselves, this returns an error too. - pub fn remove_system( + pub fn remove_system( &mut self, - id: SystemId, - ) -> Result, RegisteredSystemError> { + id: SystemId, + ) -> Result, RegisteredSystemError> { match self.get_entity_mut(id.0) { Some(mut entity) => { let registered_system = entity - .take::>() + .take::>() .ok_or(RegisteredSystemError::SelfRemove(id))?; entity.despawn(); Ok(RemovedSystem { @@ -132,6 +132,8 @@ impl World { /// /// # Examples /// + /// ## Running a system + /// /// ```rust /// # use bevy_ecs::prelude::*; /// #[derive(Resource, Default)] @@ -150,7 +152,7 @@ impl World { /// world.run_system(counter_two); // -> 1 /// ``` /// - /// Change detection: + /// ## Change detection /// /// ```rust /// # use bevy_ecs::prelude::*; @@ -173,7 +175,43 @@ impl World { /// world.resource_mut::().set_changed(); /// let _ = world.run_system(detector); // -> Something happened! /// ``` - pub fn run_system(&mut self, id: SystemId) -> Result<(), RegisteredSystemError> { + /// + /// ## Getting system output + /// + /// ```rust + /// # use bevy_ecs::prelude::*; + /// + /// #[derive(Resource)] + /// struct PlayerScore(i32); + /// + /// #[derive(Resource)] + /// struct OpponentScore(i32); + /// + /// fn get_player_score(player_score: Res) -> i32 { + /// player_score.0 + /// } + /// + /// fn get_opponent_score(opponent_score: Res) -> i32 { + /// opponent_score.0 + /// } + /// + /// let mut world = World::default(); + /// world.insert_resource(PlayerScore(3)); + /// world.insert_resource(OpponentScore(2)); + /// + /// let scoring_systems = [ + /// ("player", world.register_system(get_player_score)), + /// ("opponent", world.register_system(get_opponent_score)), + /// ]; + /// + /// for (label, scoring_system) in scoring_systems { + /// println!("{label} has score {}", world.run_system(scoring_system).expect("system succeeded")); + /// } + /// ``` + pub fn run_system( + &mut self, + id: SystemId<(), O>, + ) -> Result> { self.run_system_with_input(id, ()) } @@ -207,11 +245,11 @@ impl World { /// ``` /// /// See [`World::run_system`] for more examples. - pub fn run_system_with_input( + pub fn run_system_with_input( &mut self, - id: SystemId, + id: SystemId, input: I, - ) -> Result<(), RegisteredSystemError> { + ) -> Result> { // lookup let mut entity = self .get_entity_mut(id.0) @@ -222,7 +260,7 @@ impl World { mut initialized, mut system, } = entity - .take::>() + .take::>() .ok_or(RegisteredSystemError::Recursive(id))?; // run the system @@ -230,17 +268,17 @@ impl World { system.initialize(self); initialized = true; } - system.run(input, self); + let result = system.run(input, self); system.apply_deferred(self); // return ownership of system trait object (if entity still exists) if let Some(mut entity) = self.get_entity_mut(id.0) { - entity.insert::>(RegisteredSystem { + entity.insert::>(RegisteredSystem { initialized, system, }); } - Ok(()) + Ok(result) } } @@ -269,21 +307,21 @@ impl Command for RunSystem { /// An operation with stored systems failed. #[derive(Error)] -pub enum RegisteredSystemError { +pub enum RegisteredSystemError { /// A system was run by id, but no system with that id was found. /// /// Did you forget to register it? #[error("System {0:?} was not registered")] - SystemIdNotRegistered(SystemId), + SystemIdNotRegistered(SystemId), /// A system tried to run itself recursively. #[error("System {0:?} tried to run itself recursively")] - Recursive(SystemId), + Recursive(SystemId), /// A system tried to remove itself. #[error("System {0:?} tried to remove itself")] - SelfRemove(SystemId), + SelfRemove(SystemId), } -impl std::fmt::Debug for RegisteredSystemError { +impl std::fmt::Debug for RegisteredSystemError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::SystemIdNotRegistered(arg0) => { @@ -393,6 +431,34 @@ mod tests { assert_eq!(*world.resource::(), Counter(24)); } + #[test] + fn output_values() { + // Verify that a non-Copy, non-Clone type can be returned. + #[derive(Eq, PartialEq, Debug)] + struct NonCopy(u8); + + fn increment_sys(mut counter: ResMut) -> NonCopy { + counter.0 += 1; + NonCopy(counter.0) + } + + let mut world = World::new(); + + let id = world.register_system(increment_sys); + + // Insert the resource after registering the system. + world.insert_resource(Counter(1)); + assert_eq!(*world.resource::(), Counter(1)); + + let output = world.run_system(id).expect("system runs successfully"); + assert_eq!(*world.resource::(), Counter(2)); + assert_eq!(output, NonCopy(2)); + + let output = world.run_system(id).expect("system runs successfully"); + assert_eq!(*world.resource::(), Counter(3)); + assert_eq!(output, NonCopy(3)); + } + #[test] fn nested_systems() { use crate::system::SystemId;