mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
Add 'World::run_system_with_input' function + allow World::run_system
to get system output (#10380)
# Objective Allows chained systems taking an `In<_>` input parameter to be run as one-shot systems. This API was mentioned in #8963. In addition, `run_system(_with_input)` returns the system output, for any `'static` output type. ## Solution A new function, `World::run_system_with_input` allows a `SystemId<I, O>` to be run by providing an `I` value as input and producing `O` as an output. `SystemId<I, O>` is now generic over the input type `I` and output type `O`, along with the related functions and types `RegisteredSystem`, `RemovedSystem`, `register_system`, `remove_system`, and `RegisteredSystemError`. These default to `()`, preserving the existing API, for all of the public types. --- ## Changelog - Added `World::run_system_with_input` function to allow one-shot systems that take `In<_>` input parameters - Changed `World::run_system` and `World::register_system` to support systems with return types beyond `()` - Added `Commands::run_system_with_input` command that schedules a one-shot system with an `In<_>` input parameter
This commit is contained in:
parent
bad55c1ad8
commit
1f05e1e2ab
2 changed files with 309 additions and 39 deletions
|
@ -5,7 +5,7 @@ use crate::{
|
|||
self as bevy_ecs,
|
||||
bundle::Bundle,
|
||||
entity::{Entities, Entity},
|
||||
system::{RunSystem, SystemId},
|
||||
system::{RunSystemWithInput, SystemId},
|
||||
world::{EntityWorldMut, FromWorld, World},
|
||||
};
|
||||
use bevy_ecs_macros::SystemParam;
|
||||
|
@ -523,8 +523,26 @@ impl<'w, 's> Commands<'w, 's> {
|
|||
/// Running slow systems can become a bottleneck.
|
||||
///
|
||||
/// Calls [`World::run_system`](crate::system::World::run_system).
|
||||
///
|
||||
/// There is no way to get the output of a system when run as a command, because the
|
||||
/// execution of the system happens later. To get the output of a system, use
|
||||
/// [`World::run_system`] or [`World::run_system_with_input`] instead of running the system as a command.
|
||||
pub fn run_system(&mut self, id: SystemId) {
|
||||
self.queue.push(RunSystem::new(id));
|
||||
self.run_system_with_input(id, ());
|
||||
}
|
||||
|
||||
/// Runs the system corresponding to the given [`SystemId`].
|
||||
/// Systems are ran in an exclusive and single threaded way.
|
||||
/// Running slow systems can become a bottleneck.
|
||||
///
|
||||
/// Calls [`World::run_system_with_input`](crate::system::World::run_system_with_input).
|
||||
///
|
||||
/// There is no way to get the output of a system when run as a command, because the
|
||||
/// execution of the system happens later. To get the output of a system, use
|
||||
/// [`World::run_system`] or [`World::run_system_with_input`] instead of running the system as a command.
|
||||
pub fn run_system_with_input<I: 'static + Send>(&mut self, id: SystemId<I>, input: I) {
|
||||
self.queue
|
||||
.push(RunSystemWithInput::new_with_input(id, input));
|
||||
}
|
||||
|
||||
/// Pushes a generic [`Command`] to the command queue.
|
||||
|
|
|
@ -7,18 +7,18 @@ use thiserror::Error;
|
|||
|
||||
/// A small wrapper for [`BoxedSystem`] that also keeps track whether or not the system has been initialized.
|
||||
#[derive(Component)]
|
||||
struct RegisteredSystem {
|
||||
struct RegisteredSystem<I, O> {
|
||||
initialized: bool,
|
||||
system: BoxedSystem,
|
||||
system: BoxedSystem<I, O>,
|
||||
}
|
||||
|
||||
/// 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::remove_system`].
|
||||
pub struct RemovedSystem {
|
||||
pub struct RemovedSystem<I = (), O = ()> {
|
||||
initialized: bool,
|
||||
system: BoxedSystem,
|
||||
system: BoxedSystem<I, O>,
|
||||
}
|
||||
|
||||
impl RemovedSystem {
|
||||
|
@ -38,8 +38,41 @@ impl RemovedSystem {
|
|||
///
|
||||
/// These are opaque identifiers, keyed to a specific [`World`],
|
||||
/// and are created via [`World::register_system`].
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct SystemId(Entity);
|
||||
#[derive(Eq)]
|
||||
pub struct SystemId<I = (), O = ()>(Entity, std::marker::PhantomData<fn(I) -> O>);
|
||||
|
||||
// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters.
|
||||
impl<I, O> Copy for SystemId<I, O> {}
|
||||
|
||||
// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters.
|
||||
impl<I, O> Clone for SystemId<I, O> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters.
|
||||
impl<I, O> PartialEq for SystemId<I, O> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0 == other.0 && self.1 == other.1
|
||||
}
|
||||
}
|
||||
|
||||
// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters.
|
||||
impl<I, O> std::hash::Hash for SystemId<I, O> {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.0.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, O> std::fmt::Debug for SystemId<I, O> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_tuple("SystemId")
|
||||
.field(&self.0)
|
||||
.field(&self.1)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl World {
|
||||
/// Registers a system and returns a [`SystemId`] so it can later be called by [`World::run_system`].
|
||||
|
@ -51,10 +84,10 @@ impl World {
|
|||
/// This allows for running systems in a pushed-based fashion.
|
||||
/// Using a [`Schedule`](crate::schedule::Schedule) is still preferred for most cases
|
||||
/// due to its better performance and abillity to run non-conflicting systems simultaneously.
|
||||
pub fn register_system<M, S: IntoSystem<(), (), M> + 'static>(
|
||||
pub fn register_system<I: 'static, O: 'static, M, S: IntoSystem<I, O, M> + 'static>(
|
||||
&mut self,
|
||||
system: S,
|
||||
) -> SystemId {
|
||||
) -> SystemId<I, O> {
|
||||
self.register_boxed_system(Box::new(IntoSystem::into_system(system)))
|
||||
}
|
||||
|
||||
|
@ -62,13 +95,17 @@ impl World {
|
|||
///
|
||||
/// 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(&mut self, system: BoxedSystem) -> SystemId {
|
||||
pub fn register_boxed_system<I: 'static, O: 'static>(
|
||||
&mut self,
|
||||
system: BoxedSystem<I, O>,
|
||||
) -> SystemId<I, O> {
|
||||
SystemId(
|
||||
self.spawn(RegisteredSystem {
|
||||
initialized: false,
|
||||
system,
|
||||
})
|
||||
.id(),
|
||||
std::marker::PhantomData,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -78,11 +115,14 @@ impl World {
|
|||
///
|
||||
/// If no system corresponds to the given [`SystemId`], this method returns an error.
|
||||
/// Systems are also not allowed to remove themselves, this returns an error too.
|
||||
pub fn remove_system(&mut self, id: SystemId) -> Result<RemovedSystem, RegisteredSystemError> {
|
||||
pub fn remove_system<I: 'static, O: 'static>(
|
||||
&mut self,
|
||||
id: SystemId<I, O>,
|
||||
) -> Result<RemovedSystem<I, O>, RegisteredSystemError<I, O>> {
|
||||
match self.get_entity_mut(id.0) {
|
||||
Some(mut entity) => {
|
||||
let registered_system = entity
|
||||
.take::<RegisteredSystem>()
|
||||
.take::<RegisteredSystem<I, O>>()
|
||||
.ok_or(RegisteredSystemError::SelfRemove(id))?;
|
||||
entity.despawn();
|
||||
Ok(RemovedSystem {
|
||||
|
@ -100,14 +140,17 @@ impl World {
|
|||
/// 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.
|
||||
///
|
||||
/// In order to run a chained system with an input, use [`World::run_system_with_input`] instead.
|
||||
///
|
||||
/// # Limitations
|
||||
///
|
||||
/// - Stored systems cannot be chained: they can neither have an [`In`](crate::system::In) nor return any values.
|
||||
/// - Stored systems cannot be recursive, they cannot call themselves through [`Commands::run_system`](crate::system::Commands).
|
||||
/// - Exclusive systems cannot be used.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ## Running a system
|
||||
///
|
||||
/// ```rust
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
/// #[derive(Resource, Default)]
|
||||
|
@ -126,7 +169,7 @@ impl World {
|
|||
/// world.run_system(counter_two); // -> 1
|
||||
/// ```
|
||||
///
|
||||
/// Change detection:
|
||||
/// ## Change detection
|
||||
///
|
||||
/// ```rust
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
|
@ -149,7 +192,81 @@ impl World {
|
|||
/// world.resource_mut::<ChangeDetector>().set_changed();
|
||||
/// let _ = world.run_system(detector); // -> Something happened!
|
||||
/// ```
|
||||
pub fn run_system(&mut self, id: SystemId) -> Result<(), RegisteredSystemError> {
|
||||
///
|
||||
/// ## Getting system output
|
||||
///
|
||||
/// ```rust
|
||||
/// # 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_input(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`].
|
||||
///
|
||||
/// # Limitations
|
||||
///
|
||||
/// - Stored systems cannot be recursive, they cannot call themselves through [`Commands::run_system`](crate::system::Commands).
|
||||
/// - Exclusive systems cannot be used.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
/// #[derive(Resource, Default)]
|
||||
/// struct Counter(u8);
|
||||
///
|
||||
/// fn increment(In(increment_by): In<u8>, mut counter: Local<Counter>) {
|
||||
/// counter.0 += increment_by;
|
||||
/// println!("{}", counter.0);
|
||||
/// }
|
||||
///
|
||||
/// let mut world = World::default();
|
||||
/// let counter_one = world.register_system(increment);
|
||||
/// let counter_two = world.register_system(increment);
|
||||
/// world.run_system_with_input(counter_one, 1); // -> 1
|
||||
/// world.run_system_with_input(counter_one, 20); // -> 21
|
||||
/// world.run_system_with_input(counter_two, 30); // -> 51
|
||||
/// ```
|
||||
///
|
||||
/// See [`World::run_system`] for more examples.
|
||||
pub fn run_system_with_input<I: 'static, O: 'static>(
|
||||
&mut self,
|
||||
id: SystemId<I, O>,
|
||||
input: I,
|
||||
) -> Result<O, RegisteredSystemError<I, O>> {
|
||||
// lookup
|
||||
let mut entity = self
|
||||
.get_entity_mut(id.0)
|
||||
|
@ -160,7 +277,7 @@ impl World {
|
|||
mut initialized,
|
||||
mut system,
|
||||
} = entity
|
||||
.take::<RegisteredSystem>()
|
||||
.take::<RegisteredSystem<I, O>>()
|
||||
.ok_or(RegisteredSystemError::Recursive(id))?;
|
||||
|
||||
// run the system
|
||||
|
@ -168,57 +285,98 @@ impl World {
|
|||
system.initialize(self);
|
||||
initialized = true;
|
||||
}
|
||||
system.run((), self);
|
||||
let result = system.run(input, self);
|
||||
system.apply_deferred(self);
|
||||
|
||||
// return ownership of system trait object (if entity still exists)
|
||||
if let Some(mut entity) = self.get_entity_mut(id.0) {
|
||||
entity.insert::<RegisteredSystem>(RegisteredSystem {
|
||||
entity.insert::<RegisteredSystem<I, O>>(RegisteredSystem {
|
||||
initialized,
|
||||
system,
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
/// The [`Command`] type for [`World::run_system`] or [`World::run_system_with_input`].
|
||||
///
|
||||
/// This command runs systems in an exclusive and single threaded way.
|
||||
/// Running slow systems can become a bottleneck.
|
||||
///
|
||||
/// If the system needs an [`In<_>`](crate::system::In) input value to run, it must
|
||||
/// be provided as part of the command.
|
||||
///
|
||||
/// There is no way to get the output of a system when run as a command, because the
|
||||
/// execution of the system happens later. To get the output of a system, use
|
||||
/// [`World::run_system`] or [`World::run_system_with_input`] instead of running the system as a command.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RunSystemWithInput<I: 'static> {
|
||||
system_id: SystemId<I>,
|
||||
input: I,
|
||||
}
|
||||
|
||||
/// The [`Command`] type for [`World::run_system`].
|
||||
///
|
||||
/// This command runs systems in an exclusive and single threaded way.
|
||||
/// Running slow systems can become a bottleneck.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RunSystem {
|
||||
system_id: SystemId,
|
||||
}
|
||||
///
|
||||
/// If the system needs an [`In<_>`](crate::system::In) input value to run, use the
|
||||
/// [`crate::system::RunSystemWithInput`] type instead.
|
||||
///
|
||||
/// There is no way to get the output of a system when run as a command, because the
|
||||
/// execution of the system happens later. To get the output of a system, use
|
||||
/// [`World::run_system`] or [`World::run_system_with_input`] instead of running the system as a command.
|
||||
pub type RunSystem = RunSystemWithInput<()>;
|
||||
|
||||
impl RunSystem {
|
||||
/// Creates a new [`Command`] struct, which can be added to [`Commands`](crate::system::Commands)
|
||||
pub fn new(system_id: SystemId) -> Self {
|
||||
Self { system_id }
|
||||
Self::new_with_input(system_id, ())
|
||||
}
|
||||
}
|
||||
|
||||
impl Command for RunSystem {
|
||||
impl<I: 'static> RunSystemWithInput<I> {
|
||||
/// Creates a new [`Command`] struct, which can be added to [`Commands`](crate::system::Commands)
|
||||
/// in order to run the specified system with the provided [`In<_>`](crate::system::In) input value.
|
||||
pub fn new_with_input(system_id: SystemId<I>, input: I) -> Self {
|
||||
Self { system_id, input }
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: 'static + Send> Command for RunSystemWithInput<I> {
|
||||
#[inline]
|
||||
fn apply(self, world: &mut World) {
|
||||
let _ = world.run_system(self.system_id);
|
||||
let _ = world.run_system_with_input(self.system_id, self.input);
|
||||
}
|
||||
}
|
||||
|
||||
/// An operation with stored systems failed.
|
||||
#[derive(Debug, Error)]
|
||||
pub enum RegisteredSystemError {
|
||||
#[derive(Error)]
|
||||
pub enum RegisteredSystemError<I = (), 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),
|
||||
SystemIdNotRegistered(SystemId<I, O>),
|
||||
/// A system tried to run itself recursively.
|
||||
#[error("System {0:?} tried to run itself recursively")]
|
||||
Recursive(SystemId),
|
||||
Recursive(SystemId<I, O>),
|
||||
/// A system tried to remove itself.
|
||||
#[error("System {0:?} tried to remove itself")]
|
||||
SelfRemove(SystemId),
|
||||
SelfRemove(SystemId<I, O>),
|
||||
}
|
||||
|
||||
impl<I, O> std::fmt::Debug for RegisteredSystemError<I, O> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::SystemIdNotRegistered(arg0) => {
|
||||
f.debug_tuple("SystemIdNotRegistered").field(arg0).finish()
|
||||
}
|
||||
Self::Recursive(arg0) => f.debug_tuple("Recursive").field(arg0).finish(),
|
||||
Self::SelfRemove(arg0) => f.debug_tuple("SelfRemove").field(arg0).finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod tests {
|
||||
|
@ -248,14 +406,14 @@ mod tests {
|
|||
assert_eq!(*world.resource::<Counter>(), Counter(0));
|
||||
// Resources are changed when they are first added.
|
||||
let id = world.register_system(count_up_iff_changed);
|
||||
let _ = world.run_system(id);
|
||||
world.run_system(id).expect("system runs successfully");
|
||||
assert_eq!(*world.resource::<Counter>(), Counter(1));
|
||||
// Nothing changed
|
||||
let _ = world.run_system(id);
|
||||
world.run_system(id).expect("system runs successfully");
|
||||
assert_eq!(*world.resource::<Counter>(), Counter(1));
|
||||
// Making a change
|
||||
world.resource_mut::<ChangeDetector>().set_changed();
|
||||
let _ = world.run_system(id);
|
||||
world.run_system(id).expect("system runs successfully");
|
||||
assert_eq!(*world.resource::<Counter>(), Counter(2));
|
||||
}
|
||||
|
||||
|
@ -271,16 +429,82 @@ mod tests {
|
|||
world.insert_resource(Counter(1));
|
||||
assert_eq!(*world.resource::<Counter>(), Counter(1));
|
||||
let id = world.register_system(doubling);
|
||||
let _ = world.run_system(id);
|
||||
world.run_system(id).expect("system runs successfully");
|
||||
assert_eq!(*world.resource::<Counter>(), Counter(1));
|
||||
let _ = world.run_system(id);
|
||||
world.run_system(id).expect("system runs successfully");
|
||||
assert_eq!(*world.resource::<Counter>(), Counter(2));
|
||||
let _ = world.run_system(id);
|
||||
world.run_system(id).expect("system runs successfully");
|
||||
assert_eq!(*world.resource::<Counter>(), Counter(4));
|
||||
let _ = world.run_system(id);
|
||||
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_input(id, NonCopy(1))
|
||||
.expect("system runs successfully");
|
||||
assert_eq!(*world.resource::<Counter>(), Counter(2));
|
||||
|
||||
world
|
||||
.run_system_with_input(id, NonCopy(1))
|
||||
.expect("system runs successfully");
|
||||
assert_eq!(*world.resource::<Counter>(), Counter(3));
|
||||
|
||||
world
|
||||
.run_system_with_input(id, NonCopy(20))
|
||||
.expect("system runs successfully");
|
||||
assert_eq!(*world.resource::<Counter>(), Counter(23));
|
||||
|
||||
world
|
||||
.run_system_with_input(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 nested_systems() {
|
||||
use crate::system::SystemId;
|
||||
|
@ -310,4 +534,32 @@ mod tests {
|
|||
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<u8>, u8);
|
||||
|
||||
fn nested(query: Query<&Callback>, mut commands: Commands) {
|
||||
for callback in query.iter() {
|
||||
commands.run_system_with_input(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));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue