Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_ecs/src/system/system_registry.rs
6604 views
1
#[cfg(feature = "hotpatching")]
2
use crate::{change_detection::DetectChanges, HotPatchChanges};
3
use crate::{
4
change_detection::Mut,
5
entity::Entity,
6
entity_disabling::Internal,
7
error::BevyError,
8
system::{
9
input::SystemInput, BoxedSystem, IntoSystem, RunSystemError, SystemParamValidationError,
10
},
11
world::World,
12
};
13
use alloc::boxed::Box;
14
use bevy_ecs_macros::{Component, Resource};
15
use bevy_utils::prelude::DebugName;
16
use core::{any::TypeId, marker::PhantomData};
17
use thiserror::Error;
18
19
/// A small wrapper for [`BoxedSystem`] that also keeps track whether or not the system has been initialized.
20
#[derive(Component)]
21
#[require(SystemIdMarker = SystemIdMarker::typed_system_id_marker::<I, O>(), Internal)]
22
pub(crate) struct RegisteredSystem<I, O> {
23
initialized: bool,
24
system: BoxedSystem<I, O>,
25
}
26
27
impl<I, O> RegisteredSystem<I, O> {
28
pub fn new(system: BoxedSystem<I, O>) -> Self {
29
RegisteredSystem {
30
initialized: false,
31
system,
32
}
33
}
34
}
35
36
#[derive(Debug, Clone)]
37
struct TypeIdAndName {
38
type_id: TypeId,
39
name: DebugName,
40
}
41
42
impl TypeIdAndName {
43
fn new<T: 'static>() -> Self {
44
Self {
45
type_id: TypeId::of::<T>(),
46
name: DebugName::type_name::<T>(),
47
}
48
}
49
}
50
51
impl Default for TypeIdAndName {
52
fn default() -> Self {
53
Self {
54
type_id: TypeId::of::<()>(),
55
name: DebugName::type_name::<()>(),
56
}
57
}
58
}
59
60
/// Marker [`Component`](bevy_ecs::component::Component) for identifying [`SystemId`] [`Entity`]s.
61
#[derive(Debug, Default, Clone, Component)]
62
pub struct SystemIdMarker {
63
input_type_id: TypeIdAndName,
64
output_type_id: TypeIdAndName,
65
}
66
67
impl SystemIdMarker {
68
fn typed_system_id_marker<I: 'static, O: 'static>() -> Self {
69
Self {
70
input_type_id: TypeIdAndName::new::<I>(),
71
output_type_id: TypeIdAndName::new::<O>(),
72
}
73
}
74
}
75
76
/// A system that has been removed from the registry.
77
/// It contains the system and whether or not it has been initialized.
78
///
79
/// This struct is returned by [`World::unregister_system`].
80
pub struct RemovedSystem<I = (), O = ()> {
81
initialized: bool,
82
system: BoxedSystem<I, O>,
83
}
84
85
impl<I, O> RemovedSystem<I, O> {
86
/// Is the system initialized?
87
/// A system is initialized the first time it's ran.
88
pub fn initialized(&self) -> bool {
89
self.initialized
90
}
91
92
/// The system removed from the storage.
93
pub fn system(self) -> BoxedSystem<I, O> {
94
self.system
95
}
96
}
97
98
/// An identifier for a registered system.
99
///
100
/// These are opaque identifiers, keyed to a specific [`World`],
101
/// and are created via [`World::register_system`].
102
pub struct SystemId<I: SystemInput = (), O = ()> {
103
pub(crate) entity: Entity,
104
pub(crate) marker: PhantomData<fn(I) -> O>,
105
}
106
107
impl<I: SystemInput, O> SystemId<I, O> {
108
/// Transforms a [`SystemId`] into the [`Entity`] that holds the one-shot system's state.
109
///
110
/// It's trivial to convert [`SystemId`] into an [`Entity`] since a one-shot system
111
/// is really an entity with associated handler function.
112
///
113
/// For example, this is useful if you want to assign a name label to a system.
114
pub fn entity(self) -> Entity {
115
self.entity
116
}
117
118
/// Create [`SystemId`] from an [`Entity`]. Useful when you only have entity handles to avoid
119
/// adding extra components that have a [`SystemId`] everywhere. To run a system with this ID
120
/// - The entity must be a system
121
/// - The `I` + `O` types must be correct
122
pub fn from_entity(entity: Entity) -> Self {
123
Self {
124
entity,
125
marker: PhantomData,
126
}
127
}
128
}
129
130
impl<I: SystemInput, O> Eq for SystemId<I, O> {}
131
132
// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters.
133
impl<I: SystemInput, O> Copy for SystemId<I, O> {}
134
135
// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters.
136
impl<I: SystemInput, O> Clone for SystemId<I, O> {
137
fn clone(&self) -> Self {
138
*self
139
}
140
}
141
142
// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters.
143
impl<I: SystemInput, O> PartialEq for SystemId<I, O> {
144
fn eq(&self, other: &Self) -> bool {
145
self.entity == other.entity && self.marker == other.marker
146
}
147
}
148
149
// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters.
150
impl<I: SystemInput, O> core::hash::Hash for SystemId<I, O> {
151
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
152
self.entity.hash(state);
153
}
154
}
155
156
impl<I: SystemInput, O> core::fmt::Debug for SystemId<I, O> {
157
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
158
f.debug_tuple("SystemId").field(&self.entity).finish()
159
}
160
}
161
162
/// A cached [`SystemId`] distinguished by the unique function type of its system.
163
///
164
/// This resource is inserted by [`World::register_system_cached`].
165
#[derive(Resource)]
166
pub struct CachedSystemId<S> {
167
/// The cached `SystemId` as an `Entity`.
168
pub entity: Entity,
169
_marker: PhantomData<fn() -> S>,
170
}
171
172
impl<S> CachedSystemId<S> {
173
/// Creates a new `CachedSystemId` struct given a `SystemId`.
174
pub fn new<I: SystemInput, O>(id: SystemId<I, O>) -> Self {
175
Self {
176
entity: id.entity(),
177
_marker: PhantomData,
178
}
179
}
180
}
181
182
impl World {
183
/// Registers a system and returns a [`SystemId`] so it can later be called by [`World::run_system`].
184
///
185
/// It's possible to register multiple copies of the same system by calling this function
186
/// multiple times. If that's not what you want, consider using [`World::register_system_cached`]
187
/// instead.
188
///
189
/// This is different from adding systems to a [`Schedule`](crate::schedule::Schedule),
190
/// because the [`SystemId`] that is returned can be used anywhere in the [`World`] to run the associated system.
191
/// This allows for running systems in a pushed-based fashion.
192
/// Using a [`Schedule`](crate::schedule::Schedule) is still preferred for most cases
193
/// due to its better performance and ability to run non-conflicting systems simultaneously.
194
pub fn register_system<I, O, M>(
195
&mut self,
196
system: impl IntoSystem<I, O, M> + 'static,
197
) -> SystemId<I, O>
198
where
199
I: SystemInput + 'static,
200
O: 'static,
201
{
202
self.register_boxed_system(Box::new(IntoSystem::into_system(system)))
203
}
204
205
/// Similar to [`Self::register_system`], but allows passing in a [`BoxedSystem`].
206
///
207
/// This is useful if the [`IntoSystem`] implementor has already been turned into a
208
/// [`System`](crate::system::System) trait object and put in a [`Box`].
209
pub fn register_boxed_system<I, O>(&mut self, system: BoxedSystem<I, O>) -> SystemId<I, O>
210
where
211
I: SystemInput + 'static,
212
O: 'static,
213
{
214
let entity = self.spawn(RegisteredSystem::new(system)).id();
215
SystemId::from_entity(entity)
216
}
217
218
/// Removes a registered system and returns the system, if it exists.
219
/// After removing a system, the [`SystemId`] becomes invalid and attempting to use it afterwards will result in errors.
220
/// Re-adding the removed system will register it on a new [`SystemId`].
221
///
222
/// If no system corresponds to the given [`SystemId`], this method returns an error.
223
/// Systems are also not allowed to remove themselves, this returns an error too.
224
pub fn unregister_system<I, O>(
225
&mut self,
226
id: SystemId<I, O>,
227
) -> Result<RemovedSystem<I, O>, RegisteredSystemError<I, O>>
228
where
229
I: SystemInput + 'static,
230
O: 'static,
231
{
232
match self.get_entity_mut(id.entity) {
233
Ok(mut entity) => {
234
let registered_system = entity
235
.take::<RegisteredSystem<I, O>>()
236
.ok_or(RegisteredSystemError::SelfRemove(id))?;
237
entity.despawn();
238
Ok(RemovedSystem {
239
initialized: registered_system.initialized,
240
system: registered_system.system,
241
})
242
}
243
Err(_) => Err(RegisteredSystemError::SystemIdNotRegistered(id)),
244
}
245
}
246
247
/// Run stored systems by their [`SystemId`].
248
/// Before running a system, it must first be registered.
249
/// The method [`World::register_system`] stores a given system and returns a [`SystemId`].
250
/// This is different from [`RunSystemOnce::run_system_once`](crate::system::RunSystemOnce::run_system_once),
251
/// because it keeps local state between calls and change detection works correctly.
252
///
253
/// Also runs any queued-up commands.
254
///
255
/// In order to run a chained system with an input, use [`World::run_system_with`] instead.
256
///
257
/// # Examples
258
///
259
/// ## Running a system
260
///
261
/// ```
262
/// # use bevy_ecs::prelude::*;
263
/// fn increment(mut counter: Local<u8>) {
264
/// *counter += 1;
265
/// println!("{}", *counter);
266
/// }
267
///
268
/// let mut world = World::default();
269
/// let counter_one = world.register_system(increment);
270
/// let counter_two = world.register_system(increment);
271
/// world.run_system(counter_one); // -> 1
272
/// world.run_system(counter_one); // -> 2
273
/// world.run_system(counter_two); // -> 1
274
/// ```
275
///
276
/// ## Change detection
277
///
278
/// ```
279
/// # use bevy_ecs::prelude::*;
280
/// #[derive(Resource, Default)]
281
/// struct ChangeDetector;
282
///
283
/// let mut world = World::default();
284
/// world.init_resource::<ChangeDetector>();
285
/// let detector = world.register_system(|change_detector: ResMut<ChangeDetector>| {
286
/// if change_detector.is_changed() {
287
/// println!("Something happened!");
288
/// } else {
289
/// println!("Nothing happened.");
290
/// }
291
/// });
292
///
293
/// // Resources are changed when they are first added
294
/// let _ = world.run_system(detector); // -> Something happened!
295
/// let _ = world.run_system(detector); // -> Nothing happened.
296
/// world.resource_mut::<ChangeDetector>().set_changed();
297
/// let _ = world.run_system(detector); // -> Something happened!
298
/// ```
299
///
300
/// ## Getting system output
301
///
302
/// ```
303
/// # use bevy_ecs::prelude::*;
304
///
305
/// #[derive(Resource)]
306
/// struct PlayerScore(i32);
307
///
308
/// #[derive(Resource)]
309
/// struct OpponentScore(i32);
310
///
311
/// fn get_player_score(player_score: Res<PlayerScore>) -> i32 {
312
/// player_score.0
313
/// }
314
///
315
/// fn get_opponent_score(opponent_score: Res<OpponentScore>) -> i32 {
316
/// opponent_score.0
317
/// }
318
///
319
/// let mut world = World::default();
320
/// world.insert_resource(PlayerScore(3));
321
/// world.insert_resource(OpponentScore(2));
322
///
323
/// let scoring_systems = [
324
/// ("player", world.register_system(get_player_score)),
325
/// ("opponent", world.register_system(get_opponent_score)),
326
/// ];
327
///
328
/// for (label, scoring_system) in scoring_systems {
329
/// println!("{label} has score {}", world.run_system(scoring_system).expect("system succeeded"));
330
/// }
331
/// ```
332
pub fn run_system<O: 'static>(
333
&mut self,
334
id: SystemId<(), O>,
335
) -> Result<O, RegisteredSystemError<(), O>> {
336
self.run_system_with(id, ())
337
}
338
339
/// Run a stored chained system by its [`SystemId`], providing an input value.
340
/// Before running a system, it must first be registered.
341
/// The method [`World::register_system`] stores a given system and returns a [`SystemId`].
342
///
343
/// Also runs any queued-up commands.
344
///
345
/// # Examples
346
///
347
/// ```
348
/// # use bevy_ecs::prelude::*;
349
/// fn increment(In(increment_by): In<u8>, mut counter: Local<u8>) -> u8 {
350
/// *counter += increment_by;
351
/// *counter
352
/// }
353
///
354
/// let mut world = World::default();
355
/// let counter_one = world.register_system(increment);
356
/// let counter_two = world.register_system(increment);
357
/// assert_eq!(world.run_system_with(counter_one, 1).unwrap(), 1);
358
/// assert_eq!(world.run_system_with(counter_one, 20).unwrap(), 21);
359
/// assert_eq!(world.run_system_with(counter_two, 30).unwrap(), 30);
360
/// ```
361
///
362
/// See [`World::run_system`] for more examples.
363
pub fn run_system_with<I, O>(
364
&mut self,
365
id: SystemId<I, O>,
366
input: I::Inner<'_>,
367
) -> Result<O, RegisteredSystemError<I, O>>
368
where
369
I: SystemInput + 'static,
370
O: 'static,
371
{
372
// Lookup
373
let mut entity = self
374
.get_entity_mut(id.entity)
375
.map_err(|_| RegisteredSystemError::SystemIdNotRegistered(id))?;
376
377
// Take ownership of system trait object
378
let Some(RegisteredSystem {
379
mut initialized,
380
mut system,
381
}) = entity.take::<RegisteredSystem<I, O>>()
382
else {
383
let Some(system_id_marker) = entity.get::<SystemIdMarker>() else {
384
return Err(RegisteredSystemError::SystemIdNotRegistered(id));
385
};
386
if system_id_marker.input_type_id.type_id != TypeId::of::<I>()
387
|| system_id_marker.output_type_id.type_id != TypeId::of::<O>()
388
{
389
return Err(RegisteredSystemError::IncorrectType(
390
id,
391
system_id_marker.clone(),
392
));
393
}
394
return Err(RegisteredSystemError::Recursive(id));
395
};
396
397
// Initialize the system
398
if !initialized {
399
system.initialize(self);
400
initialized = true;
401
}
402
403
// refresh hotpatches for stored systems
404
#[cfg(feature = "hotpatching")]
405
if self
406
.get_resource_ref::<HotPatchChanges>()
407
.map(|r| r.last_changed())
408
.unwrap_or_default()
409
.is_newer_than(system.get_last_run(), self.change_tick())
410
{
411
system.refresh_hotpatch();
412
}
413
414
// Wait to run the commands until the system is available again.
415
// This is needed so the systems can recursively run themselves.
416
let result = system.run_without_applying_deferred(input, self);
417
system.queue_deferred(self.into());
418
419
// Return ownership of system trait object (if entity still exists)
420
if let Ok(mut entity) = self.get_entity_mut(id.entity) {
421
entity.insert::<RegisteredSystem<I, O>>(RegisteredSystem {
422
initialized,
423
system,
424
});
425
}
426
427
// Run any commands enqueued by the system
428
self.flush();
429
Ok(result?)
430
}
431
432
/// Registers a system or returns its cached [`SystemId`].
433
///
434
/// If you want to run the system immediately and you don't need its `SystemId`, see
435
/// [`World::run_system_cached`].
436
///
437
/// The first time this function is called for a particular system, it will register it and
438
/// store its [`SystemId`] in a [`CachedSystemId`] resource for later. If you would rather
439
/// manage the `SystemId` yourself, or register multiple copies of the same system, use
440
/// [`World::register_system`] instead.
441
///
442
/// # Limitations
443
///
444
/// This function only accepts ZST (zero-sized) systems to guarantee that any two systems of
445
/// the same type must be equal. This means that closures that capture the environment, and
446
/// function pointers, are not accepted.
447
///
448
/// If you want to access values from the environment within a system, consider passing them in
449
/// as inputs via [`World::run_system_cached_with`]. If that's not an option, consider
450
/// [`World::register_system`] instead.
451
pub fn register_system_cached<I, O, M, S>(&mut self, system: S) -> SystemId<I, O>
452
where
453
I: SystemInput + 'static,
454
O: 'static,
455
S: IntoSystem<I, O, M> + 'static,
456
{
457
const {
458
assert!(
459
size_of::<S>() == 0,
460
"Non-ZST systems (e.g. capturing closures, function pointers) cannot be cached.",
461
);
462
}
463
464
if !self.contains_resource::<CachedSystemId<S>>() {
465
let id = self.register_system(system);
466
self.insert_resource(CachedSystemId::<S>::new(id));
467
return id;
468
}
469
470
self.resource_scope(|world, mut id: Mut<CachedSystemId<S>>| {
471
if let Ok(mut entity) = world.get_entity_mut(id.entity) {
472
if !entity.contains::<RegisteredSystem<I, O>>() {
473
entity.insert(RegisteredSystem::new(Box::new(IntoSystem::into_system(
474
system,
475
))));
476
}
477
} else {
478
id.entity = world.register_system(system).entity();
479
}
480
SystemId::from_entity(id.entity)
481
})
482
}
483
484
/// Removes a cached system and its [`CachedSystemId`] resource.
485
///
486
/// See [`World::register_system_cached`] for more information.
487
pub fn unregister_system_cached<I, O, M, S>(
488
&mut self,
489
_system: S,
490
) -> Result<RemovedSystem<I, O>, RegisteredSystemError<I, O>>
491
where
492
I: SystemInput + 'static,
493
O: 'static,
494
S: IntoSystem<I, O, M> + 'static,
495
{
496
let id = self
497
.remove_resource::<CachedSystemId<S>>()
498
.ok_or(RegisteredSystemError::SystemNotCached)?;
499
self.unregister_system(SystemId::<I, O>::from_entity(id.entity))
500
}
501
502
/// Runs a cached system, registering it if necessary.
503
///
504
/// See [`World::register_system_cached`] for more information.
505
pub fn run_system_cached<O: 'static, M, S: IntoSystem<(), O, M> + 'static>(
506
&mut self,
507
system: S,
508
) -> Result<O, RegisteredSystemError<(), O>> {
509
self.run_system_cached_with(system, ())
510
}
511
512
/// Runs a cached system with an input, registering it if necessary.
513
///
514
/// See [`World::register_system_cached`] for more information.
515
pub fn run_system_cached_with<I, O, M, S>(
516
&mut self,
517
system: S,
518
input: I::Inner<'_>,
519
) -> Result<O, RegisteredSystemError<I, O>>
520
where
521
I: SystemInput + 'static,
522
O: 'static,
523
S: IntoSystem<I, O, M> + 'static,
524
{
525
let id = self.register_system_cached(system);
526
self.run_system_with(id, input)
527
}
528
}
529
530
/// An operation with stored systems failed.
531
#[derive(Error)]
532
pub enum RegisteredSystemError<I: SystemInput = (), O = ()> {
533
/// A system was run by id, but no system with that id was found.
534
///
535
/// Did you forget to register it?
536
#[error("System {0:?} was not registered")]
537
SystemIdNotRegistered(SystemId<I, O>),
538
/// A cached system was removed by value, but no system with its type was found.
539
///
540
/// Did you forget to register it?
541
#[error("Cached system was not found")]
542
SystemNotCached,
543
/// A system tried to run itself recursively.
544
#[error("System {0:?} tried to run itself recursively")]
545
Recursive(SystemId<I, O>),
546
/// A system tried to remove itself.
547
#[error("System {0:?} tried to remove itself")]
548
SelfRemove(SystemId<I, O>),
549
/// System could not be run due to parameters that failed validation.
550
/// This is not considered an error.
551
#[error("System did not run due to failed parameter validation: {0}")]
552
Skipped(SystemParamValidationError),
553
/// System returned an error or failed required parameter validation.
554
#[error("System returned error: {0}")]
555
Failed(BevyError),
556
/// [`SystemId`] had different input and/or output types than [`SystemIdMarker`]
557
#[error("Could not get system from `{}`, entity was `SystemId<{}, {}>`", DebugName::type_name::<SystemId<I, O>>(), .1.input_type_id.name, .1.output_type_id.name)]
558
IncorrectType(SystemId<I, O>, SystemIdMarker),
559
}
560
561
impl<I: SystemInput, O> From<RunSystemError> for RegisteredSystemError<I, O> {
562
fn from(value: RunSystemError) -> Self {
563
match value {
564
RunSystemError::Skipped(err) => Self::Skipped(err),
565
RunSystemError::Failed(err) => Self::Failed(err),
566
}
567
}
568
}
569
570
impl<I: SystemInput, O> core::fmt::Debug for RegisteredSystemError<I, O> {
571
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
572
match self {
573
Self::SystemIdNotRegistered(arg0) => {
574
f.debug_tuple("SystemIdNotRegistered").field(arg0).finish()
575
}
576
Self::SystemNotCached => write!(f, "SystemNotCached"),
577
Self::Recursive(arg0) => f.debug_tuple("Recursive").field(arg0).finish(),
578
Self::SelfRemove(arg0) => f.debug_tuple("SelfRemove").field(arg0).finish(),
579
Self::Skipped(arg0) => f.debug_tuple("Skipped").field(arg0).finish(),
580
Self::Failed(arg0) => f.debug_tuple("Failed").field(arg0).finish(),
581
Self::IncorrectType(arg0, arg1) => f
582
.debug_tuple("IncorrectType")
583
.field(arg0)
584
.field(arg1)
585
.finish(),
586
}
587
}
588
}
589
590
#[cfg(test)]
591
mod tests {
592
use core::cell::Cell;
593
594
use bevy_utils::default;
595
596
use crate::{
597
prelude::*,
598
system::{RegisteredSystemError, SystemId},
599
};
600
601
#[derive(Resource, Default, PartialEq, Debug)]
602
struct Counter(u8);
603
604
#[test]
605
fn change_detection() {
606
#[derive(Resource, Default)]
607
struct ChangeDetector;
608
609
fn count_up_iff_changed(
610
mut counter: ResMut<Counter>,
611
change_detector: ResMut<ChangeDetector>,
612
) {
613
if change_detector.is_changed() {
614
counter.0 += 1;
615
}
616
}
617
618
let mut world = World::new();
619
world.init_resource::<ChangeDetector>();
620
world.init_resource::<Counter>();
621
assert_eq!(*world.resource::<Counter>(), Counter(0));
622
// Resources are changed when they are first added.
623
let id = world.register_system(count_up_iff_changed);
624
world.run_system(id).expect("system runs successfully");
625
assert_eq!(*world.resource::<Counter>(), Counter(1));
626
// Nothing changed
627
world.run_system(id).expect("system runs successfully");
628
assert_eq!(*world.resource::<Counter>(), Counter(1));
629
// Making a change
630
world.resource_mut::<ChangeDetector>().set_changed();
631
world.run_system(id).expect("system runs successfully");
632
assert_eq!(*world.resource::<Counter>(), Counter(2));
633
}
634
635
#[test]
636
fn local_variables() {
637
// The `Local` begins at the default value of 0
638
fn doubling(last_counter: Local<Counter>, mut counter: ResMut<Counter>) {
639
counter.0 += last_counter.0 .0;
640
last_counter.0 .0 = counter.0;
641
}
642
643
let mut world = World::new();
644
world.insert_resource(Counter(1));
645
assert_eq!(*world.resource::<Counter>(), Counter(1));
646
let id = world.register_system(doubling);
647
world.run_system(id).expect("system runs successfully");
648
assert_eq!(*world.resource::<Counter>(), Counter(1));
649
world.run_system(id).expect("system runs successfully");
650
assert_eq!(*world.resource::<Counter>(), Counter(2));
651
world.run_system(id).expect("system runs successfully");
652
assert_eq!(*world.resource::<Counter>(), Counter(4));
653
world.run_system(id).expect("system runs successfully");
654
assert_eq!(*world.resource::<Counter>(), Counter(8));
655
}
656
657
#[test]
658
fn input_values() {
659
// Verify that a non-Copy, non-Clone type can be passed in.
660
struct NonCopy(u8);
661
662
fn increment_sys(In(NonCopy(increment_by)): In<NonCopy>, mut counter: ResMut<Counter>) {
663
counter.0 += increment_by;
664
}
665
666
let mut world = World::new();
667
668
let id = world.register_system(increment_sys);
669
670
// Insert the resource after registering the system.
671
world.insert_resource(Counter(1));
672
assert_eq!(*world.resource::<Counter>(), Counter(1));
673
674
world
675
.run_system_with(id, NonCopy(1))
676
.expect("system runs successfully");
677
assert_eq!(*world.resource::<Counter>(), Counter(2));
678
679
world
680
.run_system_with(id, NonCopy(1))
681
.expect("system runs successfully");
682
assert_eq!(*world.resource::<Counter>(), Counter(3));
683
684
world
685
.run_system_with(id, NonCopy(20))
686
.expect("system runs successfully");
687
assert_eq!(*world.resource::<Counter>(), Counter(23));
688
689
world
690
.run_system_with(id, NonCopy(1))
691
.expect("system runs successfully");
692
assert_eq!(*world.resource::<Counter>(), Counter(24));
693
}
694
695
#[test]
696
fn output_values() {
697
// Verify that a non-Copy, non-Clone type can be returned.
698
#[derive(Eq, PartialEq, Debug)]
699
struct NonCopy(u8);
700
701
fn increment_sys(mut counter: ResMut<Counter>) -> NonCopy {
702
counter.0 += 1;
703
NonCopy(counter.0)
704
}
705
706
let mut world = World::new();
707
708
let id = world.register_system(increment_sys);
709
710
// Insert the resource after registering the system.
711
world.insert_resource(Counter(1));
712
assert_eq!(*world.resource::<Counter>(), Counter(1));
713
714
let output = world.run_system(id).expect("system runs successfully");
715
assert_eq!(*world.resource::<Counter>(), Counter(2));
716
assert_eq!(output, NonCopy(2));
717
718
let output = world.run_system(id).expect("system runs successfully");
719
assert_eq!(*world.resource::<Counter>(), Counter(3));
720
assert_eq!(output, NonCopy(3));
721
}
722
723
#[test]
724
fn fallible_system() {
725
fn sys() -> Result<()> {
726
Err("error")?;
727
Ok(())
728
}
729
730
let mut world = World::new();
731
let fallible_system_id = world.register_system(sys);
732
let output = world.run_system(fallible_system_id);
733
assert!(matches!(output, Ok(Err(_))));
734
}
735
736
#[test]
737
fn exclusive_system() {
738
let mut world = World::new();
739
let exclusive_system_id = world.register_system(|world: &mut World| {
740
world.spawn_empty();
741
});
742
let entity_count = world.entities.len();
743
let _ = world.run_system(exclusive_system_id);
744
assert_eq!(world.entities.len(), entity_count + 1);
745
}
746
747
#[test]
748
fn nested_systems() {
749
use crate::system::SystemId;
750
751
#[derive(Component)]
752
struct Callback(SystemId);
753
754
fn nested(query: Query<&Callback>, mut commands: Commands) {
755
for callback in query.iter() {
756
commands.run_system(callback.0);
757
}
758
}
759
760
let mut world = World::new();
761
world.insert_resource(Counter(0));
762
763
let increment_two = world.register_system(|mut counter: ResMut<Counter>| {
764
counter.0 += 2;
765
});
766
let increment_three = world.register_system(|mut counter: ResMut<Counter>| {
767
counter.0 += 3;
768
});
769
let nested_id = world.register_system(nested);
770
771
world.spawn(Callback(increment_two));
772
world.spawn(Callback(increment_three));
773
let _ = world.run_system(nested_id);
774
assert_eq!(*world.resource::<Counter>(), Counter(5));
775
}
776
777
#[test]
778
fn nested_systems_with_inputs() {
779
use crate::system::SystemId;
780
781
#[derive(Component)]
782
struct Callback(SystemId<In<u8>>, u8);
783
784
fn nested(query: Query<&Callback>, mut commands: Commands) {
785
for callback in query.iter() {
786
commands.run_system_with(callback.0, callback.1);
787
}
788
}
789
790
let mut world = World::new();
791
world.insert_resource(Counter(0));
792
793
let increment_by =
794
world.register_system(|In(amt): In<u8>, mut counter: ResMut<Counter>| {
795
counter.0 += amt;
796
});
797
let nested_id = world.register_system(nested);
798
799
world.spawn(Callback(increment_by, 2));
800
world.spawn(Callback(increment_by, 3));
801
let _ = world.run_system(nested_id);
802
assert_eq!(*world.resource::<Counter>(), Counter(5));
803
}
804
805
#[test]
806
fn cached_system() {
807
use crate::system::RegisteredSystemError;
808
809
fn four() -> i32 {
810
4
811
}
812
813
let mut world = World::new();
814
let old = world.register_system_cached(four);
815
let new = world.register_system_cached(four);
816
assert_eq!(old, new);
817
818
let result = world.unregister_system_cached(four);
819
assert!(result.is_ok());
820
let new = world.register_system_cached(four);
821
assert_ne!(old, new);
822
823
let output = world.run_system(old);
824
assert!(matches!(
825
output,
826
Err(RegisteredSystemError::SystemIdNotRegistered(x)) if x == old,
827
));
828
let output = world.run_system(new);
829
assert!(matches!(output, Ok(x) if x == four()));
830
let output = world.run_system_cached(four);
831
assert!(matches!(output, Ok(x) if x == four()));
832
let output = world.run_system_cached_with(four, ());
833
assert!(matches!(output, Ok(x) if x == four()));
834
}
835
836
#[test]
837
fn cached_fallible_system() {
838
fn sys() -> Result<()> {
839
Err("error")?;
840
Ok(())
841
}
842
843
let mut world = World::new();
844
let fallible_system_id = world.register_system_cached(sys);
845
let output = world.run_system(fallible_system_id);
846
assert!(matches!(output, Ok(Err(_))));
847
let output = world.run_system_cached(sys);
848
assert!(matches!(output, Ok(Err(_))));
849
let output = world.run_system_cached_with(sys, ());
850
assert!(matches!(output, Ok(Err(_))));
851
}
852
853
#[test]
854
fn cached_system_commands() {
855
fn sys(mut counter: ResMut<Counter>) {
856
counter.0 += 1;
857
}
858
859
let mut world = World::new();
860
world.insert_resource(Counter(0));
861
world.commands().run_system_cached(sys);
862
world.flush_commands();
863
assert_eq!(world.resource::<Counter>().0, 1);
864
world.commands().run_system_cached_with(sys, ());
865
world.flush_commands();
866
assert_eq!(world.resource::<Counter>().0, 2);
867
}
868
869
#[test]
870
fn cached_fallible_system_commands() {
871
fn sys(mut counter: ResMut<Counter>) -> Result {
872
counter.0 += 1;
873
Ok(())
874
}
875
876
let mut world = World::new();
877
world.insert_resource(Counter(0));
878
world.commands().run_system_cached(sys);
879
world.flush_commands();
880
assert_eq!(world.resource::<Counter>().0, 1);
881
world.commands().run_system_cached_with(sys, ());
882
world.flush_commands();
883
assert_eq!(world.resource::<Counter>().0, 2);
884
}
885
886
#[test]
887
#[should_panic(expected = "This system always fails")]
888
fn cached_fallible_system_commands_can_fail() {
889
use crate::system::command;
890
fn sys() -> Result {
891
Err("This system always fails".into())
892
}
893
894
let mut world = World::new();
895
world.commands().queue(command::run_system_cached(sys));
896
world.flush_commands();
897
}
898
899
#[test]
900
fn cached_system_adapters() {
901
fn four() -> i32 {
902
4
903
}
904
905
fn double(In(i): In<i32>) -> i32 {
906
i * 2
907
}
908
909
let mut world = World::new();
910
911
let output = world.run_system_cached(four.pipe(double));
912
assert!(matches!(output, Ok(8)));
913
914
let output = world.run_system_cached(four.map(|i| i * 2));
915
assert!(matches!(output, Ok(8)));
916
}
917
918
#[test]
919
fn cached_system_into_same_system_type() {
920
struct Foo;
921
impl IntoSystem<(), (), ()> for Foo {
922
type System = ApplyDeferred;
923
fn into_system(_: Self) -> Self::System {
924
ApplyDeferred
925
}
926
}
927
928
struct Bar;
929
impl IntoSystem<(), (), ()> for Bar {
930
type System = ApplyDeferred;
931
fn into_system(_: Self) -> Self::System {
932
ApplyDeferred
933
}
934
}
935
936
let mut world = World::new();
937
let foo1 = world.register_system_cached(Foo);
938
let foo2 = world.register_system_cached(Foo);
939
let bar1 = world.register_system_cached(Bar);
940
let bar2 = world.register_system_cached(Bar);
941
942
// The `S: IntoSystem` types are different, so they should be cached
943
// as separate systems, even though the `<S as IntoSystem>::System`
944
// types / values are the same (`ApplyDeferred`).
945
assert_ne!(foo1, bar1);
946
947
// But if the `S: IntoSystem` types are the same, they'll be cached
948
// as the same system.
949
assert_eq!(foo1, foo2);
950
assert_eq!(bar1, bar2);
951
}
952
953
#[test]
954
fn system_with_input_ref() {
955
fn with_ref(InRef(input): InRef<u8>, mut counter: ResMut<Counter>) {
956
counter.0 += *input;
957
}
958
959
let mut world = World::new();
960
world.insert_resource(Counter(0));
961
962
let id = world.register_system(with_ref);
963
world.run_system_with(id, &2).unwrap();
964
assert_eq!(*world.resource::<Counter>(), Counter(2));
965
}
966
967
#[test]
968
fn system_with_input_mut() {
969
#[derive(Event)]
970
struct MyEvent {
971
cancelled: bool,
972
}
973
974
fn post(InMut(event): InMut<MyEvent>, counter: ResMut<Counter>) {
975
if counter.0 > 0 {
976
event.cancelled = true;
977
}
978
}
979
980
let mut world = World::new();
981
world.insert_resource(Counter(0));
982
let post_system = world.register_system(post);
983
984
let mut event = MyEvent { cancelled: false };
985
world.run_system_with(post_system, &mut event).unwrap();
986
assert!(!event.cancelled);
987
988
world.resource_mut::<Counter>().0 = 1;
989
world.run_system_with(post_system, &mut event).unwrap();
990
assert!(event.cancelled);
991
}
992
993
#[test]
994
fn run_system_invalid_params() {
995
use crate::system::RegisteredSystemError;
996
use alloc::string::ToString;
997
998
struct T;
999
impl Resource for T {}
1000
fn system(_: Res<T>) {}
1001
1002
let mut world = World::new();
1003
let id = world.register_system(system);
1004
// This fails because `T` has not been added to the world yet.
1005
let result = world.run_system(id);
1006
1007
assert!(matches!(result, Err(RegisteredSystemError::Failed { .. })));
1008
let expected = "Resource does not exist";
1009
let actual = result.unwrap_err().to_string();
1010
1011
assert!(
1012
actual.contains(expected),
1013
"Expected error message to contain `{}` but got `{}`",
1014
expected,
1015
actual
1016
);
1017
}
1018
1019
#[test]
1020
fn run_system_recursive() {
1021
std::thread_local! {
1022
static INVOCATIONS_LEFT: Cell<i32> = const { Cell::new(3) };
1023
static SYSTEM_ID: Cell<Option<SystemId>> = default();
1024
}
1025
1026
fn system(mut commands: Commands) {
1027
let count = INVOCATIONS_LEFT.get() - 1;
1028
INVOCATIONS_LEFT.set(count);
1029
if count > 0 {
1030
commands.run_system(SYSTEM_ID.get().unwrap());
1031
}
1032
}
1033
1034
let mut world = World::new();
1035
let id = world.register_system(system);
1036
SYSTEM_ID.set(Some(id));
1037
world.run_system(id).unwrap();
1038
1039
assert_eq!(INVOCATIONS_LEFT.get(), 0);
1040
}
1041
1042
#[test]
1043
fn run_system_exclusive_adapters() {
1044
let mut world = World::new();
1045
fn system(_: &mut World) {}
1046
world.run_system_cached(system).unwrap();
1047
world.run_system_cached(system.pipe(system)).unwrap();
1048
world.run_system_cached(system.map(|()| {})).unwrap();
1049
}
1050
1051
#[test]
1052
fn wrong_system_type() {
1053
fn test() -> Result<(), u8> {
1054
Ok(())
1055
}
1056
1057
let mut world = World::new();
1058
1059
let entity = world.register_system_cached(test).entity();
1060
1061
match world.run_system::<u8>(SystemId::from_entity(entity)) {
1062
Ok(_) => panic!("Should fail since called `run_system` with wrong SystemId type."),
1063
Err(RegisteredSystemError::IncorrectType(_, _)) => (),
1064
Err(err) => panic!("Failed with wrong error. `{:?}`", err),
1065
}
1066
}
1067
}
1068
1069