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