@@ -7,18 +7,18 @@ use thiserror::Error;
7
7
8
8
/// A small wrapper for [`BoxedSystem`] that also keeps track whether or not the system has been initialized.
9
9
#[ derive( Component ) ]
10
- struct RegisteredSystem < I > {
10
+ struct RegisteredSystem < I , O > {
11
11
initialized : bool ,
12
- system : BoxedSystem < I > ,
12
+ system : BoxedSystem < I , O > ,
13
13
}
14
14
15
15
/// A system that has been removed from the registry.
16
16
/// It contains the system and whether or not it has been initialized.
17
17
///
18
18
/// This struct is returned by [`World::remove_system`].
19
- pub struct RemovedSystem < I = ( ) > {
19
+ pub struct RemovedSystem < I = ( ) , O = ( ) > {
20
20
initialized : bool ,
21
- system : BoxedSystem < I > ,
21
+ system : BoxedSystem < I , O > ,
22
22
}
23
23
24
24
impl RemovedSystem {
@@ -39,29 +39,29 @@ impl RemovedSystem {
39
39
/// These are opaque identifiers, keyed to a specific [`World`],
40
40
/// and are created via [`World::register_system`].
41
41
#[ 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 > ) ;
43
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 > {
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 > {
48
48
fn clone ( & self ) -> Self {
49
49
* self
50
50
}
51
51
}
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 > {
54
54
fn eq ( & self , other : & Self ) -> bool {
55
55
self . 0 == other. 0 && self . 1 == other. 1
56
56
}
57
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 > {
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 > {
60
60
fn hash < H : std:: hash:: Hasher > ( & self , state : & mut H ) {
61
61
self . 0 . hash ( state) ;
62
62
}
63
63
}
64
- impl < I > std:: fmt:: Debug for SystemId < I > {
64
+ impl < I , O > std:: fmt:: Debug for SystemId < I , O > {
65
65
fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
66
66
// The PhantomData field is omitted for simplicity.
67
67
f. debug_tuple ( "SystemId" ) . field ( & self . 0 ) . finish ( )
@@ -78,18 +78,21 @@ impl World {
78
78
/// This allows for running systems in a pushed-based fashion.
79
79
/// Using a [`Schedule`](crate::schedule::Schedule) is still preferred for most cases
80
80
/// 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 > (
82
82
& mut self ,
83
83
system : S ,
84
- ) -> SystemId < I > {
84
+ ) -> SystemId < I , O > {
85
85
self . register_boxed_system ( Box :: new ( IntoSystem :: into_system ( system) ) )
86
86
}
87
87
88
88
/// Similar to [`Self::register_system`], but allows passing in a [`BoxedSystem`].
89
89
///
90
90
/// This is useful if the [`IntoSystem`] implementor has already been turned into a
91
91
/// [`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 > {
93
96
SystemId (
94
97
self . spawn ( RegisteredSystem {
95
98
initialized : false ,
@@ -106,14 +109,14 @@ impl World {
106
109
///
107
110
/// If no system corresponds to the given [`SystemId`], this method returns an error.
108
111
/// 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 > (
110
113
& 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 > > {
113
116
match self . get_entity_mut ( id. 0 ) {
114
117
Some ( mut entity) => {
115
118
let registered_system = entity
116
- . take :: < RegisteredSystem < I > > ( )
119
+ . take :: < RegisteredSystem < I , O > > ( )
117
120
. ok_or ( RegisteredSystemError :: SelfRemove ( id) ) ?;
118
121
entity. despawn ( ) ;
119
122
Ok ( RemovedSystem {
@@ -140,6 +143,8 @@ impl World {
140
143
///
141
144
/// # Examples
142
145
///
146
+ /// ## Running a system
147
+ ///
143
148
/// ```rust
144
149
/// # use bevy_ecs::prelude::*;
145
150
/// #[derive(Resource, Default)]
@@ -158,7 +163,7 @@ impl World {
158
163
/// world.run_system(counter_two); // -> 1
159
164
/// ```
160
165
///
161
- /// Change detection:
166
+ /// ## Change detection
162
167
///
163
168
/// ```rust
164
169
/// # use bevy_ecs::prelude::*;
@@ -181,7 +186,43 @@ impl World {
181
186
/// world.resource_mut::<ChangeDetector>().set_changed();
182
187
/// let _ = world.run_system(detector); // -> Something happened!
183
188
/// ```
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 > > {
185
226
self . run_system_with_input ( id, ( ) )
186
227
}
187
228
@@ -215,11 +256,11 @@ impl World {
215
256
/// ```
216
257
///
217
258
/// 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 > (
219
260
& mut self ,
220
- id : SystemId < I > ,
261
+ id : SystemId < I , O > ,
221
262
input : I ,
222
- ) -> Result < ( ) , RegisteredSystemError < I > > {
263
+ ) -> Result < O , RegisteredSystemError < I , O > > {
223
264
// lookup
224
265
let mut entity = self
225
266
. get_entity_mut ( id. 0 )
@@ -230,25 +271,25 @@ impl World {
230
271
mut initialized,
231
272
mut system,
232
273
} = entity
233
- . take :: < RegisteredSystem < I > > ( )
274
+ . take :: < RegisteredSystem < I , O > > ( )
234
275
. ok_or ( RegisteredSystemError :: Recursive ( id) ) ?;
235
276
236
277
// run the system
237
278
if !initialized {
238
279
system. initialize ( self ) ;
239
280
initialized = true ;
240
281
}
241
- system. run ( input, self ) ;
282
+ let result = system. run ( input, self ) ;
242
283
system. apply_deferred ( self ) ;
243
284
244
285
// return ownership of system trait object (if entity still exists)
245
286
if let Some ( mut entity) = self . get_entity_mut ( id. 0 ) {
246
- entity. insert :: < RegisteredSystem < I > > ( RegisteredSystem {
287
+ entity. insert :: < RegisteredSystem < I , O > > ( RegisteredSystem {
247
288
initialized,
248
289
system,
249
290
} ) ;
250
291
}
251
- Ok ( ( ) )
292
+ Ok ( result )
252
293
}
253
294
}
254
295
@@ -277,21 +318,21 @@ impl Command for RunSystem {
277
318
278
319
/// An operation with stored systems failed.
279
320
#[ derive( Error ) ]
280
- pub enum RegisteredSystemError < I = ( ) > {
321
+ pub enum RegisteredSystemError < I = ( ) , O = ( ) > {
281
322
/// A system was run by id, but no system with that id was found.
282
323
///
283
324
/// Did you forget to register it?
284
325
#[ error( "System {0:?} was not registered" ) ]
285
- SystemIdNotRegistered ( SystemId < I > ) ,
326
+ SystemIdNotRegistered ( SystemId < I , O > ) ,
286
327
/// A system tried to run itself recursively.
287
328
#[ error( "System {0:?} tried to run itself recursively" ) ]
288
- Recursive ( SystemId < I > ) ,
329
+ Recursive ( SystemId < I , O > ) ,
289
330
/// A system tried to remove itself.
290
331
#[ error( "System {0:?} tried to remove itself" ) ]
291
- SelfRemove ( SystemId < I > ) ,
332
+ SelfRemove ( SystemId < I , O > ) ,
292
333
}
293
334
294
- impl < I > std:: fmt:: Debug for RegisteredSystemError < I > {
335
+ impl < I , O > std:: fmt:: Debug for RegisteredSystemError < I , O > {
295
336
fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
296
337
match self {
297
338
Self :: SystemIdNotRegistered ( arg0) => {
@@ -401,6 +442,34 @@ mod tests {
401
442
assert_eq ! ( * world. resource:: <Counter >( ) , Counter ( 24 ) ) ;
402
443
}
403
444
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
+
404
473
#[ test]
405
474
fn nested_systems ( ) {
406
475
use crate :: system:: SystemId ;
0 commit comments