Skip to main content

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    prelude::{FromTemplate, Template},
8    system::{
9        input::SystemInput, BoxedSystem, Commands, If, IntoSystem, Res, RunSystemError,
10        SystemParamValidationError,
11    },
12    template::TemplateContext,
13    world::World,
14};
15use alloc::boxed::Box;
16use bevy_ecs_macros::{Component, Resource};
17use bevy_platform::sync::{Arc, Mutex};
18use bevy_utils::prelude::DebugName;
19use concurrent_queue::ConcurrentQueue;
20use core::{any::TypeId, marker::PhantomData};
21use thiserror::Error;
22
23/// A small wrapper for [`BoxedSystem`] that also keeps track whether or not the system has been initialized.
24#[derive(Component)]
25#[require(SystemIdMarker = SystemIdMarker::typed_system_id_marker::<I, O>())]
26pub(crate) struct RegisteredSystem<I, O> {
27    initialized: bool,
28    system: Option<BoxedSystem<I, O>>,
29}
30
31impl<I, O> RegisteredSystem<I, O> {
32    pub fn new(system: BoxedSystem<I, O>) -> Self {
33        RegisteredSystem {
34            initialized: false,
35            system: Some(system),
36        }
37    }
38}
39
40#[derive(Debug, Clone)]
41struct TypeIdAndName {
42    type_id: TypeId,
43    name: DebugName,
44}
45
46impl TypeIdAndName {
47    fn new<T: 'static>() -> Self {
48        Self {
49            type_id: TypeId::of::<T>(),
50            name: DebugName::type_name::<T>(),
51        }
52    }
53}
54
55impl Default for TypeIdAndName {
56    fn default() -> Self {
57        Self {
58            type_id: TypeId::of::<()>(),
59            name: DebugName::type_name::<()>(),
60        }
61    }
62}
63
64/// Marker [`Component`](bevy_ecs::component::Component) for identifying [`SystemId`] [`Entity`]s.
65#[derive(Debug, Default, Clone, Component)]
66pub struct SystemIdMarker {
67    input_type_id: TypeIdAndName,
68    output_type_id: TypeIdAndName,
69}
70
71impl SystemIdMarker {
72    fn typed_system_id_marker<I: 'static, O: 'static>() -> Self {
73        Self {
74            input_type_id: TypeIdAndName::new::<I>(),
75            output_type_id: TypeIdAndName::new::<O>(),
76        }
77    }
78}
79
80/// A system that has been removed from the registry.
81/// It contains the system and whether or not it has been initialized.
82///
83/// This struct is returned by [`World::unregister_system`].
84pub struct RemovedSystem<I = (), O = ()> {
85    initialized: bool,
86    system: BoxedSystem<I, O>,
87}
88
89impl<I, O> RemovedSystem<I, O> {
90    /// Is the system initialized?
91    /// A system is initialized the first time it's ran.
92    pub fn initialized(&self) -> bool {
93        self.initialized
94    }
95
96    /// The system removed from the storage.
97    pub fn system(self) -> BoxedSystem<I, O> {
98        self.system
99    }
100}
101
102/// A system that despawns any registered system entities whose [`SystemHandle`]
103/// reference count has reached zero.
104pub fn despawn_unused_registered_systems(
105    // `RegisteredSystemDespawner` is initialized lazily the first time a system
106    // is registered, so it's possible that it doesn't exist yet when this system runs.
107    despawner: If<Res<RegisteredSystemDespawner>>,
108    mut commands: Commands,
109) {
110    for entity in despawner.queue.try_iter() {
111        // In case the entity was already despawned manually, we ignore the error here.
112        commands.entity(entity).try_despawn();
113    }
114}
115
116/// A resource that stores the channel for despawning unused registered system
117/// entities.
118#[derive(Resource)]
119pub struct RegisteredSystemDespawner {
120    queue: Arc<ConcurrentQueue<Entity>>,
121}
122
123impl Default for RegisteredSystemDespawner {
124    fn default() -> Self {
125        Self {
126            queue: Arc::new(ConcurrentQueue::unbounded()),
127        }
128    }
129}
130
131/// A maybe-strong handle to an entity acting as a registered system. Strong
132/// handles are created by [`World::register_tracked_system`] or
133/// [`World::register_tracked_boxed_system`].
134///
135/// Strong handles provide automatic cleanup of registered systems once all clones
136/// of the handle are dropped, while weak handles do not. However, the **existence
137/// of a strong handle does not prevent the registered system entity from being
138/// despawned manually**, like with [`World::unregister_system`] or
139/// [`World::unregister_system_cached`].
140///
141/// # Cleanup
142///
143/// Registered system entities are cleaned up by the [`despawn_unused_registered_systems`]
144/// system, which is automatically added to the default app by the `bevy_app`
145/// crate when the "std" feature is enabled. If not using the default app, the
146/// "std" feature, or `bevy_app` in general, consider running this system
147/// yourself to ensure proper cleanup of registered systems.
148pub enum SystemHandle<I: SystemInput = (), O = ()> {
149    /// A strong handle keeps the system entity alive as long as the handle
150    /// (and any clones of it) exist, as long as the system entity isn't
151    /// manually despawned.
152    Strong(Arc<StrongSystemHandle>),
153    /// A weak handle does not keep the system entity alive.
154    Weak(SystemId<I, O>),
155}
156
157impl<I: SystemInput, O> SystemHandle<I, O> {
158    /// Returns the [`Entity`] of the registered system associated with this handle.
159    pub fn entity(&self) -> Entity {
160        match self {
161            SystemHandle::Strong(strong) => strong.entity,
162            SystemHandle::Weak(weak) => weak.entity,
163        }
164    }
165}
166
167impl<I: SystemInput, O> Eq for SystemHandle<I, O> {}
168
169// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters.
170impl<I: SystemInput, O> Clone for SystemHandle<I, O> {
171    fn clone(&self) -> Self {
172        match self {
173            SystemHandle::Strong(strong) => SystemHandle::Strong(Arc::clone(strong)),
174            SystemHandle::Weak(weak) => SystemHandle::Weak(*weak),
175        }
176    }
177}
178
179// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters,
180// and so that strong and weak handles can be compared for equality based on their entities.
181impl<I: SystemInput, O> PartialEq for SystemHandle<I, O> {
182    fn eq(&self, other: &Self) -> bool {
183        self.entity() == other.entity()
184    }
185}
186
187impl<I: SystemInput, O> PartialEq<SystemId<I, O>> for SystemHandle<I, O> {
188    fn eq(&self, other: &SystemId<I, O>) -> bool {
189        self.entity() == other.entity
190    }
191}
192
193// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters,
194// and so that the handle can be hashed based on its entity instead of its handle type.
195impl<I: SystemInput, O> core::hash::Hash for SystemHandle<I, O> {
196    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
197        self.entity().hash(state);
198    }
199}
200
201impl<I: SystemInput, O> core::fmt::Debug for SystemHandle<I, O> {
202    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
203        let name = if matches!(self, SystemHandle::Strong(_)) {
204            "StrongSystemHandle"
205        } else {
206            "WeakSystemHandle"
207        };
208        f.debug_tuple(name).field(&self.entity()).finish()
209    }
210}
211
212impl<I: SystemInput, O> From<SystemId<I, O>> for SystemHandle<I, O> {
213    fn from(id: SystemId<I, O>) -> Self {
214        SystemHandle::Weak(id)
215    }
216}
217
218/// A strong handle for a registered system that despawns the entity when dropped.
219pub struct StrongSystemHandle {
220    entity: Entity,
221    drop_queue: Arc<ConcurrentQueue<Entity>>,
222}
223
224impl Drop for StrongSystemHandle {
225    fn drop(&mut self) {
226        // Send the entity to be despawned by the world when the last strong handle is dropped.
227        let _ = self.drop_queue.push(self.entity);
228    }
229}
230
231/// An identifier for a registered system.
232///
233/// These are opaque identifiers, keyed to a specific [`World`],
234/// and are created via [`World::register_system`].
235pub struct SystemId<I: SystemInput = (), O = ()> {
236    pub(crate) entity: Entity,
237    pub(crate) marker: PhantomData<fn(I) -> O>,
238}
239
240impl<I: SystemInput, O> SystemId<I, O> {
241    /// Transforms a [`SystemId`] into the [`Entity`] that holds the one-shot system's state.
242    ///
243    /// It's trivial to convert [`SystemId`] into an [`Entity`] since a one-shot system
244    /// is really an entity with associated handler function.
245    ///
246    /// For example, this is useful if you want to assign a name label to a system.
247    pub fn entity(self) -> Entity {
248        self.entity
249    }
250
251    /// Create [`SystemId`] from an [`Entity`]. Useful when you only have entity handles to avoid
252    /// adding extra components that have a [`SystemId`] everywhere. To run a system with this ID
253    ///  - The entity must be a system
254    ///  - The `I` + `O` types must be correct
255    pub fn from_entity(entity: Entity) -> Self {
256        Self {
257            entity,
258            marker: PhantomData,
259        }
260    }
261}
262
263impl<I: SystemInput, O> Eq for SystemId<I, O> {}
264
265// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters.
266impl<I: SystemInput, O> Copy for SystemId<I, O> {}
267
268// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters.
269impl<I: SystemInput, O> Clone for SystemId<I, O> {
270    fn clone(&self) -> Self {
271        *self
272    }
273}
274
275// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters.
276impl<I: SystemInput, O> PartialEq for SystemId<I, O> {
277    fn eq(&self, other: &Self) -> bool {
278        self.entity == other.entity && self.marker == other.marker
279    }
280}
281
282// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters.
283impl<I: SystemInput, O> core::hash::Hash for SystemId<I, O> {
284    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
285        self.entity.hash(state);
286    }
287}
288
289impl<I: SystemInput, O> core::fmt::Debug for SystemId<I, O> {
290    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
291        f.debug_tuple("SystemId").field(&self.entity).finish()
292    }
293}
294
295impl<I: SystemInput, O> From<&SystemHandle<I, O>> for SystemId<I, O> {
296    fn from(handle: &SystemHandle<I, O>) -> Self {
297        Self::from_entity(handle.entity())
298    }
299}
300
301impl<I: SystemInput, O> From<SystemHandle<I, O>> for SystemId<I, O> {
302    fn from(handle: SystemHandle<I, O>) -> Self {
303        (&handle).into()
304    }
305}
306
307impl<I: SystemInput + 'static, O: 'static> FromTemplate for SystemHandle<I, O> {
308    type Template = SystemHandleTemplate<I, O>;
309}
310
311/// A [`Template`] that produces a [`SystemHandle`].
312pub enum SystemHandleTemplate<I: SystemInput + 'static = (), O: 'static = ()> {
313    /// Creates a [`SystemHandle`] by cloning the given [`SystemHandle`] value.
314    Handle(SystemHandle<I, O>),
315    /// Creates a [`SystemHandle`] by registering the given system value using
316    /// [`World::register_tracked_boxed_system`]. This will cache the resulting
317    /// [`SystemHandle`]
318    /// on the template and reuse it for future template builds.
319    ///
320    /// This should generally be constructed using [`SystemHandleTemplate::value`]
321    /// or [`system_value`].
322    Value(SystemHandleValue<I, O>),
323}
324
325/// Stores an [`Arc<Mutex<SystemHandleOrValue<I, O>>>`].
326pub struct SystemHandleValue<I: SystemInput + 'static = (), O: 'static = ()>(
327    Arc<Mutex<SystemHandleOrValue<I, O>>>,
328);
329
330impl<I: SystemInput + 'static, O: 'static> Clone for SystemHandleValue<I, O> {
331    fn clone(&self) -> Self {
332        Self(Arc::clone(&self.0))
333    }
334}
335
336enum SystemHandleOrValue<I: SystemInput + 'static = (), O: 'static = ()> {
337    Handle(SystemHandle<I, O>),
338    Value(Option<BoxedSystem<I, O>>),
339}
340
341impl<I: SystemInput + 'static, O: 'static> SystemHandleTemplate<I, O> {
342    /// This will create a new [`SystemHandleTemplate`] for the given `system` value.
343    /// This makes it possible to define systems "inline" in templates / scenes
344    /// that produce a [`SystemId`].
345    pub fn value<M>(system: impl IntoSystem<I, O, M>) -> Self {
346        Self::Value(SystemHandleValue(Arc::new(Mutex::new(
347            SystemHandleOrValue::Value(Some(Box::new(IntoSystem::into_system(system)))),
348        ))))
349    }
350}
351
352impl<I: SystemInput + 'static, O: 'static> Template for SystemHandleTemplate<I, O> {
353    type Output = SystemHandle<I, O>;
354
355    fn build_template(
356        &self,
357        context: &mut TemplateContext,
358    ) -> crate::prelude::Result<Self::Output> {
359        match self {
360            Self::Handle(handle) => Ok(handle.clone()),
361            Self::Value(value) => {
362                let mut value_or_id = value.0.lock().unwrap();
363                match &mut *value_or_id {
364                    SystemHandleOrValue::Handle(handle) => Ok(handle.clone()),
365                    SystemHandleOrValue::Value(system) => {
366                        let system = system.take().unwrap();
367                        let id = context
368                            .entity
369                            .world_scope(|world| world.register_tracked_boxed_system(system));
370                        *value_or_id = SystemHandleOrValue::Handle(id.clone());
371                        Ok(id)
372                    }
373                }
374            }
375        }
376    }
377
378    fn clone_template(&self) -> Self {
379        match self {
380            Self::Handle(handle) => Self::Handle(handle.clone()),
381            Self::Value(value) => Self::Value(value.clone()),
382        }
383    }
384}
385
386impl<I: SystemInput + 'static, O: 'static> Default for SystemHandleTemplate<I, O> {
387    fn default() -> Self {
388        Self::Handle(SystemHandle::Weak(SystemId::from_entity(
389            Entity::PLACEHOLDER,
390        )))
391    }
392}
393
394impl<I: SystemInput + 'static, O: 'static> From<SystemHandle<I, O>> for SystemHandleTemplate<I, O> {
395    fn from(handle: SystemHandle<I, O>) -> Self {
396        Self::Handle(handle)
397    }
398}
399
400impl<I: SystemInput + 'static, O: 'static> From<BoxedSystem<I, O>> for SystemHandleTemplate<I, O> {
401    fn from(system: BoxedSystem<I, O>) -> Self {
402        Self::Value(SystemHandleValue(Arc::new(Mutex::new(
403            SystemHandleOrValue::Value(Some(system)),
404        ))))
405    }
406}
407
408impl<I: SystemInput + 'static, O: 'static> From<SystemId<I, O>> for SystemHandleTemplate<I, O> {
409    fn from(id: SystemId<I, O>) -> Self {
410        Self::Handle(SystemHandle::Weak(id))
411    }
412}
413
414/// This will create a new [`SystemHandleTemplate`] for the given `system` value.
415/// This makes it possible to define systems "inline" in templates / scenes that
416/// produce a [`SystemHandle`].
417pub fn system_value<I: SystemInput + 'static, O: 'static, M>(
418    system: impl IntoSystem<I, O, M>,
419) -> SystemHandleTemplate<I, O> {
420    SystemHandleTemplate::value(system)
421}
422
423/// A cached [`SystemId`] distinguished by the unique function type of its system.
424///
425/// This resource is inserted by [`World::register_system_cached`].
426#[derive(Resource)]
427pub struct CachedSystemId<S> {
428    /// The cached `SystemId` as an `Entity`.
429    pub entity: Entity,
430    _marker: PhantomData<fn() -> S>,
431}
432
433impl<S> CachedSystemId<S> {
434    /// Creates a new `CachedSystemId` struct given a `SystemId`.
435    pub fn new<I: SystemInput, O>(id: SystemId<I, O>) -> Self {
436        Self {
437            entity: id.entity(),
438            _marker: PhantomData,
439        }
440    }
441}
442
443impl World {
444    /// Registers a system and returns a [`SystemId`] so it can later be called by [`World::run_system`].
445    ///
446    /// It's possible to register multiple copies of the same system by calling this function
447    /// multiple times. If that's not what you want, consider using [`World::register_system_cached`]
448    /// instead.
449    ///
450    /// This is different from adding systems to a [`Schedule`](crate::schedule::Schedule),
451    /// because the [`SystemId`] that is returned can be used anywhere in the [`World`] to run the associated system.
452    /// This allows for running systems in a pushed-based fashion.
453    /// Using a [`Schedule`](crate::schedule::Schedule) is still preferred for most cases
454    /// due to its better performance and ability to run non-conflicting systems simultaneously.
455    pub fn register_system<I, O, M>(
456        &mut self,
457        system: impl IntoSystem<I, O, M> + 'static,
458    ) -> SystemId<I, O>
459    where
460        I: SystemInput + 'static,
461        O: 'static,
462    {
463        self.register_boxed_system(Box::new(IntoSystem::into_system(system)))
464    }
465
466    /// Similar to [`Self::register_system`], but allows passing in a [`BoxedSystem`].
467    ///
468    ///  This is useful if the [`IntoSystem`] implementor has already been turned into a
469    /// [`System`](crate::system::System) trait object and put in a [`Box`].
470    pub fn register_boxed_system<I, O>(&mut self, system: BoxedSystem<I, O>) -> SystemId<I, O>
471    where
472        I: SystemInput + 'static,
473        O: 'static,
474    {
475        let entity = self.spawn(RegisteredSystem::new(system)).id();
476        SystemId::from_entity(entity)
477    }
478
479    /// Registers a system and returns a tracked [`SystemHandle`] so it can later
480    /// be called by [`World::run_system`]. The system entity will be automatically
481    /// queued for despawn when the last clone of the returned handle is dropped.
482    ///
483    /// By default, unused tracked system entities are despawned by the
484    /// [`despawn_unused_registered_systems`] system in the `Last` schedule of
485    /// the default app. Otherwise, it needs to be run manually to ensure proper
486    /// cleanup of registered systems.
487    ///
488    /// It's possible to register multiple copies of the same system by calling
489    /// this function multiple times. If that's not what you want, consider using
490    /// [`World::register_system_cached`] instead.
491    pub fn register_tracked_system<I, O, M>(
492        &mut self,
493        system: impl IntoSystem<I, O, M> + 'static,
494    ) -> SystemHandle<I, O>
495    where
496        I: SystemInput + 'static,
497        O: 'static,
498    {
499        self.register_tracked_boxed_system(Box::new(IntoSystem::into_system(system)))
500    }
501
502    /// Similar to [`Self::register_tracked_system`], but allows passing in a
503    /// [`BoxedSystem`].
504    ///
505    /// This is useful if the [`IntoSystem`] implementor has already been turned
506    /// into a [`System`](crate::system::System) trait object and put in a [`Box`].
507    pub fn register_tracked_boxed_system<I, O>(
508        &mut self,
509        system: BoxedSystem<I, O>,
510    ) -> SystemHandle<I, O>
511    where
512        I: SystemInput + 'static,
513        O: 'static,
514    {
515        let entity = self.spawn(RegisteredSystem::new(system)).id();
516        let despawner = self.get_resource_or_init::<RegisteredSystemDespawner>();
517
518        SystemHandle::Strong(Arc::new(StrongSystemHandle {
519            entity,
520            drop_queue: despawner.queue.clone(),
521        }))
522    }
523
524    /// Removes a registered system and returns the system, if it exists.
525    /// After removing a system, the [`SystemId`] becomes invalid and attempting to use it afterwards will result in errors.
526    /// Re-adding the removed system will register it on a new [`SystemId`].
527    ///
528    /// If no system corresponds to the given [`SystemId`], this method returns an error.
529    /// Systems are also not allowed to remove themselves, this returns an error too.
530    pub fn unregister_system<I, O>(
531        &mut self,
532        id: SystemId<I, O>,
533    ) -> Result<RemovedSystem<I, O>, RegisteredSystemError<I, O>>
534    where
535        I: SystemInput + 'static,
536        O: 'static,
537    {
538        match self.get_entity_mut(id.entity) {
539            Ok(mut entity) => {
540                let registered_system = entity
541                    .take::<RegisteredSystem<I, O>>()
542                    .ok_or(RegisteredSystemError::SelfRemove(id))?;
543                entity.despawn();
544                Ok(RemovedSystem {
545                    initialized: registered_system.initialized,
546                    system: registered_system
547                        .system
548                        .ok_or(RegisteredSystemError::SystemMissing(id))?,
549                })
550            }
551            Err(_) => Err(RegisteredSystemError::SystemIdNotRegistered(id)),
552        }
553    }
554
555    /// Run stored systems by their [`SystemId`].
556    /// Before running a system, it must first be registered.
557    /// The method [`World::register_system`] stores a given system and returns a [`SystemId`].
558    /// This is different from [`RunSystemOnce::run_system_once`](crate::system::RunSystemOnce::run_system_once),
559    /// because it keeps local state between calls and change detection works correctly.
560    ///
561    /// Also runs any queued-up commands.
562    ///
563    /// In order to run a chained system with an input, use [`World::run_system_with`] instead.
564    ///
565    /// # Examples
566    ///
567    /// ## Running a system
568    ///
569    /// ```
570    /// # use bevy_ecs::prelude::*;
571    /// fn increment(mut counter: Local<u8>) {
572    ///    *counter += 1;
573    ///    println!("{}", *counter);
574    /// }
575    ///
576    /// let mut world = World::default();
577    /// let counter_one = world.register_system(increment);
578    /// let counter_two = world.register_system(increment);
579    /// world.run_system(counter_one); // -> 1
580    /// world.run_system(counter_one); // -> 2
581    /// world.run_system(counter_two); // -> 1
582    /// ```
583    ///
584    /// ## Change detection
585    ///
586    /// ```
587    /// # use bevy_ecs::prelude::*;
588    /// #[derive(Resource, Default)]
589    /// struct ChangeDetector;
590    ///
591    /// let mut world = World::default();
592    /// world.init_resource::<ChangeDetector>();
593    /// let detector = world.register_system(|change_detector: ResMut<ChangeDetector>| {
594    ///     if change_detector.is_changed() {
595    ///         println!("Something happened!");
596    ///     } else {
597    ///         println!("Nothing happened.");
598    ///     }
599    /// });
600    ///
601    /// // Resources are changed when they are first added
602    /// let _ = world.run_system(detector); // -> Something happened!
603    /// let _ = world.run_system(detector); // -> Nothing happened.
604    /// world.resource_mut::<ChangeDetector>().set_changed();
605    /// let _ = world.run_system(detector); // -> Something happened!
606    /// ```
607    ///
608    /// ## Getting system output
609    ///
610    /// ```
611    /// # use bevy_ecs::prelude::*;
612    ///
613    /// #[derive(Resource)]
614    /// struct PlayerScore(i32);
615    ///
616    /// #[derive(Resource)]
617    /// struct OpponentScore(i32);
618    ///
619    /// fn get_player_score(player_score: Res<PlayerScore>) -> i32 {
620    ///   player_score.0
621    /// }
622    ///
623    /// fn get_opponent_score(opponent_score: Res<OpponentScore>) -> i32 {
624    ///   opponent_score.0
625    /// }
626    ///
627    /// let mut world = World::default();
628    /// world.insert_resource(PlayerScore(3));
629    /// world.insert_resource(OpponentScore(2));
630    ///
631    /// let scoring_systems = [
632    ///   ("player", world.register_system(get_player_score)),
633    ///   ("opponent", world.register_system(get_opponent_score)),
634    /// ];
635    ///
636    /// for (label, scoring_system) in scoring_systems {
637    ///   println!("{label} has score {}", world.run_system(scoring_system).expect("system succeeded"));
638    /// }
639    /// ```
640    pub fn run_system<O: 'static>(
641        &mut self,
642        id: impl Into<SystemId<(), O>>,
643    ) -> Result<O, RegisteredSystemError<(), O>> {
644        self.run_system_with(id, ())
645    }
646
647    /// Run a stored chained system by its [`SystemId`], providing an input value.
648    /// Before running a system, it must first be registered.
649    /// The method [`World::register_system`] stores a given system and returns a [`SystemId`].
650    ///
651    /// To use the supplied input, the system should have a [`SystemInput`] as the first parameter.
652    /// Also runs any queued-up commands.
653    ///
654    /// # Examples
655    ///
656    /// ```
657    /// # use bevy_ecs::prelude::*;
658    /// fn increment(In(increment_by): In<u8>, mut counter: Local<u8>) -> u8 {
659    ///   *counter += increment_by;
660    ///   *counter
661    /// }
662    ///
663    /// let mut world = World::default();
664    /// let counter_one = world.register_system(increment);
665    /// let counter_two = world.register_system(increment);
666    /// assert_eq!(world.run_system_with(counter_one, 1).unwrap(), 1);
667    /// assert_eq!(world.run_system_with(counter_one, 20).unwrap(), 21);
668    /// assert_eq!(world.run_system_with(counter_two, 30).unwrap(), 30);
669    /// ```
670    ///
671    /// See [`World::run_system`] for more examples.
672    pub fn run_system_with<I, O>(
673        &mut self,
674        id: impl Into<SystemId<I, O>>,
675        input: I::Inner<'_>,
676    ) -> Result<O, RegisteredSystemError<I, O>>
677    where
678        I: SystemInput + 'static,
679        O: 'static,
680    {
681        let id = id.into();
682        // Lookup
683        let mut entity = self
684            .get_entity_mut(id.entity)
685            .map_err(|_| RegisteredSystemError::SystemIdNotRegistered(id))?;
686
687        // Take ownership of system trait object
688        let Some(mut registered_system) = entity.get_mut::<RegisteredSystem<I, O>>() else {
689            let Some(system_id_marker) = entity.get::<SystemIdMarker>() else {
690                return Err(RegisteredSystemError::SystemIdNotRegistered(id));
691            };
692            if system_id_marker.input_type_id.type_id != TypeId::of::<I>()
693                || system_id_marker.output_type_id.type_id != TypeId::of::<O>()
694            {
695                return Err(RegisteredSystemError::IncorrectType(
696                    id,
697                    system_id_marker.clone(),
698                ));
699            }
700            return Err(RegisteredSystemError::MissingRegisteredSystemComponent(id));
701        };
702
703        let mut system = registered_system
704            .system
705            .take()
706            .ok_or(RegisteredSystemError::SystemMissing(id))?;
707
708        // Initialize the system
709        if !registered_system.initialized {
710            system.initialize(self);
711        }
712
713        // refresh hotpatches for stored systems
714        #[cfg(feature = "hotpatching")]
715        if self
716            .get_resource_ref::<HotPatchChanges>()
717            .is_none_or(|r| r.is_changed_after(system.get_last_run()))
718        {
719            system.refresh_hotpatch();
720        }
721
722        // Wait to run the commands until the system is available again.
723        // This is needed so the systems can recursively run themselves.
724        let result = system.run_without_applying_deferred(input, self);
725        system.queue_deferred(self.into());
726
727        // Return ownership of system trait object (if entity still exists)
728        if let Ok(mut entity) = self.get_entity_mut(id.entity)
729            && let Some(mut registered_system) = entity.get_mut::<RegisteredSystem<I, O>>()
730        {
731            registered_system.system = Some(system);
732            registered_system.initialized = true;
733        }
734
735        // Run any commands enqueued by the system
736        self.flush();
737        Ok(result?)
738    }
739
740    /// Registers a system or returns its cached [`SystemId`].
741    ///
742    /// If you want to run the system immediately and you don't need its `SystemId`, see
743    /// [`World::run_system_cached`].
744    ///
745    /// The first time this function is called for a particular system, it will register it and
746    /// store its [`SystemId`] in a [`CachedSystemId`] resource for later. If you would rather
747    /// manage the `SystemId` yourself, or register multiple copies of the same system, use
748    /// [`World::register_system`] instead.
749    ///
750    /// # Limitations
751    ///
752    /// This function only accepts ZST (zero-sized) systems to guarantee that any two systems of
753    /// the same type must be equal. This means that closures that capture the environment, and
754    /// function pointers, are not accepted.
755    ///
756    /// If you want to access values from the environment within a system, consider passing them in
757    /// as inputs via [`World::run_system_cached_with`]. If that's not an option, consider
758    /// [`World::register_system`] instead.
759    pub fn register_system_cached<I, O, M, S>(&mut self, system: S) -> SystemId<I, O>
760    where
761        I: SystemInput + 'static,
762        O: 'static,
763        S: IntoSystem<I, O, M> + 'static,
764    {
765        const {
766            assert!(
767                size_of::<S>() == 0,
768                "Non-ZST systems (e.g. capturing closures, function pointers) cannot be cached.",
769            );
770        }
771
772        if !self.contains_resource::<CachedSystemId<S>>() {
773            let id = self.register_system(system);
774            self.insert_resource(CachedSystemId::<S>::new(id));
775            return id;
776        }
777
778        self.resource_scope(|world, mut id: Mut<CachedSystemId<S>>| {
779            if let Ok(mut entity) = world.get_entity_mut(id.entity) {
780                if !entity.contains::<RegisteredSystem<I, O>>() {
781                    entity.insert(RegisteredSystem::new(Box::new(IntoSystem::into_system(
782                        system,
783                    ))));
784                }
785            } else {
786                id.entity = world.register_system(system).entity();
787            }
788            SystemId::from_entity(id.entity)
789        })
790    }
791
792    /// Removes a cached system and its [`CachedSystemId`] resource.
793    ///
794    /// See [`World::register_system_cached`] for more information.
795    pub fn unregister_system_cached<I, O, M, S>(
796        &mut self,
797        _system: S,
798    ) -> Result<RemovedSystem<I, O>, RegisteredSystemError<I, O>>
799    where
800        I: SystemInput + 'static,
801        O: 'static,
802        S: IntoSystem<I, O, M> + 'static,
803    {
804        let id = self
805            .remove_resource::<CachedSystemId<S>>()
806            .ok_or(RegisteredSystemError::SystemNotCached)?;
807        self.unregister_system(SystemId::<I, O>::from_entity(id.entity))
808    }
809
810    /// Runs a cached system, registering it if necessary.
811    ///
812    /// See [`World::register_system_cached`] for more information.
813    pub fn run_system_cached<O: 'static, M, S: IntoSystem<(), O, M> + 'static>(
814        &mut self,
815        system: S,
816    ) -> Result<O, RegisteredSystemError<(), O>> {
817        self.run_system_cached_with(system, ())
818    }
819
820    /// Runs a cached system with an input, registering it if necessary.
821    ///
822    /// To use the supplied input, the system should have a [`SystemInput`] as the first parameter.
823    /// See [`World::register_system_cached`] for more information.
824    pub fn run_system_cached_with<I, O, M, S>(
825        &mut self,
826        system: S,
827        input: I::Inner<'_>,
828    ) -> Result<O, RegisteredSystemError<I, O>>
829    where
830        I: SystemInput + 'static,
831        O: 'static,
832        S: IntoSystem<I, O, M> + 'static,
833    {
834        let id = self.register_system_cached(system);
835        self.run_system_with(id, input)
836    }
837}
838
839/// An operation with stored systems failed.
840#[derive(Error)]
841pub enum RegisteredSystemError<I: SystemInput = (), O = ()> {
842    /// A system was run by id, but no system with that id was found.
843    ///
844    /// Did you forget to register it?
845    #[error("System {0:?} was not registered")]
846    SystemIdNotRegistered(SystemId<I, O>),
847    /// A cached system was removed by value, but no system with its type was found.
848    ///
849    /// Did you forget to register it?
850    #[error("Cached system was not found")]
851    SystemNotCached,
852    /// The `RegisteredSystem` component is missing.
853    #[error("System {0:?} does not have a RegisteredSystem component. This only happens if app code removed the component.")]
854    MissingRegisteredSystemComponent(SystemId<I, O>),
855    /// A system tried to remove itself.
856    #[error("System {0:?} tried to remove itself")]
857    SelfRemove(SystemId<I, O>),
858    /// System could not be run due to parameters that failed validation.
859    /// This is not considered an error.
860    #[error("System did not run due to failed parameter validation: {0}")]
861    Skipped(SystemParamValidationError),
862    /// System returned an error or failed required parameter validation.
863    #[error("System returned error: {0}")]
864    Failed(BevyError),
865    /// [`SystemId`] had different input and/or output types than [`SystemIdMarker`]
866    #[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)]
867    IncorrectType(SystemId<I, O>, SystemIdMarker),
868    /// System is not present in the `RegisteredSystem` component.
869    // TODO: We should consider using catch_unwind to protect against the panic case.
870    #[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.")]
871    SystemMissing(SystemId<I, O>),
872}
873
874impl<I: SystemInput, O> From<RunSystemError> for RegisteredSystemError<I, O> {
875    fn from(value: RunSystemError) -> Self {
876        match value {
877            RunSystemError::Skipped(err) => Self::Skipped(err),
878            RunSystemError::Failed(err) => Self::Failed(err),
879        }
880    }
881}
882
883impl<I: SystemInput, O> core::fmt::Debug for RegisteredSystemError<I, O> {
884    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
885        match self {
886            Self::SystemIdNotRegistered(arg0) => {
887                f.debug_tuple("SystemIdNotRegistered").field(arg0).finish()
888            }
889            Self::SystemNotCached => write!(f, "SystemNotCached"),
890            Self::MissingRegisteredSystemComponent(arg0) => f
891                .debug_tuple("MissingRegisteredSystemComponent")
892                .field(arg0)
893                .finish(),
894            Self::SelfRemove(arg0) => f.debug_tuple("SelfRemove").field(arg0).finish(),
895            Self::Skipped(arg0) => f.debug_tuple("Skipped").field(arg0).finish(),
896            Self::Failed(arg0) => f.debug_tuple("Failed").field(arg0).finish(),
897            Self::IncorrectType(arg0, arg1) => f
898                .debug_tuple("IncorrectType")
899                .field(arg0)
900                .field(arg1)
901                .finish(),
902            Self::SystemMissing(arg0) => f.debug_tuple("SystemMissing").field(arg0).finish(),
903        }
904    }
905}
906
907#[cfg(test)]
908mod tests {
909    use core::cell::Cell;
910
911    use bevy_utils::default;
912
913    use crate::{
914        prelude::*,
915        system::{
916            despawn_unused_registered_systems, system_value, RegisteredSystemError, SystemHandle,
917            SystemHandleTemplate, SystemId,
918        },
919    };
920
921    #[derive(Resource, Default, PartialEq, Debug)]
922    struct Counter(u8);
923
924    #[test]
925    fn change_detection() {
926        #[derive(Resource, Default)]
927        struct ChangeDetector;
928
929        fn count_up_iff_changed(
930            mut counter: ResMut<Counter>,
931            change_detector: ResMut<ChangeDetector>,
932        ) {
933            if change_detector.is_changed() {
934                counter.0 += 1;
935            }
936        }
937
938        let mut world = World::new();
939        world.init_resource::<ChangeDetector>();
940        world.init_resource::<Counter>();
941        assert_eq!(*world.resource::<Counter>(), Counter(0));
942        // Resources are changed when they are first added.
943        let id = world.register_system(count_up_iff_changed);
944        world.run_system(id).expect("system runs successfully");
945        assert_eq!(*world.resource::<Counter>(), Counter(1));
946        // Nothing changed
947        world.run_system(id).expect("system runs successfully");
948        assert_eq!(*world.resource::<Counter>(), Counter(1));
949        // Making a change
950        world.resource_mut::<ChangeDetector>().set_changed();
951        world.run_system(id).expect("system runs successfully");
952        assert_eq!(*world.resource::<Counter>(), Counter(2));
953    }
954
955    #[test]
956    fn local_variables() {
957        // The `Local` begins at the default value of 0
958        fn doubling(last_counter: Local<Counter>, mut counter: ResMut<Counter>) {
959            counter.0 += last_counter.0 .0;
960            last_counter.0 .0 = counter.0;
961        }
962
963        let mut world = World::new();
964        world.insert_resource(Counter(1));
965        assert_eq!(*world.resource::<Counter>(), Counter(1));
966        let id = world.register_system(doubling);
967        world.run_system(id).expect("system runs successfully");
968        assert_eq!(*world.resource::<Counter>(), Counter(1));
969        world.run_system(id).expect("system runs successfully");
970        assert_eq!(*world.resource::<Counter>(), Counter(2));
971        world.run_system(id).expect("system runs successfully");
972        assert_eq!(*world.resource::<Counter>(), Counter(4));
973        world.run_system(id).expect("system runs successfully");
974        assert_eq!(*world.resource::<Counter>(), Counter(8));
975    }
976
977    #[test]
978    fn input_values() {
979        // Verify that a non-Copy, non-Clone type can be passed in.
980        struct NonCopy(u8);
981
982        fn increment_sys(In(NonCopy(increment_by)): In<NonCopy>, mut counter: ResMut<Counter>) {
983            counter.0 += increment_by;
984        }
985
986        let mut world = World::new();
987
988        let id = world.register_system(increment_sys);
989
990        // Insert the resource after registering the system.
991        world.insert_resource(Counter(1));
992        assert_eq!(*world.resource::<Counter>(), Counter(1));
993
994        world
995            .run_system_with(id, NonCopy(1))
996            .expect("system runs successfully");
997        assert_eq!(*world.resource::<Counter>(), Counter(2));
998
999        world
1000            .run_system_with(id, NonCopy(1))
1001            .expect("system runs successfully");
1002        assert_eq!(*world.resource::<Counter>(), Counter(3));
1003
1004        world
1005            .run_system_with(id, NonCopy(20))
1006            .expect("system runs successfully");
1007        assert_eq!(*world.resource::<Counter>(), Counter(23));
1008
1009        world
1010            .run_system_with(id, NonCopy(1))
1011            .expect("system runs successfully");
1012        assert_eq!(*world.resource::<Counter>(), Counter(24));
1013    }
1014
1015    #[test]
1016    fn output_values() {
1017        // Verify that a non-Copy, non-Clone type can be returned.
1018        #[derive(Eq, PartialEq, Debug)]
1019        struct NonCopy(u8);
1020
1021        fn increment_sys(mut counter: ResMut<Counter>) -> NonCopy {
1022            counter.0 += 1;
1023            NonCopy(counter.0)
1024        }
1025
1026        let mut world = World::new();
1027
1028        let id = world.register_system(increment_sys);
1029
1030        // Insert the resource after registering the system.
1031        world.insert_resource(Counter(1));
1032        assert_eq!(*world.resource::<Counter>(), Counter(1));
1033
1034        let output = world.run_system(id).expect("system runs successfully");
1035        assert_eq!(*world.resource::<Counter>(), Counter(2));
1036        assert_eq!(output, NonCopy(2));
1037
1038        let output = world.run_system(id).expect("system runs successfully");
1039        assert_eq!(*world.resource::<Counter>(), Counter(3));
1040        assert_eq!(output, NonCopy(3));
1041    }
1042
1043    #[test]
1044    fn fallible_system() {
1045        fn sys() -> Result<()> {
1046            Err("error")?;
1047            Ok(())
1048        }
1049
1050        let mut world = World::new();
1051        let fallible_system_id = world.register_system(sys);
1052        let output = world.run_system(fallible_system_id);
1053        assert!(matches!(output, Ok(Err(_))));
1054    }
1055
1056    #[test]
1057    fn exclusive_system() {
1058        let mut world = World::new();
1059        let exclusive_system_id = world.register_system(|world: &mut World| {
1060            world.spawn_empty();
1061        });
1062        let entity_count = world.entities.count_spawned();
1063        let _ = world.run_system(exclusive_system_id);
1064        assert_eq!(world.entities.count_spawned(), entity_count + 1);
1065    }
1066
1067    #[test]
1068    fn nested_systems() {
1069        use crate::system::SystemId;
1070
1071        #[derive(Component)]
1072        struct Callback(SystemId);
1073
1074        fn nested(query: Query<&Callback>, mut commands: Commands) {
1075            for callback in query.iter() {
1076                commands.run_system(callback.0);
1077            }
1078        }
1079
1080        let mut world = World::new();
1081        world.insert_resource(Counter(0));
1082
1083        let increment_two = world.register_system(|mut counter: ResMut<Counter>| {
1084            counter.0 += 2;
1085        });
1086        let increment_three = world.register_system(|mut counter: ResMut<Counter>| {
1087            counter.0 += 3;
1088        });
1089        let nested_id = world.register_system(nested);
1090
1091        world.spawn(Callback(increment_two));
1092        world.spawn(Callback(increment_three));
1093        let _ = world.run_system(nested_id);
1094        assert_eq!(*world.resource::<Counter>(), Counter(5));
1095    }
1096
1097    #[test]
1098    fn nested_systems_with_inputs() {
1099        use crate::system::SystemId;
1100
1101        #[derive(Component)]
1102        struct Callback(SystemId<In<u8>>, u8);
1103
1104        fn nested(query: Query<&Callback>, mut commands: Commands) {
1105            for callback in query.iter() {
1106                commands.run_system_with(callback.0, callback.1);
1107            }
1108        }
1109
1110        let mut world = World::new();
1111        world.insert_resource(Counter(0));
1112
1113        let increment_by =
1114            world.register_system(|In(amt): In<u8>, mut counter: ResMut<Counter>| {
1115                counter.0 += amt;
1116            });
1117        let nested_id = world.register_system(nested);
1118
1119        world.spawn(Callback(increment_by, 2));
1120        world.spawn(Callback(increment_by, 3));
1121        let _ = world.run_system(nested_id);
1122        assert_eq!(*world.resource::<Counter>(), Counter(5));
1123    }
1124
1125    #[test]
1126    fn cached_system() {
1127        use crate::system::RegisteredSystemError;
1128
1129        fn four() -> i32 {
1130            4
1131        }
1132
1133        let mut world = World::new();
1134        let old = world.register_system_cached(four);
1135        let new = world.register_system_cached(four);
1136        assert_eq!(old, new);
1137
1138        let result = world.unregister_system_cached(four);
1139        assert!(result.is_ok());
1140        let new = world.register_system_cached(four);
1141        assert_ne!(old, new);
1142
1143        let output = world.run_system(old);
1144        assert!(matches!(
1145            output,
1146            Err(RegisteredSystemError::SystemIdNotRegistered(x)) if x == old,
1147        ));
1148        let output = world.run_system(new);
1149        assert!(matches!(output, Ok(x) if x == four()));
1150        let output = world.run_system_cached(four);
1151        assert!(matches!(output, Ok(x) if x == four()));
1152        let output = world.run_system_cached_with(four, ());
1153        assert!(matches!(output, Ok(x) if x == four()));
1154    }
1155
1156    #[test]
1157    fn cached_fallible_system() {
1158        fn sys() -> Result<()> {
1159            Err("error")?;
1160            Ok(())
1161        }
1162
1163        let mut world = World::new();
1164        let fallible_system_id = world.register_system_cached(sys);
1165        let output = world.run_system(fallible_system_id);
1166        assert!(matches!(output, Ok(Err(_))));
1167        let output = world.run_system_cached(sys);
1168        assert!(matches!(output, Ok(Err(_))));
1169        let output = world.run_system_cached_with(sys, ());
1170        assert!(matches!(output, Ok(Err(_))));
1171    }
1172
1173    #[test]
1174    fn cached_system_commands() {
1175        fn sys(mut counter: ResMut<Counter>) {
1176            counter.0 += 1;
1177        }
1178
1179        let mut world = World::new();
1180        world.insert_resource(Counter(0));
1181        world.commands().run_system_cached(sys);
1182        world.flush_commands();
1183        assert_eq!(world.resource::<Counter>().0, 1);
1184        world.commands().run_system_cached_with(sys, ());
1185        world.flush_commands();
1186        assert_eq!(world.resource::<Counter>().0, 2);
1187    }
1188
1189    #[test]
1190    fn cached_fallible_system_commands() {
1191        fn sys(mut counter: ResMut<Counter>) -> Result {
1192            counter.0 += 1;
1193            Ok(())
1194        }
1195
1196        let mut world = World::new();
1197        world.insert_resource(Counter(0));
1198        world.commands().run_system_cached(sys);
1199        world.flush_commands();
1200        assert_eq!(world.resource::<Counter>().0, 1);
1201        world.commands().run_system_cached_with(sys, ());
1202        world.flush_commands();
1203        assert_eq!(world.resource::<Counter>().0, 2);
1204    }
1205
1206    #[test]
1207    #[should_panic(expected = "This system always fails")]
1208    fn cached_fallible_system_commands_can_fail() {
1209        use crate::system::command;
1210        fn sys() -> Result {
1211            Err("This system always fails".into())
1212        }
1213
1214        let mut world = World::new();
1215        world.commands().queue(command::run_system_cached(sys));
1216        world.flush_commands();
1217    }
1218
1219    #[test]
1220    fn cached_system_adapters() {
1221        fn four() -> i32 {
1222            4
1223        }
1224
1225        fn double(In(i): In<i32>) -> i32 {
1226            i * 2
1227        }
1228
1229        let mut world = World::new();
1230
1231        let output = world.run_system_cached(four.pipe(double));
1232        assert!(matches!(output, Ok(8)));
1233
1234        let output = world.run_system_cached(four.map(|i| i * 2));
1235        assert!(matches!(output, Ok(8)));
1236    }
1237
1238    #[test]
1239    fn cached_system_into_same_system_type() {
1240        struct Foo;
1241        impl IntoSystem<(), (), ()> for Foo {
1242            type System = ApplyDeferred;
1243            fn into_system(_: Self) -> Self::System {
1244                ApplyDeferred
1245            }
1246        }
1247
1248        struct Bar;
1249        impl IntoSystem<(), (), ()> for Bar {
1250            type System = ApplyDeferred;
1251            fn into_system(_: Self) -> Self::System {
1252                ApplyDeferred
1253            }
1254        }
1255
1256        let mut world = World::new();
1257        let foo1 = world.register_system_cached(Foo);
1258        let foo2 = world.register_system_cached(Foo);
1259        let bar1 = world.register_system_cached(Bar);
1260        let bar2 = world.register_system_cached(Bar);
1261
1262        // The `S: IntoSystem` types are different, so they should be cached
1263        // as separate systems, even though the `<S as IntoSystem>::System`
1264        // types / values are the same (`ApplyDeferred`).
1265        assert_ne!(foo1, bar1);
1266
1267        // But if the `S: IntoSystem` types are the same, they'll be cached
1268        // as the same system.
1269        assert_eq!(foo1, foo2);
1270        assert_eq!(bar1, bar2);
1271    }
1272
1273    #[test]
1274    fn system_with_input_ref() {
1275        fn with_ref(InRef(input): InRef<u8>, mut counter: ResMut<Counter>) {
1276            counter.0 += *input;
1277        }
1278
1279        let mut world = World::new();
1280        world.insert_resource(Counter(0));
1281
1282        let id = world.register_system(with_ref);
1283        world.run_system_with(id, &2).unwrap();
1284        assert_eq!(*world.resource::<Counter>(), Counter(2));
1285    }
1286
1287    #[test]
1288    fn system_with_input_mut() {
1289        #[derive(Event)]
1290        struct MyEvent {
1291            cancelled: bool,
1292        }
1293
1294        fn post(InMut(event): InMut<MyEvent>, counter: ResMut<Counter>) {
1295            if counter.0 > 0 {
1296                event.cancelled = true;
1297            }
1298        }
1299
1300        let mut world = World::new();
1301        world.insert_resource(Counter(0));
1302        let post_system = world.register_system(post);
1303
1304        let mut event = MyEvent { cancelled: false };
1305        world.run_system_with(post_system, &mut event).unwrap();
1306        assert!(!event.cancelled);
1307
1308        world.resource_mut::<Counter>().0 = 1;
1309        world.run_system_with(post_system, &mut event).unwrap();
1310        assert!(event.cancelled);
1311    }
1312
1313    #[test]
1314    fn run_system_invalid_params() {
1315        use crate::system::RegisteredSystemError;
1316        use alloc::string::ToString;
1317
1318        #[derive(Resource)]
1319        struct T;
1320
1321        fn system(_: Res<T>) {}
1322
1323        let mut world = World::new();
1324        let id = world.register_system(system);
1325        // This fails because `T` has not been added to the world yet.
1326        let result = world.run_system(id);
1327
1328        assert!(matches!(result, Err(RegisteredSystemError::Failed { .. })));
1329        let expected = "does not exist";
1330        let actual = result.unwrap_err().to_string();
1331
1332        assert!(
1333            actual.contains(expected),
1334            "Expected error message to contain `{}` but got `{}`",
1335            expected,
1336            actual
1337        );
1338    }
1339
1340    #[test]
1341    fn run_system_recursive() {
1342        std::thread_local! {
1343            static INVOCATIONS_LEFT: Cell<i32> = const { Cell::new(3) };
1344            static SYSTEM_ID: Cell<Option<SystemId>> = default();
1345        }
1346
1347        fn system(mut commands: Commands) {
1348            let count = INVOCATIONS_LEFT.get() - 1;
1349            INVOCATIONS_LEFT.set(count);
1350            if count > 0 {
1351                commands.run_system(SYSTEM_ID.get().unwrap());
1352            }
1353        }
1354
1355        let mut world = World::new();
1356        let id = world.register_system(system);
1357        SYSTEM_ID.set(Some(id));
1358        world.run_system(id).unwrap();
1359
1360        assert_eq!(INVOCATIONS_LEFT.get(), 0);
1361    }
1362
1363    #[test]
1364    fn run_system_exclusive_adapters() {
1365        let mut world = World::new();
1366        fn system(_: &mut World) {}
1367        world.run_system_cached(system).unwrap();
1368        world.run_system_cached(system.pipe(system)).unwrap();
1369        world.run_system_cached(system.map(|()| {})).unwrap();
1370    }
1371
1372    #[test]
1373    fn wrong_system_type() {
1374        fn test() -> Result<(), u8> {
1375            Ok(())
1376        }
1377
1378        let mut world = World::new();
1379
1380        let entity = world.register_system_cached(test).entity();
1381
1382        match world.run_system::<u8>(SystemId::from_entity(entity)) {
1383            Ok(_) => panic!("Should fail since called `run_system` with wrong SystemId type."),
1384            Err(RegisteredSystemError::IncorrectType(_, _)) => (),
1385            Err(err) => panic!("Failed with wrong error. `{:?}`", err),
1386        }
1387    }
1388
1389    #[test]
1390    fn despawn_unused() {
1391        let mut world = World::new();
1392
1393        fn system() {}
1394
1395        let handle = world.register_tracked_system(system);
1396        let entity = handle.entity();
1397        drop(handle);
1398
1399        assert!(world.get_entity(entity).is_ok());
1400
1401        world
1402            .run_system_cached(despawn_unused_registered_systems)
1403            .unwrap();
1404
1405        assert!(world.get_entity(entity).is_err());
1406    }
1407
1408    #[test]
1409    fn system_handle_template() {
1410        fn my_system() {}
1411
1412        let mut world = World::new();
1413
1414        {
1415            let my_system_handle = world.register_tracked_system(my_system);
1416            let system_handle = world
1417                .spawn_empty()
1418                .build_template(&SystemHandleTemplate::Handle(my_system_handle.clone()))
1419                .unwrap();
1420            assert_eq!(system_handle, my_system_handle);
1421        }
1422
1423        {
1424            let template = system_value(my_system);
1425
1426            let a = world.spawn_empty().build_template(&template).unwrap();
1427            let b = world.spawn_empty().build_template(&template).unwrap();
1428
1429            assert!(matches!(a, SystemHandle::Strong(_)));
1430            assert!(matches!(b, SystemHandle::Strong(_)));
1431
1432            assert_eq!(a, b);
1433        }
1434    }
1435
1436    #[test]
1437    fn run_system_with_owned_system_handle() {
1438        fn increment(mut counter: ResMut<Counter>) {
1439            counter.0 += 1;
1440        }
1441
1442        let mut world = World::new();
1443        world.insert_resource(Counter(0));
1444
1445        let handle = world.register_tracked_system(increment);
1446        world.run_system(handle).expect("system runs successfully");
1447
1448        assert_eq!(*world.resource::<Counter>(), Counter(1));
1449    }
1450}