diff --git a/crates/bevy_ecs/src/observer/mod.rs b/crates/bevy_ecs/src/observer/mod.rs index 5dcc7f70b65cb..0e2e1edec1a51 100644 --- a/crates/bevy_ecs/src/observer/mod.rs +++ b/crates/bevy_ecs/src/observer/mod.rs @@ -13,6 +13,7 @@ use crate::{ entity::EntityHashMap, observer::entity_observer::ObservedBy, prelude::*, + query::QueryData, system::IntoObserverSystem, world::{DeferredWorld, *}, }; @@ -27,24 +28,42 @@ use core::{ /// Type containing triggered [`Event`] information for a given run of an [`Observer`]. This contains the /// [`Event`] data itself. If it was triggered for a specific [`Entity`], it includes that as well. It also /// contains event propagation information. See [`Trigger::propagate`] for more information. -pub struct Trigger<'w, E, B: Bundle = ()> { - event: &'w mut E, - propagate: &'w mut bool, +pub struct Trigger<'world, 'input, E, B: Bundle = (), D: QueryData + 'static = ()> { + event: &'input mut E, + propagate: &'input mut bool, trigger: ObserverTrigger, + data: Option>, _marker: PhantomData, } -impl<'w, E, B: Bundle> Trigger<'w, E, B> { +impl<'i, E, B: Bundle> Trigger<'static, 'i, E, B, ()> { /// Creates a new trigger for the given event and observer information. - pub fn new(event: &'w mut E, propagate: &'w mut bool, trigger: ObserverTrigger) -> Self { + pub fn new(event: &'i mut E, propagate: &'i mut bool, trigger: ObserverTrigger) -> Self { Self { event, propagate, trigger, + data: None, _marker: PhantomData, } } + /// Creates a new trigger with the given event and observer information, and target query data. + pub fn with_data<'w, D: QueryData>( + self, + data: Option>, + ) -> Trigger<'w, 'i, E, B, D> { + Trigger { + event: self.event, + propagate: self.propagate, + trigger: self.trigger, + data, + _marker: PhantomData, + } + } +} + +impl<'w, E, B: Bundle, D: QueryData> Trigger<'w, '_, E, B, D> { /// Returns the event type of this trigger. pub fn event_type(&self) -> ComponentId { self.trigger.event_type @@ -65,6 +84,11 @@ impl<'w, E, B: Bundle> Trigger<'w, E, B> { Ptr::from(&self.event) } + /// Returns a reference to the query data associated with the trigger. + pub fn data(&mut self) -> Option<&mut D::Item<'w>> { + self.data.as_mut() + } + /// Returns the [`Entity`] that triggered the observer, could be [`Entity::PLACEHOLDER`]. pub fn entity(&self) -> Entity { self.trigger.entity @@ -120,7 +144,7 @@ impl<'w, E, B: Bundle> Trigger<'w, E, B> { } } -impl<'w, E: Debug, B: Bundle> Debug for Trigger<'w, E, B> { +impl<'w, 'i, E: Debug, B: Bundle> Debug for Trigger<'w, 'i, E, B> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("Trigger") .field("event", &self.event) @@ -131,7 +155,7 @@ impl<'w, E: Debug, B: Bundle> Debug for Trigger<'w, E, B> { } } -impl<'w, E, B: Bundle> Deref for Trigger<'w, E, B> { +impl<'w, 'i, E, B: Bundle> Deref for Trigger<'w, 'i, E, B> { type Target = E; fn deref(&self) -> &Self::Target { @@ -139,7 +163,7 @@ impl<'w, E, B: Bundle> Deref for Trigger<'w, E, B> { } } -impl<'w, E, B: Bundle> DerefMut for Trigger<'w, E, B> { +impl<'w, 'i, E, B: Bundle> DerefMut for Trigger<'w, 'i, E, B> { fn deref_mut(&mut self) -> &mut Self::Target { self.event } diff --git a/crates/bevy_ecs/src/observer/runner.rs b/crates/bevy_ecs/src/observer/runner.rs index 02340b6128289..08c0e3bf2d290 100644 --- a/crates/bevy_ecs/src/observer/runner.rs +++ b/crates/bevy_ecs/src/observer/runner.rs @@ -374,7 +374,7 @@ fn observer_system_runner>( // - system is the same type erased system from above unsafe { (*system).update_archetype_component_access(world); - if (*system).validate_param_unsafe(world) { + if (*system).validate_param_unsafe(&trigger, world) { (*system).run_unsafe(trigger, world); (*system).queue_deferred(world.into_deferred()); } diff --git a/crates/bevy_ecs/src/schedule/executor/multi_threaded.rs b/crates/bevy_ecs/src/schedule/executor/multi_threaded.rs index 53453240dcb88..3d5c55f8c89a1 100644 --- a/crates/bevy_ecs/src/schedule/executor/multi_threaded.rs +++ b/crates/bevy_ecs/src/schedule/executor/multi_threaded.rs @@ -572,7 +572,7 @@ impl ExecutorState { // - The caller ensures that `world` has permission to read any data // required by the system. // - `update_archetype_component_access` has been called for system. - let valid_params = unsafe { system.validate_param_unsafe(world) }; + let valid_params = unsafe { system.validate_param_unsafe(&(), world) }; if !valid_params { self.skipped_systems.insert(system_index); } @@ -748,7 +748,7 @@ unsafe fn evaluate_and_fold_conditions( // - The caller ensures that `world` has permission to read any data // required by the condition. // - `update_archetype_component_access` has been called for condition. - if !unsafe { condition.validate_param_unsafe(world) } { + if !unsafe { condition.validate_param_unsafe(&(), world) } { return false; } // SAFETY: diff --git a/crates/bevy_ecs/src/schedule/executor/simple.rs b/crates/bevy_ecs/src/schedule/executor/simple.rs index 508f1fbffd07a..9918a0556c3c2 100644 --- a/crates/bevy_ecs/src/schedule/executor/simple.rs +++ b/crates/bevy_ecs/src/schedule/executor/simple.rs @@ -81,7 +81,7 @@ impl SystemExecutor for SimpleExecutor { let system = &mut schedule.systems[system_index]; if should_run { - let valid_params = system.validate_param(world); + let valid_params = system.validate_param(&(), world); should_run &= valid_params; } @@ -134,7 +134,7 @@ fn evaluate_and_fold_conditions(conditions: &mut [BoxedCondition], world: &mut W conditions .iter_mut() .map(|condition| { - if !condition.validate_param(world) { + if !condition.validate_param(&(), world) { return false; } __rust_begin_short_backtrace::readonly_run(&mut **condition, world) diff --git a/crates/bevy_ecs/src/schedule/executor/single_threaded.rs b/crates/bevy_ecs/src/schedule/executor/single_threaded.rs index 9cc6199ce6a4a..b7b6f9317f366 100644 --- a/crates/bevy_ecs/src/schedule/executor/single_threaded.rs +++ b/crates/bevy_ecs/src/schedule/executor/single_threaded.rs @@ -87,7 +87,7 @@ impl SystemExecutor for SingleThreadedExecutor { let system = &mut schedule.systems[system_index]; if should_run { - let valid_params = system.validate_param(world); + let valid_params = system.validate_param(&(), world); should_run &= valid_params; } @@ -166,7 +166,7 @@ fn evaluate_and_fold_conditions(conditions: &mut [BoxedCondition], world: &mut W conditions .iter_mut() .map(|condition| { - if !condition.validate_param(world) { + if !condition.validate_param(&(), world) { return false; } __rust_begin_short_backtrace::readonly_run(&mut **condition, world) diff --git a/crates/bevy_ecs/src/system/adapter_system.rs b/crates/bevy_ecs/src/system/adapter_system.rs index ef5e51c08f8e2..68071d858b8fe 100644 --- a/crates/bevy_ecs/src/system/adapter_system.rs +++ b/crates/bevy_ecs/src/system/adapter_system.rs @@ -82,7 +82,7 @@ pub struct IsAdapterSystemMarker; impl IntoSystem for IntoAdapterSystem where - Func: Adapt, + Func: Adapt, I: SystemInput, S: IntoSystem, { @@ -118,7 +118,7 @@ where impl System for AdapterSystem where Func: Adapt, - S: System, + S: System, { type In = Func::In; type Out = Func::Out; @@ -179,9 +179,13 @@ where } #[inline] - unsafe fn validate_param_unsafe(&mut self, world: UnsafeWorldCell) -> bool { + unsafe fn validate_param_unsafe( + &mut self, + input: &SystemIn<'_, Self>, + world: UnsafeWorldCell, + ) -> bool { // SAFETY: Delegate to other `System` implementations. - unsafe { self.system.validate_param_unsafe(world) } + unsafe { self.system.validate_param_unsafe(input, world) } } fn initialize(&mut self, world: &mut crate::prelude::World) { @@ -214,7 +218,7 @@ where unsafe impl ReadOnlySystem for AdapterSystem where Func: Adapt, - S: ReadOnlySystem, + S: ReadOnlySystem, { } diff --git a/crates/bevy_ecs/src/system/builder.rs b/crates/bevy_ecs/src/system/builder.rs index 91aa67f3ee5d5..da6cd12e794fa 100644 --- a/crates/bevy_ecs/src/system/builder.rs +++ b/crates/bevy_ecs/src/system/builder.rs @@ -4,7 +4,8 @@ use crate::{ prelude::QueryBuilder, query::{QueryData, QueryFilter, QueryState}, system::{ - DynSystemParam, DynSystemParamState, Local, ParamSet, Query, SystemMeta, SystemParam, + DynSystemParam, DynSystemParamState, Local, ParamSet, Query, SystemInput, SystemMeta, + SystemParam, }, world::{ FilteredResources, FilteredResourcesBuilder, FilteredResourcesMut, @@ -116,7 +117,7 @@ pub unsafe trait SystemParamBuilder: Sized { /// Create a [`SystemState`] from a [`SystemParamBuilder`]. /// To create a system, call [`SystemState::build_system`] on the result. - fn build_state(self, world: &mut World) -> SystemState

{ + fn build_state(self, world: &mut World) -> SystemState { SystemState::from_builder(world, self) } } diff --git a/crates/bevy_ecs/src/system/combinator.rs b/crates/bevy_ecs/src/system/combinator.rs index e21f35eee4e82..0255dfdb2e979 100644 --- a/crates/bevy_ecs/src/system/combinator.rs +++ b/crates/bevy_ecs/src/system/combinator.rs @@ -88,7 +88,7 @@ use super::{IntoSystem, ReadOnlySystem, System}; label = "invalid system combination", note = "the inputs and outputs of `{A}` and `{B}` are not compatible with this combiner" )] -pub trait Combine { +pub trait Combine, B: System> { /// The [input](System::In) type for a [`CombinatorSystem`]. type In: SystemInput; @@ -137,8 +137,8 @@ impl CombinatorSystem { impl System for CombinatorSystem where Func: Combine + 'static, - A: System, - B: System, + A: System, + B: System, { type In = Func::In; type Out = Func::Out; @@ -212,9 +212,15 @@ where } #[inline] - unsafe fn validate_param_unsafe(&mut self, world: UnsafeWorldCell) -> bool { + unsafe fn validate_param_unsafe( + &mut self, + input: &SystemIn<'_, Self>, + world: UnsafeWorldCell, + ) -> bool { // SAFETY: Delegate to other `System` implementations. - unsafe { self.a.validate_param_unsafe(world) && self.b.validate_param_unsafe(world) } + unsafe { + self.a.validate_param_unsafe(input, world) && self.b.validate_param_unsafe(input, world) + } } fn initialize(&mut self, world: &mut World) { @@ -259,8 +265,8 @@ where unsafe impl ReadOnlySystem for CombinatorSystem where Func: Combine + 'static, - A: ReadOnlySystem, - B: ReadOnlySystem, + A: ReadOnlySystem, + B: ReadOnlySystem, { } @@ -431,13 +437,21 @@ where self.b.queue_deferred(world); } - unsafe fn validate_param_unsafe(&mut self, world: UnsafeWorldCell) -> bool { + unsafe fn validate_param_unsafe( + &mut self, + input: &SystemIn<'_, Self>, + world: UnsafeWorldCell, + ) -> bool { // SAFETY: Delegate to other `System` implementations. - unsafe { self.a.validate_param_unsafe(world) && self.b.validate_param_unsafe(world) } + unsafe { + // TODO + self.a.validate_param_unsafe(input, world) /* && self.b.validate_param_unsafe(input, world) */ + } } - fn validate_param(&mut self, world: &World) -> bool { - self.a.validate_param(world) && self.b.validate_param(world) + fn validate_param(&mut self, input: &SystemIn<'_, Self>, world: &World) -> bool { + // TODO + self.a.validate_param(input, world) /* && self.b.validate_param(input, world) */ } fn initialize(&mut self, world: &mut World) { diff --git a/crates/bevy_ecs/src/system/exclusive_function_system.rs b/crates/bevy_ecs/src/system/exclusive_function_system.rs index 8b1a06fb3a60c..ca953c1813da9 100644 --- a/crates/bevy_ecs/src/system/exclusive_function_system.rs +++ b/crates/bevy_ecs/src/system/exclusive_function_system.rs @@ -4,8 +4,9 @@ use crate::{ query::Access, schedule::{InternedSystemSet, SystemSet}, system::{ - check_system_change_tick, ExclusiveSystemParam, ExclusiveSystemParamItem, IntoSystem, - System, SystemIn, SystemInput, SystemMeta, + check_system_change_tick, ExclusiveSystemInput, ExclusiveSystemParam, + ExclusiveSystemParamItem, IntoSystem, System, SystemIn, SystemInput, SystemInputItem, + SystemMeta, SYSTEM_INPUT_NOT_FOUND, SYSTEM_PARAM_NOT_FOUND, }, world::{unsafe_world_cell::UnsafeWorldCell, World}, }; @@ -25,6 +26,7 @@ where F: ExclusiveSystemParamFunction, { func: F, + input_state: Option<::State>, param_state: Option<::State>, system_meta: SystemMeta, // NOTE: PhantomData T> gives this safe Send/Sync impls @@ -57,6 +59,7 @@ where fn into_system(func: Self) -> Self::System { ExclusiveFunctionSystem { func, + input_state: None, param_state: None, system_meta: SystemMeta::new::(), marker: PhantomData, @@ -64,8 +67,6 @@ where } } -const PARAM_MESSAGE: &str = "System's param_state was not found. Did you forget to initialize this system before running it?"; - impl System for ExclusiveFunctionSystem where Marker: 'static, @@ -122,8 +123,13 @@ where #[cfg(feature = "trace")] let _span_guard = self.system_meta.system_span.enter(); + let input = F::In::get_einput( + input, + self.input_state.as_mut().expect(SYSTEM_INPUT_NOT_FOUND), + &self.system_meta, + ); let params = F::Param::get_param( - self.param_state.as_mut().expect(PARAM_MESSAGE), + self.param_state.as_mut().expect(SYSTEM_PARAM_NOT_FOUND), &self.system_meta, ); let out = self.func.run(world, input, params); @@ -150,7 +156,11 @@ where } #[inline] - unsafe fn validate_param_unsafe(&mut self, _world: UnsafeWorldCell) -> bool { + unsafe fn validate_param_unsafe( + &mut self, + _input: &SystemIn<'_, Self>, + _world: UnsafeWorldCell, + ) -> bool { // All exclusive system params are always available. true } @@ -158,6 +168,7 @@ where #[inline] fn initialize(&mut self, world: &mut World) { self.system_meta.last_run = world.change_tick().relative_to(Tick::MAX); + self.input_state = Some(F::In::init_istate(world, &mut self.system_meta)); self.param_state = Some(F::Param::init(world, &mut self.system_meta)); } @@ -196,7 +207,7 @@ where )] pub trait ExclusiveSystemParamFunction: Send + Sync + 'static { /// The input type to this system. See [`System::In`]. - type In: SystemInput; + type In: ExclusiveSystemInput; /// The return type of this system. See [`System::Out`]. type Out; @@ -208,7 +219,7 @@ pub trait ExclusiveSystemParamFunction: Send + Sync + 'static { fn run( &mut self, world: &mut World, - input: ::Inner<'_>, + input: SystemInputItem, param_value: ExclusiveSystemParamItem, ) -> Self::Out; } @@ -255,26 +266,26 @@ macro_rules! impl_exclusive_system_function { 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, + FnMut(SystemInputItem, &mut World, $(ExclusiveSystemParamItem<$param>),*) -> Out, + In: ExclusiveSystemInput + 'static, Out: 'static, { type In = In; type Out = Out; type Param = ($($param,)*); #[inline] - fn run(&mut self, world: &mut World, input: In::Inner<'_>, param_value: ExclusiveSystemParamItem< ($($param,)*)>) -> Out { + fn run(&mut self, world: &mut World, input: SystemInputItem, 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::Param<'_>, &mut World, $($param,)*) -> Out, - input: In::Inner<'_>, + fn call_inner( + mut f: impl FnMut(SystemInputItem, &mut World, $($param,)*) -> Out, + input: SystemInputItem, world: &mut World, $($param: $param,)* ) -> Out { - f(In::wrap(input), world, $($param,)*) + f(input, world, $($param,)*) } let ($($param,)*) = param_value; call_inner(self, input, world, $($param),*) diff --git a/crates/bevy_ecs/src/system/exclusive_system_param.rs b/crates/bevy_ecs/src/system/exclusive_system_param.rs index 9d3279e3e053a..a4be44a12c45c 100644 --- a/crates/bevy_ecs/src/system/exclusive_system_param.rs +++ b/crates/bevy_ecs/src/system/exclusive_system_param.rs @@ -1,7 +1,7 @@ use crate::{ prelude::{FromWorld, QueryState}, query::{QueryData, QueryFilter}, - system::{Local, SystemMeta, SystemParam, SystemState}, + system::{Local, SystemInput, SystemMeta, SystemParam, SystemState}, world::World, }; use bevy_utils::{all_tuples, synccell::SyncCell}; @@ -48,9 +48,11 @@ impl<'a, D: QueryData + 'static, F: QueryFilter + 'static> ExclusiveSystemParam } } -impl<'a, P: SystemParam + 'static> ExclusiveSystemParam for &'a mut SystemState

{ - type State = SystemState

; - type Item<'s> = &'s mut SystemState

; +impl<'a, P: SystemParam + 'static, In: SystemInput + 'static> ExclusiveSystemParam + for &'a mut SystemState +{ + type State = SystemState; + type Item<'s> = &'s mut SystemState; fn init(world: &mut World, _system_meta: &mut SystemMeta) -> Self::State { SystemState::new(world) diff --git a/crates/bevy_ecs/src/system/function_system.rs b/crates/bevy_ecs/src/system/function_system.rs index fcd6a829e8afd..530d3af9c643c 100644 --- a/crates/bevy_ecs/src/system/function_system.rs +++ b/crates/bevy_ecs/src/system/function_system.rs @@ -5,8 +5,8 @@ use crate::{ query::{Access, FilteredAccessSet}, schedule::{InternedSystemSet, SystemSet}, system::{ - check_system_change_tick, ReadOnlySystemParam, System, SystemIn, SystemInput, SystemParam, - SystemParamItem, + check_system_change_tick, ReadOnlySystemParam, System, SystemIn, SystemInput, + SystemInputItem, SystemParam, SystemParamItem, }, world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, World, WorldId}, }; @@ -18,7 +18,7 @@ use core::marker::PhantomData; #[cfg(feature = "trace")] use bevy_utils::tracing::{info_span, Span}; -use super::{In, IntoSystem, ReadOnlySystem, SystemParamBuilder}; +use super::{IntoSystem, ReadOnlySystem, SystemParamBuilder}; /// The metadata of a [`System`]. #[derive(Clone)] @@ -293,8 +293,9 @@ where /// } /// }); /// ``` -pub struct SystemState { +pub struct SystemState { meta: SystemMeta, + input_state: In::State, param_state: Param::State, world_id: WorldId, archetype_generation: ArchetypeGeneration, @@ -324,16 +325,17 @@ macro_rules! impl_build_system { { self.build_any_system(func) } + } + impl<$($param: SystemParam,)* In: SystemInput> SystemState<($($param,)*), In> { /// Create a [`FunctionSystem`] from a [`SystemState`]. /// 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: SystemInput, Out: 'static, Marker, - F: FnMut(In, $(SystemParamItem<$param>),*) -> Out - + SystemParamFunction, + F: FnMut(SystemInputItem, $(SystemParamItem<$param>),*) -> Out + + SystemParamFunction, >( self, func: F, @@ -346,7 +348,7 @@ macro_rules! impl_build_system { all_tuples!(impl_build_system, 0, 16, P); -impl SystemState { +impl SystemState { /// Creates a new [`SystemState`] with default state. /// /// ## Note @@ -357,9 +359,11 @@ impl SystemState { pub fn new(world: &mut World) -> Self { let mut meta = SystemMeta::new::(); meta.last_run = world.change_tick().relative_to(Tick::MAX); + let input_state = In::init_istate(world, &mut meta); let param_state = Param::init_state(world, &mut meta); Self { meta, + input_state, param_state, world_id: world.id(), archetype_generation: ArchetypeGeneration::initial(), @@ -370,9 +374,11 @@ impl SystemState { pub(crate) fn from_builder(world: &mut World, builder: impl SystemParamBuilder) -> Self { let mut meta = SystemMeta::new::(); meta.last_run = world.change_tick().relative_to(Tick::MAX); + let input_state = In::init_istate(world, &mut meta); let param_state = builder.build(world, &mut meta); Self { meta, + input_state, param_state, world_id: world.id(), archetype_generation: ArchetypeGeneration::initial(), @@ -382,12 +388,13 @@ impl SystemState { /// Create a [`FunctionSystem`] from a [`SystemState`]. /// This method signature allows any system function, but the compiler will not perform type inference on closure parameters. /// You can use [`SystemState::build_system()`] or [`SystemState::build_system_with_input()`] to get type inference on parameters. - pub fn build_any_system>( + pub fn build_any_system>( self, func: F, ) -> FunctionSystem { FunctionSystem { func, + input_state: Some(self.input_state), param_state: Some(self.param_state), system_meta: self.meta, world_id: Some(self.world_id), @@ -497,6 +504,8 @@ impl SystemState { core::mem::replace(&mut self.archetype_generation, archetypes.generation()); for archetype in &archetypes[old_generation..] { + // SAFETY: The assertion above ensures that the input_state was initialized from `world`. + unsafe { In::new_archetype(&mut self.input_state, archetype, &mut self.meta) }; // SAFETY: The assertion above ensures that the param_state was initialized from `world`. unsafe { Param::new_archetype(&mut self.param_state, archetype, &mut self.meta) }; } @@ -574,7 +583,7 @@ impl SystemState { } } -impl FromWorld for SystemState { +impl FromWorld for SystemState { fn from_world(world: &mut World) -> Self { Self::new(world) } @@ -595,6 +604,7 @@ where F: SystemParamFunction, { func: F, + pub(crate) input_state: Option<::State>, pub(crate) param_state: Option<::State>, pub(crate) system_meta: SystemMeta, world_id: Option, @@ -624,6 +634,7 @@ where fn clone(&self) -> Self { Self { func: self.func.clone(), + input_state: None, param_state: None, system_meta: SystemMeta::new::(), world_id: None, @@ -646,6 +657,7 @@ where fn into_system(func: Self) -> Self::System { FunctionSystem { func, + input_state: None, param_state: None, system_meta: SystemMeta::new::(), world_id: None, @@ -655,15 +667,14 @@ where } } -impl FunctionSystem -where - F: SystemParamFunction, -{ - /// Message shown when a system isn't initialised - // When lines get too long, rustfmt can sometimes refuse to format them. - // Work around this by storing the message separately. - const PARAM_MESSAGE: &'static str = "System's param_state was not found. Did you forget to initialize this system before running it?"; -} +/// Message shown when a system isn't initialised +// When lines get too long, rustfmt can sometimes refuse to format them. +// Work around this by storing the message separately. +pub(super) const SYSTEM_PARAM_NOT_FOUND: &str = "System's param_state was not found. Did you forget to initialize this system before running it?"; +/// Message shown when a system isn't initialised +// When lines get too long, rustfmt can sometimes refuse to format them. +// Work around this by storing the message separately. +pub(super) const SYSTEM_INPUT_NOT_FOUND: &str = "System's input_state was not found. Did you forget to initialize this system before running it?"; impl System for FunctionSystem where @@ -714,6 +725,21 @@ where let change_tick = world.increment_change_tick(); + // SAFETY: + // - The caller has invoked `update_archetype_component_access`, which will panic + // if the world does not match. + // - All world accesses used by `F::In` have been registered, so the caller + // will ensure that there are no data access conflicts. + let input = unsafe { + F::In::get_input( + input, + self.input_state.as_mut().expect(SYSTEM_INPUT_NOT_FOUND), + &self.system_meta, + world, + change_tick, + ) + }; + // SAFETY: // - The caller has invoked `update_archetype_component_access`, which will panic // if the world does not match. @@ -721,7 +747,7 @@ where // will ensure that there are no data access conflicts. let params = unsafe { F::Param::get_param( - self.param_state.as_mut().expect(Self::PARAM_MESSAGE), + self.param_state.as_mut().expect(SYSTEM_PARAM_NOT_FOUND), &self.system_meta, world, change_tick, @@ -734,19 +760,36 @@ where #[inline] fn apply_deferred(&mut self, world: &mut World) { - let param_state = self.param_state.as_mut().expect(Self::PARAM_MESSAGE); + let param_state = self.param_state.as_mut().expect(SYSTEM_PARAM_NOT_FOUND); F::Param::apply(param_state, &self.system_meta, world); } #[inline] fn queue_deferred(&mut self, world: DeferredWorld) { - let param_state = self.param_state.as_mut().expect(Self::PARAM_MESSAGE); + let param_state = self.param_state.as_mut().expect(SYSTEM_PARAM_NOT_FOUND); F::Param::queue(param_state, &self.system_meta, world); } #[inline] - unsafe fn validate_param_unsafe(&mut self, world: UnsafeWorldCell) -> bool { - let param_state = self.param_state.as_ref().expect(Self::PARAM_MESSAGE); + unsafe fn validate_param_unsafe( + &mut self, + input: &SystemIn<'_, Self>, + world: UnsafeWorldCell, + ) -> bool { + let input_state = self.input_state.as_ref().expect(SYSTEM_INPUT_NOT_FOUND); + // SAFETY: + // - The caller has invoked `update_archetype_component_access`, which will panic + // if the world does not match. + // - All world accesses used by `F::In` have been registered, so the caller + // will ensure that there are no data access conflicts. + let is_valid = + unsafe { F::In::validate_input(input, input_state, &self.system_meta, world) }; + if !is_valid { + // TODO advance_input_warn_policy + return false; + } + + let param_state = self.param_state.as_ref().expect(SYSTEM_PARAM_NOT_FOUND); // SAFETY: // - The caller has invoked `update_archetype_component_access`, which will panic // if the world does not match. @@ -769,6 +812,7 @@ where ); } else { self.world_id = Some(world.id()); + self.input_state = Some(F::In::init_istate(world, &mut self.system_meta)); self.param_state = Some(F::Param::init_state(world, &mut self.system_meta)); } self.system_meta.last_run = world.change_tick().relative_to(Tick::MAX); @@ -781,7 +825,11 @@ where core::mem::replace(&mut self.archetype_generation, archetypes.generation()); for archetype in &archetypes[old_generation..] { - let param_state = self.param_state.as_mut().unwrap(); + let input_state = self.input_state.as_mut().expect(SYSTEM_INPUT_NOT_FOUND); + // SAFETY: The assertion above ensures that the input_state was initialized from `world`. + unsafe { F::In::new_archetype(input_state, archetype, &mut self.system_meta) }; + + let param_state = self.param_state.as_mut().expect(SYSTEM_PARAM_NOT_FOUND); // SAFETY: The assertion above ensures that the param_state was initialized from `world`. unsafe { F::Param::new_archetype(param_state, archetype, &mut self.system_meta) }; } @@ -896,7 +944,7 @@ pub trait SystemParamFunction: Send + Sync + 'static { /// Executes this system once. See [`System::run`] or [`System::run_unsafe`]. fn run( &mut self, - input: ::Inner<'_>, + input: SystemInputItem, param_value: SystemParamItem, ) -> Self::Out; } @@ -942,7 +990,7 @@ macro_rules! impl_system_function { Func: Send + Sync + 'static, for <'a> &'a mut Func: FnMut(In, $($param),*) -> Out + - FnMut(In::Param<'_>, $(SystemParamItem<$param>),*) -> Out, + FnMut(SystemInputItem, $(SystemParamItem<$param>),*) -> Out, In: SystemInput + 'static, Out: 'static { @@ -950,14 +998,14 @@ macro_rules! impl_system_function { type Out = Out; type Param = ($($param,)*); #[inline] - fn run(&mut self, input: In::Inner<'_>, param_value: SystemParamItem< ($($param,)*)>) -> Out { + fn run(&mut self, input: SystemInputItem, param_value: SystemParamItem< ($($param,)*)>) -> Out { #[allow(clippy::too_many_arguments)] fn call_inner( - mut f: impl FnMut(In::Param<'_>, $($param,)*)->Out, - input: In::Inner<'_>, + mut f: impl FnMut(SystemInputItem, $($param,)*)->Out, + input: SystemInputItem, $($param: $param,)* )->Out{ - f(In::wrap(input), $($param,)*) + f(input, $($param,)*) } let ($($param,)*) = param_value; call_inner(self, input, $($param),*) diff --git a/crates/bevy_ecs/src/system/input.rs b/crates/bevy_ecs/src/system/input.rs index 57032f71928ef..3dc1b71d514f8 100644 --- a/crates/bevy_ecs/src/system/input.rs +++ b/crates/bevy_ecs/src/system/input.rs @@ -1,6 +1,15 @@ use core::ops::{Deref, DerefMut}; -use crate::{bundle::Bundle, prelude::Trigger, system::System}; +use crate::{ + archetype::Archetype, + bundle::Bundle, + component::Tick, + entity::Entity, + prelude::Trigger, + query::{QueryData, QueryState}, + system::{init_query_param, System, SystemMeta}, + world::{unsafe_world_cell::UnsafeWorldCell, World}, +}; /// Trait for types that can be used as input to [`System`]s. /// @@ -12,31 +21,166 @@ use crate::{bundle::Bundle, prelude::Trigger, system::System}; /// - [`Trigger`]: For [`ObserverSystem`]s /// - [`StaticSystemInput`]: For arbitrary [`SystemInput`]s in generic contexts /// +/// # Safety +/// +/// Implementors must ensure the following is true: +/// - [`SystemInput::init_state`] correctly registers all [`World`] accesses used +/// by [`SystemInput::get_input`] with the provided [`system_meta`](SystemMeta). +/// - None of the world accesses may conflict with any prior accesses registered +/// on `system_meta`. +/// /// [`ObserverSystem`]: crate::system::ObserverSystem -pub trait SystemInput: Sized { - /// The wrapper input type that is defined as the first argument to [`FunctionSystem`]s. +pub unsafe trait SystemInput: Sized { + /// Used to store data which persists across invocations of a system. + type State: Send + Sync + 'static; + + /// The wrapper input type that is defined as the first argument to + /// [`FunctionSystem`]s. /// /// [`FunctionSystem`]: crate::system::FunctionSystem - type Param<'i>: SystemInput; + type Item<'world, 'state, 'input>: SystemInput< + State = Self::State, + Inner<'input> = Self::Inner<'input>, + >; + /// 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>; + type Inner<'input>; + + /// Registers any [`World`] access used by this [`SystemInput`] + /// and creates a new instance of this param's [`State`](SystemInput::State). + fn init_istate(world: &mut World, system_meta: &mut SystemMeta) -> Self::State; + + /// For the specified [`Archetype`], registers the components accessed by + /// this [`SystemInput`] (if applicable). + /// + /// # Safety + /// + /// `archetype` must be from the [`World`] used to [initialize `state`](SystemInput::init_state). + #[inline] + unsafe fn new_archetype( + state: &mut Self::State, + archetype: &Archetype, + system_meta: &mut SystemMeta, + ) { + let _ = (state, archetype, system_meta); + } + + /// Validates that the input can be acquired by the + /// [`get_input`](SystemInput::get_input) function. Built-in executors use + /// this to prevent systems with invalid params from running. + /// + /// However calling and respecting [`SystemInput::validate_input`] is not a + /// strict requirement, [`SystemInput::get_input`] should provide its own + /// safety mechanism to prevent undefined behavior. + /// + /// The [`world`](UnsafeWorldCell) can only be used to read the input's + /// queried data and world metadata. No data can be written. + /// + /// When using system input that require `change_tick`, you can use + /// [`UnsafeWorldCell::change_tick`]. Even if this isn't the exact + /// same tick used for [`SystemInput::get_input`], the world access + /// ensures that the queried data will be the same in both calls. + /// + /// This method has to be called directly before [`SystemInput::get_input`] + /// with no other (relevant) world mutations in-between. Otherwise, while + /// it won't lead to any undefined behavior, the validity of the param may + /// change. + /// + /// # Safety + /// + /// - The passed [`UnsafeWorldCell`] must have read-only access to world + /// data registered in [`init_state`](SystemInput::init_state). + /// - `world` must the same [`World`] that was used to + /// [initialize `state`](SystemInput::init_state). + /// - All `world` archetypes have been processed by + /// [`new_archetype`](SystemInput::new_archetype). + unsafe fn validate_input( + input: &Self::Inner<'_>, + state: &Self::State, + system_meta: &SystemMeta, + world: UnsafeWorldCell, + ) -> bool { + let _ = (input, state, system_meta, world); + // By default we allow panics in [`SystemInput::get_input`] and return `true`. + // Preventing panics is an optional feature. + true + } + + /// Creates an input value to be passed into a + /// [`SystemParamFunction`](super::SystemParamFunction). + /// + /// # Safety + /// + /// - The passed [`UnsafeWorldCell`] must have access to any world data + /// registered in [`init_state`](SystemInput::init_state). + /// - `world` must be the same [`World`] that was used to [initialize `state`](SystemInput::init_state). + /// - All `world` archetypes have been processed by [`new_archetype`](SystemInput::new_archetype). + unsafe fn get_input<'world, 'state, 'input>( + input: Self::Inner<'input>, + state: &'state mut Self::State, + system_meta: &SystemMeta, + world: UnsafeWorldCell<'world>, + change_tick: Tick, + ) -> Self::Item<'world, 'state, 'input>; +} - /// Converts a [`SystemInput::Inner`] into a [`SystemInput::Param`]. - fn wrap(this: Self::Inner<'_>) -> Self::Param<'_>; +/// Trait for types that can be used as input to exclusive [`System`]s. +/// +/// Provided implementations are: +/// - `()`: No input +/// - [`In`]: For values +/// - [`InRef`]: For read-only references to values +/// - [`InMut`]: For mutable references to values +/// - [`StaticSystemInput`]: For arbitrary [`SystemInput`]s in generic contexts +pub trait ExclusiveSystemInput: SystemInput { + /// Creates an input value to be passed into an + /// [`ExclusiveSystemParamFunction`]. + /// + /// [`ExclusiveSystemParamFunction`]: crate::system::ExclusiveSystemParamFunction + fn get_einput<'state, 'input>( + input: Self::Inner<'input>, + state: &'state mut Self::State, + system_meta: &SystemMeta, + ) -> Self::Item<'static, 'state, 'input>; } -/// Shorthand way to get the [`System::In`] for a [`System`] as a [`SystemInput::Inner`]. +/// Shorthand way to get the [`System::In`] for a [`System`] as a +/// [`SystemInput::Inner`]. pub type SystemIn<'a, S> = <::In as SystemInput>::Inner<'a>; +/// Shorthand way of accessing the associated type [`SystemInput::Item`] for a +/// given [`SystemInput`]. +pub type SystemInputItem<'w, 's, 'i, S> = ::Item<'w, 's, 'i>; + /// [`SystemInput`] type for systems that take no input. -impl SystemInput for () { - type Param<'i> = (); - type Inner<'i> = (); +// SAFETY: Doesn't access any world data. +unsafe impl SystemInput for () { + type State = (); + type Item<'world, 'state, 'input> = (); + type Inner<'input> = (); + + fn init_istate(_world: &mut World, _system_metaa: &mut SystemMeta) -> Self::State {} + + unsafe fn get_input<'world, 'state, 'input>( + _input: Self::Inner<'input>, + _state: &'state mut Self::State, + _system_meta: &SystemMeta, + _world: UnsafeWorldCell<'world>, + _change_tick: Tick, + ) -> Self::Item<'world, 'state, 'input> { + } +} - fn wrap(_this: Self::Inner<'_>) -> Self::Param<'_> {} +impl ExclusiveSystemInput for () { + fn get_einput<'state, 'input>( + _input: Self::Inner<'input>, + _state: &'state mut Self::State, + _system_meta: &SystemMeta, + ) -> Self::Item<'static, 'state, 'input> { + } } /// A [`SystemInput`] type which denotes that a [`System`] receives @@ -70,12 +214,32 @@ impl SystemInput for () { #[derive(Debug)] pub struct In(pub T); -impl SystemInput for In { - type Param<'i> = In; - type Inner<'i> = T; +// SAFETY: Doesn't access any world data. +unsafe impl SystemInput for In { + type State = (); + type Item<'world, 'state, 'input> = In; + type Inner<'input> = T; + + fn init_istate(_world: &mut World, _system_meta: &mut SystemMeta) -> Self::State {} - fn wrap(this: Self::Inner<'_>) -> Self::Param<'_> { - In(this) + unsafe fn get_input<'world, 'state, 'input>( + input: Self::Inner<'input>, + _state: &'state mut Self::State, + _system_meta: &SystemMeta, + _world: UnsafeWorldCell<'world>, + _change_tick: Tick, + ) -> Self::Item<'world, 'state, 'input> { + In(input) + } +} + +impl ExclusiveSystemInput for In { + fn get_einput<'state, 'input>( + input: Self::Inner<'input>, + _state: &'state mut Self::State, + _system_meta: &SystemMeta, + ) -> Self::Item<'static, 'state, 'input> { + In(input) } } @@ -127,12 +291,32 @@ impl DerefMut for In { #[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; +// SAFETY: Doesn't access any world data. +unsafe impl SystemInput for InRef<'_, T> { + type State = (); + type Item<'world, 'state, 'input> = InRef<'input, T>; + type Inner<'input> = &'input T; + + fn init_istate(_world: &mut World, _system_meta: &mut SystemMeta) -> Self::State {} - fn wrap(this: Self::Inner<'_>) -> Self::Param<'_> { - InRef(this) + unsafe fn get_input<'world, 'state, 'input>( + input: Self::Inner<'input>, + _state: &'state mut Self::State, + _system_meta: &SystemMeta, + _world: UnsafeWorldCell<'world>, + _change_tick: Tick, + ) -> Self::Item<'world, 'state, 'input> { + InRef(input) + } +} + +impl ExclusiveSystemInput for InRef<'_, T> { + fn get_einput<'state, 'input>( + input: Self::Inner<'input>, + _state: &'state mut Self::State, + _system_meta: &SystemMeta, + ) -> Self::Item<'static, 'state, 'input> { + InRef(input) } } @@ -174,12 +358,34 @@ impl<'i, T: ?Sized> Deref for InRef<'i, T> { #[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; +// SAFETY: Doesn't access any world data. +unsafe impl SystemInput for InMut<'_, T> { + type State = (); + + type Item<'world, 'state, 'input> = InMut<'input, T>; + + type Inner<'input> = &'input mut T; + + fn init_istate(_world: &mut World, _system_meta: &mut SystemMeta) -> Self::State {} + + unsafe fn get_input<'world, 'state, 'input>( + input: Self::Inner<'input>, + _state: &'state mut Self::State, + _system_meta: &SystemMeta, + _world: UnsafeWorldCell<'world>, + _change_tick: Tick, + ) -> Self::Item<'world, 'state, 'input> { + InMut(input) + } +} - fn wrap(this: Self::Inner<'_>) -> Self::Param<'_> { - InMut(this) +impl ExclusiveSystemInput for InMut<'_, T> { + fn get_einput<'state, 'input>( + input: Self::Inner<'input>, + _state: &'state mut Self::State, + _system_meta: &SystemMeta, + ) -> Self::Item<'static, 'state, 'input> { + InMut(input) } } @@ -200,12 +406,60 @@ impl<'i, T: ?Sized> DerefMut for InMut<'i, T> { /// 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>; +// SAFETY: All world access is registered in `init_state`. +unsafe impl SystemInput + for Trigger<'_, '_, E, B, D> +{ + type State = QueryState; + type Item<'world, 'state, 'input> = Trigger<'world, 'input, E, B, D>; + type Inner<'input> = Trigger<'static, 'input, E, B, ()>; + + fn init_istate(world: &mut World, system_meta: &mut SystemMeta) -> Self::State { + let state = QueryState::new_with_access(world, &mut system_meta.archetype_component_access); + init_query_param(world, system_meta, &state); + state + } + + unsafe fn validate_input( + input: &Self::Inner<'_>, + state: &Self::State, + system_meta: &SystemMeta, + world: UnsafeWorldCell, + ) -> bool { + if input.entity() == Entity::PLACEHOLDER { + return true; + } - fn wrap(this: Self::Inner<'_>) -> Self::Param<'_> { - this + state.validate_world(world.id()); + // SAFETY: We registered access to the components in `init_state`. + let result = unsafe { + state.as_readonly().get_unchecked_manual( + world, + input.entity(), + system_meta.last_run, + world.change_tick(), + ) + }; + result + .inspect_err(|e| { + // TODO system warn + eprintln!("{}", e); + }) + .is_ok() + } + + unsafe fn get_input<'world, 'state, 'input>( + input: Self::Inner<'input>, + state: &'state mut Self::State, + _system_meta: &SystemMeta, + world: UnsafeWorldCell<'world>, + _change_tick: Tick, + ) -> Self::Item<'world, 'state, 'input> { + let entity = input.entity(); + + // SAFETY: We registered access to the components in `init_state`. + let data = unsafe { state.get_unchecked(world, entity) }; + input.with_data(data.ok()) } } @@ -217,13 +471,46 @@ impl SystemInput for Trigger<'_, E, B> { /// /// This makes it useful for having arbitrary [`SystemInput`]s in /// function systems. -pub struct StaticSystemInput<'a, I: SystemInput>(pub I::Inner<'a>); +pub struct StaticSystemInput<'w, 's, 'i, I: SystemInput>(pub I::Item<'w, 's, 'i>); + +// SAFETY: All safety requirements are delegated to the inner `SystemInput`. +unsafe impl SystemInput for StaticSystemInput<'_, '_, '_, I> { + type State = I::State; -impl<'a, I: SystemInput> SystemInput for StaticSystemInput<'a, I> { - type Param<'i> = StaticSystemInput<'i, I>; - type Inner<'i> = I::Inner<'i>; + type Item<'world, 'state, 'input> = StaticSystemInput<'world, 'state, 'input, I>; + + type Inner<'input> = I::Inner<'input>; + + fn init_istate(world: &mut World, system_meta: &mut SystemMeta) -> Self::State { + I::init_istate(world, system_meta) + } + + unsafe fn validate_input( + input: &Self::Inner<'_>, + state: &Self::State, + system_meta: &SystemMeta, + world: UnsafeWorldCell, + ) -> bool { + I::validate_input(input, state, system_meta, world) + } + + unsafe fn get_input<'world, 'state, 'input>( + input: Self::Inner<'input>, + state: &'state mut Self::State, + system_meta: &SystemMeta, + world: UnsafeWorldCell<'world>, + change_tick: Tick, + ) -> Self::Item<'world, 'state, 'input> { + StaticSystemInput(I::get_input(input, state, system_meta, world, change_tick)) + } +} - fn wrap(this: Self::Inner<'_>) -> Self::Param<'_> { - StaticSystemInput(this) +impl ExclusiveSystemInput for StaticSystemInput<'static, '_, '_, I> { + fn get_einput<'state, 'input>( + input: Self::Inner<'input>, + state: &'state mut Self::State, + system_meta: &SystemMeta, + ) -> Self::Item<'static, 'state, 'input> { + StaticSystemInput(I::get_einput(input, state, system_meta)) } } diff --git a/crates/bevy_ecs/src/system/observer_system.rs b/crates/bevy_ecs/src/system/observer_system.rs index 55173ccf4f22a..373ab20febfc4 100644 --- a/crates/bevy_ecs/src/system/observer_system.rs +++ b/crates/bevy_ecs/src/system/observer_system.rs @@ -7,7 +7,7 @@ use super::IntoSystem; /// Implemented for [`System`]s that have a [`Trigger`] as the first argument. pub trait ObserverSystem: - System, Out = Out> + Send + 'static + System, Out = Out> + Send + 'static { } @@ -15,7 +15,7 @@ impl< E: 'static, B: Bundle, Out, - T: System, Out = Out> + Send + 'static, + T: System, Out = Out> + Send + 'static, > ObserverSystem for T { } @@ -35,7 +35,7 @@ pub trait IntoObserverSystem: Send + 'static } impl< - S: IntoSystem, Out, M> + Send + 'static, + S: IntoSystem, Out, M> + Send + 'static, M, Out, E: 'static, @@ -44,7 +44,7 @@ impl< where S::System: ObserverSystem, { - type System = , Out, M>>::System; + type System = , Out, M>>::System; fn into_system(this: Self) -> Self::System { IntoSystem::into_system(this) diff --git a/crates/bevy_ecs/src/system/system.rs b/crates/bevy_ecs/src/system/system.rs index ded89235e72ef..b13efc7562f76 100644 --- a/crates/bevy_ecs/src/system/system.rs +++ b/crates/bevy_ecs/src/system/system.rs @@ -117,17 +117,21 @@ 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 validate_param_unsafe(&mut self, world: UnsafeWorldCell) -> bool; + unsafe fn validate_param_unsafe( + &mut self, + input: &SystemIn<'_, Self>, + world: UnsafeWorldCell, + ) -> bool; /// Safe version of [`System::validate_param_unsafe`]. /// that runs on exclusive, single-threaded `world` pointer. - fn validate_param(&mut self, world: &World) -> bool { + fn validate_param(&mut self, input: &SystemIn<'_, Self>, world: &World) -> bool { let world_cell = world.as_unsafe_world_cell_readonly(); self.update_archetype_component_access(world_cell); // SAFETY: // - We have exclusive access to the entire world. // - `update_archetype_component_access` has been called. - unsafe { self.validate_param_unsafe(world_cell) } + unsafe { self.validate_param_unsafe(input, world_cell) } } /// Initialize the system. @@ -348,7 +352,7 @@ impl RunSystemOnce for &mut World { { let mut system: T::System = IntoSystem::into_system(system); system.initialize(self); - if system.validate_param(self) { + if system.validate_param(&input, self) { Ok(system.run(input, self)) } else { Err(RunSystemError::InvalidParams(system.name())) diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 9d06aec04d9ef..7552bf4467ae8 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -194,7 +194,7 @@ pub unsafe trait SystemParam: Sized { /// and creates a new instance of this param's [`State`](SystemParam::State). fn init_state(world: &mut World, system_meta: &mut SystemMeta) -> Self::State; - /// For the specified [`Archetype`], registers the components accessed by this [`SystemParam`] (if applicable).a + /// For the specified [`Archetype`], registers the components accessed by this [`SystemParam`] (if applicable). /// /// # Safety /// `archetype` must be from the [`World`] used to initialize `state` in [`SystemParam::init_state`]. @@ -227,7 +227,7 @@ pub unsafe trait SystemParam: Sized { /// /// However calling and respecting [`SystemParam::validate_param`] /// is not a strict requirement, [`SystemParam::get_param`] should - /// provide it's own safety mechanism to prevent undefined behavior. + /// provide its own safety mechanism to prevent undefined behavior. /// /// The [`world`](UnsafeWorldCell) can only be used to read param's data /// and world metadata. No data can be written. diff --git a/crates/bevy_ecs/src/system/system_registry.rs b/crates/bevy_ecs/src/system/system_registry.rs index 4b5508190acf1..8931669e5d28b 100644 --- a/crates/bevy_ecs/src/system/system_registry.rs +++ b/crates/bevy_ecs/src/system/system_registry.rs @@ -343,7 +343,7 @@ impl World { initialized = true; } - let result = if system.validate_param(self) { + let result = if system.validate_param(&input, self) { Ok(system.run(input, self)) } else { Err(RegisteredSystemError::InvalidParams(id))