Skip to main content

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