Skip to content

Commit 6346ccd

Browse files
committed
allow 'run_system' to get system output
1 parent f8df323 commit 6346ccd

File tree

1 file changed

+104
-35
lines changed

1 file changed

+104
-35
lines changed

crates/bevy_ecs/src/system/system_registry.rs

+104-35
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<I> {
10+
struct RegisteredSystem<I, O> {
1111
initialized: bool,
12-
system: BoxedSystem<I>,
12+
system: BoxedSystem<I, O>,
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<I = ()> {
19+
pub struct RemovedSystem<I = (), O = ()> {
2020
initialized: bool,
21-
system: BoxedSystem<I>,
21+
system: BoxedSystem<I, O>,
2222
}
2323

2424
impl RemovedSystem {
@@ -39,29 +39,29 @@ impl RemovedSystem {
3939
/// These are opaque identifiers, keyed to a specific [`World`],
4040
/// and are created via [`World::register_system`].
4141
#[derive(Eq)]
42-
pub struct SystemId<I = ()>(Entity, std::marker::PhantomData<I>);
42+
pub struct SystemId<I = (), O = ()>(Entity, std::marker::PhantomData<fn(I) -> O>);
4343

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> {
44+
// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters.
45+
impl<I, O> Copy for SystemId<I, O> {}
46+
// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters.
47+
impl<I, O> Clone for SystemId<I, O> {
4848
fn clone(&self) -> Self {
4949
*self
5050
}
5151
}
52-
// A manual impl is used because the trait bounds should ignore the `I` phantom parameter.
53-
impl<I> PartialEq for SystemId<I> {
52+
// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters.
53+
impl<I, O> PartialEq for SystemId<I, O> {
5454
fn eq(&self, other: &Self) -> bool {
5555
self.0 == other.0 && self.1 == other.1
5656
}
5757
}
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> {
58+
// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters.
59+
impl<I, O> std::hash::Hash for SystemId<I, O> {
6060
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
6161
self.0.hash(state);
6262
}
6363
}
64-
impl<I> std::fmt::Debug for SystemId<I> {
64+
impl<I, O> std::fmt::Debug for SystemId<I, O> {
6565
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
6666
// The PhantomData field is omitted for simplicity.
6767
f.debug_tuple("SystemId").field(&self.0).finish()
@@ -78,18 +78,21 @@ impl World {
7878
/// This allows for running systems in a pushed-based fashion.
7979
/// Using a [`Schedule`](crate::schedule::Schedule) is still preferred for most cases
8080
/// due to its better performance and abillity to run non-conflicting systems simultaneously.
81-
pub fn register_system<I: 'static, M, S: IntoSystem<I, (), M> + 'static>(
81+
pub fn register_system<I: 'static, O: 'static, M, S: IntoSystem<I, O, M> + 'static>(
8282
&mut self,
8383
system: S,
84-
) -> SystemId<I> {
84+
) -> SystemId<I, O> {
8585
self.register_boxed_system(Box::new(IntoSystem::into_system(system)))
8686
}
8787

8888
/// Similar to [`Self::register_system`], but allows passing in a [`BoxedSystem`].
8989
///
9090
/// This is useful if the [`IntoSystem`] implementor has already been turned into a
9191
/// [`System`](crate::system::System) trait object and put in a [`Box`].
92-
pub fn register_boxed_system<I: 'static>(&mut self, system: BoxedSystem<I>) -> SystemId<I> {
92+
pub fn register_boxed_system<I: 'static, O: 'static>(
93+
&mut self,
94+
system: BoxedSystem<I, O>,
95+
) -> SystemId<I, O> {
9396
SystemId(
9497
self.spawn(RegisteredSystem {
9598
initialized: false,
@@ -106,14 +109,14 @@ impl World {
106109
///
107110
/// If no system corresponds to the given [`SystemId`], this method returns an error.
108111
/// Systems are also not allowed to remove themselves, this returns an error too.
109-
pub fn remove_system<I: 'static>(
112+
pub fn remove_system<I: 'static, O: 'static>(
110113
&mut self,
111-
id: SystemId<I>,
112-
) -> Result<RemovedSystem<I>, RegisteredSystemError<I>> {
114+
id: SystemId<I, O>,
115+
) -> Result<RemovedSystem<I, O>, RegisteredSystemError<I, O>> {
113116
match self.get_entity_mut(id.0) {
114117
Some(mut entity) => {
115118
let registered_system = entity
116-
.take::<RegisteredSystem<I>>()
119+
.take::<RegisteredSystem<I, O>>()
117120
.ok_or(RegisteredSystemError::SelfRemove(id))?;
118121
entity.despawn();
119122
Ok(RemovedSystem {
@@ -140,6 +143,8 @@ impl World {
140143
///
141144
/// # Examples
142145
///
146+
/// ## Running a system
147+
///
143148
/// ```rust
144149
/// # use bevy_ecs::prelude::*;
145150
/// #[derive(Resource, Default)]
@@ -158,7 +163,7 @@ impl World {
158163
/// world.run_system(counter_two); // -> 1
159164
/// ```
160165
///
161-
/// Change detection:
166+
/// ## Change detection
162167
///
163168
/// ```rust
164169
/// # use bevy_ecs::prelude::*;
@@ -181,7 +186,43 @@ impl World {
181186
/// world.resource_mut::<ChangeDetector>().set_changed();
182187
/// let _ = world.run_system(detector); // -> Something happened!
183188
/// ```
184-
pub fn run_system(&mut self, id: SystemId) -> Result<(), RegisteredSystemError> {
189+
///
190+
/// ## Getting system output
191+
///
192+
/// ```rust
193+
/// # use bevy_ecs::prelude::*;
194+
///
195+
/// #[derive(Resource)]
196+
/// struct PlayerScore(i32);
197+
///
198+
/// #[derive(Resource)]
199+
/// struct OpponentScore(i32);
200+
///
201+
/// fn get_player_score(player_score: Res<PlayerScore>) -> i32 {
202+
/// player_score.0
203+
/// }
204+
///
205+
/// fn get_opponent_score(opponent_score: Res<OpponentScore>) -> i32 {
206+
/// opponent_score.0
207+
/// }
208+
///
209+
/// let mut world = World::default();
210+
/// world.insert_resource(PlayerScore(3));
211+
/// world.insert_resource(OpponentScore(2));
212+
///
213+
/// let scoring_systems = [
214+
/// ("player", world.register_system(get_player_score)),
215+
/// ("opponent", world.register_system(get_opponent_score)),
216+
/// ];
217+
///
218+
/// for (label, scoring_system) in scoring_systems {
219+
/// println!("{label} has score {}", world.run_system(scoring_system).expect("system succeeded"));
220+
/// }
221+
/// ```
222+
pub fn run_system<O: 'static>(
223+
&mut self,
224+
id: SystemId<(), O>,
225+
) -> Result<O, RegisteredSystemError<(), O>> {
185226
self.run_system_with_input(id, ())
186227
}
187228

@@ -215,11 +256,11 @@ impl World {
215256
/// ```
216257
///
217258
/// See [`World::run_system`] for more examples.
218-
pub fn run_system_with_input<I: 'static>(
259+
pub fn run_system_with_input<I: 'static, O: 'static>(
219260
&mut self,
220-
id: SystemId<I>,
261+
id: SystemId<I, O>,
221262
input: I,
222-
) -> Result<(), RegisteredSystemError<I>> {
263+
) -> Result<O, RegisteredSystemError<I, O>> {
223264
// lookup
224265
let mut entity = self
225266
.get_entity_mut(id.0)
@@ -230,25 +271,25 @@ impl World {
230271
mut initialized,
231272
mut system,
232273
} = entity
233-
.take::<RegisteredSystem<I>>()
274+
.take::<RegisteredSystem<I, O>>()
234275
.ok_or(RegisteredSystemError::Recursive(id))?;
235276

236277
// run the system
237278
if !initialized {
238279
system.initialize(self);
239280
initialized = true;
240281
}
241-
system.run(input, self);
282+
let result = system.run(input, self);
242283
system.apply_deferred(self);
243284

244285
// return ownership of system trait object (if entity still exists)
245286
if let Some(mut entity) = self.get_entity_mut(id.0) {
246-
entity.insert::<RegisteredSystem<I>>(RegisteredSystem {
287+
entity.insert::<RegisteredSystem<I, O>>(RegisteredSystem {
247288
initialized,
248289
system,
249290
});
250291
}
251-
Ok(())
292+
Ok(result)
252293
}
253294
}
254295

@@ -277,21 +318,21 @@ impl Command for RunSystem {
277318

278319
/// An operation with stored systems failed.
279320
#[derive(Error)]
280-
pub enum RegisteredSystemError<I = ()> {
321+
pub enum RegisteredSystemError<I = (), O = ()> {
281322
/// A system was run by id, but no system with that id was found.
282323
///
283324
/// Did you forget to register it?
284325
#[error("System {0:?} was not registered")]
285-
SystemIdNotRegistered(SystemId<I>),
326+
SystemIdNotRegistered(SystemId<I, O>),
286327
/// A system tried to run itself recursively.
287328
#[error("System {0:?} tried to run itself recursively")]
288-
Recursive(SystemId<I>),
329+
Recursive(SystemId<I, O>),
289330
/// A system tried to remove itself.
290331
#[error("System {0:?} tried to remove itself")]
291-
SelfRemove(SystemId<I>),
332+
SelfRemove(SystemId<I, O>),
292333
}
293334

294-
impl<I> std::fmt::Debug for RegisteredSystemError<I> {
335+
impl<I, O> std::fmt::Debug for RegisteredSystemError<I, O> {
295336
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
296337
match self {
297338
Self::SystemIdNotRegistered(arg0) => {
@@ -401,6 +442,34 @@ mod tests {
401442
assert_eq!(*world.resource::<Counter>(), Counter(24));
402443
}
403444

445+
#[test]
446+
fn output_values() {
447+
// Verify that a non-Copy, non-Clone type can be returned.
448+
#[derive(Eq, PartialEq, Debug)]
449+
struct NonCopy(u8);
450+
451+
fn increment_sys(mut counter: ResMut<Counter>) -> NonCopy {
452+
counter.0 += 1;
453+
NonCopy(counter.0)
454+
}
455+
456+
let mut world = World::new();
457+
458+
let id = world.register_system(increment_sys);
459+
460+
// Insert the resource after registering the system.
461+
world.insert_resource(Counter(1));
462+
assert_eq!(*world.resource::<Counter>(), Counter(1));
463+
464+
let output = world.run_system(id).expect("system runs successfully");
465+
assert_eq!(*world.resource::<Counter>(), Counter(2));
466+
assert_eq!(output, NonCopy(2));
467+
468+
let output = world.run_system(id).expect("system runs successfully");
469+
assert_eq!(*world.resource::<Counter>(), Counter(3));
470+
assert_eq!(output, NonCopy(3));
471+
}
472+
404473
#[test]
405474
fn nested_systems() {
406475
use crate::system::SystemId;

0 commit comments

Comments
 (0)