mirror of
https://github.com/bevyengine/bevy
synced 2024-11-22 04:33:37 +00:00
Support systems that take references as input (#15184)
# Objective - Fixes #14924 - Closes #9584 ## Solution - We introduce a new trait, `SystemInput`, that serves as a type function from the `'static` form of the input, to its lifetime'd version, similarly to `SystemParam` or `WorldQuery`. - System functions now take the lifetime'd wrapped version, `SystemInput::Param<'_>`, which prevents the issue presented in #14924 (i.e. `InRef<T>`). - Functions for running systems now take the lifetime'd unwrapped version, `SystemInput::Inner<'_>` (i.e. `&T`). - Due to the above change, system piping had to be re-implemented as a standalone type, rather than `CombinatorSystem` as it was previously. - Removes the `Trigger<'static, E, B>` transmute in observer runner code. ## Testing - All current tests pass. - Added additional tests and doc-tests. --- ## Showcase ```rust let mut world = World::new(); let mut value = 2; // Currently possible: fn square(In(input): In<usize>) -> usize { input * input } value = world.run_system_once_with(value, square); // Now possible: fn square_mut(InMut(input): InMut<usize>) { *input *= *input; } world.run_system_once_with(&mut value, square_mut); // Or: fn square_ref(InRef(input): InRef<usize>) -> usize { *input * *input } value = world.run_system_once_with(&value, square_ref); ``` ## Migration Guide - All current explicit usages of the following types must be changed in the way specified: - `SystemId<I, O>` to `SystemId<In<I>, O>` - `System<In = T>` to `System<In = In<T>>` - `IntoSystem<I, O, M>` to `IntoSystem<In<I>, O, M>` - `Condition<M, T>` to `Condition<M, In<T>>` - `In<Trigger<E, B>>` is no longer a valid input parameter type. Use `Trigger<E, B>` directly, instead. --------- Co-authored-by: Giacomo Stevanato <giaco.stevanato@gmail.com>
This commit is contained in:
parent
4c087daa20
commit
c7ec456e50
17 changed files with 746 additions and 295 deletions
|
@ -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<I: 'static, O: 'static, M, S: IntoSystem<I, O, M> + 'static>(
|
||||
pub fn register_system<I, O, M>(
|
||||
&mut self,
|
||||
system: S,
|
||||
) -> SystemId<I, O> {
|
||||
system: impl IntoSystem<I, O, M> + 'static,
|
||||
) -> SystemId<I, O>
|
||||
where
|
||||
I: SystemInput + 'static,
|
||||
O: 'static,
|
||||
{
|
||||
self.main_mut().register_system(system)
|
||||
}
|
||||
|
||||
|
|
|
@ -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<I: 'static, O: 'static, M, S: IntoSystem<I, O, M> + 'static>(
|
||||
pub fn register_system<I, O, M>(
|
||||
&mut self,
|
||||
system: S,
|
||||
) -> SystemId<I, O> {
|
||||
system: impl IntoSystem<I, O, M> + 'static,
|
||||
) -> SystemId<I, O>
|
||||
where
|
||||
I: SystemInput + 'static,
|
||||
O: 'static,
|
||||
{
|
||||
self.world.register_system(system)
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -357,13 +357,6 @@ fn observer_system_runner<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
|
|||
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.
|
||||
|
|
|
@ -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<In = ()> = Box<dyn ReadOnlySystem<In = In, Out = bool>>;
|
|||
///
|
||||
/// ```
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
/// fn identity() -> impl Condition<(), bool> {
|
||||
/// fn identity() -> impl Condition<(), In<bool>> {
|
||||
/// IntoSystem::into_system(|In(x)| x)
|
||||
/// }
|
||||
///
|
||||
|
@ -70,7 +71,7 @@ pub type BoxedCondition<In = ()> = Box<dyn ReadOnlySystem<In = In, Out = bool>>;
|
|||
/// # world.insert_resource(DidRun(false));
|
||||
/// # app.run(&mut world);
|
||||
/// # assert!(world.resource::<DidRun>().0);
|
||||
pub trait Condition<Marker, In = ()>: sealed::Condition<Marker, In> {
|
||||
pub trait Condition<Marker, In: SystemInput = ()>: sealed::Condition<Marker, In> {
|
||||
/// 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<Marker, In = ()>: sealed::Condition<Marker, In> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<Marker, In, F> Condition<Marker, In> for F where F: sealed::Condition<Marker, In> {}
|
||||
impl<Marker, In: SystemInput, F> Condition<Marker, In> for F where F: sealed::Condition<Marker, In> {}
|
||||
|
||||
mod sealed {
|
||||
use crate::system::{IntoSystem, ReadOnlySystem};
|
||||
use crate::system::{IntoSystem, ReadOnlySystem, SystemInput};
|
||||
|
||||
pub trait Condition<Marker, In>:
|
||||
pub trait Condition<Marker, In: SystemInput>:
|
||||
IntoSystem<In, bool, Marker, System = Self::ReadOnlySystem>
|
||||
{
|
||||
// This associated type is necessary to let the compiler
|
||||
|
@ -479,7 +480,7 @@ mod sealed {
|
|||
type ReadOnlySystem: ReadOnlySystem<In = In, Out = bool>;
|
||||
}
|
||||
|
||||
impl<Marker, In, F> Condition<Marker, In> for F
|
||||
impl<Marker, In: SystemInput, F> Condition<Marker, In> for F
|
||||
where
|
||||
F: IntoSystem<In, bool, Marker>,
|
||||
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::<Counter>().0, 2);
|
||||
/// ```
|
||||
pub fn condition_changed<Marker, CIn, C: Condition<Marker, CIn>>(
|
||||
condition: C,
|
||||
) -> impl Condition<(), CIn> {
|
||||
condition.pipe(|In(new): In<bool>, mut prev: Local<bool>| -> bool {
|
||||
pub fn condition_changed<Marker, CIn, C>(condition: C) -> impl Condition<(), CIn>
|
||||
where
|
||||
CIn: SystemInput,
|
||||
C: Condition<Marker, CIn>,
|
||||
{
|
||||
condition.pipe(|In(new): In<bool>, mut prev: Local<bool>| {
|
||||
let changed = *prev != new;
|
||||
*prev = new;
|
||||
changed
|
||||
|
@ -1164,10 +1167,11 @@ pub mod common_conditions {
|
|||
/// app.run(&mut world);
|
||||
/// assert_eq!(world.resource::<Counter>().0, 2);
|
||||
/// ```
|
||||
pub fn condition_changed_to<Marker, CIn, C: Condition<Marker, CIn>>(
|
||||
to: bool,
|
||||
condition: C,
|
||||
) -> impl Condition<(), CIn> {
|
||||
pub fn condition_changed_to<Marker, CIn, C>(to: bool, condition: C) -> impl Condition<(), CIn>
|
||||
where
|
||||
CIn: SystemInput,
|
||||
C: Condition<Marker, CIn>,
|
||||
{
|
||||
condition.pipe(move |In(new): In<bool>, mut prev: Local<bool>| -> 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<T> = AdapterSystem<NotMarker, T>;
|
||||
pub type NotSystem<S> = AdapterSystem<NotMarker, S>;
|
||||
|
||||
/// Used with [`AdapterSystem`] to negate the output of a system via the [`Not`] operator.
|
||||
#[doc(hidden)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct NotMarker;
|
||||
|
||||
impl<T: System> Adapt<T> for NotMarker
|
||||
where
|
||||
T::Out: Not,
|
||||
{
|
||||
type In = T::In;
|
||||
type Out = <T::Out as Not>::Output;
|
||||
impl<S: System<Out: Not>> Adapt<S> for NotMarker {
|
||||
type In = S::In;
|
||||
type Out = <S::Out as Not>::Output;
|
||||
|
||||
fn adapt(&mut self, input: Self::In, run_system: impl FnOnce(T::In) -> T::Out) -> Self::Out {
|
||||
fn adapt(
|
||||
&mut self,
|
||||
input: <Self::In as SystemInput>::Inner<'_>,
|
||||
run_system: impl FnOnce(SystemIn<'_, S>) -> S::Out,
|
||||
) -> Self::Out {
|
||||
!run_system(input)
|
||||
}
|
||||
}
|
||||
|
@ -1221,7 +1226,7 @@ pub struct AndMarker;
|
|||
|
||||
impl<In, A, B> Combine<A, B> for AndMarker
|
||||
where
|
||||
In: Copy,
|
||||
for<'a> In: SystemInput<Inner<'a>: Copy>,
|
||||
A: System<In = In, Out = bool>,
|
||||
B: System<In = In, Out = bool>,
|
||||
{
|
||||
|
@ -1229,9 +1234,9 @@ where
|
|||
type Out = bool;
|
||||
|
||||
fn combine(
|
||||
input: Self::In,
|
||||
a: impl FnOnce(<A as System>::In) -> <A as System>::Out,
|
||||
b: impl FnOnce(<B as System>::In) -> <B as System>::Out,
|
||||
input: <Self::In as SystemInput>::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<In, A, B> Combine<A, B> for NandMarker
|
||||
where
|
||||
In: Copy,
|
||||
for<'a> In: SystemInput<Inner<'a>: Copy>,
|
||||
A: System<In = In, Out = bool>,
|
||||
B: System<In = In, Out = bool>,
|
||||
{
|
||||
|
@ -1250,9 +1255,9 @@ where
|
|||
type Out = bool;
|
||||
|
||||
fn combine(
|
||||
input: Self::In,
|
||||
a: impl FnOnce(<A as System>::In) -> <A as System>::Out,
|
||||
b: impl FnOnce(<B as System>::In) -> <B as System>::Out,
|
||||
input: <Self::In as SystemInput>::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<In, A, B> Combine<A, B> for NorMarker
|
||||
where
|
||||
In: Copy,
|
||||
for<'a> In: SystemInput<Inner<'a>: Copy>,
|
||||
A: System<In = In, Out = bool>,
|
||||
B: System<In = In, Out = bool>,
|
||||
{
|
||||
|
@ -1271,9 +1276,9 @@ where
|
|||
type Out = bool;
|
||||
|
||||
fn combine(
|
||||
input: Self::In,
|
||||
a: impl FnOnce(<A as System>::In) -> <A as System>::Out,
|
||||
b: impl FnOnce(<B as System>::In) -> <B as System>::Out,
|
||||
input: <Self::In as SystemInput>::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<In, A, B> Combine<A, B> for OrMarker
|
||||
where
|
||||
In: Copy,
|
||||
for<'a> In: SystemInput<Inner<'a>: Copy>,
|
||||
A: System<In = In, Out = bool>,
|
||||
B: System<In = In, Out = bool>,
|
||||
{
|
||||
|
@ -1292,9 +1297,9 @@ where
|
|||
type Out = bool;
|
||||
|
||||
fn combine(
|
||||
input: Self::In,
|
||||
a: impl FnOnce(<A as System>::In) -> <A as System>::Out,
|
||||
b: impl FnOnce(<B as System>::In) -> <B as System>::Out,
|
||||
input: <Self::In as SystemInput>::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<In, A, B> Combine<A, B> for XnorMarker
|
||||
where
|
||||
In: Copy,
|
||||
for<'a> In: SystemInput<Inner<'a>: Copy>,
|
||||
A: System<In = In, Out = bool>,
|
||||
B: System<In = In, Out = bool>,
|
||||
{
|
||||
|
@ -1313,9 +1318,9 @@ where
|
|||
type Out = bool;
|
||||
|
||||
fn combine(
|
||||
input: Self::In,
|
||||
a: impl FnOnce(<A as System>::In) -> <A as System>::Out,
|
||||
b: impl FnOnce(<B as System>::In) -> <B as System>::Out,
|
||||
input: <Self::In as SystemInput>::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<In, A, B> Combine<A, B> for XorMarker
|
||||
where
|
||||
In: Copy,
|
||||
for<'a> In: SystemInput<Inner<'a>: Copy>,
|
||||
A: System<In = In, Out = bool>,
|
||||
B: System<In = In, Out = bool>,
|
||||
{
|
||||
|
@ -1334,9 +1339,9 @@ where
|
|||
type Out = bool;
|
||||
|
||||
fn combine(
|
||||
input: Self::In,
|
||||
a: impl FnOnce(<A as System>::In) -> <A as System>::Out,
|
||||
b: impl FnOnce(<B as System>::In) -> <B as System>::Out,
|
||||
input: <Self::In as SystemInput>::Inner<'_>,
|
||||
a: impl FnOnce(SystemIn<'_, A>) -> A::Out,
|
||||
b: impl FnOnce(SystemIn<'_, B>) -> B::Out,
|
||||
) -> Self::Out {
|
||||
a(input) ^ b(input)
|
||||
}
|
||||
|
|
|
@ -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: <Self::In as SystemInput>::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<S: System>: 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: <Self::In as SystemInput>::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<F, S, Out> Adapt<S> 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: <Self::In as SystemInput>::Inner<'_>,
|
||||
run_system: impl FnOnce(SystemIn<'_, S>) -> S::Out,
|
||||
) -> Out {
|
||||
self(run_system(input))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<A: System, B: System> {
|
||||
/// 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<A: System, B: System> {
|
|||
///
|
||||
/// 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: <Self::In as SystemInput>::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<A, B, Func> ReadOnlySystem for CombinatorSystem<Func, A, B>
|
||||
unsafe impl<Func, A, B> ReadOnlySystem for CombinatorSystem<Func, A, B>
|
||||
where
|
||||
Func: Combine<A, B> + 'static,
|
||||
A: ReadOnlySystem,
|
||||
|
@ -307,25 +312,140 @@ where
|
|||
/// result.ok().filter(|&n| n < 100)
|
||||
/// }
|
||||
/// ```
|
||||
pub type PipeSystem<SystemA, SystemB> = CombinatorSystem<Pipe, SystemA, SystemB>;
|
||||
pub struct PipeSystem<A, B> {
|
||||
a: A,
|
||||
b: B,
|
||||
name: Cow<'static, str>,
|
||||
component_access: Access<ComponentId>,
|
||||
archetype_component_access: Access<ArchetypeComponentId>,
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct Pipe;
|
||||
|
||||
impl<A, B> Combine<A, B> for Pipe
|
||||
impl<A, B> PipeSystem<A, B>
|
||||
where
|
||||
A: System,
|
||||
B: System<In = A::Out>,
|
||||
B: System,
|
||||
for<'a> B::In: SystemInput<Inner<'a> = 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<A, B> System for PipeSystem<A, B>
|
||||
where
|
||||
A: System,
|
||||
B: System,
|
||||
for<'a> B::In: SystemInput<Inner<'a> = 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<ComponentId> {
|
||||
&self.component_access
|
||||
}
|
||||
|
||||
fn archetype_component_access(&self) -> &Access<ArchetypeComponentId> {
|
||||
&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<InternedSystemSet> {
|
||||
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<A, B> ReadOnlySystem for PipeSystem<A, B>
|
||||
where
|
||||
A: ReadOnlySystem,
|
||||
B: ReadOnlySystem,
|
||||
for<'a> B::In: SystemInput<Inner<'a> = A::Out>,
|
||||
{
|
||||
}
|
||||
|
|
|
@ -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<I: 'static + Send>(&mut self, id: SystemId<I>, input: I) {
|
||||
pub fn run_system_with_input<I>(&mut self, id: SystemId<I>, input: I::Inner<'static>)
|
||||
where
|
||||
I: SystemInput<Inner<'static>: 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::<Counter>().0);
|
||||
/// # bevy_ecs::system::assert_is_system(register_system);
|
||||
/// ```
|
||||
pub fn register_system<
|
||||
I: 'static + Send,
|
||||
O: 'static + Send,
|
||||
M,
|
||||
S: IntoSystem<I, O, M> + 'static,
|
||||
>(
|
||||
pub fn register_system<I, O, M>(
|
||||
&mut self,
|
||||
system: S,
|
||||
) -> SystemId<I, O> {
|
||||
system: impl IntoSystem<I, O, M> + 'static,
|
||||
) -> SystemId<I, O>
|
||||
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<I, M, S>(&mut self, system: S, input: I::Inner<'static>)
|
||||
where
|
||||
I: SystemInput<Inner<'static>: Send> + Send + 'static,
|
||||
M: 'static,
|
||||
S: IntoSystem<I, (), M> + 'static,
|
||||
>(
|
||||
&mut self,
|
||||
system: S,
|
||||
input: I,
|
||||
) {
|
||||
{
|
||||
self.queue(RunSystemCachedWith::new(system, input));
|
||||
}
|
||||
|
||||
|
|
|
@ -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<Marker>: 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<Marker>: Send + Sync + 'static {
|
|||
fn run(
|
||||
&mut self,
|
||||
world: &mut World,
|
||||
input: Self::In,
|
||||
input: <Self::In as SystemInput>::Inner<'_>,
|
||||
param_value: ExclusiveSystemParamItem<Self::Param>,
|
||||
) -> 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<Out, Func: Send + Sync + 'static, $($param: ExclusiveSystemParam),*> ExclusiveSystemParamFunction<fn($($param,)*) -> Out> for Func
|
||||
impl<Out, Func, $($param: ExclusiveSystemParam),*> ExclusiveSystemParamFunction<fn($($param,)*) -> 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<Input, Out, Func: Send + Sync + 'static, $($param: ExclusiveSystemParam),*> ExclusiveSystemParamFunction<fn(In<Input>, $($param,)*) -> Out> for Func
|
||||
impl<In, Out, Func, $($param: ExclusiveSystemParam),*> ExclusiveSystemParamFunction<(HasExclusiveSystemInput, fn(In, $($param,)*) -> Out)> for Func
|
||||
where
|
||||
for <'a> &'a mut Func:
|
||||
FnMut(In<Input>, &mut World, $($param),*) -> Out +
|
||||
FnMut(In<Input>, &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<Input, Out, $($param,)*>(
|
||||
mut f: impl FnMut(In<Input>, &mut World, $($param,)*) -> Out,
|
||||
input: Input,
|
||||
fn call_inner<In: SystemInput, Out, $($param,)*>(
|
||||
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<T, In, Out, Marker>(function: T)
|
||||
fn test<T, In: SystemInput, Out, Marker>(function: T)
|
||||
where
|
||||
T: IntoSystem<In, Out, Marker> + Copy,
|
||||
{
|
||||
|
|
|
@ -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<Input>, $(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<A, B, AMarker, BMarker>(
|
||||
/// mut a: A,
|
||||
/// mut b: B,
|
||||
/// ) -> impl FnMut(In<A::In>, ParamSet<(A::Param, B::Param)>) -> B::Out
|
||||
/// ) -> impl FnMut(StaticSystemInput<A::In>, ParamSet<(A::Param, B::Param)>) -> B::Out
|
||||
/// where
|
||||
/// // We need A and B to be systems, add those bounds
|
||||
/// A: SystemParamFunction<AMarker>,
|
||||
/// B: SystemParamFunction<BMarker, In = A::Out>,
|
||||
/// B: SystemParamFunction<BMarker>,
|
||||
/// for<'a> B::In: SystemInput<Inner<'a> = 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<Marker>: 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<Marker>: 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::Param>) -> Self::Out;
|
||||
fn run(
|
||||
&mut self,
|
||||
input: <Self::In as SystemInput>::Inner<'_>,
|
||||
param_value: SystemParamItem<Self::Param>,
|
||||
) -> 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<Out, Func: Send + Sync + 'static, $($param: SystemParam),*> SystemParamFunction<fn($($param,)*) -> Out> for Func
|
||||
impl<Out, Func, $($param: SystemParam),*> SystemParamFunction<fn($($param,)*) -> 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<Input, Out, Func: Send + Sync + 'static, $($param: SystemParam),*> SystemParamFunction<fn(In<Input>, $($param,)*) -> Out> for Func
|
||||
impl<In, Out, Func, $($param: SystemParam),*> SystemParamFunction<(HasSystemInput, fn(In, $($param,)*) -> Out)> for Func
|
||||
where
|
||||
for <'a> &'a mut Func:
|
||||
FnMut(In<Input>, $($param),*) -> Out +
|
||||
FnMut(In<Input>, $(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<Input, Out, $($param,)*>(
|
||||
mut f: impl FnMut(In<Input>, $($param,)*)->Out,
|
||||
input: In<Input>,
|
||||
fn call_inner<In: SystemInput, Out, $($param,)*>(
|
||||
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<T, In, Out, Marker>(function: T)
|
||||
fn test<T, In: SystemInput, Out, Marker>(function: T)
|
||||
where
|
||||
T: IntoSystem<In, Out, Marker> + Copy,
|
||||
{
|
||||
|
|
229
crates/bevy_ecs/src/system/input.rs
Normal file
229
crates/bevy_ecs/src/system/input.rs
Normal file
|
@ -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<T>`]: For values
|
||||
/// - [`InRef<T>`]: For read-only references to values
|
||||
/// - [`InMut<T>`]: For mutable references to values
|
||||
/// - [`Trigger<E, B>`]: For [`ObserverSystem`]s
|
||||
/// - [`StaticSystemInput<I>`]: 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> = <<S as System>::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>) -> 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<T>(pub T);
|
||||
|
||||
impl<T: 'static> SystemInput for In<T> {
|
||||
type Param<'i> = In<T>;
|
||||
type Inner<'i> = T;
|
||||
|
||||
fn wrap(this: Self::Inner<'_>) -> Self::Param<'_> {
|
||||
In(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for In<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for In<T> {
|
||||
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<str>, mut log: ResMut<Log>) {
|
||||
/// writeln!(log.0, "{}", msg).unwrap();
|
||||
/// }
|
||||
///
|
||||
/// let mut world = World::new();
|
||||
/// world.init_resource::<Log>();
|
||||
/// 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::<Log>().unwrap().0, "Hello, world!\n");
|
||||
/// ```
|
||||
///
|
||||
/// [`SystemParam`]: crate::system::SystemParam
|
||||
#[derive(Debug)]
|
||||
pub struct InRef<'i, T: ?Sized>(pub &'i T);
|
||||
|
||||
impl<T: ?Sized + 'static> 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<usize>) {
|
||||
/// *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<T: ?Sized + 'static> 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<E: 'static, B: Bundle> 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)
|
||||
}
|
||||
}
|
|
@ -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<In, Out, Marker>: Sized {
|
||||
pub trait IntoSystem<In: SystemInput, Out, Marker>: Sized {
|
||||
/// The type of [`System`] that this instance converts into.
|
||||
type System: System<In = In, Out = Out>;
|
||||
|
||||
|
@ -166,9 +168,11 @@ pub trait IntoSystem<In, Out, Marker>: Sized {
|
|||
///
|
||||
/// The second system must have [`In<T>`](crate::system::In) as its first parameter,
|
||||
/// where `T` is the return type of the first system.
|
||||
fn pipe<B, Final, MarkerB>(self, system: B) -> PipeSystem<Self::System, B::System>
|
||||
fn pipe<B, BIn, BOut, MarkerB>(self, system: B) -> PipeSystem<Self::System, B::System>
|
||||
where
|
||||
B: IntoSystem<Out, Final, MarkerB>,
|
||||
Out: 'static,
|
||||
B: IntoSystem<BIn, BOut, MarkerB>,
|
||||
for<'a> BIn: SystemInput<Inner<'a> = Out>,
|
||||
{
|
||||
let system_a = IntoSystem::into_system(self);
|
||||
let system_b = IntoSystem::into_system(system);
|
||||
|
@ -219,34 +223,6 @@ impl<T: System> IntoSystem<T::In, T::Out, ()> 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>) -> usize {
|
||||
/// input * input
|
||||
/// }
|
||||
/// ```
|
||||
pub struct In<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<In>(pub In);
|
|||
///
|
||||
/// assert_is_system(my_system);
|
||||
/// ```
|
||||
pub fn assert_is_system<In: 'static, Out: 'static, Marker>(
|
||||
pub fn assert_is_system<In: SystemInput, Out: 'static, Marker>(
|
||||
system: impl IntoSystem<In, Out, Marker>,
|
||||
) {
|
||||
let mut system = IntoSystem::into_system(system);
|
||||
|
@ -304,8 +280,10 @@ pub fn assert_is_system<In: 'static, Out: 'static, Marker>(
|
|||
///
|
||||
/// assert_is_read_only_system(my_system);
|
||||
/// ```
|
||||
pub fn assert_is_read_only_system<In: 'static, Out: 'static, Marker, S>(system: S)
|
||||
pub fn assert_is_read_only_system<In, Out, Marker, S>(system: S)
|
||||
where
|
||||
In: SystemInput,
|
||||
Out: 'static,
|
||||
S: IntoSystem<In, Out, Marker>,
|
||||
S::System: ReadOnlySystem,
|
||||
{
|
||||
|
@ -324,20 +302,6 @@ pub fn assert_system_does_not_conflict<Out, Params, S: IntoSystem<(), Out, Param
|
|||
system.run((), &mut world);
|
||||
}
|
||||
|
||||
impl<T> std::ops::Deref for In<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::ops::DerefMut for In<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use bevy_utils::default;
|
||||
|
|
|
@ -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<E: 'static, B: Bundle, Out, Func: Send + Sync + 'static, $($param: SystemParam),*> SystemParamFunction<fn(Trigger<E, B>, $($param,)*)> for Func
|
||||
where
|
||||
for <'a> &'a mut Func:
|
||||
FnMut(Trigger<E, B>, $($param),*) -> Out +
|
||||
FnMut(Trigger<E, B>, $(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<E: 'static, B: Bundle, Out, $($param,)*>(
|
||||
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::{
|
||||
|
|
|
@ -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<In: 'static, Out: 'static> Debug for dyn System<In = In, Out = Out> {
|
||||
impl<In, Out> Debug for dyn System<In = In, Out = Out>
|
||||
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<In: 'static, Out: 'static> Debug for dyn System<In = In, Out = Out> {
|
|||
/// ```
|
||||
pub trait RunSystemOnce: Sized {
|
||||
/// Runs a system and applies its deferred parameters.
|
||||
fn run_system_once<T: IntoSystem<(), Out, Marker>, Out, Marker>(self, system: T) -> Out {
|
||||
fn run_system_once<T, Out, Marker>(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<T: IntoSystem<In, Out, Marker>, In, Out, Marker>(
|
||||
fn run_system_once_with<T, In, Out, Marker>(
|
||||
self,
|
||||
input: In,
|
||||
input: SystemIn<'_, T::System>,
|
||||
system: T,
|
||||
) -> Out;
|
||||
) -> Out
|
||||
where
|
||||
T: IntoSystem<In, Out, Marker>,
|
||||
In: SystemInput;
|
||||
}
|
||||
|
||||
impl RunSystemOnce for &mut World {
|
||||
fn run_system_once_with<T: IntoSystem<In, Out, Marker>, In, Out, Marker>(
|
||||
fn run_system_once_with<T, In, Out, Marker>(
|
||||
self,
|
||||
input: In,
|
||||
input: SystemIn<'_, T::System>,
|
||||
system: T,
|
||||
) -> Out {
|
||||
) -> Out
|
||||
where
|
||||
T: IntoSystem<In, Out, Marker>,
|
||||
In: SystemInput,
|
||||
{
|
||||
let mut system: T::System = IntoSystem::into_system(system);
|
||||
system.initialize(self);
|
||||
system.run(input, self)
|
||||
|
|
|
@ -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`)
|
||||
///
|
||||
|
|
|
@ -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<I, O> RemovedSystem<I, O> {
|
|||
///
|
||||
/// These are opaque identifiers, keyed to a specific [`World`],
|
||||
/// and are created via [`World::register_system`].
|
||||
pub struct SystemId<I = (), O = ()> {
|
||||
pub struct SystemId<I: SystemInput = (), O = ()> {
|
||||
pub(crate) entity: Entity,
|
||||
pub(crate) marker: std::marker::PhantomData<fn(I) -> O>,
|
||||
}
|
||||
|
||||
impl<I, O> SystemId<I, O> {
|
||||
impl<I: SystemInput, O> SystemId<I, O> {
|
||||
/// 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<I, O> SystemId<I, O> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<I, O> Eq for SystemId<I, O> {}
|
||||
impl<I: SystemInput, O> Eq for SystemId<I, O> {}
|
||||
|
||||
// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters.
|
||||
impl<I, O> Copy for SystemId<I, O> {}
|
||||
impl<I: SystemInput, O> Copy for SystemId<I, O> {}
|
||||
|
||||
// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters.
|
||||
impl<I, O> Clone for SystemId<I, O> {
|
||||
impl<I: SystemInput, O> Clone for SystemId<I, O> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters.
|
||||
impl<I, O> PartialEq for SystemId<I, O> {
|
||||
impl<I: SystemInput, O> PartialEq for SystemId<I, O> {
|
||||
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<I, O> std::hash::Hash for SystemId<I, O> {
|
||||
impl<I: SystemInput, O> std::hash::Hash for SystemId<I, O> {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.entity.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, O> std::fmt::Debug for SystemId<I, O> {
|
||||
impl<I: SystemInput, O> std::fmt::Debug for SystemId<I, O> {
|
||||
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<I: 'static, O: 'static, M, S: IntoSystem<I, O, M> + 'static>(
|
||||
pub fn register_system<I, O, M>(
|
||||
&mut self,
|
||||
system: S,
|
||||
) -> SystemId<I, O> {
|
||||
system: impl IntoSystem<I, O, M> + 'static,
|
||||
) -> SystemId<I, O>
|
||||
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<I: 'static, O: 'static>(
|
||||
&mut self,
|
||||
system: BoxedSystem<I, O>,
|
||||
) -> SystemId<I, O> {
|
||||
pub fn register_boxed_system<I, O>(&mut self, system: BoxedSystem<I, O>) -> SystemId<I, O>
|
||||
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<I: 'static, O: 'static>(
|
||||
pub fn remove_system<I, O>(
|
||||
&mut self,
|
||||
id: SystemId<I, O>,
|
||||
) -> Result<RemovedSystem<I, O>, RegisteredSystemError<I, O>> {
|
||||
) -> Result<RemovedSystem<I, O>, RegisteredSystemError<I, O>>
|
||||
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<I: 'static, O: 'static>(
|
||||
pub fn run_system_with_input<I, O>(
|
||||
&mut self,
|
||||
id: SystemId<I, O>,
|
||||
input: I,
|
||||
) -> Result<O, RegisteredSystemError<I, O>> {
|
||||
input: I::Inner<'_>,
|
||||
) -> Result<O, RegisteredSystemError<I, O>>
|
||||
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<I: 'static, O: 'static, M, S: IntoSystem<I, O, M> + 'static>(
|
||||
&mut self,
|
||||
system: S,
|
||||
) -> SystemId<I, O> {
|
||||
pub fn register_system_cached<I, O, M, S>(&mut self, system: S) -> SystemId<I, O>
|
||||
where
|
||||
I: SystemInput + 'static,
|
||||
O: 'static,
|
||||
S: IntoSystem<I, O, M> + 'static,
|
||||
{
|
||||
const {
|
||||
assert!(
|
||||
size_of::<S>() == 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<I: 'static, O: 'static, M, S: IntoSystem<I, O, M> + 'static>(
|
||||
pub fn remove_system_cached<I, O, M, S>(
|
||||
&mut self,
|
||||
_system: S,
|
||||
) -> Result<RemovedSystem<I, O>, RegisteredSystemError<I, O>> {
|
||||
) -> Result<RemovedSystem<I, O>, RegisteredSystemError<I, O>>
|
||||
where
|
||||
I: SystemInput + 'static,
|
||||
O: 'static,
|
||||
S: IntoSystem<I, O, M> + 'static,
|
||||
{
|
||||
let id = self
|
||||
.remove_resource::<CachedSystemId<S::System>>()
|
||||
.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<I: 'static, O: 'static, M, S: IntoSystem<I, O, M> + 'static>(
|
||||
pub fn run_system_cached_with<I, O, M, S>(
|
||||
&mut self,
|
||||
system: S,
|
||||
input: I,
|
||||
) -> Result<O, RegisteredSystemError<I, O>> {
|
||||
input: I::Inner<'_>,
|
||||
) -> Result<O, RegisteredSystemError<I, O>>
|
||||
where
|
||||
I: SystemInput + 'static,
|
||||
O: 'static,
|
||||
S: IntoSystem<I, O, M> + '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<I: 'static> {
|
||||
pub struct RunSystemWithInput<I: SystemInput + 'static> {
|
||||
system_id: SystemId<I>,
|
||||
input: I,
|
||||
input: I::Inner<'static>,
|
||||
}
|
||||
|
||||
/// The [`Command`] type for [`World::run_system`].
|
||||
|
@ -453,15 +479,18 @@ impl RunSystem {
|
|||
}
|
||||
}
|
||||
|
||||
impl<I: 'static> RunSystemWithInput<I> {
|
||||
impl<I: SystemInput + 'static> RunSystemWithInput<I> {
|
||||
/// 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<I>, input: I) -> Self {
|
||||
pub fn new_with_input(system_id: SystemId<I>, input: I::Inner<'static>) -> Self {
|
||||
Self { system_id, input }
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: 'static + Send> Command for RunSystemWithInput<I> {
|
||||
impl<I> Command for RunSystemWithInput<I>
|
||||
where
|
||||
I: SystemInput<Inner<'static>: 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<I: 'static + Send> Command for RunSystemWithInput<I> {
|
|||
/// 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<I: 'static, O: 'static> {
|
||||
pub struct RegisterSystem<I: SystemInput + 'static, O: 'static> {
|
||||
system: BoxedSystem<I, O>,
|
||||
entity: Entity,
|
||||
}
|
||||
|
||||
impl<I: 'static, O: 'static> RegisterSystem<I, O> {
|
||||
impl<I, O> RegisterSystem<I, O>
|
||||
where
|
||||
I: SystemInput + 'static,
|
||||
O: 'static,
|
||||
{
|
||||
/// Creates a new [`Command`] struct, which can be added to [`Commands`](crate::system::Commands).
|
||||
pub fn new<M, S: IntoSystem<I, O, M> + 'static>(system: S, entity: Entity) -> Self {
|
||||
Self {
|
||||
|
@ -486,7 +519,11 @@ impl<I: 'static, O: 'static> RegisterSystem<I, O> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<I: 'static + Send, O: 'static + Send> Command for RegisterSystem<I, O> {
|
||||
impl<I, O> Command for RegisterSystem<I, O>
|
||||
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<I: 'static + Send, O: 'static + Send> Command for RegisterSystem<I, O> {
|
|||
/// See [`World::register_system_cached`] for more information.
|
||||
pub struct RunSystemCachedWith<S: System<Out = ()>> {
|
||||
system: S,
|
||||
input: S::In,
|
||||
input: SystemIn<'static, S>,
|
||||
}
|
||||
|
||||
impl<S: System<Out = ()>> RunSystemCachedWith<S> {
|
||||
/// Creates a new [`Command`] struct, which can be added to
|
||||
/// [`Commands`](crate::system::Commands).
|
||||
pub fn new<M>(system: impl IntoSystem<S::In, (), M, System = S>, input: S::In) -> Self {
|
||||
pub fn new<M>(
|
||||
system: impl IntoSystem<S::In, (), M, System = S>,
|
||||
input: SystemIn<'static, S>,
|
||||
) -> Self {
|
||||
Self {
|
||||
system: IntoSystem::into_system(system),
|
||||
input,
|
||||
|
@ -516,7 +556,7 @@ impl<S: System<Out = ()>> RunSystemCachedWith<S> {
|
|||
|
||||
impl<S: System<Out = ()>> Command for RunSystemCachedWith<S>
|
||||
where
|
||||
S::In: Send,
|
||||
S::In: SystemInput<Inner<'static>: 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<I = (), O = ()> {
|
||||
pub enum RegisteredSystemError<I: SystemInput = (), O = ()> {
|
||||
/// 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<I = (), O = ()> {
|
|||
SelfRemove(SystemId<I, O>),
|
||||
}
|
||||
|
||||
impl<I, O> std::fmt::Debug for RegisteredSystemError<I, O> {
|
||||
impl<I: SystemInput, O> std::fmt::Debug for RegisteredSystemError<I, O> {
|
||||
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>, u8);
|
||||
struct Callback(SystemId<In<u8>>, 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<u8>, mut counter: ResMut<Counter>) {
|
||||
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>(), Counter(2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn system_with_input_mut() {
|
||||
#[derive(Event)]
|
||||
struct MyEvent {
|
||||
cancelled: bool,
|
||||
}
|
||||
|
||||
fn post(InMut(event): InMut<MyEvent>, counter: ResMut<Counter>) {
|
||||
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::<Counter>().0 = 1;
|
||||
world
|
||||
.run_system_with_input(post_system, &mut event)
|
||||
.unwrap();
|
||||
assert!(event.cancelled);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue