2024-09-27 00:59:59 +00:00
|
|
|
use alloc::borrow::Cow;
|
|
|
|
use core::marker::PhantomData;
|
2023-02-20 18:16:11 +00:00
|
|
|
|
|
|
|
use crate::{
|
2023-03-09 17:17:02 +00:00
|
|
|
archetype::ArchetypeComponentId,
|
|
|
|
component::{ComponentId, Tick},
|
|
|
|
prelude::World,
|
|
|
|
query::Access,
|
Replace all labels with interned labels (#7762)
# Objective
First of all, this PR took heavy inspiration from #7760 and #5715. It
intends to also fix #5569, but with a slightly different approach.
This also fixes #9335 by reexporting `DynEq`.
## Solution
The advantage of this API is that we can intern a value without
allocating for zero-sized-types and for enum variants that have no
fields. This PR does this automatically in the `SystemSet` and
`ScheduleLabel` derive macros for unit structs and fieldless enum
variants. So this should cover many internal and external use cases of
`SystemSet` and `ScheduleLabel`. In these optimal use cases, no memory
will be allocated.
- The interning returns a `Interned<dyn SystemSet>`, which is just a
wrapper around a `&'static dyn SystemSet`.
- `Hash` and `Eq` are implemented in terms of the pointer value of the
reference, similar to my first approach of anonymous system sets in
#7676.
- Therefore, `Interned<T>` does not implement `Borrow<T>`, only `Deref`.
- The debug output of `Interned<T>` is the same as the interned value.
Edit:
- `AppLabel` is now also interned and the old
`derive_label`/`define_label` macros were replaced with the new
interning implementation.
- Anonymous set ids are reused for different `Schedule`s, reducing the
amount of leaked memory.
### Pros
- `InternedSystemSet` and `InternedScheduleLabel` behave very similar to
the current `BoxedSystemSet` and `BoxedScheduleLabel`, but can be copied
without an allocation.
- Many use cases don't allocate at all.
- Very fast lookups and comparisons when using `InternedSystemSet` and
`InternedScheduleLabel`.
- The `intern` module might be usable in other areas.
- `Interned{ScheduleLabel, SystemSet, AppLabel}` does implement
`{ScheduleLabel, SystemSet, AppLabel}`, increasing ergonomics.
### Cons
- Implementors of `SystemSet` and `ScheduleLabel` still need to
implement `Hash` and `Eq` (and `Clone`) for it to work.
## Changelog
### Added
- Added `intern` module to `bevy_utils`.
- Added reexports of `DynEq` to `bevy_ecs` and `bevy_app`.
### Changed
- Replaced `BoxedSystemSet` and `BoxedScheduleLabel` with
`InternedSystemSet` and `InternedScheduleLabel`.
- Replaced `impl AsRef<dyn ScheduleLabel>` with `impl ScheduleLabel`.
- Replaced `AppLabelId` with `InternedAppLabel`.
- Changed `AppLabel` to use `Debug` for error messages.
- Changed `AppLabel` to use interning.
- Changed `define_label`/`derive_label` to use interning.
- Replaced `define_boxed_label`/`derive_boxed_label` with
`define_label`/`derive_label`.
- Changed anonymous set ids to be only unique inside a schedule, not
globally.
- Made interned label types implement their label trait.
### Removed
- Removed `define_boxed_label` and `derive_boxed_label`.
## Migration guide
- Replace `BoxedScheduleLabel` and `Box<dyn ScheduleLabel>` with
`InternedScheduleLabel` or `Interned<dyn ScheduleLabel>`.
- Replace `BoxedSystemSet` and `Box<dyn SystemSet>` with
`InternedSystemSet` or `Interned<dyn SystemSet>`.
- Replace `AppLabelId` with `InternedAppLabel` or `Interned<dyn
AppLabel>`.
- Types manually implementing `ScheduleLabel`, `AppLabel` or `SystemSet`
need to implement:
- `dyn_hash` directly instead of implementing `DynHash`
- `as_dyn_eq`
- Pass labels to `World::try_schedule_scope`, `World::schedule_scope`,
`World::try_run_schedule`. `World::run_schedule`, `Schedules::remove`,
`Schedules::remove_entry`, `Schedules::contains`, `Schedules::get` and
`Schedules::get_mut` by value instead of by reference.
---------
Co-authored-by: Joseph <21144246+JoJoJet@users.noreply.github.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2023-10-25 21:39:23 +00:00
|
|
|
schedule::InternedSystemSet,
|
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>
2024-09-23 17:37:29 +00:00
|
|
|
system::{input::SystemInput, SystemIn},
|
2023-05-29 15:22:10 +00:00
|
|
|
world::unsafe_world_cell::UnsafeWorldCell,
|
2023-02-20 18:16:11 +00:00
|
|
|
};
|
|
|
|
|
2024-09-24 17:35:44 +00:00
|
|
|
use super::{IntoSystem, ReadOnlySystem, System};
|
2023-02-20 18:16:11 +00:00
|
|
|
|
|
|
|
/// Customizes the behavior of a [`CombinatorSystem`].
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use bevy_ecs::prelude::*;
|
|
|
|
/// use bevy_ecs::system::{CombinatorSystem, Combine};
|
|
|
|
///
|
|
|
|
/// // A system combinator that performs an exclusive-or (XOR)
|
|
|
|
/// // operation on the output of two systems.
|
|
|
|
/// pub type Xor<A, B> = CombinatorSystem<XorMarker, A, B>;
|
|
|
|
///
|
|
|
|
/// // This struct is used to customize the behavior of our combinator.
|
|
|
|
/// pub struct XorMarker;
|
|
|
|
///
|
|
|
|
/// impl<A, B> Combine<A, B> for XorMarker
|
|
|
|
/// where
|
|
|
|
/// A: System<In = (), Out = bool>,
|
|
|
|
/// B: System<In = (), Out = bool>,
|
|
|
|
/// {
|
|
|
|
/// type In = ();
|
|
|
|
/// type Out = bool;
|
|
|
|
///
|
|
|
|
/// fn combine(
|
|
|
|
/// _input: Self::In,
|
|
|
|
/// a: impl FnOnce(A::In) -> A::Out,
|
|
|
|
/// b: impl FnOnce(B::In) -> B::Out,
|
|
|
|
/// ) -> Self::Out {
|
|
|
|
/// a(()) ^ b(())
|
|
|
|
/// }
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// # #[derive(Resource, PartialEq, Eq)] struct A(u32);
|
|
|
|
/// # #[derive(Resource, PartialEq, Eq)] struct B(u32);
|
|
|
|
/// # #[derive(Resource, Default)] struct RanFlag(bool);
|
|
|
|
/// # let mut world = World::new();
|
|
|
|
/// # world.init_resource::<RanFlag>();
|
|
|
|
/// #
|
2023-08-28 20:44:48 +00:00
|
|
|
/// # let mut app = Schedule::default();
|
2023-03-18 01:45:34 +00:00
|
|
|
/// app.add_systems(my_system.run_if(Xor::new(
|
2023-02-20 18:16:11 +00:00
|
|
|
/// IntoSystem::into_system(resource_equals(A(1))),
|
|
|
|
/// IntoSystem::into_system(resource_equals(B(1))),
|
|
|
|
/// // The name of the combined system.
|
|
|
|
/// std::borrow::Cow::Borrowed("a ^ b"),
|
|
|
|
/// )));
|
|
|
|
/// # fn my_system(mut flag: ResMut<RanFlag>) { flag.0 = true; }
|
|
|
|
/// #
|
|
|
|
/// # world.insert_resource(A(0));
|
|
|
|
/// # world.insert_resource(B(0));
|
|
|
|
/// # app.run(&mut world);
|
|
|
|
/// # // Neither condition passes, so the system does not run.
|
|
|
|
/// # assert!(!world.resource::<RanFlag>().0);
|
|
|
|
/// #
|
|
|
|
/// # world.insert_resource(A(1));
|
|
|
|
/// # app.run(&mut world);
|
|
|
|
/// # // Only the first condition passes, so the system runs.
|
|
|
|
/// # assert!(world.resource::<RanFlag>().0);
|
|
|
|
/// # world.resource_mut::<RanFlag>().0 = false;
|
|
|
|
/// #
|
|
|
|
/// # world.insert_resource(B(1));
|
|
|
|
/// # app.run(&mut world);
|
|
|
|
/// # // Both conditions pass, so the system does not run.
|
|
|
|
/// # assert!(!world.resource::<RanFlag>().0);
|
|
|
|
/// #
|
|
|
|
/// # world.insert_resource(A(0));
|
|
|
|
/// # app.run(&mut world);
|
|
|
|
/// # // Only the second condition passes, so the system runs.
|
|
|
|
/// # assert!(world.resource::<RanFlag>().0);
|
|
|
|
/// # world.resource_mut::<RanFlag>().0 = false;
|
|
|
|
/// ```
|
2024-06-04 00:31:34 +00:00
|
|
|
#[diagnostic::on_unimplemented(
|
|
|
|
message = "`{Self}` can not combine systems `{A}` and `{B}`",
|
|
|
|
label = "invalid system combination",
|
|
|
|
note = "the inputs and outputs of `{A}` and `{B}` are not compatible with this combiner"
|
|
|
|
)]
|
2023-02-20 18:16:11 +00:00
|
|
|
pub trait Combine<A: System, B: System> {
|
|
|
|
/// The [input](System::In) type for a [`CombinatorSystem`].
|
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>
2024-09-23 17:37:29 +00:00
|
|
|
type In: SystemInput;
|
2023-02-20 18:16:11 +00:00
|
|
|
|
|
|
|
/// The [output](System::Out) type for a [`CombinatorSystem`].
|
|
|
|
type Out;
|
|
|
|
|
|
|
|
/// When used in a [`CombinatorSystem`], this function customizes how
|
|
|
|
/// the two composite systems are invoked and their outputs are combined.
|
|
|
|
///
|
|
|
|
/// See the trait-level docs for [`Combine`] for an example implementation.
|
|
|
|
fn combine(
|
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>
2024-09-23 17:37:29 +00:00
|
|
|
input: <Self::In as SystemInput>::Inner<'_>,
|
|
|
|
a: impl FnOnce(SystemIn<'_, A>) -> A::Out,
|
|
|
|
b: impl FnOnce(SystemIn<'_, B>) -> B::Out,
|
2023-02-20 18:16:11 +00:00
|
|
|
) -> Self::Out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A [`System`] defined by combining two other systems.
|
|
|
|
/// The behavior of this combinator is specified by implementing the [`Combine`] trait.
|
|
|
|
/// For a full usage example, see the docs for [`Combine`].
|
|
|
|
pub struct CombinatorSystem<Func, A, B> {
|
|
|
|
_marker: PhantomData<fn() -> Func>,
|
|
|
|
a: A,
|
|
|
|
b: B,
|
|
|
|
name: Cow<'static, str>,
|
|
|
|
component_access: Access<ComponentId>,
|
|
|
|
archetype_component_access: Access<ArchetypeComponentId>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<Func, A, B> CombinatorSystem<Func, A, B> {
|
2023-06-10 23:23:48 +00:00
|
|
|
/// Creates a new system that combines two inner systems.
|
|
|
|
///
|
|
|
|
/// The returned system will only be usable if `Func` implements [`Combine<A, B>`].
|
2023-02-20 18:16:11 +00:00
|
|
|
pub const fn new(a: A, b: B, name: Cow<'static, str>) -> Self {
|
|
|
|
Self {
|
|
|
|
_marker: PhantomData,
|
|
|
|
a,
|
|
|
|
b,
|
|
|
|
name,
|
|
|
|
component_access: Access::new(),
|
|
|
|
archetype_component_access: Access::new(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<A, B, Func> System for CombinatorSystem<Func, A, B>
|
|
|
|
where
|
|
|
|
Func: Combine<A, B> + 'static,
|
|
|
|
A: System,
|
|
|
|
B: System,
|
|
|
|
{
|
|
|
|
type In = Func::In;
|
|
|
|
type Out = Func::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()
|
|
|
|
}
|
|
|
|
|
2023-12-14 16:34:01 +00:00
|
|
|
fn has_deferred(&self) -> bool {
|
|
|
|
self.a.has_deferred() || self.b.has_deferred()
|
|
|
|
}
|
|
|
|
|
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>
2024-09-23 17:37:29 +00:00
|
|
|
unsafe fn run_unsafe(
|
|
|
|
&mut self,
|
|
|
|
input: SystemIn<'_, Self>,
|
|
|
|
world: UnsafeWorldCell,
|
|
|
|
) -> Self::Out {
|
2023-02-20 18:16:11 +00:00
|
|
|
Func::combine(
|
|
|
|
input,
|
|
|
|
// SAFETY: The world accesses for both underlying systems have been registered,
|
|
|
|
// so the caller will guarantee that no other systems will conflict with `a` or `b`.
|
2023-02-26 02:27:41 +00:00
|
|
|
// Since these closures are `!Send + !Sync + !'static`, they can never be called
|
2023-02-20 18:16:11 +00:00
|
|
|
// in parallel, so their world accesses will not conflict with each other.
|
2023-04-17 15:20:42 +00:00
|
|
|
// Additionally, `update_archetype_component_access` has been called,
|
|
|
|
// which forwards to the implementations for `self.a` and `self.b`.
|
2024-02-22 00:04:38 +00:00
|
|
|
|input| unsafe { self.a.run_unsafe(input, world) },
|
|
|
|
// SAFETY: See the comment above.
|
|
|
|
|input| unsafe { self.b.run_unsafe(input, world) },
|
2023-02-20 18:16:11 +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>
2024-09-23 17:37:29 +00:00
|
|
|
fn run(&mut self, input: SystemIn<'_, Self>, world: &mut World) -> Self::Out {
|
2024-08-11 13:58:10 +00:00
|
|
|
let world = world.as_unsafe_world_cell();
|
2023-02-20 18:16:11 +00:00
|
|
|
Func::combine(
|
|
|
|
input,
|
2023-02-26 02:27:41 +00:00
|
|
|
// SAFETY: Since these closures are `!Send + !Sync + !'static`, they can never
|
2023-02-20 18:16:11 +00:00
|
|
|
// be called in parallel. Since mutable access to `world` only exists within
|
|
|
|
// the scope of either closure, we can be sure they will never alias one another.
|
2024-08-11 13:58:10 +00:00
|
|
|
|input| self.a.run(input, unsafe { world.world_mut() }),
|
2023-02-20 18:16:11 +00:00
|
|
|
#[allow(clippy::undocumented_unsafe_blocks)]
|
2024-08-11 13:58:10 +00:00
|
|
|
|input| self.b.run(input, unsafe { world.world_mut() }),
|
2023-02-20 18:16:11 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
Reduce runtime panics through `SystemParam` validation (#15276)
# Objective
The goal of this PR is to introduce `SystemParam` validation in order to
reduce runtime panics.
Fixes #15265
## Solution
`SystemParam` now has a new method `validate_param(...) -> bool`, which
takes immutable variants of `get_param` arguments. The returned value
indicates whether the parameter can be acquired from the world. If
parameters cannot be acquired for a system, it won't be executed,
similarly to run conditions. This reduces panics when using params like
`Res`, `ResMut`, etc. as well as allows for new, ergonomic params like
#15264 or #15302.
Param validation happens at the level of executors. All validation
happens directly before executing a system, in case of normal systems
they are skipped, in case of conditions they return false.
Warning about system skipping is primitive and subject to change in
subsequent PRs.
## Testing
Two executor tests check that all executors:
- skip systems which have invalid parameters:
- piped systems get skipped together,
- dependent systems still run correctly,
- skip systems with invalid run conditions:
- system conditions have invalid parameters,
- system set conditions have invalid parameters.
2024-09-23 16:54:21 +00:00
|
|
|
#[inline]
|
2023-06-02 14:04:13 +00:00
|
|
|
fn apply_deferred(&mut self, world: &mut World) {
|
|
|
|
self.a.apply_deferred(world);
|
|
|
|
self.b.apply_deferred(world);
|
2023-02-20 18:16:11 +00:00
|
|
|
}
|
|
|
|
|
Generalised ECS reactivity with Observers (#10839)
# Objective
- Provide an expressive way to register dynamic behavior in response to
ECS changes that is consistent with existing bevy types and traits as to
provide a smooth user experience.
- Provide a mechanism for immediate changes in response to events during
command application in order to facilitate improved query caching on the
path to relations.
## Solution
- A new fundamental ECS construct, the `Observer`; inspired by flec's
observers but adapted to better fit bevy's access patterns and rust's
type system.
---
## Examples
There are 3 main ways to register observers. The first is a "component
observer" that looks like this:
```rust
world.observe(|trigger: Trigger<OnAdd, Transform>, query: Query<&Transform>| {
let transform = query.get(trigger.entity()).unwrap();
});
```
The above code will spawn a new entity representing the observer that
will run it's callback whenever the `Transform` component is added to an
entity. This is a system-like function that supports dependency
injection for all the standard bevy types: `Query`, `Res`, `Commands`
etc. It also has a `Trigger` parameter that provides information about
the trigger such as the target entity, and the event being triggered.
Importantly these systems run during command application which is key
for their future use to keep ECS internals up to date. There are similar
events for `OnInsert` and `OnRemove`, and this will be expanded with
things such as `ArchetypeCreated`, `TableEmpty` etc. in follow up PRs.
Another way to register an observer is an "entity observer" that looks
like this:
```rust
world.entity_mut(entity).observe(|trigger: Trigger<Resize>| {
// ...
});
```
Entity observers run whenever an event of their type is triggered
targeting that specific entity. This type of observer will de-spawn
itself if the entity (or entities) it is observing is ever de-spawned so
as to not leave dangling observers.
Entity observers can also be spawned from deferred contexts such as
other observers, systems, or hooks using commands:
```rust
commands.entity(entity).observe(|trigger: Trigger<Resize>| {
// ...
});
```
Observers are not limited to in built event types, they can be used with
any type that implements `Event` (which has been extended to implement
Component). This means events can also carry data:
```rust
#[derive(Event)]
struct Resize { x: u32, y: u32 }
commands.entity(entity).observe(|trigger: Trigger<Resize>, query: Query<&mut Size>| {
let event = trigger.event();
// ...
});
// Will trigger the observer when commands are applied.
commands.trigger_targets(Resize { x: 10, y: 10 }, entity);
```
You can also trigger events that target more than one entity at a time:
```rust
commands.trigger_targets(Resize { x: 10, y: 10 }, [e1, e2]);
```
Additionally, Observers don't _need_ entity targets:
```rust
app.observe(|trigger: Trigger<Quit>| {
})
commands.trigger(Quit);
```
In these cases, `trigger.entity()` will be a placeholder.
Observers are actually just normal entities with an `ObserverState` and
`Observer` component! The `observe()` functions above are just shorthand
for:
```rust
world.spawn(Observer::new(|trigger: Trigger<Resize>| {});
```
This will spawn the `Observer` system and use an `on_add` hook to add
the `ObserverState` component.
Dynamic components and trigger types are also fully supported allowing
for runtime defined trigger types.
## Possible Follow-ups
1. Deprecate `RemovedComponents`, observers should fulfill all use cases
while being more flexible and performant.
2. Queries as entities: Swap queries to entities and begin using
observers listening to archetype creation triggers to keep their caches
in sync, this allows unification of `ObserverState` and `QueryState` as
well as unlocking several API improvements for `Query` and the
management of `QueryState`.
3. Trigger bubbling: For some UI use cases in particular users are
likely to want some form of bubbling for entity observers, this is
trivial to implement naively but ideally this includes an acceleration
structure to cache hierarchy traversals.
4. All kinds of other in-built trigger types.
5. Optimization; in order to not bloat the complexity of the PR I have
kept the implementation straightforward, there are several areas where
performance can be improved. The focus for this PR is to get the
behavior implemented and not incur a performance cost for users who
don't use observers.
I am leaving each of these to follow up PR's in order to keep each of
them reviewable as this already includes significant changes.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: MiniaczQ <xnetroidpl@gmail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2024-06-15 01:33:26 +00:00
|
|
|
#[inline]
|
|
|
|
fn queue_deferred(&mut self, mut world: crate::world::DeferredWorld) {
|
|
|
|
self.a.queue_deferred(world.reborrow());
|
|
|
|
self.b.queue_deferred(world);
|
|
|
|
}
|
|
|
|
|
Reduce runtime panics through `SystemParam` validation (#15276)
# Objective
The goal of this PR is to introduce `SystemParam` validation in order to
reduce runtime panics.
Fixes #15265
## Solution
`SystemParam` now has a new method `validate_param(...) -> bool`, which
takes immutable variants of `get_param` arguments. The returned value
indicates whether the parameter can be acquired from the world. If
parameters cannot be acquired for a system, it won't be executed,
similarly to run conditions. This reduces panics when using params like
`Res`, `ResMut`, etc. as well as allows for new, ergonomic params like
#15264 or #15302.
Param validation happens at the level of executors. All validation
happens directly before executing a system, in case of normal systems
they are skipped, in case of conditions they return false.
Warning about system skipping is primitive and subject to change in
subsequent PRs.
## Testing
Two executor tests check that all executors:
- skip systems which have invalid parameters:
- piped systems get skipped together,
- dependent systems still run correctly,
- skip systems with invalid run conditions:
- system conditions have invalid parameters,
- system set conditions have invalid parameters.
2024-09-23 16:54:21 +00:00
|
|
|
#[inline]
|
2024-10-03 13:16:55 +00:00
|
|
|
unsafe fn validate_param_unsafe(&mut self, world: UnsafeWorldCell) -> bool {
|
|
|
|
// SAFETY: Delegate to other `System` implementations.
|
|
|
|
unsafe { self.a.validate_param_unsafe(world) && self.b.validate_param_unsafe(world) }
|
Reduce runtime panics through `SystemParam` validation (#15276)
# Objective
The goal of this PR is to introduce `SystemParam` validation in order to
reduce runtime panics.
Fixes #15265
## Solution
`SystemParam` now has a new method `validate_param(...) -> bool`, which
takes immutable variants of `get_param` arguments. The returned value
indicates whether the parameter can be acquired from the world. If
parameters cannot be acquired for a system, it won't be executed,
similarly to run conditions. This reduces panics when using params like
`Res`, `ResMut`, etc. as well as allows for new, ergonomic params like
#15264 or #15302.
Param validation happens at the level of executors. All validation
happens directly before executing a system, in case of normal systems
they are skipped, in case of conditions they return false.
Warning about system skipping is primitive and subject to change in
subsequent PRs.
## Testing
Two executor tests check that all executors:
- skip systems which have invalid parameters:
- piped systems get skipped together,
- dependent systems still run correctly,
- skip systems with invalid run conditions:
- system conditions have invalid parameters,
- system set conditions have invalid parameters.
2024-09-23 16:54:21 +00:00
|
|
|
}
|
|
|
|
|
2023-02-20 18:16:11 +00:00
|
|
|
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());
|
|
|
|
}
|
|
|
|
|
2023-05-29 15:22:10 +00:00
|
|
|
fn update_archetype_component_access(&mut self, world: UnsafeWorldCell) {
|
2023-02-20 18:16:11 +00:00
|
|
|
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());
|
|
|
|
}
|
|
|
|
|
2023-03-09 17:17:02 +00:00
|
|
|
fn check_change_tick(&mut self, change_tick: Tick) {
|
2023-02-20 18:16:11 +00:00
|
|
|
self.a.check_change_tick(change_tick);
|
|
|
|
self.b.check_change_tick(change_tick);
|
|
|
|
}
|
|
|
|
|
2023-12-13 21:19:49 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2023-03-09 17:17:02 +00:00
|
|
|
fn get_last_run(&self) -> Tick {
|
|
|
|
self.a.get_last_run()
|
2023-02-20 18:16:11 +00:00
|
|
|
}
|
|
|
|
|
2023-03-09 17:17:02 +00:00
|
|
|
fn set_last_run(&mut self, last_run: Tick) {
|
|
|
|
self.a.set_last_run(last_run);
|
|
|
|
self.b.set_last_run(last_run);
|
2023-02-20 18:16:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// SAFETY: Both systems are read-only, so any system created by combining them will only read from the world.
|
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>
2024-09-23 17:37:29 +00:00
|
|
|
unsafe impl<Func, A, B> ReadOnlySystem for CombinatorSystem<Func, A, B>
|
2023-02-20 18:16:11 +00:00
|
|
|
where
|
|
|
|
Func: Combine<A, B> + 'static,
|
|
|
|
A: ReadOnlySystem,
|
|
|
|
B: ReadOnlySystem,
|
|
|
|
{
|
|
|
|
}
|
2023-04-17 21:10:57 +00:00
|
|
|
|
2023-06-12 19:44:51 +00:00
|
|
|
impl<Func, A, B> Clone for CombinatorSystem<Func, A, B>
|
|
|
|
where
|
|
|
|
A: Clone,
|
|
|
|
B: Clone,
|
|
|
|
{
|
|
|
|
/// Clone the combined system. The cloned instance must be `.initialize()`d before it can run.
|
|
|
|
fn clone(&self) -> Self {
|
|
|
|
CombinatorSystem::new(self.a.clone(), self.b.clone(), self.name.clone())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-24 17:35:44 +00:00
|
|
|
/// An [`IntoSystem`] creating an instance of [`PipeSystem`].
|
|
|
|
pub struct IntoPipeSystem<A, B> {
|
|
|
|
a: A,
|
|
|
|
b: B,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<A, B> IntoPipeSystem<A, B> {
|
|
|
|
/// Creates a new [`IntoSystem`] that pipes two inner systems.
|
|
|
|
pub const fn new(a: A, b: B) -> Self {
|
|
|
|
Self { a, b }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[doc(hidden)]
|
|
|
|
pub struct IsPipeSystemMarker;
|
|
|
|
|
|
|
|
impl<A, B, IA, OA, IB, OB, MA, MB> IntoSystem<IA, OB, (IsPipeSystemMarker, OA, IB, MA, MB)>
|
|
|
|
for IntoPipeSystem<A, B>
|
|
|
|
where
|
|
|
|
IA: SystemInput,
|
|
|
|
A: IntoSystem<IA, OA, MA>,
|
|
|
|
B: IntoSystem<IB, OB, MB>,
|
|
|
|
for<'a> IB: SystemInput<Inner<'a> = OA>,
|
|
|
|
{
|
|
|
|
type System = PipeSystem<A::System, B::System>;
|
|
|
|
|
|
|
|
fn into_system(this: Self) -> Self::System {
|
|
|
|
let system_a = IntoSystem::into_system(this.a);
|
|
|
|
let system_b = IntoSystem::into_system(this.b);
|
|
|
|
let name = format!("Pipe({}, {})", system_a.name(), system_b.name());
|
|
|
|
PipeSystem::new(system_a, system_b, Cow::Owned(name))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-17 21:10:57 +00:00
|
|
|
/// A [`System`] created by piping the output of the first system into the input of the second.
|
|
|
|
///
|
|
|
|
/// This can be repeated indefinitely, but system pipes cannot branch: the output is consumed by the receiving system.
|
|
|
|
///
|
|
|
|
/// Given two systems `A` and `B`, A may be piped into `B` as `A.pipe(B)` if the output type of `A` is
|
|
|
|
/// equal to the input type of `B`.
|
|
|
|
///
|
|
|
|
/// Note that for [`FunctionSystem`](crate::system::FunctionSystem)s the output is the return value
|
|
|
|
/// of the function and the input is the first [`SystemParam`](crate::system::SystemParam) if it is
|
|
|
|
/// tagged with [`In`](crate::system::In) or `()` if the function has no designated input parameter.
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use std::num::ParseIntError;
|
|
|
|
///
|
|
|
|
/// use bevy_ecs::prelude::*;
|
|
|
|
///
|
|
|
|
/// fn main() {
|
|
|
|
/// let mut world = World::default();
|
|
|
|
/// world.insert_resource(Message("42".to_string()));
|
|
|
|
///
|
|
|
|
/// // pipe the `parse_message_system`'s output into the `filter_system`s input
|
2024-09-24 17:35:44 +00:00
|
|
|
/// let mut piped_system = IntoSystem::into_system(parse_message_system.pipe(filter_system));
|
2023-04-17 21:10:57 +00:00
|
|
|
/// piped_system.initialize(&mut world);
|
|
|
|
/// assert_eq!(piped_system.run((), &mut world), Some(42));
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// #[derive(Resource)]
|
|
|
|
/// struct Message(String);
|
|
|
|
///
|
|
|
|
/// fn parse_message_system(message: Res<Message>) -> Result<usize, ParseIntError> {
|
|
|
|
/// message.0.parse::<usize>()
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// fn filter_system(In(result): In<Result<usize, ParseIntError>>) -> Option<usize> {
|
|
|
|
/// result.ok().filter(|&n| n < 100)
|
|
|
|
/// }
|
|
|
|
/// ```
|
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>
2024-09-23 17:37:29 +00:00
|
|
|
pub struct PipeSystem<A, B> {
|
|
|
|
a: A,
|
|
|
|
b: B,
|
|
|
|
name: Cow<'static, str>,
|
|
|
|
component_access: Access<ComponentId>,
|
|
|
|
archetype_component_access: Access<ArchetypeComponentId>,
|
|
|
|
}
|
2023-04-17 21:10:57 +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>
2024-09-23 17:37:29 +00:00
|
|
|
impl<A, B> PipeSystem<A, B>
|
|
|
|
where
|
|
|
|
A: System,
|
|
|
|
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(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-04-17 21:10:57 +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>
2024-09-23 17:37:29 +00:00
|
|
|
impl<A, B> System for PipeSystem<A, B>
|
2023-04-17 21:10:57 +00:00
|
|
|
where
|
|
|
|
A: System,
|
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>
2024-09-23 17:37:29 +00:00
|
|
|
B: System,
|
|
|
|
for<'a> B::In: SystemInput<Inner<'a> = A::Out>,
|
2023-04-17 21:10:57 +00:00
|
|
|
{
|
|
|
|
type In = A::In;
|
|
|
|
type Out = B::Out;
|
|
|
|
|
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>
2024-09-23 17:37:29 +00:00
|
|
|
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,
|
2023-04-17 21:10:57 +00:00
|
|
|
) -> Self::Out {
|
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>
2024-09-23 17:37:29 +00:00
|
|
|
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);
|
2023-04-17 21:10:57 +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>
2024-09-23 17:37:29 +00:00
|
|
|
|
|
|
|
fn queue_deferred(&mut self, mut world: crate::world::DeferredWorld) {
|
|
|
|
self.a.queue_deferred(world.reborrow());
|
|
|
|
self.b.queue_deferred(world);
|
|
|
|
}
|
|
|
|
|
2024-10-03 13:16:55 +00:00
|
|
|
unsafe fn validate_param_unsafe(&mut self, world: UnsafeWorldCell) -> bool {
|
|
|
|
// SAFETY: Delegate to other `System` implementations.
|
|
|
|
unsafe { self.a.validate_param_unsafe(world) && self.b.validate_param_unsafe(world) }
|
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>
2024-09-23 17:37:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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>,
|
|
|
|
{
|
2023-04-17 21:10:57 +00:00
|
|
|
}
|