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    error::BevyError,
7    system::{
8        input::SystemInput, BoxedSystem, IntoSystem, RunSystemError, SystemParamValidationError,
9    },
10    world::World,
11};
12use alloc::boxed::Box;
13use bevy_ecs_macros::{Component, Resource};
14use bevy_utils::prelude::DebugName;
15use core::{any::TypeId, marker::PhantomData};
16use 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>())]
21pub(crate) struct RegisteredSystem<I, O> {
22    initialized: bool,
23    system: Option<BoxedSystem<I, O>>,
24}
25
26impl<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)]
36struct TypeIdAndName {
37    type_id: TypeId,
38    name: DebugName,
39}
40
41impl 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
50impl 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)]
61pub struct SystemIdMarker {
62    input_type_id: TypeIdAndName,
63    output_type_id: TypeIdAndName,
64}
65
66impl 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`].
79pub struct RemovedSystem<I = (), O = ()> {
80    initialized: bool,
81    system: BoxedSystem<I, O>,
82}
83
84impl<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`].
101pub struct SystemId<I: SystemInput = (), O = ()> {
102    pub(crate) entity: Entity,
103    pub(crate) marker: PhantomData<fn(I) -> O>,
104}
105
106impl<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
129impl<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.
132impl<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.
135impl<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.
142impl<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.
149impl<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
155impl<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)]
165pub struct CachedSystemId<S> {
166    /// The cached `SystemId` as an `Entity`.
167    pub entity: Entity,
168    _marker: PhantomData<fn() -> S>,
169}
170
171impl<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
181impl 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)]
533pub 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
566impl<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
575impl<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)]
600mod 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}