Skip to content

Commit ea11dd5

Browse files
committed
Add 'World::run_system_with_input' function
1 parent 32a5c7d commit ea11dd5

File tree

1 file changed

+146
-26
lines changed

1 file changed

+146
-26
lines changed

crates/bevy_ecs/src/system/system_registry.rs

+146-26
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,18 @@ use thiserror::Error;
77

88
/// A small wrapper for [`BoxedSystem`] that also keeps track whether or not the system has been initialized.
99
#[derive(Component)]
10-
struct RegisteredSystem {
10+
struct RegisteredSystem<I> {
1111
initialized: bool,
12-
system: BoxedSystem,
12+
system: BoxedSystem<I>,
1313
}
1414

1515
/// A system that has been removed from the registry.
1616
/// It contains the system and whether or not it has been initialized.
1717
///
1818
/// This struct is returned by [`World::remove_system`].
19-
pub struct RemovedSystem {
19+
pub struct RemovedSystem<I = ()> {
2020
initialized: bool,
21-
system: BoxedSystem,
21+
system: BoxedSystem<I>,
2222
}
2323

2424
impl RemovedSystem {
@@ -38,8 +38,35 @@ impl RemovedSystem {
3838
///
3939
/// These are opaque identifiers, keyed to a specific [`World`],
4040
/// and are created via [`World::register_system`].
41-
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
42-
pub struct SystemId(Entity);
41+
#[derive(Eq)]
42+
pub struct SystemId<I = ()>(Entity, std::marker::PhantomData<I>);
43+
44+
// A manual impl is used because the trait bounds should ignore the `I` phantom parameter.
45+
impl<I> Copy for SystemId<I> {}
46+
// A manual impl is used because the trait bounds should ignore the `I` phantom parameter.
47+
impl<I> Clone for SystemId<I> {
48+
fn clone(&self) -> Self {
49+
*self
50+
}
51+
}
52+
// A manual impl is used because the trait bounds should ignore the `I` phantom parameter.
53+
impl<I> PartialEq for SystemId<I> {
54+
fn eq(&self, other: &Self) -> bool {
55+
self.0 == other.0 && self.1 == other.1
56+
}
57+
}
58+
// A manual impl is used because the trait bounds should ignore the `I` phantom parameter.
59+
impl<I> std::hash::Hash for SystemId<I> {
60+
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
61+
self.0.hash(state);
62+
}
63+
}
64+
impl<I> std::fmt::Debug for SystemId<I> {
65+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66+
// The PhantomData field is omitted for simplicity.
67+
f.debug_tuple("SystemId").field(&self.0).finish()
68+
}
69+
}
4370

4471
impl World {
4572
/// Registers a system and returns a [`SystemId`] so it can later be called by [`World::run_system`].
@@ -51,16 +78,17 @@ impl World {
5178
/// This allows for running systems in a pushed-based fashion.
5279
/// Using a [`Schedule`](crate::schedule::Schedule) is still preferred for most cases
5380
/// due to its better performance and abillity to run non-conflicting systems simultaneously.
54-
pub fn register_system<M, S: IntoSystem<(), (), M> + 'static>(
81+
pub fn register_system<I: 'static, M, S: IntoSystem<I, (), M> + 'static>(
5582
&mut self,
5683
system: S,
57-
) -> SystemId {
84+
) -> SystemId<I> {
5885
SystemId(
5986
self.spawn(RegisteredSystem {
6087
initialized: false,
6188
system: Box::new(IntoSystem::into_system(system)),
6289
})
6390
.id(),
91+
std::marker::PhantomData,
6492
)
6593
}
6694

@@ -70,11 +98,14 @@ impl World {
7098
///
7199
/// If no system corresponds to the given [`SystemId`], this method returns an error.
72100
/// Systems are also not allowed to remove themselves, this returns an error too.
73-
pub fn remove_system(&mut self, id: SystemId) -> Result<RemovedSystem, RegisteredSystemError> {
101+
pub fn remove_system<I: 'static>(
102+
&mut self,
103+
id: SystemId<I>,
104+
) -> Result<RemovedSystem<I>, RegisteredSystemError<I>> {
74105
match self.get_entity_mut(id.0) {
75106
Some(mut entity) => {
76107
let registered_system = entity
77-
.take::<RegisteredSystem>()
108+
.take::<RegisteredSystem<I>>()
78109
.ok_or(RegisteredSystemError::SelfRemove(id))?;
79110
entity.despawn();
80111
Ok(RemovedSystem {
@@ -92,9 +123,10 @@ impl World {
92123
/// This is different from [`RunSystemOnce::run_system_once`](crate::system::RunSystemOnce::run_system_once),
93124
/// because it keeps local state between calls and change detection works correctly.
94125
///
126+
/// In order to run a chained system with an input, use [`World::run_system_with_input`] instead.
127+
///
95128
/// # Limitations
96129
///
97-
/// - Stored systems cannot be chained: they can neither have an [`In`](crate::system::In) nor return any values.
98130
/// - Stored systems cannot be recursive, they cannot call themselves through [`Commands::run_system`](crate::system::Commands).
99131
/// - Exclusive systems cannot be used.
100132
///
@@ -142,6 +174,44 @@ impl World {
142174
/// let _ = world.run_system(detector); // -> Something happened!
143175
/// ```
144176
pub fn run_system(&mut self, id: SystemId) -> Result<(), RegisteredSystemError> {
177+
self.run_system_with_input(id, ())
178+
}
179+
180+
/// Run a stored chained system by its [`SystemId`], providing an input value.
181+
/// Before running a system, it must first be registered.
182+
/// The method [`World::register_system`] stores a given system and returns a [`SystemId`].
183+
///
184+
/// # Limitations
185+
///
186+
/// - Stored systems cannot be recursive, they cannot call themselves through [`Commands::run_system`](crate::system::Commands).
187+
/// - Exclusive systems cannot be used.
188+
///
189+
/// # Examples
190+
///
191+
/// ```rust
192+
/// # use bevy_ecs::prelude::*;
193+
/// #[derive(Resource, Default)]
194+
/// struct Counter(u8);
195+
///
196+
/// fn increment(In(increment_by): In<u8> mut counter: Local<Counter>) {
197+
/// counter.0 += increment_by;
198+
/// println!("{}", counter.0);
199+
/// }
200+
///
201+
/// let mut world = World::default();
202+
/// let counter_one = world.register_system(increment);
203+
/// let counter_two = world.register_system(increment);
204+
/// world.run_system_with_input(counter_one, 1); // -> 1
205+
/// world.run_system_with_input(counter_one, 20); // -> 21
206+
/// world.run_system_with_input(counter_two, 30); // -> 51
207+
/// ```
208+
///
209+
/// See [`World::run_system`] for more examples.
210+
pub fn run_system_with_input<I: 'static>(
211+
&mut self,
212+
id: SystemId<I>,
213+
input: I,
214+
) -> Result<(), RegisteredSystemError<I>> {
145215
// lookup
146216
let mut entity = self
147217
.get_entity_mut(id.0)
@@ -152,20 +222,20 @@ impl World {
152222
mut initialized,
153223
mut system,
154224
} = entity
155-
.take::<RegisteredSystem>()
225+
.take::<RegisteredSystem<I>>()
156226
.ok_or(RegisteredSystemError::Recursive(id))?;
157227

158228
// run the system
159229
if !initialized {
160230
system.initialize(self);
161231
initialized = true;
162232
}
163-
system.run((), self);
233+
system.run(input, self);
164234
system.apply_deferred(self);
165235

166236
// return ownership of system trait object (if entity still exists)
167237
if let Some(mut entity) = self.get_entity_mut(id.0) {
168-
entity.insert::<RegisteredSystem>(RegisteredSystem {
238+
entity.insert::<RegisteredSystem<I>>(RegisteredSystem {
169239
initialized,
170240
system,
171241
});
@@ -198,19 +268,31 @@ impl Command for RunSystem {
198268
}
199269

200270
/// An operation with stored systems failed.
201-
#[derive(Debug, Error)]
202-
pub enum RegisteredSystemError {
271+
#[derive(Error)]
272+
pub enum RegisteredSystemError<I = ()> {
203273
/// A system was run by id, but no system with that id was found.
204274
///
205275
/// Did you forget to register it?
206276
#[error("System {0:?} was not registered")]
207-
SystemIdNotRegistered(SystemId),
277+
SystemIdNotRegistered(SystemId<I>),
208278
/// A system tried to run itself recursively.
209279
#[error("System {0:?} tried to run itself recursively")]
210-
Recursive(SystemId),
280+
Recursive(SystemId<I>),
211281
/// A system tried to remove itself.
212282
#[error("System {0:?} tried to remove itself")]
213-
SelfRemove(SystemId),
283+
SelfRemove(SystemId<I>),
284+
}
285+
286+
impl<I> std::fmt::Debug for RegisteredSystemError<I> {
287+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
288+
match self {
289+
Self::SystemIdNotRegistered(arg0) => {
290+
f.debug_tuple("SystemIdNotRegistered").field(arg0).finish()
291+
}
292+
Self::Recursive(arg0) => f.debug_tuple("Recursive").field(arg0).finish(),
293+
Self::SelfRemove(arg0) => f.debug_tuple("SelfRemove").field(arg0).finish(),
294+
}
295+
}
214296
}
215297

216298
mod tests {
@@ -240,14 +322,14 @@ mod tests {
240322
assert_eq!(*world.resource::<Counter>(), Counter(0));
241323
// Resources are changed when they are first added.
242324
let id = world.register_system(count_up_iff_changed);
243-
let _ = world.run_system(id);
325+
world.run_system(id).expect("system runs successfully");
244326
assert_eq!(*world.resource::<Counter>(), Counter(1));
245327
// Nothing changed
246-
let _ = world.run_system(id);
328+
world.run_system(id).expect("system runs successfully");
247329
assert_eq!(*world.resource::<Counter>(), Counter(1));
248330
// Making a change
249331
world.resource_mut::<ChangeDetector>().set_changed();
250-
let _ = world.run_system(id);
332+
world.run_system(id).expect("system runs successfully");
251333
assert_eq!(*world.resource::<Counter>(), Counter(2));
252334
}
253335

@@ -263,16 +345,54 @@ mod tests {
263345
world.insert_resource(Counter(1));
264346
assert_eq!(*world.resource::<Counter>(), Counter(1));
265347
let id = world.register_system(doubling);
266-
let _ = world.run_system(id);
348+
world.run_system(id).expect("system runs successfully");
267349
assert_eq!(*world.resource::<Counter>(), Counter(1));
268-
let _ = world.run_system(id);
350+
world.run_system(id).expect("system runs successfully");
269351
assert_eq!(*world.resource::<Counter>(), Counter(2));
270-
let _ = world.run_system(id);
352+
world.run_system(id).expect("system runs successfully");
271353
assert_eq!(*world.resource::<Counter>(), Counter(4));
272-
let _ = world.run_system(id);
354+
world.run_system(id).expect("system runs successfully");
273355
assert_eq!(*world.resource::<Counter>(), Counter(8));
274356
}
275357

358+
#[test]
359+
fn input_values() {
360+
// Verify that a non-Copy, non-Clone type can be passed in.
361+
struct NonCopy(u8);
362+
363+
fn increment_sys(In(NonCopy(increment_by)): In<NonCopy>, mut counter: ResMut<Counter>) {
364+
counter.0 += increment_by;
365+
}
366+
367+
let mut world = World::new();
368+
369+
let id = world.register_system(increment_sys);
370+
371+
// Insert the resource after registering the system.
372+
world.insert_resource(Counter(1));
373+
assert_eq!(*world.resource::<Counter>(), Counter(1));
374+
375+
world
376+
.run_system_with_input(id, NonCopy(1))
377+
.expect("system runs successfully");
378+
assert_eq!(*world.resource::<Counter>(), Counter(2));
379+
380+
world
381+
.run_system_with_input(id, NonCopy(1))
382+
.expect("system runs successfully");
383+
assert_eq!(*world.resource::<Counter>(), Counter(3));
384+
385+
world
386+
.run_system_with_input(id, NonCopy(20))
387+
.expect("system runs successfully");
388+
assert_eq!(*world.resource::<Counter>(), Counter(23));
389+
390+
world
391+
.run_system_with_input(id, NonCopy(1))
392+
.expect("system runs successfully");
393+
assert_eq!(*world.resource::<Counter>(), Counter(24));
394+
}
395+
276396
#[test]
277397
fn nested_systems() {
278398
use crate::system::SystemId;

0 commit comments

Comments
 (0)