From 4f1803849f8501d0164da144c881efbe3f129ea6 Mon Sep 17 00:00:00 2001 From: Ben Frankel Date: Sun, 25 Aug 2024 18:51:06 -0700 Subject: [PATCH 01/21] Add `run_system_cached` API --- crates/bevy_ecs/src/system/commands/mod.rs | 38 ++++- crates/bevy_ecs/src/system/system_registry.rs | 135 ++++++++++++++++-- 2 files changed, 160 insertions(+), 13 deletions(-) diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index 14748ac070eb7..8f1d56ae6565d 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -2,7 +2,9 @@ mod parallel_scope; use core::panic::Location; -use super::{Deferred, IntoObserverSystem, IntoSystem, RegisterSystem, Resource}; +use super::{ + Deferred, IntoObserverSystem, IntoSystem, RegisterSystem, RegisterSystemCached, Resource, +}; use crate::{ self as bevy_ecs, bundle::{Bundle, InsertMode}, @@ -726,6 +728,40 @@ impl<'w, 's> Commands<'w, 's> { SystemId::from_entity(entity) } + /// Similar to [`Self::register_system`], but caching the [`SystemId`] in a [`CachedSystemId`]. + pub fn register_system_cached< + I: 'static + Send, + O: 'static + Send, + M: 'static, + S: IntoSystem + 'static, + >( + &mut self, + system: S, + ) -> SystemId { + let entity = self.spawn_empty().id(); + self.push(RegisterSystemCached::new(system, entity)); + SystemId::from_entity(entity) + } + + /// Similar to [`Self::run_system`], but caching the [`SystemId`] in a [`CachedSystemId`]. + pub fn run_system_cached + 'static>(&mut self, system: S) { + self.run_system_cached_with(system, ()); + } + + /// Similar to [`Self::run_system_with_input`], but caching the [`SystemId`] in a [`CachedSystemId`]. + pub fn run_system_cached_with< + I: 'static + Send, + M: 'static, + S: IntoSystem + 'static, + >( + &mut self, + system: S, + input: I, + ) { + let id = self.register_system_cached(system); + self.run_system_with_input(id, input); + } + /// Pushes a generic [`Command`] to the command queue. /// /// `command` can be a built-in command, custom struct that implements [`Command`] or a closure diff --git a/crates/bevy_ecs/src/system/system_registry.rs b/crates/bevy_ecs/src/system/system_registry.rs index f6cf792f71a85..a70c6229e253c 100644 --- a/crates/bevy_ecs/src/system/system_registry.rs +++ b/crates/bevy_ecs/src/system/system_registry.rs @@ -2,7 +2,7 @@ use crate::entity::Entity; use crate::system::{BoxedSystem, IntoSystem}; use crate::world::{Command, World}; use crate::{self as bevy_ecs}; -use bevy_ecs_macros::Component; +use bevy_ecs_macros::{Component, Resource}; use thiserror::Error; /// A small wrapper for [`BoxedSystem`] that also keeps track whether or not the system has been initialized. @@ -102,10 +102,33 @@ impl std::fmt::Debug for SystemId { } } +/// A cached [`SystemId`] distinguished by the unique function type of its system. +/// +/// This resource is inserted as part of [`World::register_system_cached`] and +/// [`Commands::register_system_cached`]. +#[derive(Resource)] +pub struct CachedSystemId { + /// The cached `SystemId`. + pub id: SystemId, + _marker: std::marker::PhantomData S>, +} + +impl CachedSystemId { + /// Creates a new `CachedSystemId` struct given a `SystemId`. + pub fn new(id: SystemId) -> Self { + Self { + id, + _marker: std::marker::PhantomData, + } + } +} + impl World { /// Registers a system and returns a [`SystemId`] so it can later be called by [`World::run_system`]. /// - /// It's possible to register the same systems more than once, they'll be stored separately. + /// It's possible to register the same system multiple times, which will create multiple + /// registered systems. To avoid this behavior, consider using + /// [`World::register_system_cached`] instead. /// /// This is different from adding systems to a [`Schedule`](crate::schedule::Schedule), /// because the [`SystemId`] that is returned can be used anywhere in the [`World`] to run the associated system. @@ -320,6 +343,46 @@ impl World { } Ok(result) } + + /// Registers a system and caches its [`SystemId`]. + /// + /// The first time this is called for a particular system, it will register that system and + /// store its [`SystemId`] in a [`CachedSystemId`] resource. Every subsequent call will + /// retrieve the cached ID instead of creating a new registered system. + /// + /// If you don't need to cache the `SystemId`, use [`World::register_system`] instead. + pub fn register_system_cached + 'static>( + &mut self, + system: S, + ) -> SystemId { + match self.get_resource::>() { + Some(cached) => cached.id, + None => { + let id = self.register_system(system); + self.insert_resource(CachedSystemId::::new(id)); + id + } + } + } + + /// Runs a system, registering it and caching its [`SystemId`] if necessary. + pub fn run_system_cached + 'static>( + &mut self, + system: S, + ) -> Result> { + self.run_system_cached_with((), system) + } + + /// Runs a system with an input, registering the system and caching its [`SystemId`] if + /// necessary. + pub fn run_system_cached_with + 'static>( + &mut self, + input: I, + system: S, + ) -> Result> { + let id = self.register_system_cached(system); + self.run_system_with_input(id, input) + } } /// The [`Command`] type for [`World::run_system`] or [`World::run_system_with_input`]. @@ -353,7 +416,7 @@ pub struct RunSystemWithInput { pub type RunSystem = RunSystemWithInput<()>; impl RunSystem { - /// Creates a new [`Command`] struct, which can be added to [`Commands`](crate::system::Commands) + /// Creates a new [`Command`] struct, which can be added to [`Commands`](crate::system::Commands). pub fn new(system_id: SystemId) -> Self { Self::new_with_input(system_id, ()) } @@ -374,16 +437,16 @@ impl Command for RunSystemWithInput { } } -/// The [`Command`] type for registering one shot systems from [Commands](crate::system::Commands). +/// The [`Command`] type for registering one shot systems from [`Commands`](crate::system::Commands). /// -/// This command needs an already boxed system to register, and an already spawned entity +/// This command needs an already boxed system to register, and an already spawned entity. pub struct RegisterSystem { system: BoxedSystem, entity: Entity, } impl RegisterSystem { - /// Creates a new [Command] struct, which can be added to [Commands](crate::system::Commands) + /// Creates a new [`Command`] struct, which can be added to [`Commands`](crate::system::Commands). pub fn new + 'static>(system: S, entity: Entity) -> Self { Self { system: Box::new(IntoSystem::into_system(system)), @@ -394,12 +457,60 @@ impl RegisterSystem { impl Command for RegisterSystem { fn apply(self, world: &mut World) { - let _ = world.get_entity_mut(self.entity).map(|mut entity| { - entity.insert(RegisteredSystem { - initialized: false, - system: self.system, - }); - }); + if let Some(mut entity) = world.get_entity_mut(self.entity) { + entity.insert(( + RegisteredSystem { + initialized: false, + system: self.system, + }, + SystemIdMarker, + )); + } + } +} + +/// The [`Command`] type for registering and caching one shot systems from +/// [`Commands`](crate::system::Commands). +/// +/// This command needs an already boxed system to register, and an already spawned entity. +pub struct RegisterSystemCached + 'static> { + system: BoxedSystem, + entity: Entity, + _marker: std::marker::PhantomData (M, S)>, +} + +impl + 'static> RegisterSystemCached { + /// Creates a new [`Command`] struct, which can be added to + /// [`Commands`](crate::system::Commands). + pub fn new(system: S, entity: Entity) -> Self { + Self { + system: Box::new(IntoSystem::into_system(system)), + entity, + _marker: std::marker::PhantomData, + } + } +} + +impl + 'static> Command + for RegisterSystemCached +{ + fn apply(self, world: &mut World) { + if let Some(mut entity) = world.get_entity_mut(self.entity) { + entity.insert_if_new(( + RegisteredSystem { + initialized: false, + system: self.system, + }, + SystemIdMarker, + )); + } else { + return; + } + + if !world.contains_resource::>() { + let system_id = SystemId::from_entity(self.entity); + world.insert_resource(CachedSystemId::::new(system_id)); + } } } From 348ae1f28700a6c2cdaee2f41ad18dc65e8bf33a Mon Sep 17 00:00:00 2001 From: Ben Frankel Date: Sun, 25 Aug 2024 19:03:33 -0700 Subject: [PATCH 02/21] Fix arg order inconsistency --- crates/bevy_ecs/src/system/system_registry.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/system/system_registry.rs b/crates/bevy_ecs/src/system/system_registry.rs index a70c6229e253c..90ca261d25b88 100644 --- a/crates/bevy_ecs/src/system/system_registry.rs +++ b/crates/bevy_ecs/src/system/system_registry.rs @@ -370,15 +370,15 @@ impl World { &mut self, system: S, ) -> Result> { - self.run_system_cached_with((), system) + self.run_system_cached_with(system, ()) } /// Runs a system with an input, registering the system and caching its [`SystemId`] if /// necessary. pub fn run_system_cached_with + 'static>( &mut self, - input: I, system: S, + input: I, ) -> Result> { let id = self.register_system_cached(system); self.run_system_with_input(id, input) From 7444fb9e4bb60fc9069bf0edba791124eca3f2ad Mon Sep 17 00:00:00 2001 From: Ben Frankel Date: Sun, 25 Aug 2024 19:24:39 -0700 Subject: [PATCH 03/21] Fix broken doc links --- crates/bevy_ecs/src/system/commands/mod.rs | 9 ++++++--- crates/bevy_ecs/src/system/system_registry.rs | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index 8f1d56ae6565d..627e725fc021f 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -728,7 +728,8 @@ impl<'w, 's> Commands<'w, 's> { SystemId::from_entity(entity) } - /// Similar to [`Self::register_system`], but caching the [`SystemId`] in a [`CachedSystemId`]. + /// Similar to [`Self::register_system`], but caching the [`SystemId`] in a + /// [`CachedSystemId`](crate::system::CachedSystemId). pub fn register_system_cached< I: 'static + Send, O: 'static + Send, @@ -743,12 +744,14 @@ impl<'w, 's> Commands<'w, 's> { SystemId::from_entity(entity) } - /// Similar to [`Self::run_system`], but caching the [`SystemId`] in a [`CachedSystemId`]. + /// Similar to [`Self::run_system`], but caching the [`SystemId`] in a + /// [`CachedSystemId`](crate::system::CachedSystemId). pub fn run_system_cached + 'static>(&mut self, system: S) { self.run_system_cached_with(system, ()); } - /// Similar to [`Self::run_system_with_input`], but caching the [`SystemId`] in a [`CachedSystemId`]. + /// Similar to [`Self::run_system_with_input`], but caching the [`SystemId`] in a + /// [`CachedSystemId`](crate::system::CachedSystemId). pub fn run_system_cached_with< I: 'static + Send, M: 'static, diff --git a/crates/bevy_ecs/src/system/system_registry.rs b/crates/bevy_ecs/src/system/system_registry.rs index 90ca261d25b88..e05bc75780c68 100644 --- a/crates/bevy_ecs/src/system/system_registry.rs +++ b/crates/bevy_ecs/src/system/system_registry.rs @@ -105,7 +105,7 @@ impl std::fmt::Debug for SystemId { /// A cached [`SystemId`] distinguished by the unique function type of its system. /// /// This resource is inserted as part of [`World::register_system_cached`] and -/// [`Commands::register_system_cached`]. +/// [`Commands::register_system_cached`](crate::system::commands::Commands::register_system_cached). #[derive(Resource)] pub struct CachedSystemId { /// The cached `SystemId`. From 3d438ea4f832a1af272e810749699eab0dd055b2 Mon Sep 17 00:00:00 2001 From: Ben Frankel Date: Sun, 25 Aug 2024 19:32:55 -0700 Subject: [PATCH 04/21] Yeet `Commands::register_system_cached` --- crates/bevy_ecs/src/system/commands/mod.rs | 23 +--------- crates/bevy_ecs/src/system/system_registry.rs | 43 ++++++++----------- 2 files changed, 20 insertions(+), 46 deletions(-) diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index 627e725fc021f..400247e6c4ea3 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -2,9 +2,7 @@ mod parallel_scope; use core::panic::Location; -use super::{ - Deferred, IntoObserverSystem, IntoSystem, RegisterSystem, RegisterSystemCached, Resource, -}; +use super::{Deferred, IntoObserverSystem, IntoSystem, RegisterSystem, Resource, RunSystemCached}; use crate::{ self as bevy_ecs, bundle::{Bundle, InsertMode}, @@ -728,22 +726,6 @@ impl<'w, 's> Commands<'w, 's> { SystemId::from_entity(entity) } - /// Similar to [`Self::register_system`], but caching the [`SystemId`] in a - /// [`CachedSystemId`](crate::system::CachedSystemId). - pub fn register_system_cached< - I: 'static + Send, - O: 'static + Send, - M: 'static, - S: IntoSystem + 'static, - >( - &mut self, - system: S, - ) -> SystemId { - let entity = self.spawn_empty().id(); - self.push(RegisterSystemCached::new(system, entity)); - SystemId::from_entity(entity) - } - /// Similar to [`Self::run_system`], but caching the [`SystemId`] in a /// [`CachedSystemId`](crate::system::CachedSystemId). pub fn run_system_cached + 'static>(&mut self, system: S) { @@ -761,8 +743,7 @@ impl<'w, 's> Commands<'w, 's> { system: S, input: I, ) { - let id = self.register_system_cached(system); - self.run_system_with_input(id, input); + self.push(RunSystemCachedWith::new(system, input)); } /// Pushes a generic [`Command`] to the command queue. diff --git a/crates/bevy_ecs/src/system/system_registry.rs b/crates/bevy_ecs/src/system/system_registry.rs index e05bc75780c68..c911483a671de 100644 --- a/crates/bevy_ecs/src/system/system_registry.rs +++ b/crates/bevy_ecs/src/system/system_registry.rs @@ -469,48 +469,41 @@ impl Command for RegisterSystem { } } -/// The [`Command`] type for registering and caching one shot systems from +/// The [`Command`] type for registering, caching, and running a one shot system from /// [`Commands`](crate::system::Commands). /// -/// This command needs an already boxed system to register, and an already spawned entity. -pub struct RegisterSystemCached + 'static> { +/// This command needs an already boxed system to register. +pub struct RunSystemCachedWith + 'static> { system: BoxedSystem, - entity: Entity, + input: I, _marker: std::marker::PhantomData (M, S)>, } -impl + 'static> RegisterSystemCached { +impl + 'static> RunSystemCachedWith { /// Creates a new [`Command`] struct, which can be added to /// [`Commands`](crate::system::Commands). - pub fn new(system: S, entity: Entity) -> Self { + pub fn new(system: S, input: I) -> Self { Self { system: Box::new(IntoSystem::into_system(system)), - entity, + input, _marker: std::marker::PhantomData, } } } -impl + 'static> Command - for RegisterSystemCached +impl + 'static> Command + for RunSystemCachedWith { fn apply(self, world: &mut World) { - if let Some(mut entity) = world.get_entity_mut(self.entity) { - entity.insert_if_new(( - RegisteredSystem { - initialized: false, - system: self.system, - }, - SystemIdMarker, - )); - } else { - return; - } - - if !world.contains_resource::>() { - let system_id = SystemId::from_entity(self.entity); - world.insert_resource(CachedSystemId::::new(system_id)); - } + let id = match world.get_resource::>() { + Some(cached) => cached.id, + None => { + let id = world.register_boxed_system(self.system); + world.insert_resource(CachedSystemId::::new(id)); + id + } + }; + let _ = world.run_system_with_input(id, self.input); } } From d58e2beddb8e0afdbc36d550fbe5d1f96fa7381e Mon Sep 17 00:00:00 2001 From: Ben Frankel Date: Sun, 25 Aug 2024 19:34:53 -0700 Subject: [PATCH 05/21] Fix silly misnamed import --- crates/bevy_ecs/src/system/commands/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index 400247e6c4ea3..87e6c7b30c4d8 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -2,7 +2,9 @@ mod parallel_scope; use core::panic::Location; -use super::{Deferred, IntoObserverSystem, IntoSystem, RegisterSystem, Resource, RunSystemCached}; +use super::{ + Deferred, IntoObserverSystem, IntoSystem, RegisterSystem, Resource, RunSystemCachedWith, +}; use crate::{ self as bevy_ecs, bundle::{Bundle, InsertMode}, From 261af7ede044a214fca0c83e87444aff7fc15c40 Mon Sep 17 00:00:00 2001 From: Ben Frankel Date: Sun, 25 Aug 2024 19:45:10 -0700 Subject: [PATCH 06/21] Yeet remnant --- crates/bevy_ecs/src/system/system_registry.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/system/system_registry.rs b/crates/bevy_ecs/src/system/system_registry.rs index c911483a671de..67ab26a32e017 100644 --- a/crates/bevy_ecs/src/system/system_registry.rs +++ b/crates/bevy_ecs/src/system/system_registry.rs @@ -104,8 +104,7 @@ impl std::fmt::Debug for SystemId { /// A cached [`SystemId`] distinguished by the unique function type of its system. /// -/// This resource is inserted as part of [`World::register_system_cached`] and -/// [`Commands::register_system_cached`](crate::system::commands::Commands::register_system_cached). +/// This resource is inserted as part of [`World::register_system_cached`]. #[derive(Resource)] pub struct CachedSystemId { /// The cached `SystemId`. From 740928e0486a537a9ffc59f6065777198b64f1d6 Mon Sep 17 00:00:00 2001 From: Ben Frankel Date: Fri, 30 Aug 2024 09:47:09 -0700 Subject: [PATCH 07/21] Add `remove` and make `register` more robust --- crates/bevy_ecs/src/system/system_registry.rs | 94 +++++++++++-------- 1 file changed, 54 insertions(+), 40 deletions(-) diff --git a/crates/bevy_ecs/src/system/system_registry.rs b/crates/bevy_ecs/src/system/system_registry.rs index 67ab26a32e017..69674194b8643 100644 --- a/crates/bevy_ecs/src/system/system_registry.rs +++ b/crates/bevy_ecs/src/system/system_registry.rs @@ -1,3 +1,5 @@ +use crate::bundle::Bundle; +use crate::change_detection::Mut; use crate::entity::Entity; use crate::system::{BoxedSystem, IntoSystem}; use crate::world::{Command, World}; @@ -122,6 +124,17 @@ impl CachedSystemId { } } +/// Creates a [`Bundle`] for a one-shot system entity. +fn system_bundle(system: BoxedSystem) -> impl Bundle { + ( + RegisteredSystem { + initialized: false, + system, + }, + SystemIdMarker, + ) +} + impl World { /// Registers a system and returns a [`SystemId`] so it can later be called by [`World::run_system`]. /// @@ -149,18 +162,8 @@ impl World { &mut self, system: BoxedSystem, ) -> SystemId { - SystemId { - entity: self - .spawn(( - RegisteredSystem { - initialized: false, - system, - }, - SystemIdMarker, - )) - .id(), - marker: std::marker::PhantomData, - } + let entity = self.spawn_empty().insert(system_bundle(system)).id(); + SystemId::from_entity(entity) } /// Removes a registered system and returns the system, if it exists. @@ -354,14 +357,33 @@ impl World { &mut self, system: S, ) -> SystemId { - match self.get_resource::>() { - Some(cached) => cached.id, - None => { - let id = self.register_system(system); - self.insert_resource(CachedSystemId::::new(id)); - id - } + if !self.contains_resource::>() { + let id = self.register_system(system); + self.insert_resource(CachedSystemId::::new(id)); + return id; } + + self.resource_scope(|world, mut cached: Mut>| { + if let Some(mut entity) = world.get_entity_mut(cached.id.entity()) { + if !entity.contains::>() { + entity.insert(system_bundle(Box::new(IntoSystem::into_system(system)))); + } + } else { + cached.id = world.register_system(system); + } + cached.id + }) + } + + /// Removes a cached system and its [`CachedSystemId`] resource. + pub fn remove_system_cached + 'static>( + &mut self, + _system: S, + ) -> Result, RegisteredSystemError> { + let cached = self + .remove_resource::>() + .ok_or(RegisteredSystemError::SystemNotCached)?; + self.remove_system(cached.id) } /// Runs a system, registering it and caching its [`SystemId`] if necessary. @@ -457,13 +479,7 @@ impl RegisterSystem { impl Command for RegisterSystem { fn apply(self, world: &mut World) { if let Some(mut entity) = world.get_entity_mut(self.entity) { - entity.insert(( - RegisteredSystem { - initialized: false, - system: self.system, - }, - SystemIdMarker, - )); + entity.insert(system_bundle(self.system)); } } } @@ -472,10 +488,10 @@ impl Command for RegisterSystem { /// [`Commands`](crate::system::Commands). /// /// This command needs an already boxed system to register. -pub struct RunSystemCachedWith + 'static> { - system: BoxedSystem, +pub struct RunSystemCachedWith { + system: S, input: I, - _marker: std::marker::PhantomData (M, S)>, + _marker: std::marker::PhantomData (O, M)>, } impl + 'static> RunSystemCachedWith { @@ -483,26 +499,18 @@ impl + 'static> RunSystemCache /// [`Commands`](crate::system::Commands). pub fn new(system: S, input: I) -> Self { Self { - system: Box::new(IntoSystem::into_system(system)), + system, input, _marker: std::marker::PhantomData, } } } -impl + 'static> Command +impl + Send + 'static> Command for RunSystemCachedWith { fn apply(self, world: &mut World) { - let id = match world.get_resource::>() { - Some(cached) => cached.id, - None => { - let id = world.register_boxed_system(self.system); - world.insert_resource(CachedSystemId::::new(id)); - id - } - }; - let _ = world.run_system_with_input(id, self.input); + let _ = world.run_system_cached_with(self.system, self.input); } } @@ -514,6 +522,11 @@ pub enum RegisteredSystemError { /// Did you forget to register it? #[error("System {0:?} was not registered")] SystemIdNotRegistered(SystemId), + /// A cached system was removed by value, but no system with its type was found. + /// + /// Did you forget to register it? + #[error("Cached system was not found")] + SystemNotCached, /// A system tried to run itself recursively. #[error("System {0:?} tried to run itself recursively")] Recursive(SystemId), @@ -528,6 +541,7 @@ impl std::fmt::Debug for RegisteredSystemError { Self::SystemIdNotRegistered(arg0) => { f.debug_tuple("SystemIdNotRegistered").field(arg0).finish() } + Self::SystemNotCached => write!(f, "SystemNotCached"), Self::Recursive(arg0) => f.debug_tuple("Recursive").field(arg0).finish(), Self::SelfRemove(arg0) => f.debug_tuple("SelfRemove").field(arg0).finish(), } From fb4321e151465c9a23fb7deaee0c16b8b2b198ff Mon Sep 17 00:00:00 2001 From: Ben Frankel Date: Fri, 30 Aug 2024 09:55:53 -0700 Subject: [PATCH 08/21] Cache by `System` type --- crates/bevy_ecs/src/system/system_registry.rs | 36 ++++++------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/crates/bevy_ecs/src/system/system_registry.rs b/crates/bevy_ecs/src/system/system_registry.rs index 69674194b8643..2f927dd4b1512 100644 --- a/crates/bevy_ecs/src/system/system_registry.rs +++ b/crates/bevy_ecs/src/system/system_registry.rs @@ -1,7 +1,7 @@ use crate::bundle::Bundle; use crate::change_detection::Mut; use crate::entity::Entity; -use crate::system::{BoxedSystem, IntoSystem}; +use crate::system::{BoxedSystem, IntoSystem, System}; use crate::world::{Command, World}; use crate::{self as bevy_ecs}; use bevy_ecs_macros::{Component, Resource}; @@ -108,21 +108,7 @@ impl std::fmt::Debug for SystemId { /// /// This resource is inserted as part of [`World::register_system_cached`]. #[derive(Resource)] -pub struct CachedSystemId { - /// The cached `SystemId`. - pub id: SystemId, - _marker: std::marker::PhantomData S>, -} - -impl CachedSystemId { - /// Creates a new `CachedSystemId` struct given a `SystemId`. - pub fn new(id: SystemId) -> Self { - Self { - id, - _marker: std::marker::PhantomData, - } - } -} +pub struct CachedSystemId(pub SystemId); /// Creates a [`Bundle`] for a one-shot system entity. fn system_bundle(system: BoxedSystem) -> impl Bundle { @@ -357,21 +343,21 @@ impl World { &mut self, system: S, ) -> SystemId { - if !self.contains_resource::>() { + if !self.contains_resource::>() { let id = self.register_system(system); - self.insert_resource(CachedSystemId::::new(id)); + self.insert_resource(CachedSystemId::(id)); return id; } - self.resource_scope(|world, mut cached: Mut>| { - if let Some(mut entity) = world.get_entity_mut(cached.id.entity()) { + self.resource_scope(|world, mut id: Mut>| { + if let Some(mut entity) = world.get_entity_mut(id.0.entity()) { if !entity.contains::>() { entity.insert(system_bundle(Box::new(IntoSystem::into_system(system)))); } } else { - cached.id = world.register_system(system); + id.0 = world.register_system(system); } - cached.id + id.0 }) } @@ -380,10 +366,10 @@ impl World { &mut self, _system: S, ) -> Result, RegisteredSystemError> { - let cached = self - .remove_resource::>() + let id = self + .remove_resource::>() .ok_or(RegisteredSystemError::SystemNotCached)?; - self.remove_system(cached.id) + self.remove_system(id.0) } /// Runs a system, registering it and caching its [`SystemId`] if necessary. From 367a0fbe6ae53a2d9de07280fe1133e95217ae59 Mon Sep 17 00:00:00 2001 From: Ben Frankel Date: Fri, 30 Aug 2024 10:14:40 -0700 Subject: [PATCH 09/21] Make `RunSystemCachedWith` nicer --- crates/bevy_ecs/src/system/system_registry.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/crates/bevy_ecs/src/system/system_registry.rs b/crates/bevy_ecs/src/system/system_registry.rs index 2f927dd4b1512..8aa84df16d94e 100644 --- a/crates/bevy_ecs/src/system/system_registry.rs +++ b/crates/bevy_ecs/src/system/system_registry.rs @@ -470,30 +470,29 @@ impl Command for RegisterSystem { } } -/// The [`Command`] type for registering, caching, and running a one shot system from +/// The [`Command`] type for running a cached one-shot system from /// [`Commands`](crate::system::Commands). /// /// This command needs an already boxed system to register. -pub struct RunSystemCachedWith { +pub struct RunSystemCachedWith> { system: S, - input: I, - _marker: std::marker::PhantomData (O, M)>, + input: S::In, } -impl + 'static> RunSystemCachedWith { +impl> RunSystemCachedWith { /// Creates a new [`Command`] struct, which can be added to /// [`Commands`](crate::system::Commands). - pub fn new(system: S, input: I) -> Self { + pub fn new(system: impl IntoSystem, input: S::In) -> Self { Self { - system, + system: IntoSystem::into_system(system), input, - _marker: std::marker::PhantomData, } } } -impl + Send + 'static> Command - for RunSystemCachedWith +impl> Command for RunSystemCachedWith +where + S::In: Send, { fn apply(self, world: &mut World) { let _ = world.run_system_cached_with(self.system, self.input); From 17349bbdfae1f8b4ba400af7aed6ffb68261e6ea Mon Sep 17 00:00:00 2001 From: Ben Frankel Date: Fri, 30 Aug 2024 10:24:52 -0700 Subject: [PATCH 10/21] Fix redundant doc link --- crates/bevy_ecs/src/system/system_registry.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/system/system_registry.rs b/crates/bevy_ecs/src/system/system_registry.rs index 8aa84df16d94e..61d50c73d9f09 100644 --- a/crates/bevy_ecs/src/system/system_registry.rs +++ b/crates/bevy_ecs/src/system/system_registry.rs @@ -143,7 +143,7 @@ impl World { /// Similar to [`Self::register_system`], but allows passing in a [`BoxedSystem`]. /// /// This is useful if the [`IntoSystem`] implementor has already been turned into a - /// [`System`](crate::system::System) trait object and put in a [`Box`]. + /// [`System`] trait object and put in a [`Box`]. pub fn register_boxed_system( &mut self, system: BoxedSystem, From 0aa5da79008b0eb36b93f5ee00ba9726c9171042 Mon Sep 17 00:00:00 2001 From: Ben Frankel Date: Fri, 30 Aug 2024 10:34:19 -0700 Subject: [PATCH 11/21] Simplify `spawn_empty.insert` --- crates/bevy_ecs/src/system/system_registry.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/system/system_registry.rs b/crates/bevy_ecs/src/system/system_registry.rs index 61d50c73d9f09..7a144a39cb7b2 100644 --- a/crates/bevy_ecs/src/system/system_registry.rs +++ b/crates/bevy_ecs/src/system/system_registry.rs @@ -148,7 +148,7 @@ impl World { &mut self, system: BoxedSystem, ) -> SystemId { - let entity = self.spawn_empty().insert(system_bundle(system)).id(); + let entity = self.spawn(system_bundle(system)).id(); SystemId::from_entity(entity) } From 3e7352214398ed1cf4ae9a3a7057b6b78456bc7c Mon Sep 17 00:00:00 2001 From: Ben Frankel Date: Sat, 21 Sep 2024 15:34:27 -0700 Subject: [PATCH 12/21] Improve docs a bit --- crates/bevy_ecs/src/system/commands/mod.rs | 4 ++ crates/bevy_ecs/src/system/system_registry.rs | 38 ++++++++++++++----- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index 87e6c7b30c4d8..193e768cefaff 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -730,12 +730,16 @@ impl<'w, 's> Commands<'w, 's> { /// Similar to [`Self::run_system`], but caching the [`SystemId`] in a /// [`CachedSystemId`](crate::system::CachedSystemId). + /// + /// See [`World::register_system_cached`] for more information. pub fn run_system_cached + 'static>(&mut self, system: S) { self.run_system_cached_with(system, ()); } /// Similar to [`Self::run_system_with_input`], but caching the [`SystemId`] in a /// [`CachedSystemId`](crate::system::CachedSystemId). + /// + /// See [`World::register_system_cached`] for more information. pub fn run_system_cached_with< I: 'static + Send, M: 'static, diff --git a/crates/bevy_ecs/src/system/system_registry.rs b/crates/bevy_ecs/src/system/system_registry.rs index 7a144a39cb7b2..5e89a94f62d51 100644 --- a/crates/bevy_ecs/src/system/system_registry.rs +++ b/crates/bevy_ecs/src/system/system_registry.rs @@ -332,17 +332,32 @@ impl World { Ok(result) } - /// Registers a system and caches its [`SystemId`]. + /// Registers a system or returns its cached [`SystemId`]. /// - /// The first time this is called for a particular system, it will register that system and - /// store its [`SystemId`] in a [`CachedSystemId`] resource. Every subsequent call will - /// retrieve the cached ID instead of creating a new registered system. + /// If you just want to run a system, see [`World::run_system_cached`]. /// - /// If you don't need to cache the `SystemId`, use [`World::register_system`] instead. + /// The first time this function is called for a particular system, it will register it and + /// store its [`SystemId`] in a [`CachedSystemId`] resource for later. If you would rather + /// manage the `SystemId` yourself, or register multiple copies of the same system, use + /// [`World::register_system`] instead. + /// + /// # Limitations + /// + /// This function only accepts ZST (zero-sized) systems to guarantee that any two systems of + /// the same type must be equal. This means that closures that capture the environment, and + /// function pointers, are not accepted. If this limitation is an issue, consider using + /// [`World::register_system`] instead. pub fn register_system_cached + 'static>( &mut self, system: S, ) -> SystemId { + const { + assert!( + size_of::() == 0, + "Non-ZST systems (e.g. capturing closures, function pointers) cannot be cached.", + ); + } + if !self.contains_resource::>() { let id = self.register_system(system); self.insert_resource(CachedSystemId::(id)); @@ -362,6 +377,8 @@ impl World { } /// Removes a cached system and its [`CachedSystemId`] resource. + /// + /// See [`World::register_system_cached`] for more information. pub fn remove_system_cached + 'static>( &mut self, _system: S, @@ -372,7 +389,9 @@ impl World { self.remove_system(id.0) } - /// Runs a system, registering it and caching its [`SystemId`] if necessary. + /// Runs a cached system, registering it if necessary. + /// + /// See [`World::register_system_cached`] for more information. pub fn run_system_cached + 'static>( &mut self, system: S, @@ -380,8 +399,9 @@ impl World { self.run_system_cached_with(system, ()) } - /// Runs a system with an input, registering the system and caching its [`SystemId`] if - /// necessary. + /// Runs a cached system with an input, registering it if necessary. + /// + /// See [`World::register_system_cached`] for more information. pub fn run_system_cached_with + 'static>( &mut self, system: S, @@ -473,7 +493,7 @@ impl Command for RegisterSystem { /// The [`Command`] type for running a cached one-shot system from /// [`Commands`](crate::system::Commands). /// -/// This command needs an already boxed system to register. +/// See [`World::register_system_cached`] for more information. pub struct RunSystemCachedWith> { system: S, input: S::In, From 5230cada6d6bf86321661f86b4cb30483f1f9f32 Mon Sep 17 00:00:00 2001 From: Ben Frankel Date: Sat, 21 Sep 2024 15:39:59 -0700 Subject: [PATCH 13/21] Say resource --- crates/bevy_ecs/src/system/commands/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index 193e768cefaff..8e17699af9f3f 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -729,7 +729,7 @@ impl<'w, 's> Commands<'w, 's> { } /// Similar to [`Self::run_system`], but caching the [`SystemId`] in a - /// [`CachedSystemId`](crate::system::CachedSystemId). + /// [`CachedSystemId`](crate::system::CachedSystemId) resource. /// /// See [`World::register_system_cached`] for more information. pub fn run_system_cached + 'static>(&mut self, system: S) { @@ -737,7 +737,7 @@ impl<'w, 's> Commands<'w, 's> { } /// Similar to [`Self::run_system_with_input`], but caching the [`SystemId`] in a - /// [`CachedSystemId`](crate::system::CachedSystemId). + /// [`CachedSystemId`](crate::system::CachedSystemId) resource. /// /// See [`World::register_system_cached`] for more information. pub fn run_system_cached_with< From bba418e9d4b6b2d2125e8250430e9f2f72121a14 Mon Sep 17 00:00:00 2001 From: Ben Frankel Date: Sat, 21 Sep 2024 15:41:03 -0700 Subject: [PATCH 14/21] "as part of" -> "by" --- crates/bevy_ecs/src/system/system_registry.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/system/system_registry.rs b/crates/bevy_ecs/src/system/system_registry.rs index 5e89a94f62d51..fc25c5ddf67a8 100644 --- a/crates/bevy_ecs/src/system/system_registry.rs +++ b/crates/bevy_ecs/src/system/system_registry.rs @@ -106,7 +106,7 @@ impl std::fmt::Debug for SystemId { /// A cached [`SystemId`] distinguished by the unique function type of its system. /// -/// This resource is inserted as part of [`World::register_system_cached`]. +/// This resource is inserted by [`World::register_system_cached`]. #[derive(Resource)] pub struct CachedSystemId(pub SystemId); From e7db8fc312526ddf746050c1ce70654e076db7b7 Mon Sep 17 00:00:00 2001 From: Ben Frankel Date: Sat, 21 Sep 2024 16:08:16 -0700 Subject: [PATCH 15/21] More docs stuff --- crates/bevy_ecs/src/system/system.rs | 4 ++-- crates/bevy_ecs/src/system/system_registry.rs | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/bevy_ecs/src/system/system.rs b/crates/bevy_ecs/src/system/system.rs index a36900a353824..bd6b90dcfb946 100644 --- a/crates/bevy_ecs/src/system/system.rs +++ b/crates/bevy_ecs/src/system/system.rs @@ -208,8 +208,8 @@ impl Debug for dyn System { /// world.run_system_once(increment); // still prints 1 /// ``` /// -/// If you do need systems to hold onto state between runs, use the [`World::run_system`](World::run_system) -/// and run the system by their [`SystemId`](crate::system::SystemId). +/// If you do need systems to hold onto state between runs, use [`World::run_system_cached`](World::run_system_cached) +/// or [`World::run_system`](World::run_system). /// /// # Usage /// Typically, to test a system, or to extract specific diagnostics information from a world, diff --git a/crates/bevy_ecs/src/system/system_registry.rs b/crates/bevy_ecs/src/system/system_registry.rs index fc25c5ddf67a8..76e8211d90ad7 100644 --- a/crates/bevy_ecs/src/system/system_registry.rs +++ b/crates/bevy_ecs/src/system/system_registry.rs @@ -345,7 +345,10 @@ impl World { /// /// This function only accepts ZST (zero-sized) systems to guarantee that any two systems of /// the same type must be equal. This means that closures that capture the environment, and - /// function pointers, are not accepted. If this limitation is an issue, consider using + /// function pointers, are not accepted. + /// + /// If you want to access values from the environment within a system, consider passing them in + /// as inputs via [`World::run_system_cached_with`]. If that's not an option, consider /// [`World::register_system`] instead. pub fn register_system_cached + 'static>( &mut self, From 5efb8ef21ec452d898887e807a8866e20fd7a70f Mon Sep 17 00:00:00 2001 From: Ben Frankel Date: Sat, 21 Sep 2024 16:33:28 -0700 Subject: [PATCH 16/21] Add cached_system unit test --- crates/bevy_ecs/src/system/system_registry.rs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/crates/bevy_ecs/src/system/system_registry.rs b/crates/bevy_ecs/src/system/system_registry.rs index 76e8211d90ad7..c8400d2ad94ab 100644 --- a/crates/bevy_ecs/src/system/system_registry.rs +++ b/crates/bevy_ecs/src/system/system_registry.rs @@ -750,4 +750,33 @@ mod tests { let _ = world.run_system(nested_id); assert_eq!(*world.resource::(), Counter(5)); } + + #[test] + fn cached_system() { + use crate::system::SystemId; + + fn four() -> i32 { + 4 + } + + let old = world.register_system_cached(four); + let new = world.register_system_cached(four); + assert_eq!(old, new); + + world.remove_system_cached(four); + let new = world.register_system_cached(four); + assert_ne!(old, new); + + let output = world.run_system(old); + assert_eq!(output, Err(RegisteredSystemError::SystemIdNotRegistered(old))); + + let output = world.run_system(new); + assert_eq!(output, Ok(four())); + + let output = world.run_system_cached(four); + assert_eq!(output, Ok(four())); + + let output = world.run_system_cached_with(four, ()); + assert_eq!(output, Ok(four())); + } } From 0586a3833702d0e9194b31bae4a0bb0dc8636626 Mon Sep 17 00:00:00 2001 From: Ben Frankel Date: Sat, 21 Sep 2024 16:57:23 -0700 Subject: [PATCH 17/21] Fix CI errors --- crates/bevy_ecs/src/system/system_registry.rs | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/crates/bevy_ecs/src/system/system_registry.rs b/crates/bevy_ecs/src/system/system_registry.rs index c8400d2ad94ab..4f3275eb5d880 100644 --- a/crates/bevy_ecs/src/system/system_registry.rs +++ b/crates/bevy_ecs/src/system/system_registry.rs @@ -124,9 +124,9 @@ fn system_bundle(system: BoxedSystem) -> impl Bund impl World { /// Registers a system and returns a [`SystemId`] so it can later be called by [`World::run_system`]. /// - /// It's possible to register the same system multiple times, which will create multiple - /// registered systems. To avoid this behavior, consider using - /// [`World::register_system_cached`] instead. + /// It's possible to register multiple copies of the same system by calling this function + /// multiple times. If that's not what you want, consider using [`World::register_system_cached`] + /// instead. /// /// This is different from adding systems to a [`Schedule`](crate::schedule::Schedule), /// because the [`SystemId`] that is returned can be used anywhere in the [`World`] to run the associated system. @@ -334,7 +334,8 @@ impl World { /// Registers a system or returns its cached [`SystemId`]. /// - /// If you just want to run a system, see [`World::run_system_cached`]. + /// If you want to run the system immediately and you don't need its `SystemId`, see + /// [`World::run_system_cached`]. /// /// The first time this function is called for a particular system, it will register it and /// store its [`SystemId`] in a [`CachedSystemId`] resource for later. If you would rather @@ -380,7 +381,7 @@ impl World { } /// Removes a cached system and its [`CachedSystemId`] resource. - /// + /// /// See [`World::register_system_cached`] for more information. pub fn remove_system_cached + 'static>( &mut self, @@ -393,7 +394,7 @@ impl World { } /// Runs a cached system, registering it if necessary. - /// + /// /// See [`World::register_system_cached`] for more information. pub fn run_system_cached + 'static>( &mut self, @@ -753,12 +754,13 @@ mod tests { #[test] fn cached_system() { - use crate::system::SystemId; + use crate::system::RegisteredSystemError; fn four() -> i32 { 4 } + let mut world = World::new(); let old = world.register_system_cached(four); let new = world.register_system_cached(four); assert_eq!(old, new); @@ -768,7 +770,10 @@ mod tests { assert_ne!(old, new); let output = world.run_system(old); - assert_eq!(output, Err(RegisteredSystemError::SystemIdNotRegistered(old))); + assert_eq!( + output, + Err(RegisteredSystemError::SystemIdNotRegistered(old)), + ); let output = world.run_system(new); assert_eq!(output, Ok(four())); From 10f702f8244136e923fcd1de473fe6b6ccc5f3bb Mon Sep 17 00:00:00 2001 From: Ben Frankel Date: Sat, 21 Sep 2024 17:03:57 -0700 Subject: [PATCH 18/21] Fix more CI errors --- crates/bevy_ecs/src/system/system_registry.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/bevy_ecs/src/system/system_registry.rs b/crates/bevy_ecs/src/system/system_registry.rs index 4f3275eb5d880..a1dde295e2a1c 100644 --- a/crates/bevy_ecs/src/system/system_registry.rs +++ b/crates/bevy_ecs/src/system/system_registry.rs @@ -770,18 +770,18 @@ mod tests { assert_ne!(old, new); let output = world.run_system(old); - assert_eq!( + assert_matches!( output, - Err(RegisteredSystemError::SystemIdNotRegistered(old)), + Err(RegisteredSystemError::SystemIdNotRegistered(x)) if x == old, ); let output = world.run_system(new); - assert_eq!(output, Ok(four())); + assert_matches!(output, Ok(x) if x == four()); let output = world.run_system_cached(four); - assert_eq!(output, Ok(four())); + assert_matches!(output, Ok(x) if x == four()); let output = world.run_system_cached_with(four, ()); - assert_eq!(output, Ok(four())); + assert_matches!(output, Ok(x) if x == four()); } } From 90dd64b4edf2b72e400c5d69fded8cc41b445653 Mon Sep 17 00:00:00 2001 From: Ben Frankel Date: Sat, 21 Sep 2024 17:06:58 -0700 Subject: [PATCH 19/21] Fix assert_matches! --- crates/bevy_ecs/src/system/system_registry.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/bevy_ecs/src/system/system_registry.rs b/crates/bevy_ecs/src/system/system_registry.rs index a1dde295e2a1c..d55534d052c70 100644 --- a/crates/bevy_ecs/src/system/system_registry.rs +++ b/crates/bevy_ecs/src/system/system_registry.rs @@ -770,18 +770,18 @@ mod tests { assert_ne!(old, new); let output = world.run_system(old); - assert_matches!( + assert!(matches!( output, Err(RegisteredSystemError::SystemIdNotRegistered(x)) if x == old, - ); + )); let output = world.run_system(new); - assert_matches!(output, Ok(x) if x == four()); + assert!(matches!(output, Ok(x) if x == four())); let output = world.run_system_cached(four); - assert_matches!(output, Ok(x) if x == four()); + assert!(matches!(output, Ok(x) if x == four())); let output = world.run_system_cached_with(four, ()); - assert_matches!(output, Ok(x) if x == four()); + assert!(matches!(output, Ok(x) if x == four())); } } From 2253637462922e8210ff6beaabe1859179330aae Mon Sep 17 00:00:00 2001 From: Ben Frankel Date: Sat, 21 Sep 2024 17:11:36 -0700 Subject: [PATCH 20/21] Use unused result --- crates/bevy_ecs/src/system/system_registry.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/crates/bevy_ecs/src/system/system_registry.rs b/crates/bevy_ecs/src/system/system_registry.rs index d55534d052c70..1b84ad6061e34 100644 --- a/crates/bevy_ecs/src/system/system_registry.rs +++ b/crates/bevy_ecs/src/system/system_registry.rs @@ -765,7 +765,8 @@ mod tests { let new = world.register_system_cached(four); assert_eq!(old, new); - world.remove_system_cached(four); + let result = world.remove_system_cached(four); + assert!(matches!(result, Ok(_))); let new = world.register_system_cached(four); assert_ne!(old, new); @@ -774,13 +775,10 @@ mod tests { output, Err(RegisteredSystemError::SystemIdNotRegistered(x)) if x == old, )); - let output = world.run_system(new); assert!(matches!(output, Ok(x) if x == four())); - let output = world.run_system_cached(four); assert!(matches!(output, Ok(x) if x == four())); - let output = world.run_system_cached_with(four, ()); assert!(matches!(output, Ok(x) if x == four())); } From 0c9535a48998240d98464840a5e9361f3063e9e1 Mon Sep 17 00:00:00 2001 From: Ben Frankel Date: Sat, 21 Sep 2024 17:38:45 -0700 Subject: [PATCH 21/21] Fix more CI stuff ig --- crates/bevy_ecs/src/system/system_registry.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/system/system_registry.rs b/crates/bevy_ecs/src/system/system_registry.rs index 1b84ad6061e34..70b25c94bfefc 100644 --- a/crates/bevy_ecs/src/system/system_registry.rs +++ b/crates/bevy_ecs/src/system/system_registry.rs @@ -357,7 +357,7 @@ impl World { ) -> SystemId { const { assert!( - size_of::() == 0, + size_of::() == 0, "Non-ZST systems (e.g. capturing closures, function pointers) cannot be cached.", ); } @@ -766,7 +766,7 @@ mod tests { assert_eq!(old, new); let result = world.remove_system_cached(four); - assert!(matches!(result, Ok(_))); + assert!(result.is_ok()); let new = world.register_system_cached(four); assert_ne!(old, new);