bevy_ecs/system/
system_registry.rs

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