Skip to content

Commit 6f2e243

Browse files
committed
allow 'run_system' to get system output
1 parent 7728b9f commit 6f2e243

File tree

1 file changed

+100
-34
lines changed

1 file changed

+100
-34
lines changed

crates/bevy_ecs/src/system/system_registry.rs

+100-34
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,10 +78,10 @@ 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
SystemId(
8686
self.spawn(RegisteredSystem {
8787
initialized: false,
@@ -98,14 +98,14 @@ impl World {
9898
///
9999
/// If no system corresponds to the given [`SystemId`], this method returns an error.
100100
/// Systems are also not allowed to remove themselves, this returns an error too.
101-
pub fn remove_system<I: 'static>(
101+
pub fn remove_system<I: 'static, O: 'static>(
102102
&mut self,
103-
id: SystemId<I>,
104-
) -> Result<RemovedSystem<I>, RegisteredSystemError<I>> {
103+
id: SystemId<I, O>,
104+
) -> Result<RemovedSystem<I, O>, RegisteredSystemError<I, O>> {
105105
match self.get_entity_mut(id.0) {
106106
Some(mut entity) => {
107107
let registered_system = entity
108-
.take::<RegisteredSystem<I>>()
108+
.take::<RegisteredSystem<I, O>>()
109109
.ok_or(RegisteredSystemError::SelfRemove(id))?;
110110
entity.despawn();
111111
Ok(RemovedSystem {
@@ -132,6 +132,8 @@ impl World {
132132
///
133133
/// # Examples
134134
///
135+
/// ## Running a system
136+
///
135137
/// ```rust
136138
/// # use bevy_ecs::prelude::*;
137139
/// #[derive(Resource, Default)]
@@ -150,7 +152,7 @@ impl World {
150152
/// world.run_system(counter_two); // -> 1
151153
/// ```
152154
///
153-
/// Change detection:
155+
/// ## Change detection
154156
///
155157
/// ```rust
156158
/// # use bevy_ecs::prelude::*;
@@ -173,7 +175,43 @@ impl World {
173175
/// world.resource_mut::<ChangeDetector>().set_changed();
174176
/// let _ = world.run_system(detector); // -> Something happened!
175177
/// ```
176-
pub fn run_system(&mut self, id: SystemId) -> Result<(), RegisteredSystemError> {
178+
///
179+
/// ## Getting system output
180+
///
181+
/// ```rust
182+
/// # use bevy_ecs::prelude::*;
183+
///
184+
/// #[derive(Resource)]
185+
/// struct PlayerScore(i32);
186+
///
187+
/// #[derive(Resource)]
188+
/// struct OpponentScore(i32);
189+
///
190+
/// fn get_player_score(player_score: Res<PlayerScore>) -> i32 {
191+
/// player_score.0
192+
/// }
193+
///
194+
/// fn get_opponent_score(opponent_score: Res<OpponentScore>) -> i32 {
195+
/// opponent_score.0
196+
/// }
197+
///
198+
/// let mut world = World::default();
199+
/// world.insert_resource(PlayerScore(3));
200+
/// world.insert_resource(OpponentScore(2));
201+
///
202+
/// let scoring_systems = [
203+
/// ("player", world.register_system(get_player_score)),
204+
/// ("opponent", world.register_system(get_opponent_score)),
205+
/// ];
206+
///
207+
/// for (label, scoring_system) in scoring_systems {
208+
/// println!("{label} has score {}", world.run_system(scoring_system).expect("system succeeded"));
209+
/// }
210+
/// ```
211+
pub fn run_system<O: 'static>(
212+
&mut self,
213+
id: SystemId<(), O>,
214+
) -> Result<O, RegisteredSystemError<(), O>> {
177215
self.run_system_with_input(id, ())
178216
}
179217

@@ -207,11 +245,11 @@ impl World {
207245
/// ```
208246
///
209247
/// See [`World::run_system`] for more examples.
210-
pub fn run_system_with_input<I: 'static>(
248+
pub fn run_system_with_input<I: 'static, O: 'static>(
211249
&mut self,
212-
id: SystemId<I>,
250+
id: SystemId<I, O>,
213251
input: I,
214-
) -> Result<(), RegisteredSystemError<I>> {
252+
) -> Result<O, RegisteredSystemError<I, O>> {
215253
// lookup
216254
let mut entity = self
217255
.get_entity_mut(id.0)
@@ -222,25 +260,25 @@ impl World {
222260
mut initialized,
223261
mut system,
224262
} = entity
225-
.take::<RegisteredSystem<I>>()
263+
.take::<RegisteredSystem<I, O>>()
226264
.ok_or(RegisteredSystemError::Recursive(id))?;
227265

228266
// run the system
229267
if !initialized {
230268
system.initialize(self);
231269
initialized = true;
232270
}
233-
system.run(input, self);
271+
let result = system.run(input, self);
234272
system.apply_deferred(self);
235273

236274
// return ownership of system trait object (if entity still exists)
237275
if let Some(mut entity) = self.get_entity_mut(id.0) {
238-
entity.insert::<RegisteredSystem<I>>(RegisteredSystem {
276+
entity.insert::<RegisteredSystem<I, O>>(RegisteredSystem {
239277
initialized,
240278
system,
241279
});
242280
}
243-
Ok(())
281+
Ok(result)
244282
}
245283
}
246284

@@ -269,21 +307,21 @@ impl Command for RunSystem {
269307

270308
/// An operation with stored systems failed.
271309
#[derive(Error)]
272-
pub enum RegisteredSystemError<I = ()> {
310+
pub enum RegisteredSystemError<I = (), O = ()> {
273311
/// A system was run by id, but no system with that id was found.
274312
///
275313
/// Did you forget to register it?
276314
#[error("System {0:?} was not registered")]
277-
SystemIdNotRegistered(SystemId<I>),
315+
SystemIdNotRegistered(SystemId<I, O>),
278316
/// A system tried to run itself recursively.
279317
#[error("System {0:?} tried to run itself recursively")]
280-
Recursive(SystemId<I>),
318+
Recursive(SystemId<I, O>),
281319
/// A system tried to remove itself.
282320
#[error("System {0:?} tried to remove itself")]
283-
SelfRemove(SystemId<I>),
321+
SelfRemove(SystemId<I, O>),
284322
}
285323

286-
impl<I> std::fmt::Debug for RegisteredSystemError<I> {
324+
impl<I, O> std::fmt::Debug for RegisteredSystemError<I, O> {
287325
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
288326
match self {
289327
Self::SystemIdNotRegistered(arg0) => {
@@ -393,6 +431,34 @@ mod tests {
393431
assert_eq!(*world.resource::<Counter>(), Counter(24));
394432
}
395433

434+
#[test]
435+
fn output_values() {
436+
// Verify that a non-Copy, non-Clone type can be returned.
437+
#[derive(Eq, PartialEq, Debug)]
438+
struct NonCopy(u8);
439+
440+
fn increment_sys(mut counter: ResMut<Counter>) -> NonCopy {
441+
counter.0 += 1;
442+
NonCopy(counter.0)
443+
}
444+
445+
let mut world = World::new();
446+
447+
let id = world.register_system(increment_sys);
448+
449+
// Insert the resource after registering the system.
450+
world.insert_resource(Counter(1));
451+
assert_eq!(*world.resource::<Counter>(), Counter(1));
452+
453+
let output = world.run_system(id).expect("system runs successfully");
454+
assert_eq!(*world.resource::<Counter>(), Counter(2));
455+
assert_eq!(output, NonCopy(2));
456+
457+
let output = world.run_system(id).expect("system runs successfully");
458+
assert_eq!(*world.resource::<Counter>(), Counter(3));
459+
assert_eq!(output, NonCopy(3));
460+
}
461+
396462
#[test]
397463
fn nested_systems() {
398464
use crate::system::SystemId;

0 commit comments

Comments
 (0)