bevy_ecs/schedule/
set.rs

1use alloc::boxed::Box;
2use bevy_utils::prelude::DebugName;
3use core::{
4    any::TypeId,
5    fmt::Debug,
6    hash::{Hash, Hasher},
7    marker::PhantomData,
8};
9
10pub use crate::label::DynEq;
11pub use bevy_ecs_macros::{ScheduleLabel, SystemSet};
12
13use crate::{
14    define_label,
15    intern::Interned,
16    system::{
17        ExclusiveFunctionSystem, ExclusiveSystemParamFunction, FromInput, FunctionSystem,
18        IntoResult, IsExclusiveFunctionSystem, IsFunctionSystem, SystemParamFunction,
19    },
20};
21
22define_label!(
23    /// A strongly-typed class of labels used to identify a [`Schedule`].
24    ///
25    /// Each schedule in a [`World`] has a unique schedule label value, and
26    /// schedules can be automatically created from labels via [`Schedules::add_systems()`].
27    ///
28    /// # Defining new schedule labels
29    ///
30    /// By default, you should use Bevy's premade schedule labels which implement this trait.
31    /// If you are using [`bevy_ecs`] directly or if you need to run a group of systems outside
32    /// the existing schedules, you may define your own schedule labels by using
33    /// `#[derive(ScheduleLabel)]`.
34    ///
35    /// ```
36    /// use bevy_ecs::prelude::*;
37    /// use bevy_ecs::schedule::ScheduleLabel;
38    ///
39    /// // Declare a new schedule label.
40    /// #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
41    /// struct Update;
42    ///
43    /// let mut world = World::new();
44    ///
45    /// // Add a system to the schedule with that label (creating it automatically).
46    /// fn a_system_function() {}
47    /// world.get_resource_or_init::<Schedules>().add_systems(Update, a_system_function);
48    ///
49    /// // Run the schedule, and therefore run the system.
50    /// world.run_schedule(Update);
51    /// ```
52    ///
53    /// [`Schedule`]: crate::schedule::Schedule
54    /// [`Schedules::add_systems()`]: crate::schedule::Schedules::add_systems
55    /// [`World`]: crate::world::World
56    #[diagnostic::on_unimplemented(
57        note = "consider annotating `{Self}` with `#[derive(ScheduleLabel)]`"
58    )]
59    ScheduleLabel,
60    SCHEDULE_LABEL_INTERNER
61);
62
63define_label!(
64    /// System sets are tag-like labels that can be used to group systems together.
65    ///
66    /// This allows you to share configuration (like run conditions) across multiple systems,
67    /// and order systems or system sets relative to conceptual groups of systems.
68    /// To control the behavior of a system set as a whole, use [`Schedule::configure_sets`](crate::prelude::Schedule::configure_sets),
69    /// or the method of the same name on `App`.
70    ///
71    /// Systems can belong to any number of system sets, reflecting multiple roles or facets that they might have.
72    /// For example, you may want to annotate a system as "consumes input" and "applies forces",
73    /// and ensure that your systems are ordered correctly for both of those sets.
74    ///
75    /// System sets can belong to any number of other system sets,
76    /// allowing you to create nested hierarchies of system sets to group systems together.
77    /// Configuration applied to system sets will flow down to their members (including other system sets),
78    /// allowing you to set and modify the configuration in a single place.
79    ///
80    /// Systems sets are also useful for exposing a consistent public API for dependencies
81    /// to hook into across versions of your crate,
82    /// allowing them to add systems to a specific set, or order relative to that set,
83    /// without leaking implementation details of the exact systems involved.
84    ///
85    /// ## Defining new system sets
86    ///
87    /// To create a new system set, use the `#[derive(SystemSet)]` macro.
88    /// Unit structs are a good choice for one-off sets.
89    ///
90    /// ```rust
91    /// # use bevy_ecs::prelude::*;
92    ///
93    /// #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
94    /// struct PhysicsSystems;
95    /// ```
96    ///
97    /// When you want to define several related system sets,
98    /// consider creating an enum system set.
99    /// Each variant will be treated as a separate system set.
100    ///
101    /// ```rust
102    /// # use bevy_ecs::prelude::*;
103    ///
104    /// #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
105    /// enum CombatSystems {
106    ///    TargetSelection,
107    ///    DamageCalculation,
108    ///    Cleanup,
109    /// }
110    /// ```
111    ///
112    /// By convention, the listed order of the system set in the enum
113    /// corresponds to the order in which the systems are run.
114    /// Ordering must be explicitly added to ensure that this is the case,
115    /// but following this convention will help avoid confusion.
116    ///
117    /// ### Adding systems to system sets
118    ///
119    /// To add systems to a system set, call [`in_set`](crate::prelude::IntoScheduleConfigs::in_set) on the system function
120    /// while adding it to your app or schedule.
121    ///
122    /// Like usual, these methods can be chained with other configuration methods like [`before`](crate::prelude::IntoScheduleConfigs::before),
123    /// or repeated to add systems to multiple sets.
124    ///
125    /// ```rust
126    /// use bevy_ecs::prelude::*;
127    ///
128    /// #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
129    /// enum CombatSystems {
130    ///    TargetSelection,
131    ///    DamageCalculation,
132    ///    Cleanup,
133    /// }
134    ///
135    /// fn target_selection() {}
136    ///
137    /// fn enemy_damage_calculation() {}
138    ///
139    /// fn player_damage_calculation() {}
140    ///
141    /// let mut schedule = Schedule::default();
142    /// // Configuring the sets to run in order.
143    /// schedule.configure_sets((CombatSystems::TargetSelection, CombatSystems::DamageCalculation, CombatSystems::Cleanup).chain());
144    ///
145    /// // Adding a single system to a set.
146    /// schedule.add_systems(target_selection.in_set(CombatSystems::TargetSelection));
147    ///
148    /// // Adding multiple systems to a set.
149    /// schedule.add_systems((player_damage_calculation, enemy_damage_calculation).in_set(CombatSystems::DamageCalculation));
150    /// ```
151    #[diagnostic::on_unimplemented(
152        note = "consider annotating `{Self}` with `#[derive(SystemSet)]`"
153    )]
154    SystemSet,
155    SYSTEM_SET_INTERNER,
156    extra_methods: {
157        /// Returns `Some` if this system set is a [`SystemTypeSet`].
158        fn system_type(&self) -> Option<TypeId> {
159            None
160        }
161
162        /// Returns `true` if this system set is an [`AnonymousSet`].
163        fn is_anonymous(&self) -> bool {
164            false
165        }
166    },
167    extra_methods_impl: {
168        fn system_type(&self) -> Option<TypeId> {
169            (**self).system_type()
170        }
171
172        fn is_anonymous(&self) -> bool {
173            (**self).is_anonymous()
174        }
175    }
176);
177
178/// A shorthand for `Interned<dyn SystemSet>`.
179pub type InternedSystemSet = Interned<dyn SystemSet>;
180/// A shorthand for `Interned<dyn ScheduleLabel>`.
181pub type InternedScheduleLabel = Interned<dyn ScheduleLabel>;
182
183/// A [`SystemSet`] grouping instances of the same function.
184///
185/// This kind of set is automatically populated and thus has some special rules:
186/// - You cannot manually add members.
187/// - You cannot configure them.
188/// - You cannot order something relative to one if it has more than one member.
189pub struct SystemTypeSet<T: 'static>(PhantomData<fn() -> T>);
190
191impl<T: 'static> SystemTypeSet<T> {
192    pub(crate) fn new() -> Self {
193        Self(PhantomData)
194    }
195}
196
197impl<T> Debug for SystemTypeSet<T> {
198    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
199        f.debug_tuple("SystemTypeSet")
200            .field(&format_args!("fn {}()", DebugName::type_name::<T>()))
201            .finish()
202    }
203}
204
205impl<T> Hash for SystemTypeSet<T> {
206    fn hash<H: Hasher>(&self, _state: &mut H) {
207        // all systems of a given type are the same
208    }
209}
210
211impl<T> Clone for SystemTypeSet<T> {
212    fn clone(&self) -> Self {
213        *self
214    }
215}
216
217impl<T> Copy for SystemTypeSet<T> {}
218
219impl<T> PartialEq for SystemTypeSet<T> {
220    #[inline]
221    fn eq(&self, _other: &Self) -> bool {
222        // all systems of a given type are the same
223        true
224    }
225}
226
227impl<T> Eq for SystemTypeSet<T> {}
228
229impl<T> SystemSet for SystemTypeSet<T> {
230    fn system_type(&self) -> Option<TypeId> {
231        Some(TypeId::of::<T>())
232    }
233
234    fn dyn_clone(&self) -> Box<dyn SystemSet> {
235        Box::new(*self)
236    }
237}
238
239/// A [`SystemSet`] implicitly created when using
240/// [`Schedule::add_systems`](super::Schedule::add_systems) or
241/// [`Schedule::configure_sets`](super::Schedule::configure_sets).
242#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
243pub struct AnonymousSet(usize);
244
245impl AnonymousSet {
246    pub(crate) fn new(id: usize) -> Self {
247        Self(id)
248    }
249}
250
251impl SystemSet for AnonymousSet {
252    fn is_anonymous(&self) -> bool {
253        true
254    }
255
256    fn dyn_clone(&self) -> Box<dyn SystemSet> {
257        Box::new(*self)
258    }
259}
260
261/// Types that can be converted into a [`SystemSet`].
262///
263/// # Usage notes
264///
265/// This trait should only be used as a bound for trait implementations or as an
266/// argument to a function. If a system set needs to be returned from a function
267/// or stored somewhere, use [`SystemSet`] instead of this trait.
268#[diagnostic::on_unimplemented(
269    message = "`{Self}` is not a system set",
270    label = "invalid system set"
271)]
272pub trait IntoSystemSet<Marker>: Sized {
273    /// The type of [`SystemSet`] this instance converts into.
274    type Set: SystemSet;
275
276    /// Converts this instance to its associated [`SystemSet`] type.
277    fn into_system_set(self) -> Self::Set;
278}
279
280// systems sets
281impl<S: SystemSet> IntoSystemSet<()> for S {
282    type Set = Self;
283
284    #[inline]
285    fn into_system_set(self) -> Self::Set {
286        self
287    }
288}
289
290// systems
291impl<Marker, F> IntoSystemSet<(IsFunctionSystem, Marker)> for F
292where
293    Marker: 'static,
294    F: SystemParamFunction<Marker, In: FromInput<()>, Out: IntoResult<()>>,
295{
296    type Set = SystemTypeSet<FunctionSystem<Marker, (), (), F>>;
297
298    #[inline]
299    fn into_system_set(self) -> Self::Set {
300        SystemTypeSet::<FunctionSystem<Marker, (), (), F>>::new()
301    }
302}
303
304// exclusive systems
305impl<Marker, F> IntoSystemSet<(IsExclusiveFunctionSystem, Marker)> for F
306where
307    Marker: 'static,
308    F::Out: IntoResult<()>,
309    F: ExclusiveSystemParamFunction<Marker>,
310{
311    type Set = SystemTypeSet<ExclusiveFunctionSystem<Marker, (), F>>;
312
313    #[inline]
314    fn into_system_set(self) -> Self::Set {
315        SystemTypeSet::<ExclusiveFunctionSystem<Marker, (), F>>::new()
316    }
317}
318
319#[cfg(test)]
320mod tests {
321    use crate::{
322        resource::Resource,
323        schedule::{tests::ResMut, Schedule},
324        system::{IntoSystem, System},
325    };
326
327    use super::*;
328
329    #[test]
330    fn test_schedule_label() {
331        use crate::world::World;
332
333        #[derive(Resource)]
334        struct Flag(bool);
335
336        #[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
337        struct A;
338
339        #[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
340        struct B;
341
342        let mut world = World::new();
343
344        let mut schedule = Schedule::new(A);
345        schedule.add_systems(|mut flag: ResMut<Flag>| flag.0 = true);
346        world.add_schedule(schedule);
347
348        let interned = A.intern();
349
350        world.insert_resource(Flag(false));
351        world.run_schedule(interned);
352        assert!(world.resource::<Flag>().0);
353
354        world.insert_resource(Flag(false));
355        world.run_schedule(interned);
356        assert!(world.resource::<Flag>().0);
357
358        assert_ne!(A.intern(), B.intern());
359    }
360
361    #[test]
362    fn test_derive_schedule_label() {
363        #[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
364        struct UnitLabel;
365
366        #[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
367        struct TupleLabel(u32, u32);
368
369        #[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
370        struct StructLabel {
371            a: u32,
372            b: u32,
373        }
374
375        #[expect(
376            dead_code,
377            reason = "This is a derive macro compilation test. It won't be constructed."
378        )]
379        #[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
380        struct EmptyTupleLabel();
381
382        #[expect(
383            dead_code,
384            reason = "This is a derive macro compilation test. It won't be constructed."
385        )]
386        #[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
387        struct EmptyStructLabel {}
388
389        #[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
390        enum EnumLabel {
391            #[default]
392            Unit,
393            Tuple(u32, u32),
394            Struct {
395                a: u32,
396                b: u32,
397            },
398        }
399
400        #[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
401        struct GenericLabel<T>(PhantomData<T>);
402
403        assert_eq!(UnitLabel.intern(), UnitLabel.intern());
404        assert_eq!(EnumLabel::Unit.intern(), EnumLabel::Unit.intern());
405        assert_ne!(UnitLabel.intern(), EnumLabel::Unit.intern());
406        assert_ne!(UnitLabel.intern(), TupleLabel(0, 0).intern());
407        assert_ne!(EnumLabel::Unit.intern(), EnumLabel::Tuple(0, 0).intern());
408
409        assert_eq!(TupleLabel(0, 0).intern(), TupleLabel(0, 0).intern());
410        assert_eq!(
411            EnumLabel::Tuple(0, 0).intern(),
412            EnumLabel::Tuple(0, 0).intern()
413        );
414        assert_ne!(TupleLabel(0, 0).intern(), TupleLabel(0, 1).intern());
415        assert_ne!(
416            EnumLabel::Tuple(0, 0).intern(),
417            EnumLabel::Tuple(0, 1).intern()
418        );
419        assert_ne!(TupleLabel(0, 0).intern(), EnumLabel::Tuple(0, 0).intern());
420        assert_ne!(
421            TupleLabel(0, 0).intern(),
422            StructLabel { a: 0, b: 0 }.intern()
423        );
424        assert_ne!(
425            EnumLabel::Tuple(0, 0).intern(),
426            EnumLabel::Struct { a: 0, b: 0 }.intern()
427        );
428
429        assert_eq!(
430            StructLabel { a: 0, b: 0 }.intern(),
431            StructLabel { a: 0, b: 0 }.intern()
432        );
433        assert_eq!(
434            EnumLabel::Struct { a: 0, b: 0 }.intern(),
435            EnumLabel::Struct { a: 0, b: 0 }.intern()
436        );
437        assert_ne!(
438            StructLabel { a: 0, b: 0 }.intern(),
439            StructLabel { a: 0, b: 1 }.intern()
440        );
441        assert_ne!(
442            EnumLabel::Struct { a: 0, b: 0 }.intern(),
443            EnumLabel::Struct { a: 0, b: 1 }.intern()
444        );
445        assert_ne!(
446            StructLabel { a: 0, b: 0 }.intern(),
447            EnumLabel::Struct { a: 0, b: 0 }.intern()
448        );
449        assert_ne!(
450            StructLabel { a: 0, b: 0 }.intern(),
451            EnumLabel::Struct { a: 0, b: 0 }.intern()
452        );
453        assert_ne!(StructLabel { a: 0, b: 0 }.intern(), UnitLabel.intern(),);
454        assert_ne!(
455            EnumLabel::Struct { a: 0, b: 0 }.intern(),
456            EnumLabel::Unit.intern()
457        );
458
459        assert_eq!(
460            GenericLabel::<u32>(PhantomData).intern(),
461            GenericLabel::<u32>(PhantomData).intern()
462        );
463        assert_ne!(
464            GenericLabel::<u32>(PhantomData).intern(),
465            GenericLabel::<u64>(PhantomData).intern()
466        );
467    }
468
469    #[test]
470    fn test_derive_system_set() {
471        #[derive(SystemSet, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
472        struct UnitSet;
473
474        #[derive(SystemSet, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
475        struct TupleSet(u32, u32);
476
477        #[derive(SystemSet, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
478        struct StructSet {
479            a: u32,
480            b: u32,
481        }
482
483        #[expect(
484            dead_code,
485            reason = "This is a derive macro compilation test. It won't be constructed."
486        )]
487        #[derive(SystemSet, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
488        struct EmptyTupleSet();
489
490        #[expect(
491            dead_code,
492            reason = "This is a derive macro compilation test. It won't be constructed."
493        )]
494        #[derive(SystemSet, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
495        struct EmptyStructSet {}
496
497        #[derive(SystemSet, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
498        enum EnumSet {
499            #[default]
500            Unit,
501            Tuple(u32, u32),
502            Struct {
503                a: u32,
504                b: u32,
505            },
506        }
507
508        #[derive(SystemSet, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
509        struct GenericSet<T>(PhantomData<T>);
510
511        assert_eq!(UnitSet.intern(), UnitSet.intern());
512        assert_eq!(EnumSet::Unit.intern(), EnumSet::Unit.intern());
513        assert_ne!(UnitSet.intern(), EnumSet::Unit.intern());
514        assert_ne!(UnitSet.intern(), TupleSet(0, 0).intern());
515        assert_ne!(EnumSet::Unit.intern(), EnumSet::Tuple(0, 0).intern());
516
517        assert_eq!(TupleSet(0, 0).intern(), TupleSet(0, 0).intern());
518        assert_eq!(EnumSet::Tuple(0, 0).intern(), EnumSet::Tuple(0, 0).intern());
519        assert_ne!(TupleSet(0, 0).intern(), TupleSet(0, 1).intern());
520        assert_ne!(EnumSet::Tuple(0, 0).intern(), EnumSet::Tuple(0, 1).intern());
521        assert_ne!(TupleSet(0, 0).intern(), EnumSet::Tuple(0, 0).intern());
522        assert_ne!(TupleSet(0, 0).intern(), StructSet { a: 0, b: 0 }.intern());
523        assert_ne!(
524            EnumSet::Tuple(0, 0).intern(),
525            EnumSet::Struct { a: 0, b: 0 }.intern()
526        );
527
528        assert_eq!(
529            StructSet { a: 0, b: 0 }.intern(),
530            StructSet { a: 0, b: 0 }.intern()
531        );
532        assert_eq!(
533            EnumSet::Struct { a: 0, b: 0 }.intern(),
534            EnumSet::Struct { a: 0, b: 0 }.intern()
535        );
536        assert_ne!(
537            StructSet { a: 0, b: 0 }.intern(),
538            StructSet { a: 0, b: 1 }.intern()
539        );
540        assert_ne!(
541            EnumSet::Struct { a: 0, b: 0 }.intern(),
542            EnumSet::Struct { a: 0, b: 1 }.intern()
543        );
544        assert_ne!(
545            StructSet { a: 0, b: 0 }.intern(),
546            EnumSet::Struct { a: 0, b: 0 }.intern()
547        );
548        assert_ne!(
549            StructSet { a: 0, b: 0 }.intern(),
550            EnumSet::Struct { a: 0, b: 0 }.intern()
551        );
552        assert_ne!(StructSet { a: 0, b: 0 }.intern(), UnitSet.intern(),);
553        assert_ne!(
554            EnumSet::Struct { a: 0, b: 0 }.intern(),
555            EnumSet::Unit.intern()
556        );
557
558        assert_eq!(
559            GenericSet::<u32>(PhantomData).intern(),
560            GenericSet::<u32>(PhantomData).intern()
561        );
562        assert_ne!(
563            GenericSet::<u32>(PhantomData).intern(),
564            GenericSet::<u64>(PhantomData).intern()
565        );
566    }
567
568    #[test]
569    fn system_set_matches_default_system_set() {
570        fn system() {}
571        let set_from_into_system_set = IntoSystemSet::into_system_set(system).intern();
572        let system = IntoSystem::into_system(system);
573        let set_from_system = system.default_system_sets()[0];
574        assert_eq!(set_from_into_system_set, set_from_system);
575    }
576
577    #[test]
578    fn system_set_matches_default_system_set_exclusive() {
579        fn system(_: &mut crate::world::World) {}
580        let set_from_into_system_set = IntoSystemSet::into_system_set(system).intern();
581        let system = IntoSystem::into_system(system);
582        let set_from_system = system.default_system_sets()[0];
583        assert_eq!(set_from_into_system_set, set_from_system);
584    }
585}