bevy_ecs/system/
system_registry.rs

1#[cfg(feature = "hotpatching")]
2use crate::{change_detection::DetectChanges, HotPatchChanges};
3use 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};
13use alloc::boxed::Box;
14use bevy_ecs_macros::{Component, Resource};
15use bevy_utils::prelude::DebugName;
16use core::{any::TypeId, marker::PhantomData};
17use 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)]
22pub(crate) struct RegisteredSystem<I, O> {
23    initialized: bool,
24    system: BoxedSystem<I, O>,
25}
26
27impl<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)]
37struct TypeIdAndName {
38    type_id: TypeId,
39    name: DebugName,
40}
41
42impl 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
51impl 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)]
62pub struct SystemIdMarker {
63    input_type_id: TypeIdAndName,
64    output_type_id: TypeIdAndName,
65}
66
67impl 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`].
80pub struct RemovedSystem<I = (), O = ()> {
81    initialized: bool,
82    system: BoxedSystem<I, O>,
83}
84
85impl<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`].
102pub struct SystemId<I: SystemInput = (), O = ()> {
103    pub(crate) entity: Entity,
104    pub(crate) marker: PhantomData<fn(I) -> O>,
105}
106
107impl<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
130impl<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.
133impl<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.
136impl<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.
143impl<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.
150impl<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
156impl<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)]
166pub struct CachedSystemId<S> {
167    /// The cached `SystemId` as an `Entity`.
168    pub entity: Entity,
169    _marker: PhantomData<fn() -> S>,
170}
171
172impl<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
182impl 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)]
532pub 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
561impl<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
570impl<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)]
591mod 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}