Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

View File

@@ -0,0 +1,233 @@
use alloc::{borrow::Cow, vec::Vec};
use super::{IntoSystem, ReadOnlySystem, System, SystemParamValidationError};
use crate::{
schedule::InternedSystemSet,
system::{input::SystemInput, SystemIn},
world::unsafe_world_cell::UnsafeWorldCell,
};
/// Customizes the behavior of an [`AdapterSystem`]
///
/// # Examples
///
/// ```
/// # use bevy_ecs::prelude::*;
/// use bevy_ecs::system::{Adapt, AdapterSystem};
///
/// // A system adapter that inverts the result of a system.
/// // NOTE: Instead of manually implementing this, you can just use `bevy_ecs::schedule::common_conditions::not`.
/// pub type NotSystem<S> = AdapterSystem<NotMarker, S>;
///
/// // This struct is used to customize the behavior of our adapter.
/// pub struct NotMarker;
///
/// impl<S> Adapt<S> for NotMarker
/// where
/// S: System,
/// S::Out: std::ops::Not,
/// {
/// type In = S::In;
/// type Out = <S::Out as std::ops::Not>::Output;
///
/// fn adapt(
/// &mut self,
/// input: <Self::In as SystemInput>::Inner<'_>,
/// run_system: impl FnOnce(SystemIn<'_, S>) -> S::Out,
/// ) -> Self::Out {
/// !run_system(input)
/// }
/// }
/// # let mut world = World::new();
/// # let mut system = NotSystem::new(NotMarker, IntoSystem::into_system(|| false), "".into());
/// # system.initialize(&mut world);
/// # assert!(system.run((), &mut world));
/// ```
#[diagnostic::on_unimplemented(
message = "`{Self}` can not adapt a system of type `{S}`",
label = "invalid system adapter"
)]
pub trait Adapt<S: System>: Send + Sync + 'static {
/// The [input](System::In) type for an [`AdapterSystem`].
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 as SystemInput>::Inner<'_>,
run_system: impl FnOnce(SystemIn<'_, S>) -> S::Out,
) -> Self::Out;
}
/// An [`IntoSystem`] creating an instance of [`AdapterSystem`].
#[derive(Clone)]
pub struct IntoAdapterSystem<Func, S> {
func: Func,
system: S,
}
impl<Func, S> IntoAdapterSystem<Func, S> {
/// Creates a new [`IntoSystem`] that uses `func` to adapt `system`, via the [`Adapt`] trait.
pub const fn new(func: Func, system: S) -> Self {
Self { func, system }
}
}
#[doc(hidden)]
pub struct IsAdapterSystemMarker;
impl<Func, S, I, O, M> IntoSystem<Func::In, Func::Out, (IsAdapterSystemMarker, I, O, M)>
for IntoAdapterSystem<Func, S>
where
Func: Adapt<S::System>,
I: SystemInput,
S: IntoSystem<I, O, M>,
{
type System = AdapterSystem<Func, S::System>;
// Required method
fn into_system(this: Self) -> Self::System {
let system = IntoSystem::into_system(this.system);
let name = system.name();
AdapterSystem::new(this.func, system, name)
}
}
/// A [`System`] that takes the output of `S` and transforms it by applying `Func` to it.
#[derive(Clone)]
pub struct AdapterSystem<Func, S> {
func: Func,
system: S,
name: Cow<'static, str>,
}
impl<Func, S> AdapterSystem<Func, S>
where
Func: Adapt<S>,
S: System,
{
/// Creates a new [`System`] that uses `func` to adapt `system`, via the [`Adapt`] trait.
pub const fn new(func: Func, system: S, name: Cow<'static, str>) -> Self {
Self { func, system, name }
}
}
impl<Func, S> System for AdapterSystem<Func, S>
where
Func: Adapt<S>,
S: System,
{
type In = Func::In;
type Out = Func::Out;
fn name(&self) -> Cow<'static, str> {
self.name.clone()
}
fn component_access(&self) -> &crate::query::Access<crate::component::ComponentId> {
self.system.component_access()
}
#[inline]
fn archetype_component_access(
&self,
) -> &crate::query::Access<crate::archetype::ArchetypeComponentId> {
self.system.archetype_component_access()
}
fn is_send(&self) -> bool {
self.system.is_send()
}
fn is_exclusive(&self) -> bool {
self.system.is_exclusive()
}
fn has_deferred(&self) -> bool {
self.system.has_deferred()
}
#[inline]
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)
})
}
#[inline]
fn apply_deferred(&mut self, world: &mut crate::prelude::World) {
self.system.apply_deferred(world);
}
#[inline]
fn queue_deferred(&mut self, world: crate::world::DeferredWorld) {
self.system.queue_deferred(world);
}
#[inline]
unsafe fn validate_param_unsafe(
&mut self,
world: UnsafeWorldCell,
) -> Result<(), SystemParamValidationError> {
// SAFETY: Delegate to other `System` implementations.
unsafe { self.system.validate_param_unsafe(world) }
}
fn initialize(&mut self, world: &mut crate::prelude::World) {
self.system.initialize(world);
}
#[inline]
fn update_archetype_component_access(&mut self, world: UnsafeWorldCell) {
self.system.update_archetype_component_access(world);
}
fn check_change_tick(&mut self, change_tick: crate::component::Tick) {
self.system.check_change_tick(change_tick);
}
fn default_system_sets(&self) -> Vec<InternedSystemSet> {
self.system.default_system_sets()
}
fn get_last_run(&self) -> crate::component::Tick {
self.system.get_last_run()
}
fn set_last_run(&mut self, last_run: crate::component::Tick) {
self.system.set_last_run(last_run);
}
}
// SAFETY: The inner system is read-only.
unsafe impl<Func, S> ReadOnlySystem for AdapterSystem<Func, S>
where
Func: Adapt<S>,
S: ReadOnlySystem,
{
}
impl<F, S, Out> Adapt<S> for F
where
F: Send + Sync + 'static + FnMut(S::Out) -> Out,
S: System,
{
type In = S::In;
type 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))
}
}

1106
vendor/bevy_ecs/src/system/builder.rs vendored Normal file

File diff suppressed because it is too large Load Diff

512
vendor/bevy_ecs/src/system/combinator.rs vendored Normal file
View File

@@ -0,0 +1,512 @@
use alloc::{borrow::Cow, format, vec::Vec};
use core::marker::PhantomData;
use crate::{
archetype::ArchetypeComponentId,
component::{ComponentId, Tick},
prelude::World,
query::Access,
schedule::InternedSystemSet,
system::{input::SystemInput, SystemIn, SystemParamValidationError},
world::unsafe_world_cell::UnsafeWorldCell,
};
use super::{IntoSystem, ReadOnlySystem, System};
/// 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>();
/// #
/// # let mut app = Schedule::default();
/// app.add_systems(my_system.run_if(Xor::new(
/// 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;
/// ```
#[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"
)]
pub trait Combine<A: System, B: System> {
/// The [input](System::In) type for a [`CombinatorSystem`].
type In: SystemInput;
/// 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(
input: <Self::In as SystemInput>::Inner<'_>,
a: impl FnOnce(SystemIn<'_, A>) -> A::Out,
b: impl FnOnce(SystemIn<'_, B>) -> B::Out,
) -> 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> {
/// Creates a new system that combines two inner systems.
///
/// The returned system will only be usable if `Func` implements [`Combine<A, B>`].
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()
}
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 {
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`.
// If either system has `is_exclusive()`, then the combined system also has `is_exclusive`.
// Since these closures are `!Send + !Sync + !'static`, they can never be called
// in parallel, so their world accesses will not conflict with each other.
// Additionally, `update_archetype_component_access` has been called,
// which forwards to the implementations for `self.a` and `self.b`.
|input| unsafe { self.a.run_unsafe(input, world) },
// SAFETY: See the comment above.
|input| unsafe { self.b.run_unsafe(input, world) },
)
}
#[inline]
fn apply_deferred(&mut self, world: &mut World) {
self.a.apply_deferred(world);
self.b.apply_deferred(world);
}
#[inline]
fn queue_deferred(&mut self, mut world: crate::world::DeferredWorld) {
self.a.queue_deferred(world.reborrow());
self.b.queue_deferred(world);
}
#[inline]
unsafe fn validate_param_unsafe(
&mut self,
world: UnsafeWorldCell,
) -> Result<(), SystemParamValidationError> {
// SAFETY: Delegate to other `System` implementations.
unsafe { self.a.validate_param_unsafe(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 combining them will only read from the world.
unsafe impl<Func, A, B> ReadOnlySystem for CombinatorSystem<Func, A, B>
where
Func: Combine<A, B> + 'static,
A: ReadOnlySystem,
B: ReadOnlySystem,
{
}
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())
}
}
/// 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))
}
}
/// 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
/// let mut piped_system = IntoSystem::into_system(parse_message_system.pipe(filter_system));
/// 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)
/// }
/// ```
pub struct PipeSystem<A, B> {
a: A,
b: B,
name: Cow<'static, str>,
component_access: Access<ComponentId>,
archetype_component_access: Access<ArchetypeComponentId>,
}
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(),
}
}
}
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 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 = self.a.run_unsafe(input, world);
self.b.run_unsafe(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);
}
/// This method uses "early out" logic: if the first system fails validation,
/// the second system is not validated.
///
/// Because the system validation is performed upfront, this can lead to situations
/// where later systems pass validation, but fail at runtime due to changes made earlier
/// in the piped systems.
// TODO: ensure that systems are only validated just before they are run.
// Fixing this will require fundamentally rethinking how piped systems work:
// they're currently treated as a single system from the perspective of the scheduler.
// See https://github.com/bevyengine/bevy/issues/18796
unsafe fn validate_param_unsafe(
&mut self,
world: UnsafeWorldCell,
) -> Result<(), SystemParamValidationError> {
// SAFETY: Delegate to the `System` implementation for `a`.
unsafe { self.a.validate_param_unsafe(world) }?;
// SAFETY: Delegate to the `System` implementation for `b`.
unsafe { self.b.validate_param_unsafe(world) }?;
Ok(())
}
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>,
{
}
#[cfg(test)]
mod tests {
#[test]
fn exclusive_system_piping_is_possible() {
use crate::prelude::*;
fn my_exclusive_system(_world: &mut World) -> u32 {
1
}
fn out_pipe(input: In<u32>) {
assert!(input.0 == 1);
}
let mut world = World::new();
let mut schedule = Schedule::default();
schedule.add_systems(my_exclusive_system.pipe(out_pipe));
schedule.run(&mut world);
}
}

View File

@@ -0,0 +1,239 @@
//! Contains the definition of the [`Command`] trait,
//! as well as the blanket implementation of the trait for closures.
//!
//! It also contains functions that return closures for use with
//! [`Commands`](crate::system::Commands).
use crate::{
bundle::{Bundle, InsertMode, NoBundleEffect},
change_detection::MaybeLocation,
entity::Entity,
error::Result,
event::{Event, Events},
observer::TriggerTargets,
resource::Resource,
schedule::ScheduleLabel,
system::{IntoSystem, SystemId, SystemInput},
world::{FromWorld, SpawnBatchIter, World},
};
/// A [`World`] mutation.
///
/// Should be used with [`Commands::queue`](crate::system::Commands::queue).
///
/// The `Out` generic parameter is the returned "output" of the command.
///
/// # Usage
///
/// ```
/// # use bevy_ecs::prelude::*;
/// // Our world resource
/// #[derive(Resource, Default)]
/// struct Counter(u64);
///
/// // Our custom command
/// struct AddToCounter(u64);
///
/// impl Command for AddToCounter {
/// fn apply(self, world: &mut World) {
/// let mut counter = world.get_resource_or_insert_with(Counter::default);
/// counter.0 += self.0;
/// }
/// }
///
/// fn some_system(mut commands: Commands) {
/// commands.queue(AddToCounter(42));
/// }
/// ```
pub trait Command<Out = ()>: Send + 'static {
/// Applies this command, causing it to mutate the provided `world`.
///
/// This method is used to define what a command "does" when it is ultimately applied.
/// Because this method takes `self`, you can store data or settings on the type that implements this trait.
/// This data is set by the system or other source of the command, and then ultimately read in this method.
fn apply(self, world: &mut World) -> Out;
}
impl<F, Out> Command<Out> for F
where
F: FnOnce(&mut World) -> Out + Send + 'static,
{
fn apply(self, world: &mut World) -> Out {
self(world)
}
}
/// A [`Command`] that consumes an iterator of [`Bundles`](Bundle) to spawn a series of entities.
///
/// This is more efficient than spawning the entities individually.
#[track_caller]
pub fn spawn_batch<I>(bundles_iter: I) -> impl Command
where
I: IntoIterator + Send + Sync + 'static,
I::Item: Bundle<Effect: NoBundleEffect>,
{
let caller = MaybeLocation::caller();
move |world: &mut World| {
SpawnBatchIter::new(world, bundles_iter.into_iter(), caller);
}
}
/// A [`Command`] that consumes an iterator to add a series of [`Bundles`](Bundle) to a set of entities.
///
/// If any entities do not exist in the world, this command will return a
/// [`TryInsertBatchError`](crate::world::error::TryInsertBatchError).
///
/// This is more efficient than inserting the bundles individually.
#[track_caller]
pub fn insert_batch<I, B>(batch: I, insert_mode: InsertMode) -> impl Command<Result>
where
I: IntoIterator<Item = (Entity, B)> + Send + Sync + 'static,
B: Bundle<Effect: NoBundleEffect>,
{
let caller = MaybeLocation::caller();
move |world: &mut World| -> Result {
world.try_insert_batch_with_caller(batch, insert_mode, caller)?;
Ok(())
}
}
/// A [`Command`] that inserts a [`Resource`] into the world using a value
/// created with the [`FromWorld`] trait.
#[track_caller]
pub fn init_resource<R: Resource + FromWorld>() -> impl Command {
move |world: &mut World| {
world.init_resource::<R>();
}
}
/// A [`Command`] that inserts a [`Resource`] into the world.
#[track_caller]
pub fn insert_resource<R: Resource>(resource: R) -> impl Command {
let caller = MaybeLocation::caller();
move |world: &mut World| {
world.insert_resource_with_caller(resource, caller);
}
}
/// A [`Command`] that removes a [`Resource`] from the world.
pub fn remove_resource<R: Resource>() -> impl Command {
move |world: &mut World| {
world.remove_resource::<R>();
}
}
/// A [`Command`] that runs the system corresponding to the given [`SystemId`].
pub fn run_system<O: 'static>(id: SystemId<(), O>) -> impl Command<Result> {
move |world: &mut World| -> Result {
world.run_system(id)?;
Ok(())
}
}
/// A [`Command`] that runs the system corresponding to the given [`SystemId`]
/// and provides the given input value.
pub fn run_system_with<I>(id: SystemId<I>, input: I::Inner<'static>) -> impl Command<Result>
where
I: SystemInput<Inner<'static>: Send> + 'static,
{
move |world: &mut World| -> Result {
world.run_system_with(id, input)?;
Ok(())
}
}
/// A [`Command`] that runs the given system,
/// caching its [`SystemId`] in a [`CachedSystemId`](crate::system::CachedSystemId) resource.
pub fn run_system_cached<M, S>(system: S) -> impl Command<Result>
where
M: 'static,
S: IntoSystem<(), (), M> + Send + 'static,
{
move |world: &mut World| -> Result {
world.run_system_cached(system)?;
Ok(())
}
}
/// A [`Command`] that runs the given system with the given input value,
/// caching its [`SystemId`] in a [`CachedSystemId`](crate::system::CachedSystemId) resource.
pub fn run_system_cached_with<I, M, S>(system: S, input: I::Inner<'static>) -> impl Command<Result>
where
I: SystemInput<Inner<'static>: Send> + Send + 'static,
M: 'static,
S: IntoSystem<I, (), M> + Send + 'static,
{
move |world: &mut World| -> Result {
world.run_system_cached_with(system, input)?;
Ok(())
}
}
/// A [`Command`] that removes a system previously registered with
/// [`Commands::register_system`](crate::system::Commands::register_system) or
/// [`World::register_system`].
pub fn unregister_system<I, O>(system_id: SystemId<I, O>) -> impl Command<Result>
where
I: SystemInput + Send + 'static,
O: Send + 'static,
{
move |world: &mut World| -> Result {
world.unregister_system(system_id)?;
Ok(())
}
}
/// A [`Command`] that removes a system previously registered with one of the following:
/// - [`Commands::run_system_cached`](crate::system::Commands::run_system_cached)
/// - [`World::run_system_cached`]
/// - [`World::register_system_cached`]
pub fn unregister_system_cached<I, O, M, S>(system: S) -> impl Command<Result>
where
I: SystemInput + Send + 'static,
O: 'static,
M: 'static,
S: IntoSystem<I, O, M> + Send + 'static,
{
move |world: &mut World| -> Result {
world.unregister_system_cached(system)?;
Ok(())
}
}
/// A [`Command`] that runs the schedule corresponding to the given [`ScheduleLabel`].
pub fn run_schedule(label: impl ScheduleLabel) -> impl Command<Result> {
move |world: &mut World| -> Result {
world.try_run_schedule(label)?;
Ok(())
}
}
/// A [`Command`] that sends a global [`Trigger`](crate::observer::Trigger) without any targets.
#[track_caller]
pub fn trigger(event: impl Event) -> impl Command {
let caller = MaybeLocation::caller();
move |world: &mut World| {
world.trigger_with_caller(event, caller);
}
}
/// A [`Command`] that sends a [`Trigger`](crate::observer::Trigger) for the given targets.
pub fn trigger_targets(
event: impl Event,
targets: impl TriggerTargets + Send + Sync + 'static,
) -> impl Command {
let caller = MaybeLocation::caller();
move |world: &mut World| {
world.trigger_targets_with_caller(event, targets, caller);
}
}
/// A [`Command`] that sends an arbitrary [`Event`].
#[track_caller]
pub fn send_event<E: Event>(event: E) -> impl Command {
let caller = MaybeLocation::caller();
move |world: &mut World| {
let mut events = world.resource_mut::<Events<E>>();
events.send_with_caller(event, caller);
}
}

View File

@@ -0,0 +1,282 @@
//! Contains the definition of the [`EntityCommand`] trait,
//! as well as the blanket implementation of the trait for closures.
//!
//! It also contains functions that return closures for use with
//! [`EntityCommands`](crate::system::EntityCommands).
use alloc::vec::Vec;
use log::info;
use crate::{
bundle::{Bundle, InsertMode},
change_detection::MaybeLocation,
component::{Component, ComponentId, ComponentInfo},
entity::{Entity, EntityClonerBuilder},
event::Event,
relationship::RelationshipHookMode,
system::IntoObserverSystem,
world::{error::EntityMutableFetchError, EntityWorldMut, FromWorld},
};
use bevy_ptr::OwningPtr;
/// A command which gets executed for a given [`Entity`].
///
/// Should be used with [`EntityCommands::queue`](crate::system::EntityCommands::queue).
///
/// The `Out` generic parameter is the returned "output" of the command.
///
/// # Examples
///
/// ```
/// # use std::collections::HashSet;
/// # use bevy_ecs::prelude::*;
/// use bevy_ecs::system::EntityCommand;
/// #
/// # #[derive(Component, PartialEq)]
/// # struct Name(String);
/// # impl Name {
/// # fn new(s: String) -> Self { Name(s) }
/// # fn as_str(&self) -> &str { &self.0 }
/// # }
///
/// #[derive(Resource, Default)]
/// struct Counter(i64);
///
/// /// A `Command` which names an entity based on a global counter.
/// fn count_name(mut entity: EntityWorldMut) {
/// // Get the current value of the counter, and increment it for next time.
/// let i = {
/// let mut counter = entity.resource_mut::<Counter>();
/// let i = counter.0;
/// counter.0 += 1;
/// i
/// };
/// // Name the entity after the value of the counter.
/// entity.insert(Name::new(format!("Entity #{i}")));
/// }
///
/// // App creation boilerplate omitted...
/// # let mut world = World::new();
/// # world.init_resource::<Counter>();
/// #
/// # let mut setup_schedule = Schedule::default();
/// # setup_schedule.add_systems(setup);
/// # let mut assert_schedule = Schedule::default();
/// # assert_schedule.add_systems(assert_names);
/// #
/// # setup_schedule.run(&mut world);
/// # assert_schedule.run(&mut world);
///
/// fn setup(mut commands: Commands) {
/// commands.spawn_empty().queue(count_name);
/// commands.spawn_empty().queue(count_name);
/// }
///
/// fn assert_names(named: Query<&Name>) {
/// // We use a HashSet because we do not care about the order.
/// let names: HashSet<_> = named.iter().map(Name::as_str).collect();
/// assert_eq!(names, HashSet::from_iter(["Entity #0", "Entity #1"]));
/// }
/// ```
pub trait EntityCommand<Out = ()>: Send + 'static {
/// Executes this command for the given [`Entity`].
fn apply(self, entity: EntityWorldMut) -> Out;
}
/// An error that occurs when running an [`EntityCommand`] on a specific entity.
#[derive(thiserror::Error, Debug)]
pub enum EntityCommandError<E> {
/// The entity this [`EntityCommand`] tried to run on could not be fetched.
#[error(transparent)]
EntityFetchError(#[from] EntityMutableFetchError),
/// An error that occurred while running the [`EntityCommand`].
#[error("{0}")]
CommandFailed(E),
}
impl<Out, F> EntityCommand<Out> for F
where
F: FnOnce(EntityWorldMut) -> Out + Send + 'static,
{
fn apply(self, entity: EntityWorldMut) -> Out {
self(entity)
}
}
/// An [`EntityCommand`] that adds the components in a [`Bundle`] to an entity.
#[track_caller]
pub fn insert(bundle: impl Bundle, mode: InsertMode) -> impl EntityCommand {
let caller = MaybeLocation::caller();
move |mut entity: EntityWorldMut| {
entity.insert_with_caller(bundle, mode, caller, RelationshipHookMode::Run);
}
}
/// An [`EntityCommand`] that adds a dynamic component to an entity.
///
/// # Safety
///
/// - [`ComponentId`] must be from the same world as the target entity.
/// - `T` must have the same layout as the one passed during `component_id` creation.
#[track_caller]
pub unsafe fn insert_by_id<T: Send + 'static>(
component_id: ComponentId,
value: T,
mode: InsertMode,
) -> impl EntityCommand {
let caller = MaybeLocation::caller();
move |mut entity: EntityWorldMut| {
// SAFETY:
// - `component_id` safety is ensured by the caller
// - `ptr` is valid within the `make` block
OwningPtr::make(value, |ptr| unsafe {
entity.insert_by_id_with_caller(
component_id,
ptr,
mode,
caller,
RelationshipHookMode::Run,
);
});
}
}
/// An [`EntityCommand`] that adds a component to an entity using
/// the component's [`FromWorld`] implementation.
#[track_caller]
pub fn insert_from_world<T: Component + FromWorld>(mode: InsertMode) -> impl EntityCommand {
let caller = MaybeLocation::caller();
move |mut entity: EntityWorldMut| {
let value = entity.world_scope(|world| T::from_world(world));
entity.insert_with_caller(value, mode, caller, RelationshipHookMode::Run);
}
}
/// An [`EntityCommand`] that removes the components in a [`Bundle`] from an entity.
#[track_caller]
pub fn remove<T: Bundle>() -> impl EntityCommand {
let caller = MaybeLocation::caller();
move |mut entity: EntityWorldMut| {
entity.remove_with_caller::<T>(caller);
}
}
/// An [`EntityCommand`] that removes the components in a [`Bundle`] from an entity,
/// as well as the required components for each component removed.
#[track_caller]
pub fn remove_with_requires<T: Bundle>() -> impl EntityCommand {
let caller = MaybeLocation::caller();
move |mut entity: EntityWorldMut| {
entity.remove_with_requires_with_caller::<T>(caller);
}
}
/// An [`EntityCommand`] that removes a dynamic component from an entity.
#[track_caller]
pub fn remove_by_id(component_id: ComponentId) -> impl EntityCommand {
let caller = MaybeLocation::caller();
move |mut entity: EntityWorldMut| {
entity.remove_by_id_with_caller(component_id, caller);
}
}
/// An [`EntityCommand`] that removes all components from an entity.
#[track_caller]
pub fn clear() -> impl EntityCommand {
let caller = MaybeLocation::caller();
move |mut entity: EntityWorldMut| {
entity.clear_with_caller(caller);
}
}
/// An [`EntityCommand`] that removes all components from an entity,
/// except for those in the given [`Bundle`].
#[track_caller]
pub fn retain<T: Bundle>() -> impl EntityCommand {
let caller = MaybeLocation::caller();
move |mut entity: EntityWorldMut| {
entity.retain_with_caller::<T>(caller);
}
}
/// An [`EntityCommand`] that despawns an entity.
///
/// # Note
///
/// This will also despawn the entities in any [`RelationshipTarget`](crate::relationship::RelationshipTarget)
/// that is configured to despawn descendants.
///
/// For example, this will recursively despawn [`Children`](crate::hierarchy::Children).
#[track_caller]
pub fn despawn() -> impl EntityCommand {
let caller = MaybeLocation::caller();
move |entity: EntityWorldMut| {
entity.despawn_with_caller(caller);
}
}
/// An [`EntityCommand`] that creates an [`Observer`](crate::observer::Observer)
/// listening for events of type `E` targeting an entity
#[track_caller]
pub fn observe<E: Event, B: Bundle, M>(
observer: impl IntoObserverSystem<E, B, M>,
) -> impl EntityCommand {
let caller = MaybeLocation::caller();
move |mut entity: EntityWorldMut| {
entity.observe_with_caller(observer, caller);
}
}
/// An [`EntityCommand`] that sends a [`Trigger`](crate::observer::Trigger) targeting an entity.
///
/// This will run any [`Observer`](crate::observer::Observer) of the given [`Event`] watching the entity.
#[track_caller]
pub fn trigger(event: impl Event) -> impl EntityCommand {
let caller = MaybeLocation::caller();
move |mut entity: EntityWorldMut| {
let id = entity.id();
entity.world_scope(|world| {
world.trigger_targets_with_caller(event, id, caller);
});
}
}
/// An [`EntityCommand`] that clones parts of an entity onto another entity,
/// configured through [`EntityClonerBuilder`].
pub fn clone_with(
target: Entity,
config: impl FnOnce(&mut EntityClonerBuilder) + Send + Sync + 'static,
) -> impl EntityCommand {
move |mut entity: EntityWorldMut| {
entity.clone_with(target, config);
}
}
/// An [`EntityCommand`] that clones the specified components of an entity
/// and inserts them into another entity.
pub fn clone_components<B: Bundle>(target: Entity) -> impl EntityCommand {
move |mut entity: EntityWorldMut| {
entity.clone_components::<B>(target);
}
}
/// An [`EntityCommand`] that clones the specified components of an entity
/// and inserts them into another entity, then removes them from the original entity.
pub fn move_components<B: Bundle>(target: Entity) -> impl EntityCommand {
move |mut entity: EntityWorldMut| {
entity.move_components::<B>(target);
}
}
/// An [`EntityCommand`] that logs the components of an entity.
pub fn log_components() -> impl EntityCommand {
move |entity: EntityWorldMut| {
let debug_infos: Vec<_> = entity
.world()
.inspect_entity(entity.id())
.expect("Entity existence is verified before an EntityCommand is executed")
.map(ComponentInfo::name)
.collect();
info!("Entity {}: {debug_infos:?}", entity.id());
}
}

2613
vendor/bevy_ecs/src/system/commands/mod.rs vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,78 @@
use bevy_utils::Parallel;
use crate::{
entity::Entities,
prelude::World,
system::{Deferred, SystemBuffer, SystemMeta, SystemParam},
};
use super::{CommandQueue, Commands};
#[derive(Default)]
struct ParallelCommandQueue {
thread_queues: Parallel<CommandQueue>,
}
/// An alternative to [`Commands`] that can be used in parallel contexts, such as those
/// in [`Query::par_iter`](crate::system::Query::par_iter).
///
/// For cases where multiple non-computation-heavy (lightweight) bundles of the same
/// [`Bundle`](crate::prelude::Bundle) type need to be spawned, consider using
/// [`Commands::spawn_batch`] for better performance.
///
/// # Note
///
/// Because command application order will depend on how many threads are ran,
/// non-commutative commands may result in non-deterministic results.
///
/// # Example
///
/// ```
/// # use bevy_ecs::prelude::*;
/// # use bevy_tasks::ComputeTaskPool;
/// #
/// # #[derive(Component)]
/// # struct Velocity;
/// # impl Velocity { fn magnitude(&self) -> f32 { 42.0 } }
/// fn parallel_command_system(
/// mut query: Query<(Entity, &Velocity)>,
/// par_commands: ParallelCommands
/// ) {
/// query.par_iter().for_each(|(entity, velocity)| {
/// if velocity.magnitude() > 10.0 {
/// par_commands.command_scope(|mut commands| {
/// commands.entity(entity).despawn();
/// });
/// }
/// });
/// }
/// # bevy_ecs::system::assert_is_system(parallel_command_system);
/// ```
#[derive(SystemParam)]
pub struct ParallelCommands<'w, 's> {
state: Deferred<'s, ParallelCommandQueue>,
entities: &'w Entities,
}
impl SystemBuffer for ParallelCommandQueue {
#[inline]
fn apply(&mut self, _system_meta: &SystemMeta, world: &mut World) {
#[cfg(feature = "trace")]
let _system_span = _system_meta.commands_span.enter();
for cq in self.thread_queues.iter_mut() {
cq.apply(world);
}
}
}
impl<'w, 's> ParallelCommands<'w, 's> {
/// Temporarily provides access to the [`Commands`] for the current thread.
///
/// For an example, see the type-level documentation for [`ParallelCommands`].
pub fn command_scope<R>(&self, f: impl FnOnce(Commands) -> R) -> R {
self.state.thread_queues.scope(|queue| {
let commands = Commands::new_from_entities(queue, self.entities);
f(commands)
})
}
}

View File

@@ -0,0 +1,346 @@
use crate::{
archetype::ArchetypeComponentId,
component::{ComponentId, Tick},
query::Access,
schedule::{InternedSystemSet, SystemSet},
system::{
check_system_change_tick, ExclusiveSystemParam, ExclusiveSystemParamItem, IntoSystem,
System, SystemIn, SystemInput, SystemMeta,
},
world::{unsafe_world_cell::UnsafeWorldCell, World},
};
use alloc::{borrow::Cow, vec, vec::Vec};
use core::marker::PhantomData;
use variadics_please::all_tuples;
use super::SystemParamValidationError;
/// A function system that runs with exclusive [`World`] access.
///
/// You get this by calling [`IntoSystem::into_system`] on a function that only accepts
/// [`ExclusiveSystemParam`]s.
///
/// [`ExclusiveFunctionSystem`] must be `.initialized` before they can be run.
pub struct ExclusiveFunctionSystem<Marker, F>
where
F: ExclusiveSystemParamFunction<Marker>,
{
func: F,
param_state: Option<<F::Param as ExclusiveSystemParam>::State>,
system_meta: SystemMeta,
// NOTE: PhantomData<fn()-> T> gives this safe Send/Sync impls
marker: PhantomData<fn() -> Marker>,
}
impl<Marker, F> ExclusiveFunctionSystem<Marker, F>
where
F: ExclusiveSystemParamFunction<Marker>,
{
/// Return this system with a new name.
///
/// Useful to give closure systems more readable and unique names for debugging and tracing.
pub fn with_name(mut self, new_name: impl Into<Cow<'static, str>>) -> Self {
self.system_meta.set_name(new_name.into());
self
}
}
/// A marker type used to distinguish exclusive function systems from regular function systems.
#[doc(hidden)]
pub struct IsExclusiveFunctionSystem;
impl<Marker, F> IntoSystem<F::In, F::Out, (IsExclusiveFunctionSystem, Marker)> for F
where
Marker: 'static,
F: ExclusiveSystemParamFunction<Marker>,
{
type System = ExclusiveFunctionSystem<Marker, F>;
fn into_system(func: Self) -> Self::System {
ExclusiveFunctionSystem {
func,
param_state: None,
system_meta: SystemMeta::new::<F>(),
marker: PhantomData,
}
}
}
const PARAM_MESSAGE: &str = "System's param_state was not found. Did you forget to initialize this system before running it?";
impl<Marker, F> System for ExclusiveFunctionSystem<Marker, F>
where
Marker: 'static,
F: ExclusiveSystemParamFunction<Marker>,
{
type In = F::In;
type Out = F::Out;
#[inline]
fn name(&self) -> Cow<'static, str> {
self.system_meta.name.clone()
}
#[inline]
fn component_access(&self) -> &Access<ComponentId> {
self.system_meta.component_access_set.combined_access()
}
#[inline]
fn archetype_component_access(&self) -> &Access<ArchetypeComponentId> {
&self.system_meta.archetype_component_access
}
#[inline]
fn is_send(&self) -> bool {
// exclusive systems should have access to non-send resources
// the executor runs exclusive systems on the main thread, so this
// field reflects that constraint
false
}
#[inline]
fn is_exclusive(&self) -> bool {
true
}
#[inline]
fn has_deferred(&self) -> bool {
// exclusive systems have no deferred system params
false
}
#[inline]
unsafe fn run_unsafe(
&mut self,
input: SystemIn<'_, Self>,
world: UnsafeWorldCell,
) -> Self::Out {
// SAFETY: The safety is upheld by the caller.
let world = unsafe { world.world_mut() };
world.last_change_tick_scope(self.system_meta.last_run, |world| {
#[cfg(feature = "trace")]
let _span_guard = self.system_meta.system_span.enter();
let params = F::Param::get_param(
self.param_state.as_mut().expect(PARAM_MESSAGE),
&self.system_meta,
);
let out = self.func.run(world, input, params);
world.flush();
self.system_meta.last_run = world.increment_change_tick();
out
})
}
#[inline]
fn apply_deferred(&mut self, _world: &mut World) {
// "pure" exclusive systems do not have any buffers to apply.
// Systems made by piping a normal system with an exclusive system
// might have buffers to apply, but this is handled by `PipeSystem`.
}
#[inline]
fn queue_deferred(&mut self, _world: crate::world::DeferredWorld) {
// "pure" exclusive systems do not have any buffers to apply.
// Systems made by piping a normal system with an exclusive system
// might have buffers to apply, but this is handled by `PipeSystem`.
}
#[inline]
unsafe fn validate_param_unsafe(
&mut self,
_world: UnsafeWorldCell,
) -> Result<(), SystemParamValidationError> {
// All exclusive system params are always available.
Ok(())
}
#[inline]
fn initialize(&mut self, world: &mut World) {
self.system_meta.last_run = world.change_tick().relative_to(Tick::MAX);
self.param_state = Some(F::Param::init(world, &mut self.system_meta));
}
fn update_archetype_component_access(&mut self, _world: UnsafeWorldCell) {}
#[inline]
fn check_change_tick(&mut self, change_tick: Tick) {
check_system_change_tick(
&mut self.system_meta.last_run,
change_tick,
self.system_meta.name.as_ref(),
);
}
fn default_system_sets(&self) -> Vec<InternedSystemSet> {
let set = crate::schedule::SystemTypeSet::<Self>::new();
vec![set.intern()]
}
fn get_last_run(&self) -> Tick {
self.system_meta.last_run
}
fn set_last_run(&mut self, last_run: Tick) {
self.system_meta.last_run = last_run;
}
}
/// A trait implemented for all exclusive system functions that can be used as [`System`]s.
///
/// This trait can be useful for making your own systems which accept other systems,
/// sometimes called higher order systems.
#[diagnostic::on_unimplemented(
message = "`{Self}` is not an exclusive system",
label = "invalid system"
)]
pub trait ExclusiveSystemParamFunction<Marker>: Send + Sync + 'static {
/// The input type to this system. See [`System::In`].
type In: SystemInput;
/// The return type of this system. See [`System::Out`].
type Out;
/// The [`ExclusiveSystemParam`]'s defined by this system's `fn` parameters.
type Param: ExclusiveSystemParam;
/// Executes this system once. See [`System::run`].
fn run(
&mut self,
world: &mut World,
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),*) => {
#[expect(
clippy::allow_attributes,
reason = "This is within a macro, and as such, the below lints may not always apply."
)]
#[allow(
non_snake_case,
reason = "Certain variable names are provided by the caller, not by us."
)]
impl<Out, Func, $($param: ExclusiveSystemParam),*> ExclusiveSystemParamFunction<fn($($param,)*) -> Out> for Func
where
Func: Send + Sync + 'static,
for <'a> &'a mut Func:
FnMut(&mut World, $($param),*) -> Out +
FnMut(&mut World, $(ExclusiveSystemParamItem<$param>),*) -> Out,
Out: 'static,
{
type In = ();
type Out = Out;
type Param = ($($param,)*);
#[inline]
fn run(&mut self, world: &mut World, _in: (), 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`
fn call_inner<Out, $($param,)*>(
mut f: impl FnMut(&mut World, $($param,)*) -> Out,
world: &mut World,
$($param: $param,)*
) -> Out {
f(world, $($param,)*)
}
let ($($param,)*) = param_value;
call_inner(self, world, $($param),*)
}
}
#[expect(
clippy::allow_attributes,
reason = "This is within a macro, and as such, the below lints may not always apply."
)]
#[allow(
non_snake_case,
reason = "Certain variable names are provided by the caller, not by us."
)]
impl<In, Out, Func, $($param: ExclusiveSystemParam),*> ExclusiveSystemParamFunction<(HasExclusiveSystemInput, fn(In, $($param,)*) -> Out)> for Func
where
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 = In;
type Out = Out;
type Param = ($($param,)*);
#[inline]
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`
fn call_inner<In: SystemInput, Out, $($param,)*>(
_: PhantomData<In>,
mut f: impl FnMut(In::Param<'_>, &mut World, $($param,)*) -> Out,
input: In::Inner<'_>,
world: &mut World,
$($param: $param,)*
) -> Out {
f(In::wrap(input), world, $($param,)*)
}
let ($($param,)*) = param_value;
call_inner(PhantomData::<In>, self, input, world, $($param),*)
}
}
};
}
// Note that we rely on the highest impl to be <= the highest order of the tuple impls
// of `SystemParam` created.
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: SystemInput, Out, Marker>(function: T)
where
T: IntoSystem<In, Out, Marker> + Copy,
{
fn reference_system(_world: &mut World) {}
use core::any::TypeId;
let system = IntoSystem::into_system(function);
assert_eq!(
system.type_id(),
function.system_type_id(),
"System::type_id should be consistent with IntoSystem::system_type_id"
);
assert_eq!(
system.type_id(),
TypeId::of::<T::System>(),
"System::type_id should be consistent with TypeId::of::<T::System>()"
);
assert_ne!(
system.type_id(),
IntoSystem::into_system(reference_system).type_id(),
"Different systems should have different TypeIds"
);
}
fn exclusive_function_system(_world: &mut World) {}
test(exclusive_function_system);
}
}

View File

@@ -0,0 +1,168 @@
use crate::{
prelude::{FromWorld, QueryState},
query::{QueryData, QueryFilter},
system::{Local, SystemMeta, SystemParam, SystemState},
world::World,
};
use bevy_utils::synccell::SyncCell;
use core::marker::PhantomData;
use variadics_please::all_tuples;
/// A parameter that can be used in an exclusive system (a system with an `&mut World` parameter).
/// Any parameters implementing this trait must come after the `&mut World` parameter.
#[diagnostic::on_unimplemented(
message = "`{Self}` can not be used as a parameter for an exclusive system",
label = "invalid system parameter"
)]
pub trait ExclusiveSystemParam: Sized {
/// Used to store data which persists across invocations of a system.
type State: Send + Sync + 'static;
/// The item type returned when constructing this system param.
/// See [`SystemParam::Item`].
type Item<'s>: ExclusiveSystemParam<State = Self::State>;
/// Creates a new instance of this param's [`State`](Self::State).
fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self::State;
/// Creates a parameter to be passed into an [`ExclusiveSystemParamFunction`].
///
/// [`ExclusiveSystemParamFunction`]: super::ExclusiveSystemParamFunction
fn get_param<'s>(state: &'s mut Self::State, system_meta: &SystemMeta) -> Self::Item<'s>;
}
/// Shorthand way of accessing the associated type [`ExclusiveSystemParam::Item`]
/// for a given [`ExclusiveSystemParam`].
pub type ExclusiveSystemParamItem<'s, P> = <P as ExclusiveSystemParam>::Item<'s>;
impl<'a, D: QueryData + 'static, F: QueryFilter + 'static> ExclusiveSystemParam
for &'a mut QueryState<D, F>
{
type State = QueryState<D, F>;
type Item<'s> = &'s mut QueryState<D, F>;
fn init(world: &mut World, _system_meta: &mut SystemMeta) -> Self::State {
QueryState::new(world)
}
fn get_param<'s>(state: &'s mut Self::State, _system_meta: &SystemMeta) -> Self::Item<'s> {
state
}
}
impl<'a, P: SystemParam + 'static> ExclusiveSystemParam for &'a mut SystemState<P> {
type State = SystemState<P>;
type Item<'s> = &'s mut SystemState<P>;
fn init(world: &mut World, _system_meta: &mut SystemMeta) -> Self::State {
SystemState::new(world)
}
fn get_param<'s>(state: &'s mut Self::State, _system_meta: &SystemMeta) -> Self::Item<'s> {
state
}
}
impl<'_s, T: FromWorld + Send + 'static> ExclusiveSystemParam for Local<'_s, T> {
type State = SyncCell<T>;
type Item<'s> = Local<'s, T>;
fn init(world: &mut World, _system_meta: &mut SystemMeta) -> Self::State {
SyncCell::new(T::from_world(world))
}
fn get_param<'s>(state: &'s mut Self::State, _system_meta: &SystemMeta) -> Self::Item<'s> {
Local(state.get())
}
}
impl<S: ?Sized> ExclusiveSystemParam for PhantomData<S> {
type State = ();
type Item<'s> = PhantomData<S>;
fn init(_world: &mut World, _system_meta: &mut SystemMeta) -> Self::State {}
fn get_param<'s>(_state: &'s mut Self::State, _system_meta: &SystemMeta) -> Self::Item<'s> {
PhantomData
}
}
macro_rules! impl_exclusive_system_param_tuple {
($(#[$meta:meta])* $($param: ident),*) => {
#[expect(
clippy::allow_attributes,
reason = "This is within a macro, and as such, the below lints may not always apply."
)]
#[allow(
non_snake_case,
reason = "Certain variable names are provided by the caller, not by us."
)]
#[allow(
unused_variables,
reason = "Zero-length tuples won't use any of the parameters."
)]
$(#[$meta])*
impl<$($param: ExclusiveSystemParam),*> ExclusiveSystemParam for ($($param,)*) {
type State = ($($param::State,)*);
type Item<'s> = ($($param::Item<'s>,)*);
#[inline]
fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self::State {
(($($param::init(world, system_meta),)*))
}
#[inline]
fn get_param<'s>(
state: &'s mut Self::State,
system_meta: &SystemMeta,
) -> Self::Item<'s> {
let ($($param,)*) = state;
#[allow(
clippy::unused_unit,
reason = "Zero-length tuples won't have any params to get."
)]
($($param::get_param($param, system_meta),)*)
}
}
};
}
all_tuples!(
#[doc(fake_variadic)]
impl_exclusive_system_param_tuple,
0,
16,
P
);
#[cfg(test)]
mod tests {
use crate::{schedule::Schedule, system::Local, world::World};
use alloc::vec::Vec;
use bevy_ecs_macros::Resource;
use core::marker::PhantomData;
#[test]
fn test_exclusive_system_params() {
#[derive(Resource, Default)]
struct Res {
test_value: u32,
}
fn my_system(world: &mut World, mut local: Local<u32>, _phantom: PhantomData<Vec<u32>>) {
assert_eq!(world.resource::<Res>().test_value, *local);
*local += 1;
world.resource_mut::<Res>().test_value += 1;
}
let mut schedule = Schedule::default();
schedule.add_systems(my_system);
let mut world = World::default();
world.init_resource::<Res>();
schedule.run(&mut world);
schedule.run(&mut world);
assert_eq!(2, world.get_resource::<Res>().unwrap().test_value);
}
}

File diff suppressed because it is too large Load Diff

326
vendor/bevy_ecs/src/system/input.rs vendored Normal file
View File

@@ -0,0 +1,326 @@
use core::ops::{Deref, DerefMut};
use variadics_please::all_tuples;
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
/// - Tuples of [`SystemInput`]s up to 8 elements
///
/// For advanced usecases, you can implement this trait for your own types.
///
/// # Examples
///
/// ## Tuples of [`SystemInput`]s
///
/// ```
/// use bevy_ecs::prelude::*;
///
/// fn add((InMut(a), In(b)): (InMut<usize>, In<usize>)) {
/// *a += b;
/// }
/// # let mut world = World::new();
/// # let mut add = IntoSystem::into_system(add);
/// # add.initialize(&mut world);
/// # let mut a = 12;
/// # let b = 24;
/// # add.run((&mut a, b), &mut world);
/// # assert_eq!(a, 36);
/// ```
///
/// [`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>;
/// 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.
///
/// See [`SystemInput`] to learn more about system inputs in general.
///
/// # 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.
///
/// See [`SystemInput`] to learn more about system inputs in general.
///
/// # 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.
///
/// See [`SystemInput`] to learn more about system inputs in general.
///
/// # 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.
///
/// See [`SystemInput`] to learn more about system inputs in general.
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)
}
}
macro_rules! impl_system_input_tuple {
($(#[$meta:meta])* $($name:ident),*) => {
$(#[$meta])*
impl<$($name: SystemInput),*> SystemInput for ($($name,)*) {
type Param<'i> = ($($name::Param<'i>,)*);
type Inner<'i> = ($($name::Inner<'i>,)*);
#[expect(
clippy::allow_attributes,
reason = "This is in a macro; as such, the below lints may not always apply."
)]
#[allow(
non_snake_case,
reason = "Certain variable names are provided by the caller, not by us."
)]
#[allow(
clippy::unused_unit,
reason = "Zero-length tuples won't have anything to wrap."
)]
fn wrap(this: Self::Inner<'_>) -> Self::Param<'_> {
let ($($name,)*) = this;
($($name::wrap($name),)*)
}
}
};
}
all_tuples!(
#[doc(fake_variadic)]
impl_system_input_tuple,
0,
8,
I
);
#[cfg(test)]
mod tests {
use crate::{
system::{In, InMut, InRef, IntoSystem, System},
world::World,
};
#[test]
fn two_tuple() {
fn by_value((In(a), In(b)): (In<usize>, In<usize>)) -> usize {
a + b
}
fn by_ref((InRef(a), InRef(b)): (InRef<usize>, InRef<usize>)) -> usize {
*a + *b
}
fn by_mut((InMut(a), In(b)): (InMut<usize>, In<usize>)) {
*a += b;
}
let mut world = World::new();
let mut by_value = IntoSystem::into_system(by_value);
let mut by_ref = IntoSystem::into_system(by_ref);
let mut by_mut = IntoSystem::into_system(by_mut);
by_value.initialize(&mut world);
by_ref.initialize(&mut world);
by_mut.initialize(&mut world);
let mut a = 12;
let b = 24;
assert_eq!(by_value.run((a, b), &mut world), 36);
assert_eq!(by_ref.run((&a, &b), &mut world), 36);
by_mut.run((&mut a, b), &mut world);
assert_eq!(a, 36);
}
}

1882
vendor/bevy_ecs/src/system/mod.rs vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,234 @@
use alloc::{borrow::Cow, vec::Vec};
use core::marker::PhantomData;
use crate::{
archetype::ArchetypeComponentId,
component::{ComponentId, Tick},
error::Result,
never::Never,
prelude::{Bundle, Trigger},
query::Access,
schedule::{Fallible, Infallible},
system::{input::SystemIn, System},
world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, World},
};
use super::{IntoSystem, SystemParamValidationError};
/// Implemented for [`System`]s that have a [`Trigger`] as the first argument.
pub trait ObserverSystem<E: 'static, B: Bundle, Out = Result>:
System<In = Trigger<'static, E, B>, Out = Out> + Send + 'static
{
}
impl<E: 'static, B: Bundle, Out, T> ObserverSystem<E, B, Out> for T where
T: System<In = Trigger<'static, E, B>, Out = Out> + Send + 'static
{
}
/// Implemented for systems that convert into [`ObserverSystem`].
///
/// # Usage notes
///
/// This trait should only be used as a bound for trait implementations or as an
/// argument to a function. If an observer system needs to be returned from a
/// function or stored somewhere, use [`ObserverSystem`] instead of this trait.
#[diagnostic::on_unimplemented(
message = "`{Self}` cannot become an `ObserverSystem`",
label = "the trait `IntoObserverSystem` is not implemented",
note = "for function `ObserverSystem`s, ensure the first argument is a `Trigger<T>` and any subsequent ones are `SystemParam`"
)]
pub trait IntoObserverSystem<E: 'static, B: Bundle, M, Out = Result>: Send + 'static {
/// The type of [`System`] that this instance converts into.
type System: ObserverSystem<E, B, Out>;
/// Turns this value into its corresponding [`System`].
fn into_system(this: Self) -> Self::System;
}
impl<E, B, M, S, Out> IntoObserverSystem<E, B, (Fallible, M), Out> for S
where
S: IntoSystem<Trigger<'static, E, B>, Out, M> + Send + 'static,
S::System: ObserverSystem<E, B, Out>,
E: 'static,
B: Bundle,
{
type System = S::System;
fn into_system(this: Self) -> Self::System {
IntoSystem::into_system(this)
}
}
impl<E, B, M, S> IntoObserverSystem<E, B, (Infallible, M), Result> for S
where
S: IntoSystem<Trigger<'static, E, B>, (), M> + Send + 'static,
S::System: ObserverSystem<E, B, ()>,
E: Send + Sync + 'static,
B: Bundle,
{
type System = InfallibleObserverWrapper<E, B, S::System, ()>;
fn into_system(this: Self) -> Self::System {
InfallibleObserverWrapper::new(IntoSystem::into_system(this))
}
}
impl<E, B, M, S> IntoObserverSystem<E, B, (Never, M), Result> for S
where
S: IntoSystem<Trigger<'static, E, B>, Never, M> + Send + 'static,
E: Send + Sync + 'static,
B: Bundle,
{
type System = InfallibleObserverWrapper<E, B, S::System, Never>;
fn into_system(this: Self) -> Self::System {
InfallibleObserverWrapper::new(IntoSystem::into_system(this))
}
}
/// A wrapper that converts an observer system that returns `()` into one that returns `Ok(())`.
pub struct InfallibleObserverWrapper<E, B, S, Out> {
observer: S,
_marker: PhantomData<(E, B, Out)>,
}
impl<E, B, S, Out> InfallibleObserverWrapper<E, B, S, Out> {
/// Create a new `InfallibleObserverWrapper`.
pub fn new(observer: S) -> Self {
Self {
observer,
_marker: PhantomData,
}
}
}
impl<E, B, S, Out> System for InfallibleObserverWrapper<E, B, S, Out>
where
S: ObserverSystem<E, B, Out>,
E: Send + Sync + 'static,
B: Bundle,
Out: Send + Sync + 'static,
{
type In = Trigger<'static, E, B>;
type Out = Result;
#[inline]
fn name(&self) -> Cow<'static, str> {
self.observer.name()
}
#[inline]
fn component_access(&self) -> &Access<ComponentId> {
self.observer.component_access()
}
#[inline]
fn archetype_component_access(&self) -> &Access<ArchetypeComponentId> {
self.observer.archetype_component_access()
}
#[inline]
fn is_send(&self) -> bool {
self.observer.is_send()
}
#[inline]
fn is_exclusive(&self) -> bool {
self.observer.is_exclusive()
}
#[inline]
fn has_deferred(&self) -> bool {
self.observer.has_deferred()
}
#[inline]
unsafe fn run_unsafe(
&mut self,
input: SystemIn<'_, Self>,
world: UnsafeWorldCell,
) -> Self::Out {
self.observer.run_unsafe(input, world);
Ok(())
}
#[inline]
fn apply_deferred(&mut self, world: &mut World) {
self.observer.apply_deferred(world);
}
#[inline]
fn queue_deferred(&mut self, world: DeferredWorld) {
self.observer.queue_deferred(world);
}
#[inline]
unsafe fn validate_param_unsafe(
&mut self,
world: UnsafeWorldCell,
) -> Result<(), SystemParamValidationError> {
self.observer.validate_param_unsafe(world)
}
#[inline]
fn initialize(&mut self, world: &mut World) {
self.observer.initialize(world);
}
#[inline]
fn update_archetype_component_access(&mut self, world: UnsafeWorldCell) {
self.observer.update_archetype_component_access(world);
}
#[inline]
fn check_change_tick(&mut self, change_tick: Tick) {
self.observer.check_change_tick(change_tick);
}
#[inline]
fn get_last_run(&self) -> Tick {
self.observer.get_last_run()
}
#[inline]
fn set_last_run(&mut self, last_run: Tick) {
self.observer.set_last_run(last_run);
}
fn default_system_sets(&self) -> Vec<crate::schedule::InternedSystemSet> {
self.observer.default_system_sets()
}
}
#[cfg(test)]
mod tests {
use crate::{
event::Event,
observer::Trigger,
system::{In, IntoSystem},
world::World,
};
#[derive(Event)]
struct TriggerEvent;
#[test]
fn test_piped_observer_systems_no_input() {
fn a(_: Trigger<TriggerEvent>) {}
fn b() {}
let mut world = World::new();
world.add_observer(a.pipe(b));
}
#[test]
fn test_piped_observer_systems_with_inputs() {
fn a(_: Trigger<TriggerEvent>) -> u32 {
3
}
fn b(_: In<u32>) {}
let mut world = World::new();
world.add_observer(a.pipe(b));
}
}

2800
vendor/bevy_ecs/src/system/query.rs vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,121 @@
use alloc::{borrow::Cow, vec::Vec};
use crate::{
archetype::ArchetypeComponentId,
component::{ComponentId, Tick},
error::Result,
query::Access,
system::{input::SystemIn, BoxedSystem, System},
world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, World},
};
use super::{IntoSystem, SystemParamValidationError};
/// A wrapper system to change a system that returns `()` to return `Ok(())` to make it into a [`ScheduleSystem`]
pub struct InfallibleSystemWrapper<S: System<In = ()>>(S);
impl<S: System<In = ()>> InfallibleSystemWrapper<S> {
/// Create a new `OkWrapperSystem`
pub fn new(system: S) -> Self {
Self(IntoSystem::into_system(system))
}
}
impl<S: System<In = ()>> System for InfallibleSystemWrapper<S> {
type In = ();
type Out = Result;
#[inline]
fn name(&self) -> Cow<'static, str> {
self.0.name()
}
fn type_id(&self) -> core::any::TypeId {
self.0.type_id()
}
#[inline]
fn component_access(&self) -> &Access<ComponentId> {
self.0.component_access()
}
#[inline]
fn archetype_component_access(&self) -> &Access<ArchetypeComponentId> {
self.0.archetype_component_access()
}
#[inline]
fn is_send(&self) -> bool {
self.0.is_send()
}
#[inline]
fn is_exclusive(&self) -> bool {
self.0.is_exclusive()
}
#[inline]
fn has_deferred(&self) -> bool {
self.0.has_deferred()
}
#[inline]
unsafe fn run_unsafe(
&mut self,
input: SystemIn<'_, Self>,
world: UnsafeWorldCell,
) -> Self::Out {
self.0.run_unsafe(input, world);
Ok(())
}
#[inline]
fn apply_deferred(&mut self, world: &mut World) {
self.0.apply_deferred(world);
}
#[inline]
fn queue_deferred(&mut self, world: DeferredWorld) {
self.0.queue_deferred(world);
}
#[inline]
unsafe fn validate_param_unsafe(
&mut self,
world: UnsafeWorldCell,
) -> Result<(), SystemParamValidationError> {
self.0.validate_param_unsafe(world)
}
#[inline]
fn initialize(&mut self, world: &mut World) {
self.0.initialize(world);
}
#[inline]
fn update_archetype_component_access(&mut self, world: UnsafeWorldCell) {
self.0.update_archetype_component_access(world);
}
#[inline]
fn check_change_tick(&mut self, change_tick: Tick) {
self.0.check_change_tick(change_tick);
}
#[inline]
fn get_last_run(&self) -> Tick {
self.0.get_last_run()
}
#[inline]
fn set_last_run(&mut self, last_run: Tick) {
self.0.set_last_run(last_run);
}
fn default_system_sets(&self) -> Vec<crate::schedule::InternedSystemSet> {
self.0.default_system_sets()
}
}
/// Type alias for a `BoxedSystem` that a `Schedule` can store.
pub type ScheduleSystem = BoxedSystem<(), Result>;

475
vendor/bevy_ecs/src/system/system.rs vendored Normal file
View File

@@ -0,0 +1,475 @@
#![expect(
clippy::module_inception,
reason = "This instance of module inception is being discussed; see #17353."
)]
use core::fmt::Debug;
use log::warn;
use thiserror::Error;
use crate::{
archetype::ArchetypeComponentId,
component::{ComponentId, Tick},
query::Access,
schedule::InternedSystemSet,
system::{input::SystemInput, SystemIn},
world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, World},
};
use alloc::{borrow::Cow, boxed::Box, vec::Vec};
use core::any::TypeId;
use super::{IntoSystem, SystemParamValidationError};
/// An ECS system that can be added to a [`Schedule`](crate::schedule::Schedule)
///
/// Systems are functions with all arguments implementing
/// [`SystemParam`](crate::system::SystemParam).
///
/// Systems are added to an application using `App::add_systems(Update, my_system)`
/// or similar methods, and will generally run once per pass of the main loop.
///
/// Systems are executed in parallel, in opportunistic order; data access is managed automatically.
/// It's possible to specify explicit execution order between specific systems,
/// see [`IntoScheduleConfigs`](crate::schedule::IntoScheduleConfigs).
#[diagnostic::on_unimplemented(message = "`{Self}` is not a system", label = "invalid system")]
pub trait System: Send + Sync + 'static {
/// The system's input.
type In: SystemInput;
/// The system's output.
type Out;
/// Returns the system's name.
fn name(&self) -> Cow<'static, str>;
/// Returns the [`TypeId`] of the underlying system type.
#[inline]
fn type_id(&self) -> TypeId {
TypeId::of::<Self>()
}
/// Returns the system's component [`Access`].
fn component_access(&self) -> &Access<ComponentId>;
/// Returns the system's archetype component [`Access`].
fn archetype_component_access(&self) -> &Access<ArchetypeComponentId>;
/// Returns true if the system is [`Send`].
fn is_send(&self) -> bool;
/// Returns true if the system must be run exclusively.
fn is_exclusive(&self) -> bool;
/// Returns true if system has deferred buffers.
fn has_deferred(&self) -> bool;
/// Runs the system with the given input in the world. Unlike [`System::run`], this function
/// can be called in parallel with other systems and may break Rust's aliasing rules
/// if used incorrectly, making it unsafe to call.
///
/// Unlike [`System::run`], this will not apply deferred parameters, which must be independently
/// applied by calling [`System::apply_deferred`] at later point in time.
///
/// # Safety
///
/// - The caller must ensure that [`world`](UnsafeWorldCell) has permission to access any world data
/// registered in `archetype_component_access`. There must be no conflicting
/// simultaneous accesses while the system is running.
/// - If [`System::is_exclusive`] returns `true`, then it must be valid to call
/// [`UnsafeWorldCell::world_mut`] on `world`.
/// - 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: SystemIn<'_, Self>, world: UnsafeWorldCell)
-> Self::Out;
/// Runs the system with the given input in the world.
///
/// For [read-only](ReadOnlySystem) systems, see [`run_readonly`], which can be called using `&World`.
///
/// Unlike [`System::run_unsafe`], this will apply deferred parameters *immediately*.
///
/// [`run_readonly`]: ReadOnlySystem::run_readonly
fn run(&mut self, input: SystemIn<'_, Self>, world: &mut World) -> Self::Out {
let ret = self.run_without_applying_deferred(input, world);
self.apply_deferred(world);
ret
}
/// Runs the system with the given input in the world.
///
/// [`run_readonly`]: ReadOnlySystem::run_readonly
fn run_without_applying_deferred(
&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:
// - We have exclusive access to the entire world.
// - `update_archetype_component_access` has been called.
unsafe { self.run_unsafe(input, world_cell) }
}
/// Applies any [`Deferred`](crate::system::Deferred) system parameters (or other system buffers) of this system to the world.
///
/// This is where [`Commands`](crate::system::Commands) get applied.
fn apply_deferred(&mut self, world: &mut World);
/// Enqueues any [`Deferred`](crate::system::Deferred) system parameters (or other system buffers)
/// of this system into the world's command buffer.
fn queue_deferred(&mut self, world: DeferredWorld);
/// Validates that all parameters can be acquired and that system can run without panic.
/// Built-in executors use this to prevent invalid systems from running.
///
/// However calling and respecting [`System::validate_param_unsafe`] or it's safe variant
/// is not a strict requirement, both [`System::run`] and [`System::run_unsafe`]
/// should provide their own safety mechanism to prevent undefined behavior.
///
/// This method has to be called directly before [`System::run_unsafe`] with no other (relevant)
/// world mutations in between. Otherwise, while it won't lead to any undefined behavior,
/// the validity of the param may change.
///
/// # Safety
///
/// - The caller must ensure that [`world`](UnsafeWorldCell) has permission to access any world data
/// registered in `archetype_component_access`. There must be no conflicting
/// simultaneous accesses while the system is running.
/// - The method [`System::update_archetype_component_access`] must be called at some
/// point before this one, with the same exact [`World`]. If [`System::update_archetype_component_access`]
/// panics (or otherwise does not return for any reason), this method must not be called.
unsafe fn validate_param_unsafe(
&mut self,
world: UnsafeWorldCell,
) -> Result<(), SystemParamValidationError>;
/// Safe version of [`System::validate_param_unsafe`].
/// that runs on exclusive, single-threaded `world` pointer.
fn validate_param(&mut self, world: &World) -> Result<(), SystemParamValidationError> {
let world_cell = world.as_unsafe_world_cell_readonly();
self.update_archetype_component_access(world_cell);
// SAFETY:
// - We have exclusive access to the entire world.
// - `update_archetype_component_access` has been called.
unsafe { self.validate_param_unsafe(world_cell) }
}
/// Initialize the system.
fn initialize(&mut self, _world: &mut World);
/// Update the system's archetype component [`Access`].
///
/// ## Note for implementers
/// `world` may only be used to access metadata. This can be done in safe code
/// via functions such as [`UnsafeWorldCell::archetypes`].
fn update_archetype_component_access(&mut self, world: UnsafeWorldCell);
/// Checks any [`Tick`]s stored on this system and wraps their value if they get too old.
///
/// This method must be called periodically to ensure that change detection behaves correctly.
/// When using bevy's default configuration, this will be called for you as needed.
fn check_change_tick(&mut self, change_tick: Tick);
/// Returns the system's default [system sets](crate::schedule::SystemSet).
///
/// Each system will create a default system set that contains the system.
fn default_system_sets(&self) -> Vec<InternedSystemSet> {
Vec::new()
}
/// Gets the tick indicating the last time this system ran.
fn get_last_run(&self) -> Tick;
/// Overwrites the tick indicating the last time this system ran.
///
/// # Warning
/// This is a complex and error-prone operation, that can have unexpected consequences on any system relying on this code.
/// However, it can be an essential escape hatch when, for example,
/// you are trying to synchronize representations using change detection and need to avoid infinite recursion.
fn set_last_run(&mut self, last_run: Tick);
}
/// [`System`] types that do not modify the [`World`] when run.
/// This is implemented for any systems whose parameters all implement [`ReadOnlySystemParam`].
///
/// Note that systems which perform [deferred](System::apply_deferred) mutations (such as with [`Commands`])
/// may implement this trait.
///
/// [`ReadOnlySystemParam`]: crate::system::ReadOnlySystemParam
/// [`Commands`]: crate::system::Commands
///
/// # Safety
///
/// This must only be implemented for system types which do not mutate the `World`
/// when [`System::run_unsafe`] is called.
pub unsafe trait ReadOnlySystem: System {
/// Runs this system with the given input in the world.
///
/// 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: SystemIn<'_, Self>, world: &World) -> Self::Out {
let world = world.as_unsafe_world_cell_readonly();
self.update_archetype_component_access(world);
// SAFETY:
// - We have read-only access to the entire world.
// - `update_archetype_component_access` has been called.
unsafe { self.run_unsafe(input, world) }
}
}
/// A convenience type alias for a boxed [`System`] trait object.
pub type BoxedSystem<In = (), Out = ()> = Box<dyn System<In = In, Out = Out>>;
pub(crate) fn check_system_change_tick(last_run: &mut Tick, this_run: Tick, system_name: &str) {
if last_run.check_tick(this_run) {
let age = this_run.relative_to(*last_run).get();
warn!(
"System '{system_name}' has not run for {age} ticks. \
Changes older than {} ticks will not be detected.",
Tick::MAX.get() - 1,
);
}
}
impl<In, Out> Debug for dyn System<In = In, Out = Out>
where
In: SystemInput + 'static,
Out: 'static,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("System")
.field("name", &self.name())
.field("is_exclusive", &self.is_exclusive())
.field("is_send", &self.is_send())
.finish_non_exhaustive()
}
}
/// Trait used to run a system immediately on a [`World`].
///
/// # Warning
/// This function is not an efficient method of running systems and it's meant to be used as a utility
/// for testing and/or diagnostics.
///
/// Systems called through [`run_system_once`](RunSystemOnce::run_system_once) do not hold onto any state,
/// as they are created and destroyed every time [`run_system_once`](RunSystemOnce::run_system_once) is called.
/// Practically, this means that [`Local`](crate::system::Local) variables are
/// reset on every run and change detection does not work.
///
/// ```
/// # use bevy_ecs::prelude::*;
/// # use bevy_ecs::system::RunSystemOnce;
/// #[derive(Resource, Default)]
/// struct Counter(u8);
///
/// fn increment(mut counter: Local<Counter>) {
/// counter.0 += 1;
/// println!("{}", counter.0);
/// }
///
/// let mut world = World::default();
/// world.run_system_once(increment); // prints 1
/// world.run_system_once(increment); // still prints 1
/// ```
///
/// If you do need systems to hold onto state between runs, use [`World::run_system_cached`](World::run_system_cached)
/// or [`World::run_system`](World::run_system).
///
/// # Usage
/// Typically, to test a system, or to extract specific diagnostics information from a world,
/// you'd need a [`Schedule`](crate::schedule::Schedule) to run the system. This can create redundant boilerplate code
/// when writing tests or trying to quickly iterate on debug specific systems.
///
/// For these situations, this function can be useful because it allows you to execute a system
/// immediately with some custom input and retrieve its output without requiring the necessary boilerplate.
///
/// # Examples
///
/// ## Immediate Command Execution
///
/// This usage is helpful when trying to test systems or functions that operate on [`Commands`](crate::system::Commands):
/// ```
/// # use bevy_ecs::prelude::*;
/// # use bevy_ecs::system::RunSystemOnce;
/// let mut world = World::default();
/// let entity = world.run_system_once(|mut commands: Commands| {
/// commands.spawn_empty().id()
/// }).unwrap();
/// # assert!(world.get_entity(entity).is_ok());
/// ```
///
/// ## Immediate Queries
///
/// This usage is helpful when trying to run an arbitrary query on a world for testing or debugging purposes:
/// ```
/// # use bevy_ecs::prelude::*;
/// # use bevy_ecs::system::RunSystemOnce;
///
/// #[derive(Component)]
/// struct T(usize);
///
/// let mut world = World::default();
/// world.spawn(T(0));
/// world.spawn(T(1));
/// world.spawn(T(1));
/// let count = world.run_system_once(|query: Query<&T>| {
/// query.iter().filter(|t| t.0 == 1).count()
/// }).unwrap();
///
/// # assert_eq!(count, 2);
/// ```
///
/// Note that instead of closures you can also pass in regular functions as systems:
///
/// ```
/// # use bevy_ecs::prelude::*;
/// # use bevy_ecs::system::RunSystemOnce;
///
/// #[derive(Component)]
/// struct T(usize);
///
/// fn count(query: Query<&T>) -> usize {
/// query.iter().filter(|t| t.0 == 1).count()
/// }
///
/// let mut world = World::default();
/// world.spawn(T(0));
/// world.spawn(T(1));
/// world.spawn(T(1));
/// let count = world.run_system_once(count).unwrap();
///
/// # assert_eq!(count, 2);
/// ```
pub trait RunSystemOnce: Sized {
/// Tries to run a system and apply its deferred parameters.
fn run_system_once<T, Out, Marker>(self, system: T) -> Result<Out, RunSystemError>
where
T: IntoSystem<(), Out, Marker>,
{
self.run_system_once_with(system, ())
}
/// Tries to run a system with given input and apply deferred parameters.
fn run_system_once_with<T, In, Out, Marker>(
self,
system: T,
input: SystemIn<'_, T::System>,
) -> Result<Out, RunSystemError>
where
T: IntoSystem<In, Out, Marker>,
In: SystemInput;
}
impl RunSystemOnce for &mut World {
fn run_system_once_with<T, In, Out, Marker>(
self,
system: T,
input: SystemIn<'_, T::System>,
) -> Result<Out, RunSystemError>
where
T: IntoSystem<In, Out, Marker>,
In: SystemInput,
{
let mut system: T::System = IntoSystem::into_system(system);
system.initialize(self);
system
.validate_param(self)
.map_err(|err| RunSystemError::InvalidParams {
system: system.name(),
err,
})?;
Ok(system.run(input, self))
}
}
/// Running system failed.
#[derive(Error, Debug)]
pub enum RunSystemError {
/// System could not be run due to parameters that failed validation.
/// This should not be considered an error if [`field@SystemParamValidationError::skipped`] is `true`.
#[error("System {system} did not run due to failed parameter validation: {err}")]
InvalidParams {
/// The identifier of the system that was run.
system: Cow<'static, str>,
/// The returned parameter validation error.
err: SystemParamValidationError,
},
}
#[cfg(test)]
mod tests {
use super::*;
use crate::prelude::*;
use alloc::string::ToString;
#[test]
fn run_system_once() {
struct T(usize);
impl Resource for T {}
fn system(In(n): In<usize>, mut commands: Commands) -> usize {
commands.insert_resource(T(n));
n + 1
}
let mut world = World::default();
let n = world.run_system_once_with(system, 1).unwrap();
assert_eq!(n, 2);
assert_eq!(world.resource::<T>().0, 1);
}
#[derive(Resource, Default, PartialEq, Debug)]
struct Counter(u8);
fn count_up(mut counter: ResMut<Counter>) {
counter.0 += 1;
}
#[test]
fn run_two_systems() {
let mut world = World::new();
world.init_resource::<Counter>();
assert_eq!(*world.resource::<Counter>(), Counter(0));
world.run_system_once(count_up).unwrap();
assert_eq!(*world.resource::<Counter>(), Counter(1));
world.run_system_once(count_up).unwrap();
assert_eq!(*world.resource::<Counter>(), Counter(2));
}
fn spawn_entity(mut commands: Commands) {
commands.spawn_empty();
}
#[test]
fn command_processing() {
let mut world = World::new();
assert_eq!(world.entities.len(), 0);
world.run_system_once(spawn_entity).unwrap();
assert_eq!(world.entities.len(), 1);
}
#[test]
fn non_send_resources() {
fn non_send_count_down(mut ns: NonSendMut<Counter>) {
ns.0 -= 1;
}
let mut world = World::new();
world.insert_non_send_resource(Counter(10));
assert_eq!(*world.non_send_resource::<Counter>(), Counter(10));
world.run_system_once(non_send_count_down).unwrap();
assert_eq!(*world.non_send_resource::<Counter>(), Counter(9));
}
#[test]
fn run_system_once_invalid_params() {
struct T;
impl Resource for T {}
fn system(_: Res<T>) {}
let mut world = World::default();
// This fails because `T` has not been added to the world yet.
let result = world.run_system_once(system);
assert!(matches!(result, Err(RunSystemError::InvalidParams { .. })));
let expected = "System bevy_ecs::system::system::tests::run_system_once_invalid_params::system did not run due to failed parameter validation: Parameter `Res<T>` failed validation: Resource does not exist";
assert_eq!(expected, result.unwrap_err().to_string());
}
}

View File

@@ -0,0 +1,141 @@
use crate::{
component::Tick,
prelude::World,
system::{ExclusiveSystemParam, ReadOnlySystemParam, SystemMeta, SystemParam},
world::unsafe_world_cell::UnsafeWorldCell,
};
use alloc::borrow::Cow;
use core::ops::Deref;
use derive_more::derive::{AsRef, Display, Into};
/// [`SystemParam`] that returns the name of the system which it is used in.
///
/// This is not a reliable identifier, it is more so useful for debugging or logging.
///
/// # Examples
///
/// ```
/// # use bevy_ecs::system::SystemName;
/// # use bevy_ecs::system::SystemParam;
///
/// #[derive(SystemParam)]
/// struct Logger<'s> {
/// system_name: SystemName<'s>,
/// }
///
/// impl<'s> Logger<'s> {
/// fn log(&mut self, message: &str) {
/// eprintln!("{}: {}", self.system_name, message);
/// }
/// }
///
/// fn system1(mut logger: Logger) {
/// // Prints: "crate_name::mod_name::system1: Hello".
/// logger.log("Hello");
/// }
/// ```
#[derive(Debug, Into, Display, AsRef)]
#[as_ref(str)]
pub struct SystemName<'s>(&'s str);
impl<'s> SystemName<'s> {
/// Gets the name of the system.
pub fn name(&self) -> &str {
self.0
}
}
impl<'s> Deref for SystemName<'s> {
type Target = str;
fn deref(&self) -> &Self::Target {
self.name()
}
}
// SAFETY: no component value access
unsafe impl SystemParam for SystemName<'_> {
type State = Cow<'static, str>;
type Item<'w, 's> = SystemName<'s>;
fn init_state(_world: &mut World, system_meta: &mut SystemMeta) -> Self::State {
system_meta.name.clone()
}
#[inline]
unsafe fn get_param<'w, 's>(
name: &'s mut Self::State,
_system_meta: &SystemMeta,
_world: UnsafeWorldCell<'w>,
_change_tick: Tick,
) -> Self::Item<'w, 's> {
SystemName(name)
}
}
// SAFETY: Only reads internal system state
unsafe impl<'s> ReadOnlySystemParam for SystemName<'s> {}
impl ExclusiveSystemParam for SystemName<'_> {
type State = Cow<'static, str>;
type Item<'s> = SystemName<'s>;
fn init(_world: &mut World, system_meta: &mut SystemMeta) -> Self::State {
system_meta.name.clone()
}
fn get_param<'s>(state: &'s mut Self::State, _system_meta: &SystemMeta) -> Self::Item<'s> {
SystemName(state)
}
}
#[cfg(test)]
mod tests {
use crate::{
system::{IntoSystem, RunSystemOnce, SystemName},
world::World,
};
use alloc::{borrow::ToOwned, string::String};
#[test]
fn test_system_name_regular_param() {
fn testing(name: SystemName) -> String {
name.name().to_owned()
}
let mut world = World::default();
let id = world.register_system(testing);
let name = world.run_system(id).unwrap();
assert!(name.ends_with("testing"));
}
#[test]
fn test_system_name_exclusive_param() {
fn testing(_world: &mut World, name: SystemName) -> String {
name.name().to_owned()
}
let mut world = World::default();
let id = world.register_system(testing);
let name = world.run_system(id).unwrap();
assert!(name.ends_with("testing"));
}
#[test]
fn test_closure_system_name_regular_param() {
let mut world = World::default();
let system =
IntoSystem::into_system(|name: SystemName| name.name().to_owned()).with_name("testing");
let name = world.run_system_once(system).unwrap();
assert_eq!(name, "testing");
}
#[test]
fn test_exclusive_closure_system_name_regular_param() {
let mut world = World::default();
let system =
IntoSystem::into_system(|_world: &mut World, name: SystemName| name.name().to_owned())
.with_name("testing");
let name = world.run_system_once(system).unwrap();
assert_eq!(name, "testing");
}
}

3000
vendor/bevy_ecs/src/system/system_param.rs vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,918 @@
#[cfg(feature = "bevy_reflect")]
use crate::reflect::ReflectComponent;
use crate::{
change_detection::Mut,
entity::Entity,
system::{input::SystemInput, BoxedSystem, IntoSystem, SystemParamValidationError},
world::World,
};
use alloc::boxed::Box;
use bevy_ecs_macros::{Component, Resource};
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use core::marker::PhantomData;
use thiserror::Error;
/// A small wrapper for [`BoxedSystem`] that also keeps track whether or not the system has been initialized.
#[derive(Component)]
#[require(SystemIdMarker)]
pub(crate) struct RegisteredSystem<I, O> {
initialized: bool,
system: BoxedSystem<I, O>,
}
impl<I, O> RegisteredSystem<I, O> {
pub fn new(system: BoxedSystem<I, O>) -> Self {
RegisteredSystem {
initialized: false,
system,
}
}
}
/// Marker [`Component`](bevy_ecs::component::Component) for identifying [`SystemId`] [`Entity`]s.
#[derive(Component, Default)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(feature = "bevy_reflect", reflect(Component, Default))]
pub struct SystemIdMarker;
/// A system that has been removed from the registry.
/// It contains the system and whether or not it has been initialized.
///
/// This struct is returned by [`World::unregister_system`].
pub struct RemovedSystem<I = (), O = ()> {
initialized: bool,
system: BoxedSystem<I, O>,
}
impl<I, O> RemovedSystem<I, O> {
/// Is the system initialized?
/// A system is initialized the first time it's ran.
pub fn initialized(&self) -> bool {
self.initialized
}
/// The system removed from the storage.
pub fn system(self) -> BoxedSystem<I, O> {
self.system
}
}
/// An identifier for a registered system.
///
/// These are opaque identifiers, keyed to a specific [`World`],
/// and are created via [`World::register_system`].
pub struct SystemId<I: SystemInput = (), O = ()> {
pub(crate) entity: Entity,
pub(crate) marker: PhantomData<fn(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
/// is really an entity with associated handler function.
///
/// For example, this is useful if you want to assign a name label to a system.
pub fn entity(self) -> Entity {
self.entity
}
/// Create [`SystemId`] from an [`Entity`]. Useful when you only have entity handles to avoid
/// adding extra components that have a [`SystemId`] everywhere. To run a system with this ID
/// - The entity must be a system
/// - The `I` + `O` types must be correct
pub fn from_entity(entity: Entity) -> Self {
Self {
entity,
marker: PhantomData,
}
}
}
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: 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: 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: 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: SystemInput, O> core::hash::Hash for SystemId<I, O> {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.entity.hash(state);
}
}
impl<I: SystemInput, O> core::fmt::Debug for SystemId<I, O> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("SystemId").field(&self.entity).finish()
}
}
/// A cached [`SystemId`] distinguished by the unique function type of its system.
///
/// This resource is inserted by [`World::register_system_cached`].
#[derive(Resource)]
pub struct CachedSystemId<S> {
/// The cached `SystemId` as an `Entity`.
pub entity: Entity,
_marker: PhantomData<fn() -> S>,
}
impl<S> CachedSystemId<S> {
/// Creates a new `CachedSystemId` struct given a `SystemId`.
pub fn new<I: SystemInput, O>(id: SystemId<I, O>) -> Self {
Self {
entity: id.entity(),
_marker: PhantomData,
}
}
}
impl World {
/// Registers a system and returns a [`SystemId`] so it can later be called by [`World::run_system`].
///
/// It's possible to register multiple copies of the same system by calling this function
/// multiple times. If that's not what you want, consider using [`World::register_system_cached`]
/// instead.
///
/// This is different from adding systems to a [`Schedule`](crate::schedule::Schedule),
/// because the [`SystemId`] that is returned can be used anywhere in the [`World`] to run the associated system.
/// 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, O, M>(
&mut self,
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)))
}
/// Similar to [`Self::register_system`], but allows passing in a [`BoxedSystem`].
///
/// This is useful if the [`IntoSystem`] implementor has already been turned into a
/// [`System`](crate::system::System) trait object and put in a [`Box`].
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(RegisteredSystem::new(system)).id();
SystemId::from_entity(entity)
}
/// Removes a registered system and returns the system, if it exists.
/// After removing a system, the [`SystemId`] becomes invalid and attempting to use it afterwards will result in errors.
/// Re-adding the removed system will register it on a new [`SystemId`].
///
/// 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 unregister_system<I, O>(
&mut self,
id: SystemId<I, O>,
) -> Result<RemovedSystem<I, O>, RegisteredSystemError<I, O>>
where
I: SystemInput + 'static,
O: 'static,
{
match self.get_entity_mut(id.entity) {
Ok(mut entity) => {
let registered_system = entity
.take::<RegisteredSystem<I, O>>()
.ok_or(RegisteredSystemError::SelfRemove(id))?;
entity.despawn();
Ok(RemovedSystem {
initialized: registered_system.initialized,
system: registered_system.system,
})
}
Err(_) => Err(RegisteredSystemError::SystemIdNotRegistered(id)),
}
}
/// Run stored systems by their [`SystemId`].
/// Before running a system, it must first be registered.
/// The method [`World::register_system`] stores a given system and returns a [`SystemId`].
/// This is different from [`RunSystemOnce::run_system_once`](crate::system::RunSystemOnce::run_system_once),
/// because it keeps local state between calls and change detection works correctly.
///
/// Also runs any queued-up commands.
///
/// In order to run a chained system with an input, use [`World::run_system_with`] instead.
///
/// # Examples
///
/// ## Running a system
///
/// ```
/// # use bevy_ecs::prelude::*;
/// fn increment(mut counter: Local<u8>) {
/// *counter += 1;
/// println!("{}", *counter);
/// }
///
/// let mut world = World::default();
/// let counter_one = world.register_system(increment);
/// let counter_two = world.register_system(increment);
/// world.run_system(counter_one); // -> 1
/// world.run_system(counter_one); // -> 2
/// world.run_system(counter_two); // -> 1
/// ```
///
/// ## Change detection
///
/// ```
/// # use bevy_ecs::prelude::*;
/// #[derive(Resource, Default)]
/// struct ChangeDetector;
///
/// let mut world = World::default();
/// world.init_resource::<ChangeDetector>();
/// let detector = world.register_system(|change_detector: ResMut<ChangeDetector>| {
/// if change_detector.is_changed() {
/// println!("Something happened!");
/// } else {
/// println!("Nothing happened.");
/// }
/// });
///
/// // Resources are changed when they are first added
/// let _ = world.run_system(detector); // -> Something happened!
/// let _ = world.run_system(detector); // -> Nothing happened.
/// world.resource_mut::<ChangeDetector>().set_changed();
/// let _ = world.run_system(detector); // -> Something happened!
/// ```
///
/// ## Getting system output
///
/// ```
/// # use bevy_ecs::prelude::*;
///
/// #[derive(Resource)]
/// struct PlayerScore(i32);
///
/// #[derive(Resource)]
/// struct OpponentScore(i32);
///
/// fn get_player_score(player_score: Res<PlayerScore>) -> i32 {
/// player_score.0
/// }
///
/// fn get_opponent_score(opponent_score: Res<OpponentScore>) -> i32 {
/// opponent_score.0
/// }
///
/// let mut world = World::default();
/// world.insert_resource(PlayerScore(3));
/// world.insert_resource(OpponentScore(2));
///
/// let scoring_systems = [
/// ("player", world.register_system(get_player_score)),
/// ("opponent", world.register_system(get_opponent_score)),
/// ];
///
/// for (label, scoring_system) in scoring_systems {
/// println!("{label} has score {}", world.run_system(scoring_system).expect("system succeeded"));
/// }
/// ```
pub fn run_system<O: 'static>(
&mut self,
id: SystemId<(), O>,
) -> Result<O, RegisteredSystemError<(), O>> {
self.run_system_with(id, ())
}
/// Run a stored chained system by its [`SystemId`], providing an input value.
/// Before running a system, it must first be registered.
/// The method [`World::register_system`] stores a given system and returns a [`SystemId`].
///
/// Also runs any queued-up commands.
///
/// # Examples
///
/// ```
/// # use bevy_ecs::prelude::*;
/// fn increment(In(increment_by): In<u8>, mut counter: Local<u8>) -> u8 {
/// *counter += increment_by;
/// *counter
/// }
///
/// let mut world = World::default();
/// let counter_one = world.register_system(increment);
/// let counter_two = world.register_system(increment);
/// assert_eq!(world.run_system_with(counter_one, 1).unwrap(), 1);
/// assert_eq!(world.run_system_with(counter_one, 20).unwrap(), 21);
/// assert_eq!(world.run_system_with(counter_two, 30).unwrap(), 30);
/// ```
///
/// See [`World::run_system`] for more examples.
pub fn run_system_with<I, O>(
&mut self,
id: SystemId<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)
.map_err(|_| RegisteredSystemError::SystemIdNotRegistered(id))?;
// Take ownership of system trait object
let RegisteredSystem {
mut initialized,
mut system,
} = entity
.take::<RegisteredSystem<I, O>>()
.ok_or(RegisteredSystemError::Recursive(id))?;
// Run the system
if !initialized {
system.initialize(self);
initialized = true;
}
let result = system
.validate_param(self)
.map_err(|err| RegisteredSystemError::InvalidParams { system: id, err })
.map(|()| {
// Wait to run the commands until the system is available again.
// This is needed so the systems can recursively run themselves.
let ret = system.run_without_applying_deferred(input, self);
system.queue_deferred(self.into());
ret
});
// Return ownership of system trait object (if entity still exists)
if let Ok(mut entity) = self.get_entity_mut(id.entity) {
entity.insert::<RegisteredSystem<I, O>>(RegisteredSystem {
initialized,
system,
});
}
// Run any commands enqueued by the system
self.flush();
result
}
/// Registers a system or returns its cached [`SystemId`].
///
/// If you want to run the system immediately and you don't need its `SystemId`, see
/// [`World::run_system_cached`].
///
/// The first time this function is called for a particular system, it will register it and
/// store its [`SystemId`] in a [`CachedSystemId`] resource for later. If you would rather
/// manage the `SystemId` yourself, or register multiple copies of the same system, use
/// [`World::register_system`] instead.
///
/// # Limitations
///
/// This function only accepts ZST (zero-sized) systems to guarantee that any two systems of
/// the same type must be equal. This means that closures that capture the environment, and
/// function pointers, are not accepted.
///
/// If 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, 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,
"Non-ZST systems (e.g. capturing closures, function pointers) cannot be cached.",
);
}
if !self.contains_resource::<CachedSystemId<S>>() {
let id = self.register_system(system);
self.insert_resource(CachedSystemId::<S>::new(id));
return id;
}
self.resource_scope(|world, mut id: Mut<CachedSystemId<S>>| {
if let Ok(mut entity) = world.get_entity_mut(id.entity) {
if !entity.contains::<RegisteredSystem<I, O>>() {
entity.insert(RegisteredSystem::new(Box::new(IntoSystem::into_system(
system,
))));
}
} else {
id.entity = world.register_system(system).entity();
}
SystemId::from_entity(id.entity)
})
}
/// Removes a cached system and its [`CachedSystemId`] resource.
///
/// See [`World::register_system_cached`] for more information.
pub fn unregister_system_cached<I, O, M, S>(
&mut self,
_system: S,
) -> 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>>()
.ok_or(RegisteredSystemError::SystemNotCached)?;
self.unregister_system(SystemId::<I, O>::from_entity(id.entity))
}
/// Runs a cached system, registering it if necessary.
///
/// See [`World::register_system_cached`] for more information.
pub fn run_system_cached<O: 'static, M, S: IntoSystem<(), O, M> + 'static>(
&mut self,
system: S,
) -> Result<O, RegisteredSystemError<(), O>> {
self.run_system_cached_with(system, ())
}
/// 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, O, M, S>(
&mut self,
system: S,
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(id, input)
}
}
/// An operation with stored systems failed.
#[derive(Error)]
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?
#[error("System {0:?} was not registered")]
SystemIdNotRegistered(SystemId<I, O>),
/// A cached system was removed by value, but no system with its type was found.
///
/// Did you forget to register it?
#[error("Cached system was not found")]
SystemNotCached,
/// A system tried to run itself recursively.
#[error("System {0:?} tried to run itself recursively")]
Recursive(SystemId<I, O>),
/// A system tried to remove itself.
#[error("System {0:?} tried to remove itself")]
SelfRemove(SystemId<I, O>),
/// System could not be run due to parameters that failed validation.
/// This should not be considered an error if [`field@SystemParamValidationError::skipped`] is `true`.
#[error("System {system:?} did not run due to failed parameter validation: {err}")]
InvalidParams {
/// The identifier of the system that was run.
system: SystemId<I, O>,
/// The returned parameter validation error.
err: SystemParamValidationError,
},
}
impl<I: SystemInput, O> core::fmt::Debug for RegisteredSystemError<I, O> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::SystemIdNotRegistered(arg0) => {
f.debug_tuple("SystemIdNotRegistered").field(arg0).finish()
}
Self::SystemNotCached => write!(f, "SystemNotCached"),
Self::Recursive(arg0) => f.debug_tuple("Recursive").field(arg0).finish(),
Self::SelfRemove(arg0) => f.debug_tuple("SelfRemove").field(arg0).finish(),
Self::InvalidParams { system, err } => f
.debug_struct("InvalidParams")
.field("system", system)
.field("err", err)
.finish(),
}
}
}
#[cfg(test)]
mod tests {
use core::cell::Cell;
use bevy_utils::default;
use crate::{prelude::*, system::SystemId};
#[derive(Resource, Default, PartialEq, Debug)]
struct Counter(u8);
#[test]
fn change_detection() {
#[derive(Resource, Default)]
struct ChangeDetector;
fn count_up_iff_changed(
mut counter: ResMut<Counter>,
change_detector: ResMut<ChangeDetector>,
) {
if change_detector.is_changed() {
counter.0 += 1;
}
}
let mut world = World::new();
world.init_resource::<ChangeDetector>();
world.init_resource::<Counter>();
assert_eq!(*world.resource::<Counter>(), Counter(0));
// Resources are changed when they are first added.
let id = world.register_system(count_up_iff_changed);
world.run_system(id).expect("system runs successfully");
assert_eq!(*world.resource::<Counter>(), Counter(1));
// Nothing changed
world.run_system(id).expect("system runs successfully");
assert_eq!(*world.resource::<Counter>(), Counter(1));
// Making a change
world.resource_mut::<ChangeDetector>().set_changed();
world.run_system(id).expect("system runs successfully");
assert_eq!(*world.resource::<Counter>(), Counter(2));
}
#[test]
fn local_variables() {
// The `Local` begins at the default value of 0
fn doubling(last_counter: Local<Counter>, mut counter: ResMut<Counter>) {
counter.0 += last_counter.0 .0;
last_counter.0 .0 = counter.0;
}
let mut world = World::new();
world.insert_resource(Counter(1));
assert_eq!(*world.resource::<Counter>(), Counter(1));
let id = world.register_system(doubling);
world.run_system(id).expect("system runs successfully");
assert_eq!(*world.resource::<Counter>(), Counter(1));
world.run_system(id).expect("system runs successfully");
assert_eq!(*world.resource::<Counter>(), Counter(2));
world.run_system(id).expect("system runs successfully");
assert_eq!(*world.resource::<Counter>(), Counter(4));
world.run_system(id).expect("system runs successfully");
assert_eq!(*world.resource::<Counter>(), Counter(8));
}
#[test]
fn input_values() {
// Verify that a non-Copy, non-Clone type can be passed in.
struct NonCopy(u8);
fn increment_sys(In(NonCopy(increment_by)): In<NonCopy>, mut counter: ResMut<Counter>) {
counter.0 += increment_by;
}
let mut world = World::new();
let id = world.register_system(increment_sys);
// Insert the resource after registering the system.
world.insert_resource(Counter(1));
assert_eq!(*world.resource::<Counter>(), Counter(1));
world
.run_system_with(id, NonCopy(1))
.expect("system runs successfully");
assert_eq!(*world.resource::<Counter>(), Counter(2));
world
.run_system_with(id, NonCopy(1))
.expect("system runs successfully");
assert_eq!(*world.resource::<Counter>(), Counter(3));
world
.run_system_with(id, NonCopy(20))
.expect("system runs successfully");
assert_eq!(*world.resource::<Counter>(), Counter(23));
world
.run_system_with(id, NonCopy(1))
.expect("system runs successfully");
assert_eq!(*world.resource::<Counter>(), Counter(24));
}
#[test]
fn output_values() {
// Verify that a non-Copy, non-Clone type can be returned.
#[derive(Eq, PartialEq, Debug)]
struct NonCopy(u8);
fn increment_sys(mut counter: ResMut<Counter>) -> NonCopy {
counter.0 += 1;
NonCopy(counter.0)
}
let mut world = World::new();
let id = world.register_system(increment_sys);
// Insert the resource after registering the system.
world.insert_resource(Counter(1));
assert_eq!(*world.resource::<Counter>(), Counter(1));
let output = world.run_system(id).expect("system runs successfully");
assert_eq!(*world.resource::<Counter>(), Counter(2));
assert_eq!(output, NonCopy(2));
let output = world.run_system(id).expect("system runs successfully");
assert_eq!(*world.resource::<Counter>(), Counter(3));
assert_eq!(output, NonCopy(3));
}
#[test]
fn exclusive_system() {
let mut world = World::new();
let exclusive_system_id = world.register_system(|world: &mut World| {
world.spawn_empty();
});
let entity_count = world.entities.len();
let _ = world.run_system(exclusive_system_id);
assert_eq!(world.entities.len(), entity_count + 1);
}
#[test]
fn nested_systems() {
use crate::system::SystemId;
#[derive(Component)]
struct Callback(SystemId);
fn nested(query: Query<&Callback>, mut commands: Commands) {
for callback in query.iter() {
commands.run_system(callback.0);
}
}
let mut world = World::new();
world.insert_resource(Counter(0));
let increment_two = world.register_system(|mut counter: ResMut<Counter>| {
counter.0 += 2;
});
let increment_three = world.register_system(|mut counter: ResMut<Counter>| {
counter.0 += 3;
});
let nested_id = world.register_system(nested);
world.spawn(Callback(increment_two));
world.spawn(Callback(increment_three));
let _ = world.run_system(nested_id);
assert_eq!(*world.resource::<Counter>(), Counter(5));
}
#[test]
fn nested_systems_with_inputs() {
use crate::system::SystemId;
#[derive(Component)]
struct Callback(SystemId<In<u8>>, u8);
fn nested(query: Query<&Callback>, mut commands: Commands) {
for callback in query.iter() {
commands.run_system_with(callback.0, callback.1);
}
}
let mut world = World::new();
world.insert_resource(Counter(0));
let increment_by =
world.register_system(|In(amt): In<u8>, mut counter: ResMut<Counter>| {
counter.0 += amt;
});
let nested_id = world.register_system(nested);
world.spawn(Callback(increment_by, 2));
world.spawn(Callback(increment_by, 3));
let _ = world.run_system(nested_id);
assert_eq!(*world.resource::<Counter>(), Counter(5));
}
#[test]
fn cached_system() {
use crate::system::RegisteredSystemError;
fn four() -> i32 {
4
}
let mut world = World::new();
let old = world.register_system_cached(four);
let new = world.register_system_cached(four);
assert_eq!(old, new);
let result = world.unregister_system_cached(four);
assert!(result.is_ok());
let new = world.register_system_cached(four);
assert_ne!(old, new);
let output = world.run_system(old);
assert!(matches!(
output,
Err(RegisteredSystemError::SystemIdNotRegistered(x)) if x == old,
));
let output = world.run_system(new);
assert!(matches!(output, Ok(x) if x == four()));
let output = world.run_system_cached(four);
assert!(matches!(output, Ok(x) if x == four()));
let output = world.run_system_cached_with(four, ());
assert!(matches!(output, Ok(x) if x == four()));
}
#[test]
fn cached_system_commands() {
fn sys(mut counter: ResMut<Counter>) {
counter.0 = 1;
}
let mut world = World::new();
world.insert_resource(Counter(0));
world.commands().run_system_cached(sys);
world.flush_commands();
assert_eq!(world.resource::<Counter>().0, 1);
}
#[test]
fn cached_system_adapters() {
fn four() -> i32 {
4
}
fn double(In(i): In<i32>) -> i32 {
i * 2
}
let mut world = World::new();
let output = world.run_system_cached(four.pipe(double));
assert!(matches!(output, Ok(8)));
let output = world.run_system_cached(four.map(|i| i * 2));
assert!(matches!(output, Ok(8)));
}
#[test]
fn cached_system_into_same_system_type() {
use crate::error::Result;
struct Foo;
impl IntoSystem<(), Result<()>, ()> for Foo {
type System = ApplyDeferred;
fn into_system(_: Self) -> Self::System {
ApplyDeferred
}
}
struct Bar;
impl IntoSystem<(), Result<()>, ()> for Bar {
type System = ApplyDeferred;
fn into_system(_: Self) -> Self::System {
ApplyDeferred
}
}
let mut world = World::new();
let foo1 = world.register_system_cached(Foo);
let foo2 = world.register_system_cached(Foo);
let bar1 = world.register_system_cached(Bar);
let bar2 = world.register_system_cached(Bar);
// The `S: IntoSystem` types are different, so they should be cached
// as separate systems, even though the `<S as IntoSystem>::System`
// types / values are the same (`ApplyDeferred`).
assert_ne!(foo1, bar1);
// But if the `S: IntoSystem` types are the same, they'll be cached
// as the same system.
assert_eq!(foo1, foo2);
assert_eq!(bar1, bar2);
}
#[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(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(post_system, &mut event).unwrap();
assert!(!event.cancelled);
world.resource_mut::<Counter>().0 = 1;
world.run_system_with(post_system, &mut event).unwrap();
assert!(event.cancelled);
}
#[test]
fn run_system_invalid_params() {
use crate::system::RegisteredSystemError;
use alloc::{format, string::ToString};
struct T;
impl Resource for T {}
fn system(_: Res<T>) {}
let mut world = World::new();
let id = world.register_system(system);
// This fails because `T` has not been added to the world yet.
let result = world.run_system(id);
assert!(matches!(
result,
Err(RegisteredSystemError::InvalidParams { .. })
));
let expected = format!("System {id:?} did not run due to failed parameter validation: Parameter `Res<T>` failed validation: Resource does not exist");
assert_eq!(expected, result.unwrap_err().to_string());
}
#[test]
fn run_system_recursive() {
std::thread_local! {
static INVOCATIONS_LEFT: Cell<i32> = const { Cell::new(3) };
static SYSTEM_ID: Cell<Option<SystemId>> = default();
}
fn system(mut commands: Commands) {
let count = INVOCATIONS_LEFT.get() - 1;
INVOCATIONS_LEFT.set(count);
if count > 0 {
commands.run_system(SYSTEM_ID.get().unwrap());
}
}
let mut world = World::new();
let id = world.register_system(system);
SYSTEM_ID.set(Some(id));
world.run_system(id).unwrap();
assert_eq!(INVOCATIONS_LEFT.get(), 0);
}
#[test]
fn run_system_exclusive_adapters() {
let mut world = World::new();
fn system(_: &mut World) {}
world.run_system_cached(system).unwrap();
world.run_system_cached(system.pipe(system)).unwrap();
world.run_system_cached(system.map(|()| {})).unwrap();
}
}