diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 9c6c4d937c..b0d223662b 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -8,7 +8,7 @@ use bevy_ecs::{ intern::Interned, prelude::*, schedule::{ScheduleBuildSettings, ScheduleLabel}, - system::{IntoObserverSystem, SystemId}, + system::{IntoObserverSystem, SystemId, SystemInput}, }; #[cfg(feature = "trace")] use bevy_utils::tracing::info_span; @@ -302,10 +302,14 @@ impl App { /// This allows for running systems in a push-based fashion. /// Using a [`Schedule`] is still preferred for most cases /// due to its better performance and ability to run non-conflicting systems simultaneously. - pub fn register_system + 'static>( + pub fn register_system( &mut self, - system: S, - ) -> SystemId { + system: impl IntoSystem + 'static, + ) -> SystemId + where + I: SystemInput + 'static, + O: 'static, + { self.main_mut().register_system(system) } diff --git a/crates/bevy_app/src/sub_app.rs b/crates/bevy_app/src/sub_app.rs index 48ffe97388..e0d6f86f80 100644 --- a/crates/bevy_app/src/sub_app.rs +++ b/crates/bevy_app/src/sub_app.rs @@ -3,7 +3,7 @@ use bevy_ecs::{ event::EventRegistry, prelude::*, schedule::{InternedScheduleLabel, ScheduleBuildSettings, ScheduleLabel}, - system::SystemId, + system::{SystemId, SystemInput}, }; #[cfg(feature = "trace")] @@ -189,10 +189,14 @@ impl SubApp { } /// See [`App::register_system`]. - pub fn register_system + 'static>( + pub fn register_system( &mut self, - system: S, - ) -> SystemId { + system: impl IntoSystem + 'static, + ) -> SystemId + where + I: SystemInput + 'static, + O: 'static, + { self.world.register_system(system) } diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 45d007b930..aa24c15a3c 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -63,9 +63,9 @@ pub mod prelude { IntoSystemSetConfigs, Schedule, Schedules, SystemSet, }, system::{ - Commands, Deferred, EntityCommand, EntityCommands, In, IntoSystem, Local, NonSend, - NonSendMut, ParallelCommands, ParamSet, Query, ReadOnlySystem, Res, ResMut, Resource, - System, SystemParamBuilder, SystemParamFunction, + Commands, Deferred, EntityCommand, EntityCommands, In, InMut, InRef, IntoSystem, Local, + NonSend, NonSendMut, ParallelCommands, ParamSet, Query, ReadOnlySystem, Res, ResMut, + Resource, System, SystemIn, SystemInput, SystemParamBuilder, SystemParamFunction, }, world::{ Command, EntityMut, EntityRef, EntityWorldMut, FromWorld, OnAdd, OnInsert, OnRemove, diff --git a/crates/bevy_ecs/src/observer/mod.rs b/crates/bevy_ecs/src/observer/mod.rs index 304d2176b5..1e54d1f3c0 100644 --- a/crates/bevy_ecs/src/observer/mod.rs +++ b/crates/bevy_ecs/src/observer/mod.rs @@ -13,6 +13,7 @@ use crate::{archetype::ArchetypeFlags, system::IntoObserverSystem, world::*}; use crate::{component::ComponentId, prelude::*, world::DeferredWorld}; use bevy_ptr::Ptr; use bevy_utils::HashMap; +use std::ops::{Deref, DerefMut}; use std::{fmt::Debug, marker::PhantomData}; /// Type containing triggered [`Event`] information for a given run of an [`Observer`]. This contains the @@ -122,6 +123,20 @@ impl<'w, E: Debug, B: Bundle> Debug for Trigger<'w, E, B> { } } +impl<'w, E, B: Bundle> Deref for Trigger<'w, E, B> { + type Target = E; + + fn deref(&self) -> &Self::Target { + self.event + } +} + +impl<'w, E, B: Bundle> DerefMut for Trigger<'w, E, B> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.event + } +} + /// A description of what an [`Observer`] observes. #[derive(Default, Clone)] pub struct ObserverDescriptor { diff --git a/crates/bevy_ecs/src/observer/runner.rs b/crates/bevy_ecs/src/observer/runner.rs index 6ab582556d..a23172fa86 100644 --- a/crates/bevy_ecs/src/observer/runner.rs +++ b/crates/bevy_ecs/src/observer/runner.rs @@ -357,13 +357,6 @@ fn observer_system_runner>( propagate, observer_trigger, ); - // SAFETY: the static lifetime is encapsulated in Trigger / cannot leak out. - // Additionally, IntoObserverSystem is only implemented for functions starting - // with for<'a> Trigger<'a>, meaning users cannot specify Trigger<'static> manually, - // allowing the Trigger<'static> to be moved outside of the context of the system. - // This transmute is obviously not ideal, but it is safe. Ideally we can remove the - // static constraint from ObserverSystem, but so far we have not found a way. - let trigger: Trigger<'static, E, B> = unsafe { std::mem::transmute(trigger) }; // SAFETY: // - observer was triggered so must have an `Observer` component. // - observer cannot be dropped or mutated until after the system pointer is already dropped. diff --git a/crates/bevy_ecs/src/schedule/condition.rs b/crates/bevy_ecs/src/schedule/condition.rs index 317854faaf..924f8cd0b8 100644 --- a/crates/bevy_ecs/src/schedule/condition.rs +++ b/crates/bevy_ecs/src/schedule/condition.rs @@ -2,7 +2,8 @@ use std::borrow::Cow; use std::ops::Not; use crate::system::{ - Adapt, AdapterSystem, CombinatorSystem, Combine, IntoSystem, ReadOnlySystem, System, + Adapt, AdapterSystem, CombinatorSystem, Combine, IntoSystem, ReadOnlySystem, System, SystemIn, + SystemInput, }; /// A type-erased run condition stored in a [`Box`]. @@ -57,7 +58,7 @@ pub type BoxedCondition = Box>; /// /// ``` /// # use bevy_ecs::prelude::*; -/// fn identity() -> impl Condition<(), bool> { +/// fn identity() -> impl Condition<(), In> { /// IntoSystem::into_system(|In(x)| x) /// } /// @@ -70,7 +71,7 @@ pub type BoxedCondition = Box>; /// # world.insert_resource(DidRun(false)); /// # app.run(&mut world); /// # assert!(world.resource::().0); -pub trait Condition: sealed::Condition { +pub trait Condition: sealed::Condition { /// Returns a new run condition that only returns `true` /// if both this one and the passed `and` return `true`. /// @@ -466,12 +467,12 @@ pub trait Condition: sealed::Condition { } } -impl Condition for F where F: sealed::Condition {} +impl Condition for F where F: sealed::Condition {} mod sealed { - use crate::system::{IntoSystem, ReadOnlySystem}; + use crate::system::{IntoSystem, ReadOnlySystem, SystemInput}; - pub trait Condition: + pub trait Condition: IntoSystem { // This associated type is necessary to let the compiler @@ -479,7 +480,7 @@ mod sealed { type ReadOnlySystem: ReadOnlySystem; } - impl Condition for F + impl Condition for F where F: IntoSystem, F::System: ReadOnlySystem, @@ -496,7 +497,7 @@ pub mod common_conditions { event::{Event, EventReader}, prelude::{Component, Query, With}, removal_detection::RemovedComponents, - system::{In, IntoSystem, Local, Res, Resource, System}, + system::{In, IntoSystem, Local, Res, Resource, System, SystemInput}, }; /// A [`Condition`]-satisfying system that returns `true` @@ -1110,10 +1111,12 @@ pub mod common_conditions { /// app.run(&mut world); /// assert_eq!(world.resource::().0, 2); /// ``` - pub fn condition_changed>( - condition: C, - ) -> impl Condition<(), CIn> { - condition.pipe(|In(new): In, mut prev: Local| -> bool { + pub fn condition_changed(condition: C) -> impl Condition<(), CIn> + where + CIn: SystemInput, + C: Condition, + { + condition.pipe(|In(new): In, mut prev: Local| { let changed = *prev != new; *prev = new; changed @@ -1164,10 +1167,11 @@ pub mod common_conditions { /// app.run(&mut world); /// assert_eq!(world.resource::().0, 2); /// ``` - pub fn condition_changed_to>( - to: bool, - condition: C, - ) -> impl Condition<(), CIn> { + pub fn condition_changed_to(to: bool, condition: C) -> impl Condition<(), CIn> + where + CIn: SystemInput, + C: Condition, + { condition.pipe(move |In(new): In, mut prev: Local| -> bool { let now_true = *prev != new && new == to; *prev = new; @@ -1179,21 +1183,22 @@ pub mod common_conditions { /// Invokes [`Not`] with the output of another system. /// /// See [`common_conditions::not`] for examples. -pub type NotSystem = AdapterSystem; +pub type NotSystem = AdapterSystem; /// Used with [`AdapterSystem`] to negate the output of a system via the [`Not`] operator. #[doc(hidden)] #[derive(Clone, Copy)] pub struct NotMarker; -impl Adapt for NotMarker -where - T::Out: Not, -{ - type In = T::In; - type Out = ::Output; +impl> Adapt for NotMarker { + type In = S::In; + type Out = ::Output; - fn adapt(&mut self, input: Self::In, run_system: impl FnOnce(T::In) -> T::Out) -> Self::Out { + fn adapt( + &mut self, + input: ::Inner<'_>, + run_system: impl FnOnce(SystemIn<'_, S>) -> S::Out, + ) -> Self::Out { !run_system(input) } } @@ -1221,7 +1226,7 @@ pub struct AndMarker; impl Combine for AndMarker where - In: Copy, + for<'a> In: SystemInput: Copy>, A: System, B: System, { @@ -1229,9 +1234,9 @@ where type Out = bool; fn combine( - input: Self::In, - a: impl FnOnce(::In) -> ::Out, - b: impl FnOnce(::In) -> ::Out, + input: ::Inner<'_>, + a: impl FnOnce(SystemIn<'_, A>) -> A::Out, + b: impl FnOnce(SystemIn<'_, A>) -> B::Out, ) -> Self::Out { a(input) && b(input) } @@ -1242,7 +1247,7 @@ pub struct NandMarker; impl Combine for NandMarker where - In: Copy, + for<'a> In: SystemInput: Copy>, A: System, B: System, { @@ -1250,9 +1255,9 @@ where type Out = bool; fn combine( - input: Self::In, - a: impl FnOnce(::In) -> ::Out, - b: impl FnOnce(::In) -> ::Out, + input: ::Inner<'_>, + a: impl FnOnce(SystemIn<'_, A>) -> A::Out, + b: impl FnOnce(SystemIn<'_, B>) -> B::Out, ) -> Self::Out { !(a(input) && b(input)) } @@ -1263,7 +1268,7 @@ pub struct NorMarker; impl Combine for NorMarker where - In: Copy, + for<'a> In: SystemInput: Copy>, A: System, B: System, { @@ -1271,9 +1276,9 @@ where type Out = bool; fn combine( - input: Self::In, - a: impl FnOnce(::In) -> ::Out, - b: impl FnOnce(::In) -> ::Out, + input: ::Inner<'_>, + a: impl FnOnce(SystemIn<'_, A>) -> A::Out, + b: impl FnOnce(SystemIn<'_, B>) -> B::Out, ) -> Self::Out { !(a(input) || b(input)) } @@ -1284,7 +1289,7 @@ pub struct OrMarker; impl Combine for OrMarker where - In: Copy, + for<'a> In: SystemInput: Copy>, A: System, B: System, { @@ -1292,9 +1297,9 @@ where type Out = bool; fn combine( - input: Self::In, - a: impl FnOnce(::In) -> ::Out, - b: impl FnOnce(::In) -> ::Out, + input: ::Inner<'_>, + a: impl FnOnce(SystemIn<'_, A>) -> A::Out, + b: impl FnOnce(SystemIn<'_, B>) -> B::Out, ) -> Self::Out { a(input) || b(input) } @@ -1305,7 +1310,7 @@ pub struct XnorMarker; impl Combine for XnorMarker where - In: Copy, + for<'a> In: SystemInput: Copy>, A: System, B: System, { @@ -1313,9 +1318,9 @@ where type Out = bool; fn combine( - input: Self::In, - a: impl FnOnce(::In) -> ::Out, - b: impl FnOnce(::In) -> ::Out, + input: ::Inner<'_>, + a: impl FnOnce(SystemIn<'_, A>) -> A::Out, + b: impl FnOnce(SystemIn<'_, B>) -> B::Out, ) -> Self::Out { !(a(input) ^ b(input)) } @@ -1326,7 +1331,7 @@ pub struct XorMarker; impl Combine for XorMarker where - In: Copy, + for<'a> In: SystemInput: Copy>, A: System, B: System, { @@ -1334,9 +1339,9 @@ where type Out = bool; fn combine( - input: Self::In, - a: impl FnOnce(::In) -> ::Out, - b: impl FnOnce(::In) -> ::Out, + input: ::Inner<'_>, + a: impl FnOnce(SystemIn<'_, A>) -> A::Out, + b: impl FnOnce(SystemIn<'_, B>) -> B::Out, ) -> Self::Out { a(input) ^ b(input) } diff --git a/crates/bevy_ecs/src/system/adapter_system.rs b/crates/bevy_ecs/src/system/adapter_system.rs index b8c5e9a4b2..2c7aea84bf 100644 --- a/crates/bevy_ecs/src/system/adapter_system.rs +++ b/crates/bevy_ecs/src/system/adapter_system.rs @@ -1,7 +1,11 @@ use std::borrow::Cow; use super::{ReadOnlySystem, System}; -use crate::{schedule::InternedSystemSet, world::unsafe_world_cell::UnsafeWorldCell}; +use crate::{ + schedule::InternedSystemSet, + system::{input::SystemInput, SystemIn}, + world::unsafe_world_cell::UnsafeWorldCell, +}; /// Customizes the behavior of an [`AdapterSystem`] /// @@ -28,8 +32,8 @@ use crate::{schedule::InternedSystemSet, world::unsafe_world_cell::UnsafeWorldCe /// /// fn adapt( /// &mut self, -/// input: Self::In, -/// run_system: impl FnOnce(S::In) -> S::Out, +/// input: ::Inner<'_>, +/// run_system: impl FnOnce(SystemIn<'_, S>) -> S::Out, /// ) -> Self::Out { /// !run_system(input) /// } @@ -45,13 +49,17 @@ use crate::{schedule::InternedSystemSet, world::unsafe_world_cell::UnsafeWorldCe )] pub trait Adapt: Send + Sync + 'static { /// The [input](System::In) type for an [`AdapterSystem`]. - type In; + type In: SystemInput; /// The [output](System::Out) type for an [`AdapterSystem`]. type Out; /// When used in an [`AdapterSystem`], this function customizes how the system /// is run and how its inputs/outputs are adapted. - fn adapt(&mut self, input: Self::In, run_system: impl FnOnce(S::In) -> S::Out) -> Self::Out; + fn adapt( + &mut self, + input: ::Inner<'_>, + run_system: impl FnOnce(SystemIn<'_, S>) -> S::Out, + ) -> Self::Out; } /// A [`System`] that takes the output of `S` and transforms it by applying `Func` to it. @@ -109,7 +117,11 @@ where } #[inline] - unsafe fn run_unsafe(&mut self, input: Self::In, world: UnsafeWorldCell) -> Self::Out { + unsafe fn run_unsafe( + &mut self, + input: SystemIn<'_, Self>, + world: UnsafeWorldCell, + ) -> Self::Out { // SAFETY: `system.run_unsafe` has the same invariants as `self.run_unsafe`. self.func.adapt(input, |input| unsafe { self.system.run_unsafe(input, world) @@ -117,7 +129,7 @@ where } #[inline] - fn run(&mut self, input: Self::In, world: &mut crate::prelude::World) -> Self::Out { + fn run(&mut self, input: SystemIn<'_, Self>, world: &mut crate::prelude::World) -> Self::Out { self.func .adapt(input, |input| self.system.run(input, world)) } @@ -173,13 +185,17 @@ where impl Adapt for F where - S: System, F: Send + Sync + 'static + FnMut(S::Out) -> Out, + S: System, { type In = S::In; type Out = Out; - fn adapt(&mut self, input: S::In, run_system: impl FnOnce(S::In) -> S::Out) -> Out { + fn adapt( + &mut self, + input: ::Inner<'_>, + run_system: impl FnOnce(SystemIn<'_, S>) -> S::Out, + ) -> Out { self(run_system(input)) } } diff --git a/crates/bevy_ecs/src/system/combinator.rs b/crates/bevy_ecs/src/system/combinator.rs index e8512f8181..5434a0bb2d 100644 --- a/crates/bevy_ecs/src/system/combinator.rs +++ b/crates/bevy_ecs/src/system/combinator.rs @@ -6,6 +6,7 @@ use crate::{ prelude::World, query::Access, schedule::InternedSystemSet, + system::{input::SystemInput, SystemIn}, world::unsafe_world_cell::UnsafeWorldCell, }; @@ -88,7 +89,7 @@ use super::{ReadOnlySystem, System}; )] pub trait Combine { /// The [input](System::In) type for a [`CombinatorSystem`]. - type In; + type In: SystemInput; /// The [output](System::Out) type for a [`CombinatorSystem`]. type Out; @@ -98,9 +99,9 @@ pub trait Combine { /// /// See the trait-level docs for [`Combine`] for an example implementation. fn combine( - input: Self::In, - a: impl FnOnce(A::In) -> A::Out, - b: impl FnOnce(B::In) -> B::Out, + input: ::Inner<'_>, + a: impl FnOnce(SystemIn<'_, A>) -> A::Out, + b: impl FnOnce(SystemIn<'_, B>) -> B::Out, ) -> Self::Out; } @@ -165,7 +166,11 @@ where self.a.has_deferred() || self.b.has_deferred() } - unsafe fn run_unsafe(&mut self, input: Self::In, world: UnsafeWorldCell) -> Self::Out { + unsafe fn run_unsafe( + &mut self, + input: SystemIn<'_, Self>, + world: UnsafeWorldCell, + ) -> Self::Out { Func::combine( input, // SAFETY: The world accesses for both underlying systems have been registered, @@ -180,7 +185,7 @@ where ) } - fn run(&mut self, input: Self::In, world: &mut World) -> Self::Out { + fn run(&mut self, input: SystemIn<'_, Self>, world: &mut World) -> Self::Out { let world = world.as_unsafe_world_cell(); Func::combine( input, @@ -249,7 +254,7 @@ where } /// SAFETY: Both systems are read-only, so any system created by combining them will only read from the world. -unsafe impl ReadOnlySystem for CombinatorSystem +unsafe impl ReadOnlySystem for CombinatorSystem where Func: Combine + 'static, A: ReadOnlySystem, @@ -307,25 +312,140 @@ where /// result.ok().filter(|&n| n < 100) /// } /// ``` -pub type PipeSystem = CombinatorSystem; +pub struct PipeSystem { + a: A, + b: B, + name: Cow<'static, str>, + component_access: Access, + archetype_component_access: Access, +} -#[doc(hidden)] -pub struct Pipe; - -impl Combine for Pipe +impl PipeSystem where A: System, - B: System, + B: System, + for<'a> B::In: SystemInput = A::Out>, +{ + /// Creates a new system that pipes two inner systems. + pub const fn new(a: A, b: B, name: Cow<'static, str>) -> Self { + Self { + a, + b, + name, + component_access: Access::new(), + archetype_component_access: Access::new(), + } + } +} + +impl System for PipeSystem +where + A: System, + B: System, + for<'a> B::In: SystemInput = A::Out>, { type In = A::In; type Out = B::Out; - fn combine( - input: Self::In, - a: impl FnOnce(A::In) -> A::Out, - b: impl FnOnce(B::In) -> B::Out, + fn name(&self) -> Cow<'static, str> { + self.name.clone() + } + + fn component_access(&self) -> &Access { + &self.component_access + } + + fn archetype_component_access(&self) -> &Access { + &self.archetype_component_access + } + + fn is_send(&self) -> bool { + self.a.is_send() && self.b.is_send() + } + + fn is_exclusive(&self) -> bool { + self.a.is_exclusive() || self.b.is_exclusive() + } + + fn has_deferred(&self) -> bool { + self.a.has_deferred() || self.b.has_deferred() + } + + unsafe fn run_unsafe( + &mut self, + input: SystemIn<'_, Self>, + world: UnsafeWorldCell, ) -> Self::Out { - let value = a(input); - b(value) + let value = self.a.run_unsafe(input, world); + self.b.run_unsafe(value, world) + } + + fn run(&mut self, input: SystemIn<'_, Self>, world: &mut World) -> Self::Out { + let value = self.a.run(input, world); + self.b.run(value, world) + } + + fn apply_deferred(&mut self, world: &mut World) { + self.a.apply_deferred(world); + self.b.apply_deferred(world); + } + + fn queue_deferred(&mut self, mut world: crate::world::DeferredWorld) { + self.a.queue_deferred(world.reborrow()); + self.b.queue_deferred(world); + } + + unsafe fn validate_param_unsafe(&self, world: UnsafeWorldCell) -> bool { + self.a.validate_param_unsafe(world) && self.b.validate_param_unsafe(world) + } + + fn validate_param(&mut self, world: &World) -> bool { + self.a.validate_param(world) && self.b.validate_param(world) + } + + fn initialize(&mut self, world: &mut World) { + self.a.initialize(world); + self.b.initialize(world); + self.component_access.extend(self.a.component_access()); + self.component_access.extend(self.b.component_access()); + } + + fn update_archetype_component_access(&mut self, world: UnsafeWorldCell) { + self.a.update_archetype_component_access(world); + self.b.update_archetype_component_access(world); + + self.archetype_component_access + .extend(self.a.archetype_component_access()); + self.archetype_component_access + .extend(self.b.archetype_component_access()); + } + + fn check_change_tick(&mut self, change_tick: Tick) { + self.a.check_change_tick(change_tick); + self.b.check_change_tick(change_tick); + } + + fn default_system_sets(&self) -> Vec { + let mut default_sets = self.a.default_system_sets(); + default_sets.append(&mut self.b.default_system_sets()); + default_sets + } + + fn get_last_run(&self) -> Tick { + self.a.get_last_run() + } + + fn set_last_run(&mut self, last_run: Tick) { + self.a.set_last_run(last_run); + self.b.set_last_run(last_run); } } + +/// SAFETY: Both systems are read-only, so any system created by piping them will only read from the world. +unsafe impl ReadOnlySystem for PipeSystem +where + A: ReadOnlySystem, + B: ReadOnlySystem, + for<'a> B::In: SystemInput = A::Out>, +{ +} diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index 5ea5cb9f01..8220d38021 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -14,7 +14,7 @@ use crate::{ entity::{Entities, Entity}, event::{Event, SendEvent}, observer::{Observer, TriggerEvent, TriggerTargets}, - system::{RunSystemWithInput, SystemId}, + system::{input::SystemInput, RunSystemWithInput, SystemId}, world::{ command_queue::RawCommandQueue, unsafe_world_cell::UnsafeWorldCell, Command, CommandQueue, EntityWorldMut, FromWorld, SpawnBatchIter, World, @@ -713,7 +713,10 @@ impl<'w, 's> Commands<'w, 's> { /// There is no way to get the output of a system when run as a command, because the /// execution of the system happens later. To get the output of a system, use /// [`World::run_system`] or [`World::run_system_with_input`] instead of running the system as a command. - pub fn run_system_with_input(&mut self, id: SystemId, input: I) { + pub fn run_system_with_input(&mut self, id: SystemId, input: I::Inner<'static>) + where + I: SystemInput: Send> + 'static, + { self.queue(RunSystemWithInput::new_with_input(id, input)); } @@ -766,15 +769,14 @@ impl<'w, 's> Commands<'w, 's> { /// # assert_eq!(1, world.resource::().0); /// # bevy_ecs::system::assert_is_system(register_system); /// ``` - pub fn register_system< - I: 'static + Send, - O: 'static + Send, - M, - S: IntoSystem + 'static, - >( + pub fn register_system( &mut self, - system: S, - ) -> SystemId { + system: impl IntoSystem + 'static, + ) -> SystemId + where + I: SystemInput + Send + 'static, + O: Send + 'static, + { let entity = self.spawn_empty().id(); self.queue(RegisterSystem::new(system, entity)); SystemId::from_entity(entity) @@ -792,15 +794,12 @@ impl<'w, 's> Commands<'w, 's> { /// [`CachedSystemId`](crate::system::CachedSystemId) resource. /// /// See [`World::register_system_cached`] for more information. - pub fn run_system_cached_with< - I: 'static + Send, + pub fn run_system_cached_with(&mut self, system: S, input: I::Inner<'static>) + where + I: SystemInput: Send> + Send + 'static, M: 'static, S: IntoSystem + 'static, - >( - &mut self, - system: S, - input: I, - ) { + { self.queue(RunSystemCachedWith::new(system, input)); } diff --git a/crates/bevy_ecs/src/system/exclusive_function_system.rs b/crates/bevy_ecs/src/system/exclusive_function_system.rs index 8d2fbb2bcb..9019a920a2 100644 --- a/crates/bevy_ecs/src/system/exclusive_function_system.rs +++ b/crates/bevy_ecs/src/system/exclusive_function_system.rs @@ -4,8 +4,8 @@ use crate::{ query::Access, schedule::{InternedSystemSet, SystemSet}, system::{ - check_system_change_tick, ExclusiveSystemParam, ExclusiveSystemParamItem, In, IntoSystem, - System, SystemMeta, + check_system_change_tick, ExclusiveSystemParam, ExclusiveSystemParamItem, IntoSystem, + System, SystemIn, SystemInput, SystemMeta, }, world::{unsafe_world_cell::UnsafeWorldCell, World}, }; @@ -108,11 +108,15 @@ where } #[inline] - unsafe fn run_unsafe(&mut self, _input: Self::In, _world: UnsafeWorldCell) -> Self::Out { + unsafe fn run_unsafe( + &mut self, + _input: SystemIn<'_, Self>, + _world: UnsafeWorldCell, + ) -> Self::Out { 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: SystemIn<'_, Self>, world: &mut World) -> Self::Out { world.last_change_tick_scope(self.system_meta.last_run, |world| { #[cfg(feature = "trace")] let _span_guard = self.system_meta.system_span.enter(); @@ -190,7 +194,7 @@ where )] pub trait ExclusiveSystemParamFunction: Send + Sync + 'static { /// The input type to this system. See [`System::In`]. - type In; + type In: SystemInput; /// The return type of this system. See [`System::Out`]. type Out; @@ -202,17 +206,22 @@ pub trait ExclusiveSystemParamFunction: Send + Sync + 'static { fn run( &mut self, world: &mut World, - input: Self::In, + input: ::Inner<'_>, param_value: ExclusiveSystemParamItem, ) -> Self::Out; } +/// A marker type used to distinguish exclusive function systems with and without input. +#[doc(hidden)] +pub struct HasExclusiveSystemInput; + macro_rules! impl_exclusive_system_function { ($($param: ident),*) => { #[allow(non_snake_case)] - impl ExclusiveSystemParamFunction Out> for Func + impl ExclusiveSystemParamFunction Out> for Func where - for <'a> &'a mut Func: + Func: Send + Sync + 'static, + for <'a> &'a mut Func: FnMut(&mut World, $($param),*) -> Out + FnMut(&mut World, $(ExclusiveSystemParamItem<$param>),*) -> Out, Out: 'static, @@ -237,30 +246,33 @@ macro_rules! impl_exclusive_system_function { call_inner(self, world, $($param),*) } } + #[allow(non_snake_case)] - impl ExclusiveSystemParamFunction, $($param,)*) -> Out> for Func + impl ExclusiveSystemParamFunction<(HasExclusiveSystemInput, fn(In, $($param,)*) -> Out)> for Func where - for <'a> &'a mut Func: - FnMut(In, &mut World, $($param),*) -> Out + - FnMut(In, &mut World, $(ExclusiveSystemParamItem<$param>),*) -> Out, + Func: Send + Sync + 'static, + for <'a> &'a mut Func: + FnMut(In, &mut World, $($param),*) -> Out + + FnMut(In::Param<'_>, &mut World, $(ExclusiveSystemParamItem<$param>),*) -> Out, + In: SystemInput + 'static, Out: 'static, { - type In = Input; + type In = In; type Out = Out; type Param = ($($param,)*); #[inline] - fn run(&mut self, world: &mut World, input: Input, param_value: ExclusiveSystemParamItem< ($($param,)*)>) -> Out { + fn run(&mut self, world: &mut World, input: In::Inner<'_>, param_value: ExclusiveSystemParamItem< ($($param,)*)>) -> Out { // Yes, this is strange, but `rustc` fails to compile this impl // without using this function. It fails to recognize 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, + fn call_inner( + mut f: impl FnMut(In::Param<'_>, &mut World, $($param,)*) -> Out, + input: In::Inner<'_>, world: &mut World, $($param: $param,)* ) -> Out { - f(In(input), world, $($param,)*) + f(In::wrap(input), world, $($param,)*) } let ($($param,)*) = param_value; call_inner(self, input, world, $($param),*) @@ -274,11 +286,13 @@ all_tuples!(impl_exclusive_system_function, 0, 16, F); #[cfg(test)] mod tests { + use crate::system::input::SystemInput; + use super::*; #[test] fn into_system_type_id_consistency() { - fn test(function: T) + fn test(function: T) where T: IntoSystem + Copy, { diff --git a/crates/bevy_ecs/src/system/function_system.rs b/crates/bevy_ecs/src/system/function_system.rs index 31fb49d546..54b54bfd56 100644 --- a/crates/bevy_ecs/src/system/function_system.rs +++ b/crates/bevy_ecs/src/system/function_system.rs @@ -4,7 +4,10 @@ use crate::{ prelude::FromWorld, query::{Access, FilteredAccessSet}, schedule::{InternedSystemSet, SystemSet}, - system::{check_system_change_tick, ReadOnlySystemParam, System, SystemParam, SystemParamItem}, + system::{ + check_system_change_tick, ReadOnlySystemParam, System, SystemIn, SystemInput, SystemParam, + SystemParamItem, + }, world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, World, WorldId}, }; @@ -237,7 +240,7 @@ macro_rules! impl_build_system { /// This method signature allows type inference of closure parameters for a system with input. /// You can use [`SystemState::build_system()`] if you have no input, or [`SystemState::build_any_system()`] if you don't need type inference. pub fn build_system_with_input< - Input, + Input: SystemInput, Out: 'static, Marker, F: FnMut(In, $(SystemParamItem<$param>),*) -> Out @@ -612,7 +615,11 @@ where } #[inline] - unsafe fn run_unsafe(&mut self, input: Self::In, world: UnsafeWorldCell) -> Self::Out { + unsafe fn run_unsafe( + &mut self, + input: SystemIn<'_, Self>, + world: UnsafeWorldCell, + ) -> Self::Out { #[cfg(feature = "trace")] let _span_guard = self.system_meta.system_span.enter(); @@ -731,19 +738,21 @@ where /// use std::num::ParseIntError; /// /// use bevy_ecs::prelude::*; +/// use bevy_ecs::system::StaticSystemInput; /// /// /// Pipe creates a new system which calls `a`, then calls `b` with the output of `a` /// pub fn pipe( /// mut a: A, /// mut b: B, -/// ) -> impl FnMut(In, ParamSet<(A::Param, B::Param)>) -> B::Out +/// ) -> impl FnMut(StaticSystemInput, ParamSet<(A::Param, B::Param)>) -> B::Out /// where /// // We need A and B to be systems, add those bounds /// A: SystemParamFunction, -/// B: SystemParamFunction, +/// B: SystemParamFunction, +/// for<'a> B::In: SystemInput = A::Out>, /// { /// // The type of `params` is inferred based on the return of this function above -/// move |In(a_in), mut params| { +/// move |StaticSystemInput(a_in), mut params| { /// let shared = a.run(a_in, params.p0()); /// b.run(shared, params.p1()) /// } @@ -778,9 +787,8 @@ where label = "invalid system" )] pub trait SystemParamFunction: Send + Sync + 'static { - /// The input type to this system. See [`System::In`]. - type In; - + /// The input type of this system. See [`System::In`]. + type In: SystemInput; /// The return type of this system. See [`System::Out`]. type Out; @@ -788,17 +796,27 @@ pub trait SystemParamFunction: Send + Sync + 'static { type Param: SystemParam; /// Executes this system once. See [`System::run`] or [`System::run_unsafe`]. - fn run(&mut self, input: Self::In, param_value: SystemParamItem) -> Self::Out; + fn run( + &mut self, + input: ::Inner<'_>, + param_value: SystemParamItem, + ) -> Self::Out; } +/// A marker type used to distinguish function systems with and without input. +#[doc(hidden)] +pub struct HasSystemInput; + macro_rules! impl_system_function { ($($param: ident),*) => { #[allow(non_snake_case)] - impl SystemParamFunction Out> for Func + impl SystemParamFunction Out> for Func where - for <'a> &'a mut Func: + Func: Send + Sync + 'static, + for <'a> &'a mut Func: FnMut($($param),*) -> Out + - FnMut($(SystemParamItem<$param>),*) -> Out, Out: 'static + FnMut($(SystemParamItem<$param>),*) -> Out, + Out: 'static { type In = (); type Out = Out; @@ -821,27 +839,30 @@ macro_rules! impl_system_function { } #[allow(non_snake_case)] - impl SystemParamFunction, $($param,)*) -> Out> for Func + impl SystemParamFunction<(HasSystemInput, fn(In, $($param,)*) -> Out)> for Func where - for <'a> &'a mut Func: - FnMut(In, $($param),*) -> Out + - FnMut(In, $(SystemParamItem<$param>),*) -> Out, Out: 'static + Func: Send + Sync + 'static, + for <'a> &'a mut Func: + FnMut(In, $($param),*) -> Out + + FnMut(In::Param<'_>, $(SystemParamItem<$param>),*) -> Out, + In: SystemInput + 'static, + Out: 'static { - type In = Input; + type In = In; type Out = Out; type Param = ($($param,)*); #[inline] - fn run(&mut self, input: Input, param_value: SystemParamItem< ($($param,)*)>) -> Out { + fn run(&mut self, input: In::Inner<'_>, param_value: SystemParamItem< ($($param,)*)>) -> Out { #[allow(clippy::too_many_arguments)] - fn call_inner( - mut f: impl FnMut(In, $($param,)*)->Out, - input: In, + fn call_inner( + mut f: impl FnMut(In::Param<'_>, $($param,)*)->Out, + input: In::Inner<'_>, $($param: $param,)* )->Out{ - f(input, $($param,)*) + f(In::wrap(input), $($param,)*) } let ($($param,)*) = param_value; - call_inner(self, In(input), $($param),*) + call_inner(self, input, $($param),*) } } }; @@ -857,7 +878,7 @@ mod tests { #[test] fn into_system_type_id_consistency() { - fn test(function: T) + fn test(function: T) where T: IntoSystem + Copy, { diff --git a/crates/bevy_ecs/src/system/input.rs b/crates/bevy_ecs/src/system/input.rs new file mode 100644 index 0000000000..26bf5af368 --- /dev/null +++ b/crates/bevy_ecs/src/system/input.rs @@ -0,0 +1,229 @@ +use std::ops::{Deref, DerefMut}; + +use crate::{bundle::Bundle, prelude::Trigger, system::System}; + +/// Trait for types that can be used as input to [`System`]s. +/// +/// Provided implementations are: +/// - `()`: No input +/// - [`In`]: For values +/// - [`InRef`]: For read-only references to values +/// - [`InMut`]: For mutable references to values +/// - [`Trigger`]: For [`ObserverSystem`]s +/// - [`StaticSystemInput`]: For arbitrary [`SystemInput`]s in generic contexts +/// +/// [`ObserverSystem`]: crate::system::ObserverSystem +pub trait SystemInput: Sized { + /// The wrapper input type that is defined as the first argument to [`FunctionSystem`]s. + /// + /// [`FunctionSystem`]: crate::system::FunctionSystem + type Param<'i>: SystemInput; + /// The inner input type that is passed to functions that run systems, + /// such as [`System::run`]. + /// + /// [`System::run`]: crate::system::System::run + type Inner<'i>; + + /// Converts a [`SystemInput::Inner`] into a [`SystemInput::Param`]. + fn wrap(this: Self::Inner<'_>) -> Self::Param<'_>; +} + +/// Shorthand way to get the [`System::In`] for a [`System`] as a [`SystemInput::Inner`]. +pub type SystemIn<'a, S> = <::In as SystemInput>::Inner<'a>; + +/// [`SystemInput`] type for systems that take no input. +impl SystemInput for () { + type Param<'i> = (); + type Inner<'i> = (); + + fn wrap(_this: Self::Inner<'_>) -> Self::Param<'_> {} +} + +/// A [`SystemInput`] type which denotes that a [`System`] receives +/// an input value of type `T` from its caller. +/// +/// [`System`]s may take an optional input which they require to be passed to them when they +/// are being [`run`](System::run). For [`FunctionSystem`]s the input may be marked +/// with this `In` type, but only the first param of a function may be tagged as an input. This also +/// means a system can only have one or zero input parameters. +/// +/// # Examples +/// +/// Here is a simple example of a system that takes a [`usize`] and returns the square of it. +/// +/// ``` +/// # use bevy_ecs::prelude::*; +/// # +/// fn square(In(input): In) -> usize { +/// input * input +/// } +/// +/// let mut world = World::new(); +/// let mut square_system = IntoSystem::into_system(square); +/// square_system.initialize(&mut world); +/// +/// assert_eq!(square_system.run(12, &mut world), 144); +/// ``` +/// +/// [`SystemParam`]: crate::system::SystemParam +/// [`FunctionSystem`]: crate::system::FunctionSystem +#[derive(Debug)] +pub struct In(pub T); + +impl SystemInput for In { + type Param<'i> = In; + type Inner<'i> = T; + + fn wrap(this: Self::Inner<'_>) -> Self::Param<'_> { + In(this) + } +} + +impl Deref for In { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for In { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +/// A [`SystemInput`] type which denotes that a [`System`] receives +/// a read-only reference to a value of type `T` from its caller. +/// +/// This is similar to [`In`] but takes a reference to a value instead of the value itself. +/// See [`InMut`] for the mutable version. +/// +/// # Examples +/// +/// Here is a simple example of a system that logs the passed in message. +/// +/// ``` +/// # use bevy_ecs::prelude::*; +/// # use std::fmt::Write as _; +/// # +/// #[derive(Resource, Default)] +/// struct Log(String); +/// +/// fn log(InRef(msg): InRef, mut log: ResMut) { +/// writeln!(log.0, "{}", msg).unwrap(); +/// } +/// +/// let mut world = World::new(); +/// world.init_resource::(); +/// let mut log_system = IntoSystem::into_system(log); +/// log_system.initialize(&mut world); +/// +/// log_system.run("Hello, world!", &mut world); +/// # assert_eq!(world.get_resource::().unwrap().0, "Hello, world!\n"); +/// ``` +/// +/// [`SystemParam`]: crate::system::SystemParam +#[derive(Debug)] +pub struct InRef<'i, T: ?Sized>(pub &'i T); + +impl SystemInput for InRef<'_, T> { + type Param<'i> = InRef<'i, T>; + type Inner<'i> = &'i T; + + fn wrap(this: Self::Inner<'_>) -> Self::Param<'_> { + InRef(this) + } +} + +impl<'i, T: ?Sized> Deref for InRef<'i, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.0 + } +} + +/// A [`SystemInput`] type which denotes that a [`System`] receives +/// a mutable reference to a value of type `T` from its caller. +/// +/// This is similar to [`In`] but takes a mutable reference to a value instead of the value itself. +/// See [`InRef`] for the read-only version. +/// +/// # Examples +/// +/// Here is a simple example of a system that takes a `&mut usize` and squares it. +/// +/// ``` +/// # use bevy_ecs::prelude::*; +/// # +/// fn square(InMut(input): InMut) { +/// *input *= *input; +/// } +/// +/// let mut world = World::new(); +/// let mut square_system = IntoSystem::into_system(square); +/// square_system.initialize(&mut world); +/// +/// let mut value = 12; +/// square_system.run(&mut value, &mut world); +/// assert_eq!(value, 144); +/// ``` +/// +/// [`SystemParam`]: crate::system::SystemParam +#[derive(Debug)] +pub struct InMut<'a, T: ?Sized>(pub &'a mut T); + +impl SystemInput for InMut<'_, T> { + type Param<'i> = InMut<'i, T>; + type Inner<'i> = &'i mut T; + + fn wrap(this: Self::Inner<'_>) -> Self::Param<'_> { + InMut(this) + } +} + +impl<'i, T: ?Sized> Deref for InMut<'i, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.0 + } +} + +impl<'i, T: ?Sized> DerefMut for InMut<'i, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.0 + } +} + +/// Used for [`ObserverSystem`]s. +/// +/// [`ObserverSystem`]: crate::system::ObserverSystem +impl SystemInput for Trigger<'_, E, B> { + type Param<'i> = Trigger<'i, E, B>; + type Inner<'i> = Trigger<'i, E, B>; + + fn wrap(this: Self::Inner<'_>) -> Self::Param<'_> { + this + } +} + +/// A helper for using [`SystemInput`]s in generic contexts. +/// +/// This type is a [`SystemInput`] adapter which always has +/// `Self::Param == Self` (ignoring lifetimes for brevity), +/// no matter the argument [`SystemInput`] (`I`). +/// +/// This makes it useful for having arbitrary [`SystemInput`]s in +/// function systems. +pub struct StaticSystemInput<'a, I: SystemInput>(pub I::Inner<'a>); + +impl<'a, I: SystemInput> SystemInput for StaticSystemInput<'a, I> { + type Param<'i> = StaticSystemInput<'i, I>; + type Inner<'i> = I::Inner<'i>; + + fn wrap(this: Self::Inner<'_>) -> Self::Param<'_> { + StaticSystemInput(this) + } +} diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index 9de3ab40a4..c3781d96fb 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -108,6 +108,7 @@ mod commands; mod exclusive_function_system; mod exclusive_system_param; mod function_system; +mod input; mod observer_system; mod query; #[allow(clippy::module_inception)] @@ -125,6 +126,7 @@ pub use commands::*; pub use exclusive_function_system::*; pub use exclusive_system_param::*; pub use function_system::*; +pub use input::*; pub use observer_system::*; pub use query::*; pub use system::*; @@ -155,7 +157,7 @@ use crate::world::World; message = "`{Self}` is not a valid system with input `{In}` and output `{Out}`", label = "invalid system" )] -pub trait IntoSystem: Sized { +pub trait IntoSystem: Sized { /// The type of [`System`] that this instance converts into. type System: System; @@ -166,9 +168,11 @@ pub trait IntoSystem: Sized { /// /// The second system must have [`In`](crate::system::In) as its first parameter, /// where `T` is the return type of the first system. - fn pipe(self, system: B) -> PipeSystem + fn pipe(self, system: B) -> PipeSystem where - B: IntoSystem, + Out: 'static, + B: IntoSystem, + for<'a> BIn: SystemInput = Out>, { let system_a = IntoSystem::into_system(self); let system_b = IntoSystem::into_system(system); @@ -219,34 +223,6 @@ impl IntoSystem for T { } } -/// Wrapper type to mark a [`SystemParam`] as an input. -/// -/// [`System`]s may take an optional input which they require to be passed to them when they -/// are being [`run`](System::run). For [`FunctionSystems`](FunctionSystem) the input may be marked -/// with this `In` type, but only the first param of a function may be tagged as an input. This also -/// means a system can only have one or zero input parameters. -/// -/// # Examples -/// -/// Here is a simple example of a system that takes a [`usize`] returning the square of it. -/// -/// ``` -/// use bevy_ecs::prelude::*; -/// -/// fn main() { -/// let mut square_system = IntoSystem::into_system(square); -/// -/// let mut world = World::default(); -/// square_system.initialize(&mut world); -/// assert_eq!(square_system.run(12, &mut world), 144); -/// } -/// -/// fn square(In(input): In) -> usize { -/// input * input -/// } -/// ``` -pub struct In(pub In); - /// Ensure that a given function is a [system](System). /// /// This should be used when writing doc examples, @@ -271,7 +247,7 @@ pub struct In(pub In); /// /// assert_is_system(my_system); /// ``` -pub fn assert_is_system( +pub fn assert_is_system( system: impl IntoSystem, ) { let mut system = IntoSystem::into_system(system); @@ -304,8 +280,10 @@ pub fn assert_is_system( /// /// assert_is_read_only_system(my_system); /// ``` -pub fn assert_is_read_only_system(system: S) +pub fn assert_is_read_only_system(system: S) where + In: SystemInput, + Out: 'static, S: IntoSystem, S::System: ReadOnlySystem, { @@ -324,20 +302,6 @@ pub fn assert_system_does_not_conflict std::ops::Deref for In { - type Target = T; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl std::ops::DerefMut for In { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - #[cfg(test)] mod tests { use bevy_utils::default; diff --git a/crates/bevy_ecs/src/system/observer_system.rs b/crates/bevy_ecs/src/system/observer_system.rs index 28ea902b1a..7cae84d160 100644 --- a/crates/bevy_ecs/src/system/observer_system.rs +++ b/crates/bevy_ecs/src/system/observer_system.rs @@ -1,8 +1,6 @@ -use bevy_utils::all_tuples; - use crate::{ prelude::{Bundle, Trigger}, - system::{System, SystemParam, SystemParamFunction, SystemParamItem}, + system::System, }; use super::IntoSystem; @@ -55,37 +53,6 @@ where } } -macro_rules! impl_system_function { - ($($param: ident),*) => { - #[allow(non_snake_case)] - impl SystemParamFunction, $($param,)*)> for Func - where - for <'a> &'a mut Func: - FnMut(Trigger, $($param),*) -> Out + - FnMut(Trigger, $(SystemParamItem<$param>),*) -> Out, Out: 'static - { - type In = Trigger<'static, E, B>; - type Out = Out; - type Param = ($($param,)*); - #[inline] - fn run(&mut self, input: Trigger<'static, E, B>, param_value: SystemParamItem< ($($param,)*)>) -> Out { - #[allow(clippy::too_many_arguments)] - fn call_inner( - mut f: impl FnMut(Trigger<'static, E, B>, $($param,)*) -> Out, - input: Trigger<'static, E, B>, - $($param: $param,)* - ) -> Out{ - f(input, $($param,)*) - } - let ($($param,)*) = param_value; - call_inner(self, input, $($param),*) - } - } - } -} - -all_tuples!(impl_system_function, 0, 16, F); - #[cfg(test)] mod tests { use crate::{ diff --git a/crates/bevy_ecs/src/system/system.rs b/crates/bevy_ecs/src/system/system.rs index 917a588356..296bf8ea2d 100644 --- a/crates/bevy_ecs/src/system/system.rs +++ b/crates/bevy_ecs/src/system/system.rs @@ -3,6 +3,8 @@ use core::fmt::Debug; use crate::component::Tick; use crate::schedule::InternedSystemSet; +use crate::system::input::SystemInput; +use crate::system::SystemIn; use crate::world::unsafe_world_cell::UnsafeWorldCell; use crate::world::DeferredWorld; use crate::{archetype::ArchetypeComponentId, component::ComponentId, query::Access, world::World}; @@ -25,9 +27,8 @@ use super::IntoSystem; /// see [`IntoSystemConfigs`](crate::schedule::IntoSystemConfigs). #[diagnostic::on_unimplemented(message = "`{Self}` is not a system", label = "invalid system")] pub trait System: Send + Sync + 'static { - /// The system's input. See [`In`](crate::system::In) for - /// [`FunctionSystem`](crate::system::FunctionSystem)s. - type In; + /// The system's input. + type In: SystemInput; /// The system's output. type Out; /// Returns the system's name. @@ -65,7 +66,8 @@ pub trait System: Send + Sync + 'static { /// - The method [`System::update_archetype_component_access`] must be called at some /// point before this one, with the same exact [`World`]. If [`System::update_archetype_component_access`] /// panics (or otherwise does not return for any reason), this method must not be called. - unsafe fn run_unsafe(&mut self, input: Self::In, world: UnsafeWorldCell) -> Self::Out; + unsafe fn run_unsafe(&mut self, input: SystemIn<'_, Self>, world: UnsafeWorldCell) + -> Self::Out; /// Runs the system with the given input in the world. /// @@ -74,7 +76,7 @@ pub trait System: Send + Sync + 'static { /// Unlike [`System::run_unsafe`], this will apply deferred parameters *immediately*. /// /// [`run_readonly`]: ReadOnlySystem::run_readonly - fn run(&mut self, input: Self::In, world: &mut World) -> Self::Out { + fn run(&mut self, input: SystemIn<'_, Self>, world: &mut World) -> Self::Out { let world_cell = world.as_unsafe_world_cell(); self.update_archetype_component_access(world_cell); // SAFETY: @@ -175,7 +177,7 @@ pub unsafe trait ReadOnlySystem: System { /// /// Unlike [`System::run`], this can be called with a shared reference to the world, /// since this system is known not to modify the world. - fn run_readonly(&mut self, input: Self::In, world: &World) -> Self::Out { + fn run_readonly(&mut self, input: SystemIn<'_, Self>, world: &World) -> Self::Out { let world = world.as_unsafe_world_cell_readonly(); self.update_archetype_component_access(world); // SAFETY: @@ -199,7 +201,11 @@ pub(crate) fn check_system_change_tick(last_run: &mut Tick, this_run: Tick, syst } } -impl Debug for dyn System { +impl Debug for dyn System +where + In: SystemInput + 'static, + Out: 'static, +{ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("System") .field("name", &self.name()) @@ -306,24 +312,34 @@ impl Debug for dyn System { /// ``` pub trait RunSystemOnce: Sized { /// Runs a system and applies its deferred parameters. - fn run_system_once, Out, Marker>(self, system: T) -> Out { + fn run_system_once(self, system: T) -> Out + where + T: IntoSystem<(), Out, Marker>, + { self.run_system_once_with((), system) } /// Runs a system with given input and applies its deferred parameters. - fn run_system_once_with, In, Out, Marker>( + fn run_system_once_with( self, - input: In, + input: SystemIn<'_, T::System>, system: T, - ) -> Out; + ) -> Out + where + T: IntoSystem, + In: SystemInput; } impl RunSystemOnce for &mut World { - fn run_system_once_with, In, Out, Marker>( + fn run_system_once_with( self, - input: In, + input: SystemIn<'_, T::System>, system: T, - ) -> Out { + ) -> Out + where + T: IntoSystem, + In: SystemInput, + { let mut system: T::System = IntoSystem::into_system(system); system.initialize(self); system.run(input, self) diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 0f4b099e83..4b7d69eff9 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -1826,7 +1826,7 @@ pub mod lifetimeless { /// A helper for using system parameters in generic contexts /// /// This type is a [`SystemParam`] adapter which always has -/// `Self::State::Item == Self` (ignoring lifetimes for brevity), +/// `Self::Item == Self` (ignoring lifetimes for brevity), /// no matter the argument [`SystemParam`] (`P`) (other than /// that `P` must be `'static`) /// diff --git a/crates/bevy_ecs/src/system/system_registry.rs b/crates/bevy_ecs/src/system/system_registry.rs index 70b25c94bf..3374f1b15a 100644 --- a/crates/bevy_ecs/src/system/system_registry.rs +++ b/crates/bevy_ecs/src/system/system_registry.rs @@ -1,7 +1,8 @@ use crate::bundle::Bundle; use crate::change_detection::Mut; use crate::entity::Entity; -use crate::system::{BoxedSystem, IntoSystem, System}; +use crate::system::input::SystemInput; +use crate::system::{BoxedSystem, IntoSystem, System, SystemIn}; use crate::world::{Command, World}; use crate::{self as bevy_ecs}; use bevy_ecs_macros::{Component, Resource}; @@ -44,12 +45,12 @@ impl RemovedSystem { /// /// These are opaque identifiers, keyed to a specific [`World`], /// and are created via [`World::register_system`]. -pub struct SystemId { +pub struct SystemId { pub(crate) entity: Entity, pub(crate) marker: std::marker::PhantomData O>, } -impl SystemId { +impl SystemId { /// Transforms a [`SystemId`] into the [`Entity`] that holds the one-shot system's state. /// /// It's trivial to convert [`SystemId`] into an [`Entity`] since a one-shot system @@ -72,33 +73,33 @@ impl SystemId { } } -impl Eq for SystemId {} +impl Eq for SystemId {} // A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters. -impl Copy for SystemId {} +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 { +impl Clone for SystemId { fn clone(&self) -> Self { *self } } // A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters. -impl PartialEq for SystemId { +impl PartialEq for SystemId { fn eq(&self, other: &Self) -> bool { self.entity == other.entity && self.marker == other.marker } } // A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters. -impl std::hash::Hash for SystemId { +impl std::hash::Hash for SystemId { fn hash(&self, state: &mut H) { self.entity.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 { f.debug_tuple("SystemId").field(&self.entity).finish() } @@ -133,10 +134,14 @@ 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 ability to run non-conflicting systems simultaneously. - pub fn register_system + 'static>( + pub fn register_system( &mut self, - system: S, - ) -> SystemId { + system: impl IntoSystem + 'static, + ) -> SystemId + where + I: SystemInput + 'static, + O: 'static, + { self.register_boxed_system(Box::new(IntoSystem::into_system(system))) } @@ -144,10 +149,11 @@ impl World { /// /// This is useful if the [`IntoSystem`] implementor has already been turned into a /// [`System`] trait object and put in a [`Box`]. - pub fn register_boxed_system( - &mut self, - system: BoxedSystem, - ) -> SystemId { + pub fn register_boxed_system(&mut self, system: BoxedSystem) -> SystemId + where + I: SystemInput + 'static, + O: 'static, + { let entity = self.spawn(system_bundle(system)).id(); SystemId::from_entity(entity) } @@ -158,10 +164,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> { + ) -> Result, RegisteredSystemError> + where + I: SystemInput + 'static, + O: 'static, + { match self.get_entity_mut(id.entity) { Some(mut entity) => { let registered_system = entity @@ -297,11 +307,15 @@ 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, - input: I, - ) -> Result> { + input: I::Inner<'_>, + ) -> Result> + where + I: SystemInput + 'static, + O: 'static, + { // lookup let mut entity = self .get_entity_mut(id.entity) @@ -351,10 +365,12 @@ impl World { /// 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, - system: S, - ) -> SystemId { + pub fn register_system_cached(&mut self, system: S) -> SystemId + where + I: SystemInput + 'static, + O: 'static, + S: IntoSystem + 'static, + { const { assert!( size_of::() == 0, @@ -383,10 +399,15 @@ impl World { /// Removes a cached system and its [`CachedSystemId`] resource. /// /// See [`World::register_system_cached`] for more information. - pub fn remove_system_cached + 'static>( + pub fn remove_system_cached( &mut self, _system: S, - ) -> Result, RegisteredSystemError> { + ) -> Result, RegisteredSystemError> + where + I: SystemInput + 'static, + O: 'static, + S: IntoSystem + 'static, + { let id = self .remove_resource::>() .ok_or(RegisteredSystemError::SystemNotCached)?; @@ -406,11 +427,16 @@ impl World { /// 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>( + pub fn run_system_cached_with( &mut self, system: S, - input: I, - ) -> Result> { + input: I::Inner<'_>, + ) -> Result> + where + I: SystemInput + 'static, + O: 'static, + S: IntoSystem + 'static, + { let id = self.register_system_cached(system); self.run_system_with_input(id, input) } @@ -428,9 +454,9 @@ impl World { /// execution of the system happens later. To get the output of a system, use /// [`World::run_system`] or [`World::run_system_with_input`] instead of running the system as a command. #[derive(Debug, Clone)] -pub struct RunSystemWithInput { +pub struct RunSystemWithInput { system_id: SystemId, - input: I, + input: I::Inner<'static>, } /// The [`Command`] type for [`World::run_system`]. @@ -453,15 +479,18 @@ impl RunSystem { } } -impl RunSystemWithInput { +impl RunSystemWithInput { /// Creates a new [`Command`] struct, which can be added to [`Commands`](crate::system::Commands) /// in order to run the specified system with the provided [`In<_>`](crate::system::In) input value. - pub fn new_with_input(system_id: SystemId, input: I) -> Self { + pub fn new_with_input(system_id: SystemId, input: I::Inner<'static>) -> Self { Self { system_id, input } } } -impl Command for RunSystemWithInput { +impl Command for RunSystemWithInput +where + I: SystemInput: Send> + 'static, +{ #[inline] fn apply(self, world: &mut World) { let _ = world.run_system_with_input(self.system_id, self.input); @@ -471,12 +500,16 @@ impl Command for RunSystemWithInput { /// 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. -pub struct RegisterSystem { +pub struct RegisterSystem { system: BoxedSystem, entity: Entity, } -impl RegisterSystem { +impl RegisterSystem +where + I: SystemInput + 'static, + O: 'static, +{ /// Creates a new [`Command`] struct, which can be added to [`Commands`](crate::system::Commands). pub fn new + 'static>(system: S, entity: Entity) -> Self { Self { @@ -486,7 +519,11 @@ impl RegisterSystem { } } -impl Command for RegisterSystem { +impl Command for RegisterSystem +where + I: SystemInput + Send + 'static, + O: Send + 'static, +{ fn apply(self, world: &mut World) { if let Some(mut entity) = world.get_entity_mut(self.entity) { entity.insert(system_bundle(self.system)); @@ -500,13 +537,16 @@ impl Command for RegisterSystem { /// See [`World::register_system_cached`] for more information. pub struct RunSystemCachedWith> { system: S, - input: S::In, + input: SystemIn<'static, S>, } impl> RunSystemCachedWith { /// Creates a new [`Command`] struct, which can be added to /// [`Commands`](crate::system::Commands). - pub fn new(system: impl IntoSystem, input: S::In) -> Self { + pub fn new( + system: impl IntoSystem, + input: SystemIn<'static, S>, + ) -> Self { Self { system: IntoSystem::into_system(system), input, @@ -516,7 +556,7 @@ impl> RunSystemCachedWith { impl> Command for RunSystemCachedWith where - S::In: Send, + S::In: SystemInput: Send>, { fn apply(self, world: &mut World) { let _ = world.run_system_cached_with(self.system, self.input); @@ -525,7 +565,7 @@ where /// 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? @@ -544,7 +584,7 @@ pub enum RegisteredSystemError { 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) => { @@ -729,7 +769,7 @@ mod tests { use crate::system::SystemId; #[derive(Component)] - struct Callback(SystemId, u8); + struct Callback(SystemId>, u8); fn nested(query: Query<&Callback>, mut commands: Commands) { for callback in query.iter() { @@ -782,4 +822,48 @@ mod tests { let output = world.run_system_cached_with(four, ()); assert!(matches!(output, Ok(x) if x == four())); } + + #[test] + fn system_with_input_ref() { + fn with_ref(InRef(input): InRef, mut counter: ResMut) { + counter.0 += *input; + } + + let mut world = World::new(); + world.insert_resource(Counter(0)); + + let id = world.register_system(with_ref); + world.run_system_with_input(id, &2).unwrap(); + assert_eq!(*world.resource::(), Counter(2)); + } + + #[test] + fn system_with_input_mut() { + #[derive(Event)] + struct MyEvent { + cancelled: bool, + } + + fn post(InMut(event): InMut, counter: ResMut) { + if counter.0 > 0 { + event.cancelled = true; + } + } + + let mut world = World::new(); + world.insert_resource(Counter(0)); + let post_system = world.register_system(post); + + let mut event = MyEvent { cancelled: false }; + world + .run_system_with_input(post_system, &mut event) + .unwrap(); + assert!(!event.cancelled); + + world.resource_mut::().0 = 1; + world + .run_system_with_input(post_system, &mut event) + .unwrap(); + assert!(event.cancelled); + } }