bevy_ecs/schedule/
schedule.rs

1#![expect(
2    clippy::module_inception,
3    reason = "This instance of module inception is being discussed; see #17344."
4)]
5use alloc::{
6    boxed::Box,
7    collections::BTreeSet,
8    format,
9    string::{String, ToString},
10    vec,
11    vec::Vec,
12};
13use bevy_platform::{
14    collections::{HashMap, HashSet},
15    hash::FixedHasher,
16};
17use bevy_utils::{default, TypeIdMap};
18use core::{
19    any::{Any, TypeId},
20    fmt::{Debug, Write},
21};
22use fixedbitset::FixedBitSet;
23use indexmap::{IndexMap, IndexSet};
24use log::{info, warn};
25use pass::ScheduleBuildPassObj;
26use thiserror::Error;
27#[cfg(feature = "trace")]
28use tracing::info_span;
29
30use crate::{change_detection::CheckChangeTicks, system::System};
31use crate::{
32    component::{ComponentId, Components},
33    prelude::Component,
34    resource::Resource,
35    schedule::*,
36    system::ScheduleSystem,
37    world::World,
38};
39
40pub use stepping::Stepping;
41use Direction::{Incoming, Outgoing};
42
43/// Resource that stores [`Schedule`]s mapped to [`ScheduleLabel`]s excluding the current running [`Schedule`].
44#[derive(Default, Resource)]
45pub struct Schedules {
46    inner: HashMap<InternedScheduleLabel, Schedule>,
47    /// List of [`ComponentId`]s to ignore when reporting system order ambiguity conflicts
48    pub ignored_scheduling_ambiguities: BTreeSet<ComponentId>,
49}
50
51impl Schedules {
52    /// Constructs an empty `Schedules` with zero initial capacity.
53    pub fn new() -> Self {
54        Self::default()
55    }
56
57    /// Inserts a labeled schedule into the map.
58    ///
59    /// If the map already had an entry for `label`, `schedule` is inserted,
60    /// and the old schedule is returned. Otherwise, `None` is returned.
61    pub fn insert(&mut self, schedule: Schedule) -> Option<Schedule> {
62        self.inner.insert(schedule.label, schedule)
63    }
64
65    /// Removes the schedule corresponding to the `label` from the map, returning it if it existed.
66    pub fn remove(&mut self, label: impl ScheduleLabel) -> Option<Schedule> {
67        self.inner.remove(&label.intern())
68    }
69
70    /// Removes the (schedule, label) pair corresponding to the `label` from the map, returning it if it existed.
71    pub fn remove_entry(
72        &mut self,
73        label: impl ScheduleLabel,
74    ) -> Option<(InternedScheduleLabel, Schedule)> {
75        self.inner.remove_entry(&label.intern())
76    }
77
78    /// Does a schedule with the provided label already exist?
79    pub fn contains(&self, label: impl ScheduleLabel) -> bool {
80        self.inner.contains_key(&label.intern())
81    }
82
83    /// Returns a reference to the schedule associated with `label`, if it exists.
84    pub fn get(&self, label: impl ScheduleLabel) -> Option<&Schedule> {
85        self.inner.get(&label.intern())
86    }
87
88    /// Returns a mutable reference to the schedule associated with `label`, if it exists.
89    pub fn get_mut(&mut self, label: impl ScheduleLabel) -> Option<&mut Schedule> {
90        self.inner.get_mut(&label.intern())
91    }
92
93    /// Returns a mutable reference to the schedules associated with `label`, creating one if it doesn't already exist.
94    pub fn entry(&mut self, label: impl ScheduleLabel) -> &mut Schedule {
95        self.inner
96            .entry(label.intern())
97            .or_insert_with(|| Schedule::new(label))
98    }
99
100    /// Returns an iterator over all schedules. Iteration order is undefined.
101    pub fn iter(&self) -> impl Iterator<Item = (&dyn ScheduleLabel, &Schedule)> {
102        self.inner
103            .iter()
104            .map(|(label, schedule)| (&**label, schedule))
105    }
106    /// Returns an iterator over mutable references to all schedules. Iteration order is undefined.
107    pub fn iter_mut(&mut self) -> impl Iterator<Item = (&dyn ScheduleLabel, &mut Schedule)> {
108        self.inner
109            .iter_mut()
110            .map(|(label, schedule)| (&**label, schedule))
111    }
112
113    /// Iterates the change ticks of all systems in all stored schedules and clamps any older than
114    /// [`MAX_CHANGE_AGE`](crate::change_detection::MAX_CHANGE_AGE).
115    /// This prevents overflow and thus prevents false positives.
116    pub(crate) fn check_change_ticks(&mut self, check: CheckChangeTicks) {
117        #[cfg(feature = "trace")]
118        let _all_span = info_span!("check stored schedule ticks").entered();
119        #[cfg_attr(
120            not(feature = "trace"),
121            expect(
122                unused_variables,
123                reason = "The `label` variable goes unused if the `trace` feature isn't active"
124            )
125        )]
126        for (label, schedule) in &mut self.inner {
127            #[cfg(feature = "trace")]
128            let name = format!("{label:?}");
129            #[cfg(feature = "trace")]
130            let _one_span = info_span!("check schedule ticks", name = &name).entered();
131            schedule.check_change_ticks(check);
132        }
133    }
134
135    /// Applies the provided [`ScheduleBuildSettings`] to all schedules.
136    pub fn configure_schedules(&mut self, schedule_build_settings: ScheduleBuildSettings) {
137        for (_, schedule) in &mut self.inner {
138            schedule.set_build_settings(schedule_build_settings.clone());
139        }
140    }
141
142    /// Ignore system order ambiguities caused by conflicts on [`Component`]s of type `T`.
143    pub fn allow_ambiguous_component<T: Component>(&mut self, world: &mut World) {
144        self.ignored_scheduling_ambiguities
145            .insert(world.register_component::<T>());
146    }
147
148    /// Ignore system order ambiguities caused by conflicts on [`Resource`]s of type `T`.
149    pub fn allow_ambiguous_resource<T: Resource>(&mut self, world: &mut World) {
150        self.ignored_scheduling_ambiguities
151            .insert(world.components_registrator().register_resource::<T>());
152    }
153
154    /// Iterate through the [`ComponentId`]'s that will be ignored.
155    pub fn iter_ignored_ambiguities(&self) -> impl Iterator<Item = &ComponentId> + '_ {
156        self.ignored_scheduling_ambiguities.iter()
157    }
158
159    /// Prints the names of the components and resources with [`info`]
160    ///
161    /// May panic or retrieve incorrect names if [`Components`] is not from the same
162    /// world
163    pub fn print_ignored_ambiguities(&self, components: &Components) {
164        let mut message =
165            "System order ambiguities caused by conflicts on the following types are ignored:\n"
166                .to_string();
167        for id in self.iter_ignored_ambiguities() {
168            writeln!(message, "{}", components.get_name(*id).unwrap()).unwrap();
169        }
170
171        info!("{message}");
172    }
173
174    /// Adds one or more systems to the [`Schedule`] matching the provided [`ScheduleLabel`].
175    pub fn add_systems<M>(
176        &mut self,
177        schedule: impl ScheduleLabel,
178        systems: impl IntoScheduleConfigs<ScheduleSystem, M>,
179    ) -> &mut Self {
180        self.entry(schedule).add_systems(systems);
181
182        self
183    }
184
185    /// Removes all systems in a [`SystemSet`]. This will cause the schedule to be rebuilt when
186    /// the schedule is run again. A [`ScheduleError`] is returned if the schedule needs to be
187    /// [`Schedule::initialize`]'d or the `set` is not found.
188    pub fn remove_systems_in_set<M>(
189        &mut self,
190        schedule: impl ScheduleLabel,
191        set: impl IntoSystemSet<M>,
192        world: &mut World,
193        policy: ScheduleCleanupPolicy,
194    ) -> Result<usize, ScheduleError> {
195        self.get_mut(schedule)
196            .ok_or(ScheduleError::ScheduleNotFound)?
197            .remove_systems_in_set(set, world, policy)
198    }
199
200    /// Configures a collection of system sets in the provided schedule, adding any sets that do not exist.
201    #[track_caller]
202    pub fn configure_sets<M>(
203        &mut self,
204        schedule: impl ScheduleLabel,
205        sets: impl IntoScheduleConfigs<InternedSystemSet, M>,
206    ) -> &mut Self {
207        self.entry(schedule).configure_sets(sets);
208
209        self
210    }
211
212    /// Suppress warnings and errors that would result from systems in these sets having ambiguities
213    /// (conflicting access but indeterminate order) with systems in `set`.
214    ///
215    /// When possible, do this directly in the `.add_systems(Update, a.ambiguous_with(b))` call.
216    /// However, sometimes two independent plugins `A` and `B` are reported as ambiguous, which you
217    /// can only suppress as the consumer of both.
218    #[track_caller]
219    pub fn ignore_ambiguity<M1, M2, S1, S2>(
220        &mut self,
221        schedule: impl ScheduleLabel,
222        a: S1,
223        b: S2,
224    ) -> &mut Self
225    where
226        S1: IntoSystemSet<M1>,
227        S2: IntoSystemSet<M2>,
228    {
229        self.entry(schedule).ignore_ambiguity(a, b);
230
231        self
232    }
233}
234
235fn make_executor(kind: ExecutorKind) -> Box<dyn SystemExecutor> {
236    match kind {
237        ExecutorKind::SingleThreaded => Box::new(SingleThreadedExecutor::new()),
238        #[cfg(feature = "std")]
239        ExecutorKind::MultiThreaded => Box::new(MultiThreadedExecutor::new()),
240    }
241}
242
243/// Chain systems into dependencies
244#[derive(Default)]
245pub enum Chain {
246    /// Systems are independent. Nodes are allowed to run in any order.
247    #[default]
248    Unchained,
249    /// Systems are chained. `before -> after` ordering constraints
250    /// will be added between the successive elements.
251    Chained(TypeIdMap<Box<dyn Any>>),
252}
253
254impl Chain {
255    /// Specify that the systems must be chained.
256    pub fn set_chained(&mut self) {
257        if matches!(self, Chain::Unchained) {
258            *self = Self::Chained(Default::default());
259        };
260    }
261    /// Specify that the systems must be chained, and add the specified configuration for
262    /// all dependencies created between these systems.
263    pub fn set_chained_with_config<T: 'static>(&mut self, config: T) {
264        self.set_chained();
265        if let Chain::Chained(config_map) = self {
266            config_map.insert(TypeId::of::<T>(), Box::new(config));
267        } else {
268            unreachable!()
269        };
270    }
271}
272
273/// A collection of systems, and the metadata and executor needed to run them
274/// in a certain order under certain conditions.
275///
276/// # Schedule labels
277///
278/// Each schedule has a [`ScheduleLabel`] value. This value is used to uniquely identify the
279/// schedule when added to a [`World`]’s [`Schedules`], and may be used to specify which schedule
280/// a system should be added to.
281///
282/// # Example
283///
284/// Here is an example of a `Schedule` running a "Hello world" system:
285///
286/// ```
287/// # use bevy_ecs::prelude::*;
288/// fn hello_world() { println!("Hello world!") }
289///
290/// fn main() {
291///     let mut world = World::new();
292///     let mut schedule = Schedule::default();
293///     schedule.add_systems(hello_world);
294///
295///     schedule.run(&mut world);
296/// }
297/// ```
298///
299/// A schedule can also run several systems in an ordered way:
300///
301/// ```
302/// # use bevy_ecs::prelude::*;
303/// fn system_one() { println!("System 1 works!") }
304/// fn system_two() { println!("System 2 works!") }
305/// fn system_three() { println!("System 3 works!") }
306///
307/// fn main() {
308///     let mut world = World::new();
309///     let mut schedule = Schedule::default();
310///     schedule.add_systems((
311///         system_two,
312///         system_one.before(system_two),
313///         system_three.after(system_two),
314///     ));
315///
316///     schedule.run(&mut world);
317/// }
318/// ```
319///
320/// Schedules are often inserted into a [`World`] and identified by their [`ScheduleLabel`] only:
321///
322/// ```
323/// # use bevy_ecs::prelude::*;
324/// use bevy_ecs::schedule::ScheduleLabel;
325///
326/// // Declare a new schedule label.
327/// #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
328/// struct Update;
329///
330/// // This system shall be part of the schedule.
331/// fn an_update_system() {
332///     println!("Hello world!");
333/// }
334///
335/// fn main() {
336///     let mut world = World::new();
337///
338///     // Add a system to the schedule with that label (creating it automatically).
339///     world.get_resource_or_init::<Schedules>().add_systems(Update, an_update_system);
340///
341///     // Run the schedule, and therefore run the system.
342///     world.run_schedule(Update);
343/// }
344/// ```
345pub struct Schedule {
346    label: InternedScheduleLabel,
347    graph: ScheduleGraph,
348    executable: SystemSchedule,
349    executor: Box<dyn SystemExecutor>,
350    executor_initialized: bool,
351    warnings: Vec<ScheduleBuildWarning>,
352}
353
354#[derive(ScheduleLabel, Hash, PartialEq, Eq, Debug, Clone)]
355struct DefaultSchedule;
356
357impl Default for Schedule {
358    /// Creates a schedule with a default label. Only use in situations where
359    /// you don't care about the [`ScheduleLabel`]. Inserting a default schedule
360    /// into the world risks overwriting another schedule. For most situations
361    /// you should use [`Schedule::new`].
362    fn default() -> Self {
363        Self::new(DefaultSchedule)
364    }
365}
366
367impl Schedule {
368    /// Constructs an empty `Schedule`.
369    pub fn new(label: impl ScheduleLabel) -> Self {
370        let mut this = Self {
371            label: label.intern(),
372            graph: ScheduleGraph::new(),
373            executable: SystemSchedule::new(),
374            executor: make_executor(ExecutorKind::default()),
375            executor_initialized: false,
376            warnings: Vec::new(),
377        };
378        // Call `set_build_settings` to add any default build passes
379        this.set_build_settings(Default::default());
380        this
381    }
382
383    /// Returns the [`InternedScheduleLabel`] for this `Schedule`,
384    /// corresponding to the [`ScheduleLabel`] this schedule was created with.
385    pub fn label(&self) -> InternedScheduleLabel {
386        self.label
387    }
388
389    /// Add a collection of systems to the schedule.
390    pub fn add_systems<M>(
391        &mut self,
392        systems: impl IntoScheduleConfigs<ScheduleSystem, M>,
393    ) -> &mut Self {
394        self.graph.process_configs(systems.into_configs(), false);
395        self
396    }
397
398    /// Removes all systems in a [`SystemSet`]. This will cause the schedule to be rebuilt when
399    /// the schedule is run again. A [`ScheduleError`] is returned if the schedule needs to be
400    /// [`Schedule::initialize`]'d or the `set` is not found.
401    ///
402    /// Note that this can remove all systems of a type if you pass
403    /// the system to this function as systems implicitly create a set based
404    /// on the system type.
405    ///
406    /// ## Example
407    /// ```
408    /// # use bevy_ecs::prelude::*;
409    /// # use bevy_ecs::schedule::ScheduleCleanupPolicy;
410    /// #
411    /// # fn my_system() {}
412    /// #
413    /// let mut schedule = Schedule::default();
414    /// // add the system to the schedule
415    /// schedule.add_systems(my_system);
416    /// let mut world = World::default();
417    ///
418    /// // remove the system
419    /// schedule.remove_systems_in_set(my_system, &mut world, ScheduleCleanupPolicy::RemoveSystemsOnly);
420    /// ```
421    pub fn remove_systems_in_set<M>(
422        &mut self,
423        set: impl IntoSystemSet<M>,
424        world: &mut World,
425        policy: ScheduleCleanupPolicy,
426    ) -> Result<usize, ScheduleError> {
427        if self.graph.changed {
428            self.initialize(world)?;
429        }
430        self.graph.remove_systems_in_set(set, policy)
431    }
432
433    /// Suppress warnings and errors that would result from systems in these sets having ambiguities
434    /// (conflicting access but indeterminate order) with systems in `set`.
435    #[track_caller]
436    pub fn ignore_ambiguity<M1, M2, S1, S2>(&mut self, a: S1, b: S2) -> &mut Self
437    where
438        S1: IntoSystemSet<M1>,
439        S2: IntoSystemSet<M2>,
440    {
441        let a = a.into_system_set();
442        let b = b.into_system_set();
443
444        let a_id = self.graph.system_sets.get_key_or_insert(a.intern());
445        let b_id = self.graph.system_sets.get_key_or_insert(b.intern());
446
447        self.graph
448            .ambiguous_with
449            .add_edge(NodeId::Set(a_id), NodeId::Set(b_id));
450
451        self
452    }
453
454    /// Configures a collection of system sets in this schedule, adding them if they does not exist.
455    #[track_caller]
456    pub fn configure_sets<M>(
457        &mut self,
458        sets: impl IntoScheduleConfigs<InternedSystemSet, M>,
459    ) -> &mut Self {
460        self.graph.configure_sets(sets);
461        self
462    }
463
464    /// Add a custom build pass to the schedule.
465    pub fn add_build_pass<T: ScheduleBuildPass>(&mut self, pass: T) -> &mut Self {
466        self.graph.passes.insert(TypeId::of::<T>(), Box::new(pass));
467        self
468    }
469
470    /// Remove a custom build pass.
471    pub fn remove_build_pass<T: ScheduleBuildPass>(&mut self) {
472        self.graph.passes.shift_remove(&TypeId::of::<T>());
473    }
474
475    /// Changes miscellaneous build settings.
476    ///
477    /// If [`settings.auto_insert_apply_deferred`][ScheduleBuildSettings::auto_insert_apply_deferred]
478    /// is `false`, this clears `*_ignore_deferred` edge settings configured so far.
479    ///
480    /// Generally this method should be used before adding systems or set configurations to the schedule,
481    /// not after.
482    pub fn set_build_settings(&mut self, settings: ScheduleBuildSettings) -> &mut Self {
483        if settings.auto_insert_apply_deferred {
484            if !self
485                .graph
486                .passes
487                .contains_key(&TypeId::of::<passes::AutoInsertApplyDeferredPass>())
488            {
489                self.add_build_pass(passes::AutoInsertApplyDeferredPass::default());
490            }
491        } else {
492            self.remove_build_pass::<passes::AutoInsertApplyDeferredPass>();
493        }
494        self.graph.settings = settings;
495        self
496    }
497
498    /// Returns the schedule's current `ScheduleBuildSettings`.
499    pub fn get_build_settings(&self) -> ScheduleBuildSettings {
500        self.graph.settings.clone()
501    }
502
503    /// Returns the schedule's current execution strategy.
504    pub fn get_executor_kind(&self) -> ExecutorKind {
505        self.executor.kind()
506    }
507
508    /// Sets the schedule's execution strategy.
509    pub fn set_executor_kind(&mut self, executor: ExecutorKind) -> &mut Self {
510        if executor != self.executor.kind() {
511            self.executor = make_executor(executor);
512            self.executor_initialized = false;
513        }
514        self
515    }
516
517    /// Set whether the schedule applies deferred system buffers on final time or not. This is a catch-all
518    /// in case a system uses commands but was not explicitly ordered before an instance of
519    /// [`ApplyDeferred`]. By default this
520    /// setting is true, but may be disabled if needed.
521    pub fn set_apply_final_deferred(&mut self, apply_final_deferred: bool) -> &mut Self {
522        self.executor.set_apply_final_deferred(apply_final_deferred);
523        self
524    }
525
526    /// Runs all systems in this schedule on the `world`, using its current execution strategy.
527    pub fn run(&mut self, world: &mut World) {
528        #[cfg(feature = "trace")]
529        let _span = info_span!("schedule", name = ?self.label).entered();
530
531        world.check_change_ticks();
532        self.initialize(world).unwrap_or_else(|e| {
533            panic!(
534                "Error when initializing schedule {:?}: {}",
535                self.label,
536                e.to_string(self.graph(), world)
537            )
538        });
539
540        let error_handler = world.default_error_handler();
541
542        #[cfg(not(feature = "bevy_debug_stepping"))]
543        self.executor
544            .run(&mut self.executable, world, None, error_handler);
545
546        #[cfg(feature = "bevy_debug_stepping")]
547        {
548            let skip_systems = match world.get_resource_mut::<Stepping>() {
549                None => None,
550                Some(mut stepping) => stepping.skipped_systems(self),
551            };
552
553            self.executor.run(
554                &mut self.executable,
555                world,
556                skip_systems.as_ref(),
557                error_handler,
558            );
559        }
560    }
561
562    /// Initializes any newly-added systems and conditions, rebuilds the executable schedule,
563    /// and re-initializes the executor.
564    ///
565    /// Moves all systems and run conditions out of the [`ScheduleGraph`].
566    pub fn initialize(&mut self, world: &mut World) -> Result<(), ScheduleBuildError> {
567        if self.graph.changed {
568            self.graph.initialize(world);
569            let ignored_ambiguities = world
570                .get_resource_or_init::<Schedules>()
571                .ignored_scheduling_ambiguities
572                .clone();
573            self.warnings = self.graph.update_schedule(
574                world,
575                &mut self.executable,
576                &ignored_ambiguities,
577                self.label,
578            )?;
579            self.graph.changed = false;
580            self.executor_initialized = false;
581        }
582
583        if !self.executor_initialized {
584            self.executor.init(&self.executable);
585            self.executor_initialized = true;
586        }
587
588        Ok(())
589    }
590
591    /// Returns the [`ScheduleGraph`].
592    pub fn graph(&self) -> &ScheduleGraph {
593        &self.graph
594    }
595
596    /// Returns a mutable reference to the [`ScheduleGraph`].
597    pub fn graph_mut(&mut self) -> &mut ScheduleGraph {
598        &mut self.graph
599    }
600
601    /// Returns the [`SystemSchedule`].
602    pub(crate) fn executable(&self) -> &SystemSchedule {
603        &self.executable
604    }
605
606    /// Iterates the change ticks of all systems in the schedule and clamps any older than
607    /// [`MAX_CHANGE_AGE`](crate::change_detection::MAX_CHANGE_AGE).
608    /// This prevents overflow and thus prevents false positives.
609    pub fn check_change_ticks(&mut self, check: CheckChangeTicks) {
610        for system in &mut self.executable.systems {
611            if !is_apply_deferred(system) {
612                system.check_change_tick(check);
613            }
614        }
615
616        for conditions in &mut self.executable.system_conditions {
617            for condition in conditions {
618                condition.check_change_tick(check);
619            }
620        }
621
622        for conditions in &mut self.executable.set_conditions {
623            for condition in conditions {
624                condition.check_change_tick(check);
625            }
626        }
627    }
628
629    /// Directly applies any accumulated [`Deferred`](crate::system::Deferred) system parameters (like [`Commands`](crate::prelude::Commands)) to the `world`.
630    ///
631    /// Like always, deferred system parameters are applied in the "topological sort order" of the schedule graph.
632    /// As a result, buffers from one system are only guaranteed to be applied before those of other systems
633    /// if there is an explicit system ordering between the two systems.
634    ///
635    /// This is used in rendering to extract data from the main world, storing the data in system buffers,
636    /// before applying their buffers in a different world.
637    pub fn apply_deferred(&mut self, world: &mut World) {
638        for SystemWithAccess { system, .. } in &mut self.executable.systems {
639            system.apply_deferred(world);
640        }
641    }
642
643    /// Returns an iterator over all systems in this schedule.
644    ///
645    /// Note: this method will return [`ScheduleNotInitialized`] if the
646    /// schedule has never been initialized or run.
647    pub fn systems(
648        &self,
649    ) -> Result<impl Iterator<Item = (SystemKey, &ScheduleSystem)> + Sized, ScheduleNotInitialized>
650    {
651        if !self.executor_initialized {
652            return Err(ScheduleNotInitialized);
653        }
654
655        let iter = self
656            .executable
657            .system_ids
658            .iter()
659            .zip(&self.executable.systems)
660            .map(|(&node_id, system)| (node_id, &system.system));
661
662        Ok(iter)
663    }
664
665    /// Returns the number of systems in this schedule.
666    pub fn systems_len(&self) -> usize {
667        if !self.executor_initialized {
668            self.graph.systems.len()
669        } else {
670            self.executable.systems.len()
671        }
672    }
673
674    /// Returns warnings that were generated during the last call to
675    /// [`Schedule::initialize`].
676    pub fn warnings(&self) -> &[ScheduleBuildWarning] {
677        &self.warnings
678    }
679}
680
681/// Metadata for a [`Schedule`].
682///
683/// The order isn't optimized; calling `ScheduleGraph::build_schedule` will return a
684/// `SystemSchedule` where the order is optimized for execution.
685#[derive(Default)]
686pub struct ScheduleGraph {
687    /// Container of systems in the schedule.
688    pub systems: Systems,
689    /// Container of system sets in the schedule.
690    pub system_sets: SystemSets,
691    /// Directed acyclic graph of the hierarchy (which systems/sets are children of which sets)
692    hierarchy: Dag<NodeId>,
693    /// Directed acyclic graph of the dependency (which systems/sets have to run before which other systems/sets)
694    dependency: Dag<NodeId>,
695    /// Map of systems in each set
696    set_systems: DagGroups<SystemSetKey, SystemKey>,
697    ambiguous_with: UnGraph<NodeId>,
698    /// Nodes that are allowed to have ambiguous ordering relationship with any other systems.
699    pub ambiguous_with_all: HashSet<NodeId>,
700    conflicting_systems: ConflictingSystems,
701    anonymous_sets: usize,
702    changed: bool,
703    settings: ScheduleBuildSettings,
704    passes: IndexMap<TypeId, Box<dyn ScheduleBuildPassObj>, FixedHasher>,
705}
706
707impl ScheduleGraph {
708    /// Creates an empty [`ScheduleGraph`] with default settings.
709    pub fn new() -> Self {
710        Self {
711            systems: Systems::default(),
712            system_sets: SystemSets::default(),
713            hierarchy: Dag::new(),
714            dependency: Dag::new(),
715            set_systems: DagGroups::default(),
716            ambiguous_with: UnGraph::default(),
717            ambiguous_with_all: HashSet::default(),
718            conflicting_systems: ConflictingSystems::default(),
719            anonymous_sets: 0,
720            changed: false,
721            settings: default(),
722            passes: default(),
723        }
724    }
725
726    /// Returns the [`Dag`] of the hierarchy.
727    ///
728    /// The hierarchy is a directed acyclic graph of the systems and sets,
729    /// where an edge denotes that a system or set is the child of another set.
730    pub fn hierarchy(&self) -> &Dag<NodeId> {
731        &self.hierarchy
732    }
733
734    /// Returns the [`Dag`] of the dependencies in the schedule.
735    ///
736    /// Nodes in this graph are systems and sets, and edges denote that
737    /// a system or set has to run before another system or set.
738    pub fn dependency(&self) -> &Dag<NodeId> {
739        &self.dependency
740    }
741
742    /// Returns the list of systems that conflict with each other, i.e. have ambiguities in their access.
743    ///
744    /// If the `Vec<ComponentId>` is empty, the systems conflict on [`World`] access.
745    /// Must be called after [`ScheduleGraph::build_schedule`] to be non-empty.
746    pub fn conflicting_systems(&self) -> &ConflictingSystems {
747        &self.conflicting_systems
748    }
749
750    fn process_config<T: ProcessScheduleConfig + Schedulable>(
751        &mut self,
752        config: ScheduleConfig<T>,
753        collect_nodes: bool,
754    ) -> ProcessConfigsResult {
755        ProcessConfigsResult {
756            densely_chained: true,
757            nodes: collect_nodes
758                .then_some(T::process_config(self, config))
759                .into_iter()
760                .collect(),
761        }
762    }
763
764    fn apply_collective_conditions<
765        T: ProcessScheduleConfig + Schedulable<Metadata = GraphInfo, GroupMetadata = Chain>,
766    >(
767        &mut self,
768        configs: &mut [ScheduleConfigs<T>],
769        collective_conditions: Vec<BoxedCondition>,
770    ) {
771        if !collective_conditions.is_empty() {
772            if let [config] = configs {
773                for condition in collective_conditions {
774                    config.run_if_dyn(condition);
775                }
776            } else {
777                let set = self.create_anonymous_set();
778                for config in configs.iter_mut() {
779                    config.in_set_inner(set.intern());
780                }
781                let mut set_config = InternedSystemSet::into_config(set.intern());
782                set_config.conditions.extend(collective_conditions);
783                self.configure_set_inner(set_config);
784            }
785        }
786    }
787
788    /// Adds the config nodes to the graph.
789    ///
790    /// `collect_nodes` controls whether the `NodeId`s of the processed config nodes are stored in the returned [`ProcessConfigsResult`].
791    /// `process_config` is the function which processes each individual config node and returns a corresponding `NodeId`.
792    ///
793    /// The fields on the returned [`ProcessConfigsResult`] are:
794    /// - `nodes`: a vector of all node ids contained in the nested `ScheduleConfigs`
795    /// - `densely_chained`: a boolean that is true if all nested nodes are linearly chained (with successive `after` orderings) in the order they are defined
796    #[track_caller]
797    fn process_configs<
798        T: ProcessScheduleConfig + Schedulable<Metadata = GraphInfo, GroupMetadata = Chain>,
799    >(
800        &mut self,
801        configs: ScheduleConfigs<T>,
802        collect_nodes: bool,
803    ) -> ProcessConfigsResult {
804        match configs {
805            ScheduleConfigs::ScheduleConfig(config) => self.process_config(config, collect_nodes),
806            ScheduleConfigs::Configs {
807                metadata,
808                mut configs,
809                collective_conditions,
810            } => {
811                self.apply_collective_conditions(&mut configs, collective_conditions);
812
813                let is_chained = matches!(metadata, Chain::Chained(_));
814
815                // Densely chained if
816                // * chained and all configs in the chain are densely chained, or
817                // * unchained with a single densely chained config
818                let mut densely_chained = is_chained || configs.len() == 1;
819                let mut configs = configs.into_iter();
820                let mut nodes = Vec::new();
821
822                let Some(first) = configs.next() else {
823                    return ProcessConfigsResult {
824                        nodes: Vec::new(),
825                        densely_chained,
826                    };
827                };
828                let mut previous_result = self.process_configs(first, collect_nodes || is_chained);
829                densely_chained &= previous_result.densely_chained;
830
831                for current in configs {
832                    let current_result = self.process_configs(current, collect_nodes || is_chained);
833                    densely_chained &= current_result.densely_chained;
834
835                    if let Chain::Chained(chain_options) = &metadata {
836                        // if the current result is densely chained, we only need to chain the first node
837                        let current_nodes = if current_result.densely_chained {
838                            &current_result.nodes[..1]
839                        } else {
840                            &current_result.nodes
841                        };
842                        // if the previous result was densely chained, we only need to chain the last node
843                        let previous_nodes = if previous_result.densely_chained {
844                            &previous_result.nodes[previous_result.nodes.len() - 1..]
845                        } else {
846                            &previous_result.nodes
847                        };
848
849                        self.dependency
850                            .reserve_edges(previous_nodes.len() * current_nodes.len());
851                        for previous_node in previous_nodes {
852                            for current_node in current_nodes {
853                                self.dependency.add_edge(*previous_node, *current_node);
854
855                                for pass in self.passes.values_mut() {
856                                    pass.add_dependency(
857                                        *previous_node,
858                                        *current_node,
859                                        chain_options,
860                                    );
861                                }
862                            }
863                        }
864                    }
865                    if collect_nodes {
866                        nodes.append(&mut previous_result.nodes);
867                    }
868
869                    previous_result = current_result;
870                }
871                if collect_nodes {
872                    nodes.append(&mut previous_result.nodes);
873                }
874
875                ProcessConfigsResult {
876                    nodes,
877                    densely_chained,
878                }
879            }
880        }
881    }
882
883    /// Add a [`ScheduleConfig`] to the graph, including its dependencies and conditions.
884    fn add_system_inner(&mut self, config: ScheduleConfig<ScheduleSystem>) -> SystemKey {
885        let key = self.systems.insert(config.node, config.conditions);
886
887        // graph updates are immediate
888        self.update_graphs(NodeId::System(key), config.metadata);
889
890        key
891    }
892
893    #[track_caller]
894    fn configure_sets<M>(&mut self, sets: impl IntoScheduleConfigs<InternedSystemSet, M>) {
895        self.process_configs(sets.into_configs(), false);
896    }
897
898    /// Add a single `ScheduleConfig` to the graph, including its dependencies and conditions.
899    fn configure_set_inner(&mut self, config: ScheduleConfig<InternedSystemSet>) -> SystemSetKey {
900        let key = self.system_sets.insert(config.node, config.conditions);
901
902        // graph updates are immediate
903        self.update_graphs(NodeId::Set(key), config.metadata);
904
905        key
906    }
907
908    fn create_anonymous_set(&mut self) -> AnonymousSet {
909        let id = self.anonymous_sets;
910        self.anonymous_sets += 1;
911        AnonymousSet::new(id)
912    }
913
914    /// Returns a `Vec` containing all [`SystemKey`]s in a [`SystemSet`].
915    ///
916    /// # Errors
917    ///
918    /// This method may return an error. It'll be:
919    ///
920    /// - `ScheduleError::Uninitialized` if the schedule has been changed,
921    ///   and `Self::initialize` has not been called.
922    /// - `ScheduleError::NotFound` if `system_set` isn't present in the
923    ///   schedule.
924    pub fn systems_in_set(
925        &self,
926        system_set: InternedSystemSet,
927    ) -> Result<&IndexSet<SystemKey, FixedHasher>, ScheduleError> {
928        if self.changed {
929            return Err(ScheduleError::Uninitialized);
930        }
931        let system_set_id = self
932            .system_sets
933            .get_key(system_set)
934            .ok_or(ScheduleError::SetNotFound)?;
935        self.set_systems
936            .get(&system_set_id)
937            .ok_or(ScheduleError::SetNotFound)
938    }
939
940    fn add_edges_for_transitive_dependencies(&mut self, node: NodeId) {
941        let in_nodes: Vec<_> = self.hierarchy.neighbors_directed(node, Incoming).collect();
942        let out_nodes: Vec<_> = self.hierarchy.neighbors_directed(node, Outgoing).collect();
943
944        self.hierarchy
945            .reserve_edges(in_nodes.len() * out_nodes.len());
946        for &in_node in &in_nodes {
947            for &out_node in &out_nodes {
948                self.hierarchy.add_edge(in_node, out_node);
949            }
950        }
951
952        let in_nodes: Vec<_> = self.dependency.neighbors_directed(node, Incoming).collect();
953        let out_nodes: Vec<_> = self.dependency.neighbors_directed(node, Outgoing).collect();
954
955        self.dependency
956            .reserve_edges(in_nodes.len() * out_nodes.len());
957        for &in_node in &in_nodes {
958            for &out_node in &out_nodes {
959                self.dependency.add_edge(in_node, out_node);
960            }
961        }
962    }
963
964    /// Remove all systems in a set and any dependencies on those systems and set.
965    pub fn remove_systems_in_set<M>(
966        &mut self,
967        system_set: impl IntoSystemSet<M>,
968        policy: ScheduleCleanupPolicy,
969    ) -> Result<usize, ScheduleError> {
970        let set = system_set.into_system_set();
971        let interned = set.intern();
972        // clone the keys out of the schedule as the systems are getting removed from self
973        let keys = self.systems_in_set(interned)?.clone();
974
975        self.changed = true;
976
977        match policy {
978            ScheduleCleanupPolicy::RemoveSetAndSystemsAllowBreakages => {
979                let Some(set_key) = self.system_sets.get_key(interned) else {
980                    return Err(ScheduleError::SetNotFound);
981                };
982
983                self.remove_systems_by_keys(&keys);
984                self.remove_set_by_key(set_key);
985
986                Ok(keys.len())
987            }
988            ScheduleCleanupPolicy::RemoveSystemsOnlyAllowBreakages => {
989                self.remove_systems_by_keys(&keys);
990
991                Ok(keys.len())
992            }
993            ScheduleCleanupPolicy::RemoveSetAndSystems => {
994                let Some(set_key) = self.system_sets.get_key(interned) else {
995                    return Err(ScheduleError::SetNotFound);
996                };
997
998                for &key in &keys {
999                    self.add_edges_for_transitive_dependencies(key.into());
1000                }
1001
1002                self.add_edges_for_transitive_dependencies(set_key.into());
1003
1004                self.remove_systems_by_keys(&keys);
1005                self.remove_set_by_key(set_key);
1006
1007                Ok(keys.len())
1008            }
1009            ScheduleCleanupPolicy::RemoveSystemsOnly => {
1010                for &key in &keys {
1011                    self.add_edges_for_transitive_dependencies(key.into());
1012                }
1013
1014                self.remove_systems_by_keys(&keys);
1015
1016                Ok(keys.len())
1017            }
1018        }
1019    }
1020
1021    fn remove_systems_by_keys(&mut self, keys: &IndexSet<SystemKey, FixedHasher>) {
1022        for &key in keys {
1023            self.systems.remove(key);
1024
1025            self.hierarchy.remove_node(key.into());
1026            self.dependency.remove_node(key.into());
1027            self.ambiguous_with.remove_node(key.into());
1028            self.ambiguous_with_all.remove(&NodeId::from(key));
1029        }
1030    }
1031
1032    fn remove_set_by_key(&mut self, key: SystemSetKey) {
1033        self.system_sets.remove(key);
1034        self.set_systems.remove(&key);
1035        self.hierarchy.remove_node(key.into());
1036        self.dependency.remove_node(key.into());
1037        self.ambiguous_with.remove_node(key.into());
1038        self.ambiguous_with_all.remove(&NodeId::from(key));
1039    }
1040
1041    /// Update the internal graphs (hierarchy, dependency, ambiguity) by adding a single [`GraphInfo`]
1042    fn update_graphs(&mut self, id: NodeId, graph_info: GraphInfo) {
1043        self.changed = true;
1044
1045        let GraphInfo {
1046            hierarchy: sets,
1047            dependencies,
1048            ambiguous_with,
1049            ..
1050        } = graph_info;
1051
1052        self.hierarchy.add_node(id);
1053        self.dependency.add_node(id);
1054
1055        for key in sets
1056            .into_iter()
1057            .map(|set| self.system_sets.get_key_or_insert(set))
1058        {
1059            self.hierarchy.add_edge(NodeId::Set(key), id);
1060
1061            // ensure set also appears in dependency graph
1062            self.dependency.add_node(NodeId::Set(key));
1063        }
1064
1065        for (kind, key, options) in
1066            dependencies
1067                .into_iter()
1068                .map(|Dependency { kind, set, options }| {
1069                    (kind, self.system_sets.get_key_or_insert(set), options)
1070                })
1071        {
1072            let (lhs, rhs) = match kind {
1073                DependencyKind::Before => (id, NodeId::Set(key)),
1074                DependencyKind::After => (NodeId::Set(key), id),
1075            };
1076            self.dependency.add_edge(lhs, rhs);
1077            for pass in self.passes.values_mut() {
1078                pass.add_dependency(lhs, rhs, &options);
1079            }
1080
1081            // ensure set also appears in hierarchy graph
1082            self.hierarchy.add_node(NodeId::Set(key));
1083        }
1084
1085        match ambiguous_with {
1086            Ambiguity::Check => (),
1087            Ambiguity::IgnoreWithSet(ambiguous_with) => {
1088                for key in ambiguous_with
1089                    .into_iter()
1090                    .map(|set| self.system_sets.get_key_or_insert(set))
1091                {
1092                    self.ambiguous_with.add_edge(id, NodeId::Set(key));
1093                }
1094            }
1095            Ambiguity::IgnoreAll => {
1096                self.ambiguous_with_all.insert(id);
1097            }
1098        }
1099    }
1100
1101    /// Initializes any newly-added systems and conditions by calling
1102    /// [`System::initialize`](crate::system::System).
1103    pub fn initialize(&mut self, world: &mut World) {
1104        self.systems.initialize(world);
1105        self.system_sets.initialize(world);
1106    }
1107
1108    /// Builds an execution-optimized [`SystemSchedule`] from the current state
1109    /// of the graph. Also returns any warnings that were generated during the
1110    /// build process.
1111    ///
1112    /// This method also
1113    /// - checks for dependency or hierarchy cycles
1114    /// - checks for system access conflicts and reports ambiguities
1115    pub fn build_schedule(
1116        &mut self,
1117        world: &mut World,
1118        ignored_ambiguities: &BTreeSet<ComponentId>,
1119    ) -> Result<(SystemSchedule, Vec<ScheduleBuildWarning>), ScheduleBuildError> {
1120        let mut warnings = Vec::new();
1121
1122        // Check system set memberships for cycles.
1123        let hierarchy_analysis = self
1124            .hierarchy
1125            .analyze()
1126            .map_err(ScheduleBuildError::HierarchySort)?;
1127
1128        // Check for redundant system set memberships, logging warnings or
1129        // returning errors as configured.
1130        if self.settings.hierarchy_detection != LogLevel::Ignore
1131            && let Err(e) = hierarchy_analysis.check_for_redundant_edges()
1132        {
1133            match self.settings.hierarchy_detection {
1134                LogLevel::Error => return Err(ScheduleBuildWarning::HierarchyRedundancy(e).into()),
1135                LogLevel::Warn => warnings.push(ScheduleBuildWarning::HierarchyRedundancy(e)),
1136                LogLevel::Ignore => unreachable!(),
1137            }
1138        }
1139        // Remove redundant system set memberships.
1140        self.hierarchy.remove_redundant_edges(&hierarchy_analysis);
1141
1142        // Check system and system set ordering dependencies for cycles.
1143        let dependency_analysis = self
1144            .dependency
1145            .analyze()
1146            .map_err(ScheduleBuildError::DependencySort)?;
1147
1148        // System sets that share systems and have an ordering dependency cannot be ordered.
1149        dependency_analysis.check_for_cross_dependencies(&hierarchy_analysis)?;
1150
1151        // Group all systems by the system sets they belong to.
1152        self.set_systems = self
1153            .hierarchy
1154            .group_by_key(self.system_sets.len())
1155            .map_err(ScheduleBuildError::HierarchySort)?;
1156        // Check for system sets that share systems but have an ordering dependency.
1157        dependency_analysis.check_for_overlapping_groups(&self.set_systems)?;
1158
1159        // There can be no edges to system-type sets that have multiple instances.
1160        self.system_sets.check_type_set_ambiguity(
1161            &self.set_systems,
1162            &self.ambiguous_with,
1163            &self.dependency,
1164        )?;
1165
1166        // Flatten system ordering dependencies by collapsing system sets. This
1167        // means that if a system set has ordering dependencies, those
1168        // dependencies are applied to all systems in the set.
1169        let mut flat_dependency =
1170            self.set_systems
1171                .flatten(self.dependency.clone(), |set, systems, flattening, temp| {
1172                    for pass in self.passes.values_mut() {
1173                        pass.collapse_set(set, systems, flattening, temp);
1174                    }
1175                });
1176
1177        // Allow modification of the schedule graph by build passes.
1178        let mut passes = core::mem::take(&mut self.passes);
1179        for pass in passes.values_mut() {
1180            pass.build(world, self, &mut flat_dependency)?;
1181        }
1182        self.passes = passes;
1183
1184        // Check system ordering dependencies for cycles after collapsing sets
1185        // and applying build passes.
1186        let flat_dependency_analysis = flat_dependency
1187            .analyze()
1188            .map_err(ScheduleBuildError::FlatDependencySort)?;
1189        flat_dependency.remove_redundant_edges(&flat_dependency_analysis);
1190
1191        // Flatten accepted system ordering ambiguities by collapsing system sets.
1192        // This means that if a system set is allowed to have ambiguous ordering
1193        // with another set, all systems in the first set are allowed to have
1194        // ambiguous ordering with all systems in the second set.
1195        let flat_ambiguous_with = self.set_systems.flatten_undirected(&self.ambiguous_with);
1196
1197        // Find all system ordering ambiguities, ignoring those that are accepted.
1198        self.conflicting_systems = self.systems.get_conflicting_systems(
1199            &flat_dependency_analysis,
1200            &flat_ambiguous_with,
1201            &self.ambiguous_with_all,
1202            ignored_ambiguities,
1203        );
1204        // If there are any ambiguities, log warnings or return errors as configured.
1205        if self.settings.ambiguity_detection != LogLevel::Ignore
1206            && let Err(e) = self.conflicting_systems.check_if_not_empty()
1207        {
1208            match self.settings.ambiguity_detection {
1209                LogLevel::Error => return Err(ScheduleBuildWarning::Ambiguity(e).into()),
1210                LogLevel::Warn => warnings.push(ScheduleBuildWarning::Ambiguity(e)),
1211                LogLevel::Ignore => unreachable!(),
1212            }
1213        }
1214
1215        // build the schedule
1216        Ok((
1217            self.build_schedule_inner(flat_dependency, hierarchy_analysis),
1218            warnings,
1219        ))
1220    }
1221
1222    fn build_schedule_inner(
1223        &self,
1224        flat_dependency: Dag<SystemKey>,
1225        hierarchy_analysis: DagAnalysis<NodeId>,
1226    ) -> SystemSchedule {
1227        let dg_system_ids = flat_dependency.get_toposort().unwrap().to_vec();
1228        let dg_system_idx_map = dg_system_ids
1229            .iter()
1230            .cloned()
1231            .enumerate()
1232            .map(|(i, id)| (id, i))
1233            .collect::<HashMap<_, _>>();
1234
1235        let hierarchy_toposort = self.hierarchy.get_toposort().unwrap();
1236        let hg_systems = hierarchy_toposort
1237            .iter()
1238            .cloned()
1239            .enumerate()
1240            .filter_map(|(i, id)| Some((i, id.as_system()?)))
1241            .collect::<Vec<_>>();
1242        let (hg_set_with_conditions_idxs, hg_set_ids): (Vec<_>, Vec<_>) = hierarchy_toposort
1243            .iter()
1244            .cloned()
1245            .enumerate()
1246            .filter_map(|(i, id)| {
1247                // ignore system sets that have no conditions
1248                // ignore system type sets (already covered, they don't have conditions)
1249                let key = id.as_set()?;
1250                self.system_sets.has_conditions(key).then_some((i, key))
1251            })
1252            .unzip();
1253
1254        let sys_count = self.systems.len();
1255        let set_with_conditions_count = hg_set_ids.len();
1256        let hg_node_count = self.hierarchy.node_count();
1257
1258        // get the number of dependencies and the immediate dependents of each system
1259        // (needed by multi_threaded executor to run systems in the correct order)
1260        let mut system_dependencies = Vec::with_capacity(sys_count);
1261        let mut system_dependents = Vec::with_capacity(sys_count);
1262        for &sys_key in &dg_system_ids {
1263            let num_dependencies = flat_dependency
1264                .neighbors_directed(sys_key, Incoming)
1265                .count();
1266
1267            let dependents = flat_dependency
1268                .neighbors_directed(sys_key, Outgoing)
1269                .map(|dep_id| dg_system_idx_map[&dep_id])
1270                .collect::<Vec<_>>();
1271
1272            system_dependencies.push(num_dependencies);
1273            system_dependents.push(dependents);
1274        }
1275
1276        // get the rows and columns of the hierarchy graph's reachability matrix
1277        // (needed to we can evaluate conditions in the correct order)
1278        let mut systems_in_sets_with_conditions =
1279            vec![FixedBitSet::with_capacity(sys_count); set_with_conditions_count];
1280        for (i, &row) in hg_set_with_conditions_idxs.iter().enumerate() {
1281            let bitset = &mut systems_in_sets_with_conditions[i];
1282            for &(col, sys_key) in &hg_systems {
1283                let idx = dg_system_idx_map[&sys_key];
1284                let is_descendant = hierarchy_analysis.reachable()[index(row, col, hg_node_count)];
1285                bitset.set(idx, is_descendant);
1286            }
1287        }
1288
1289        let mut sets_with_conditions_of_systems =
1290            vec![FixedBitSet::with_capacity(set_with_conditions_count); sys_count];
1291        for &(col, sys_key) in &hg_systems {
1292            let i = dg_system_idx_map[&sys_key];
1293            let bitset = &mut sets_with_conditions_of_systems[i];
1294            for (idx, &row) in hg_set_with_conditions_idxs
1295                .iter()
1296                .enumerate()
1297                .take_while(|&(_idx, &row)| row < col)
1298            {
1299                let is_ancestor = hierarchy_analysis.reachable()[index(row, col, hg_node_count)];
1300                bitset.set(idx, is_ancestor);
1301            }
1302        }
1303
1304        SystemSchedule {
1305            systems: Vec::with_capacity(sys_count),
1306            system_conditions: Vec::with_capacity(sys_count),
1307            set_conditions: Vec::with_capacity(set_with_conditions_count),
1308            system_ids: dg_system_ids,
1309            set_ids: hg_set_ids,
1310            system_dependencies,
1311            system_dependents,
1312            sets_with_conditions_of_systems,
1313            systems_in_sets_with_conditions,
1314        }
1315    }
1316
1317    /// Updates the `SystemSchedule` from the `ScheduleGraph`.
1318    fn update_schedule(
1319        &mut self,
1320        world: &mut World,
1321        schedule: &mut SystemSchedule,
1322        ignored_ambiguities: &BTreeSet<ComponentId>,
1323        schedule_label: InternedScheduleLabel,
1324    ) -> Result<Vec<ScheduleBuildWarning>, ScheduleBuildError> {
1325        if !self.systems.is_initialized() || !self.system_sets.is_initialized() {
1326            return Err(ScheduleBuildError::Uninitialized);
1327        }
1328
1329        // move systems out of old schedule
1330        for ((key, system), conditions) in schedule
1331            .system_ids
1332            .drain(..)
1333            .zip(schedule.systems.drain(..))
1334            .zip(schedule.system_conditions.drain(..))
1335        {
1336            if let Some(node) = self.systems.node_mut(key) {
1337                node.inner = Some(system);
1338            }
1339
1340            if let Some(node_conditions) = self.systems.get_conditions_mut(key) {
1341                *node_conditions = conditions;
1342            }
1343        }
1344
1345        for (key, conditions) in schedule
1346            .set_ids
1347            .drain(..)
1348            .zip(schedule.set_conditions.drain(..))
1349        {
1350            if let Some(node_conditions) = self.system_sets.get_conditions_mut(key) {
1351                *node_conditions = conditions;
1352            }
1353        }
1354
1355        let (new_schedule, warnings) = self.build_schedule(world, ignored_ambiguities)?;
1356        *schedule = new_schedule;
1357
1358        for warning in &warnings {
1359            warn!(
1360                "{:?} schedule built successfully, however: {}",
1361                schedule_label,
1362                warning.to_string(self, world)
1363            );
1364        }
1365
1366        // move systems into new schedule
1367        for &key in &schedule.system_ids {
1368            let system = self.systems.node_mut(key).unwrap().inner.take().unwrap();
1369            let conditions = core::mem::take(self.systems.get_conditions_mut(key).unwrap());
1370            schedule.systems.push(system);
1371            schedule.system_conditions.push(conditions);
1372        }
1373
1374        for &key in &schedule.set_ids {
1375            let conditions = core::mem::take(self.system_sets.get_conditions_mut(key).unwrap());
1376            schedule.set_conditions.push(conditions);
1377        }
1378
1379        Ok(warnings)
1380    }
1381}
1382
1383/// Values returned by [`ScheduleGraph::process_configs`]
1384struct ProcessConfigsResult {
1385    /// All nodes contained inside this `process_configs` call's [`ScheduleConfigs`] hierarchy,
1386    /// if `ancestor_chained` is true
1387    nodes: Vec<NodeId>,
1388    /// True if and only if all nodes are "densely chained", meaning that all nested nodes
1389    /// are linearly chained (as if `after` system ordering had been applied between each node)
1390    /// in the order they are defined
1391    densely_chained: bool,
1392}
1393
1394/// Trait used by [`ScheduleGraph::process_configs`] to process a single [`ScheduleConfig`].
1395trait ProcessScheduleConfig: Schedulable + Sized {
1396    /// Process a single [`ScheduleConfig`].
1397    fn process_config(schedule_graph: &mut ScheduleGraph, config: ScheduleConfig<Self>) -> NodeId;
1398}
1399
1400impl ProcessScheduleConfig for ScheduleSystem {
1401    fn process_config(schedule_graph: &mut ScheduleGraph, config: ScheduleConfig<Self>) -> NodeId {
1402        NodeId::System(schedule_graph.add_system_inner(config))
1403    }
1404}
1405
1406impl ProcessScheduleConfig for InternedSystemSet {
1407    fn process_config(schedule_graph: &mut ScheduleGraph, config: ScheduleConfig<Self>) -> NodeId {
1408        NodeId::Set(schedule_graph.configure_set_inner(config))
1409    }
1410}
1411
1412/// Policy to use when removing systems.
1413#[derive(Default)]
1414pub enum ScheduleCleanupPolicy {
1415    /// Remove the referenced set and any systems in the set.
1416    /// Attempts to maintain the order between the transitive dependencies by adding new edges
1417    /// between the existing before and after dependencies on the set and the systems.
1418    /// This does not remove sets that might sub sets of the set.
1419    #[default]
1420    RemoveSetAndSystems,
1421    /// Remove only the systems in the set. The set
1422    /// Attempts to maintain the order between the transitive dependencies by adding new edges
1423    /// between the existing before and after dependencies on the systems.
1424    RemoveSystemsOnly,
1425    /// Remove the set and any systems in the set.
1426    /// Note that this will not add new edges and
1427    /// so will break any transitive dependencies on that set or systems.
1428    /// This does not remove sets that might sub sets of the set.
1429    RemoveSetAndSystemsAllowBreakages,
1430    /// Remove only the systems in the set.
1431    /// Note that this will not add new edges and
1432    /// so will break any transitive dependencies on that set or systems.
1433    RemoveSystemsOnlyAllowBreakages,
1434}
1435
1436// methods for reporting errors
1437impl ScheduleGraph {
1438    /// Returns the name of the node with the given [`NodeId`]. Resolves
1439    /// anonymous sets to a string that describes their contents.
1440    ///
1441    /// Also displays the set(s) the node is contained in if
1442    /// [`ScheduleBuildSettings::report_sets`] is true, and shortens system names
1443    /// if [`ScheduleBuildSettings::use_shortnames`] is true.
1444    pub fn get_node_name(&self, id: &NodeId) -> String {
1445        self.get_node_name_inner(id, self.settings.report_sets)
1446    }
1447
1448    #[inline]
1449    fn get_node_name_inner(&self, id: &NodeId, report_sets: bool) -> String {
1450        match *id {
1451            NodeId::System(key) => {
1452                let name = self.systems[key].name();
1453                let name = if self.settings.use_shortnames {
1454                    name.shortname().to_string()
1455                } else {
1456                    name.to_string()
1457                };
1458                if report_sets {
1459                    let sets = self.names_of_sets_containing_node(id);
1460                    if sets.is_empty() {
1461                        name
1462                    } else if sets.len() == 1 {
1463                        format!("{name} (in set {})", sets[0])
1464                    } else {
1465                        format!("{name} (in sets {})", sets.join(", "))
1466                    }
1467                } else {
1468                    name
1469                }
1470            }
1471            NodeId::Set(key) => {
1472                let set = &self.system_sets[key];
1473                if set.is_anonymous() {
1474                    self.anonymous_set_name(id)
1475                } else {
1476                    format!("{set:?}")
1477                }
1478            }
1479        }
1480    }
1481
1482    fn anonymous_set_name(&self, id: &NodeId) -> String {
1483        format!(
1484            "({})",
1485            self.hierarchy
1486                .edges_directed(*id, Outgoing)
1487                // never get the sets of the members or this will infinite recurse when the report_sets setting is on.
1488                .map(|(_, member_id)| self.get_node_name_inner(&member_id, false))
1489                .reduce(|a, b| format!("{a}, {b}"))
1490                .unwrap_or_default()
1491        )
1492    }
1493
1494    fn traverse_sets_containing_node(&self, id: NodeId, f: &mut impl FnMut(SystemSetKey) -> bool) {
1495        for (set_id, _) in self.hierarchy.edges_directed(id, Incoming) {
1496            let NodeId::Set(set_key) = set_id else {
1497                continue;
1498            };
1499            if f(set_key) {
1500                self.traverse_sets_containing_node(NodeId::Set(set_key), f);
1501            }
1502        }
1503    }
1504
1505    fn names_of_sets_containing_node(&self, id: &NodeId) -> Vec<String> {
1506        let mut sets = <HashSet<_>>::default();
1507        self.traverse_sets_containing_node(*id, &mut |key| {
1508            self.system_sets[key].system_type().is_none() && sets.insert(key)
1509        });
1510        let mut sets: Vec<_> = sets
1511            .into_iter()
1512            .map(|key| self.get_node_name(&NodeId::Set(key)))
1513            .collect();
1514        sets.sort();
1515        sets
1516    }
1517}
1518
1519/// Specifies how schedule construction should respond to detecting a certain kind of issue.
1520#[derive(Debug, Clone, Copy, PartialEq)]
1521pub enum LogLevel {
1522    /// Occurrences are completely ignored.
1523    Ignore,
1524    /// Occurrences are logged only.
1525    Warn,
1526    /// Occurrences are logged and result in errors.
1527    Error,
1528}
1529
1530/// Specifies miscellaneous settings for schedule construction.
1531#[derive(Clone, Debug)]
1532pub struct ScheduleBuildSettings {
1533    /// Determines whether the presence of ambiguities (systems with conflicting access but indeterminate order)
1534    /// is only logged or also results in an [`Ambiguity`](ScheduleBuildWarning::Ambiguity)
1535    /// warning or error.
1536    ///
1537    /// Defaults to [`LogLevel::Ignore`].
1538    pub ambiguity_detection: LogLevel,
1539    /// Determines whether the presence of redundant edges in the hierarchy of system sets is only
1540    /// logged or also results in a [`HierarchyRedundancy`](ScheduleBuildWarning::HierarchyRedundancy)
1541    /// warning or error.
1542    ///
1543    /// Defaults to [`LogLevel::Warn`].
1544    pub hierarchy_detection: LogLevel,
1545    /// Auto insert [`ApplyDeferred`] systems into the schedule,
1546    /// when there are [`Deferred`](crate::prelude::Deferred)
1547    /// in one system and there are ordering dependencies on that system. [`Commands`](crate::system::Commands) is one
1548    /// such deferred buffer.
1549    ///
1550    /// You may want to disable this if you only want to sync deferred params at the end of the schedule,
1551    /// or want to manually insert all your sync points.
1552    ///
1553    /// Defaults to `true`
1554    pub auto_insert_apply_deferred: bool,
1555    /// If set to true, node names will be shortened instead of the fully qualified type path.
1556    ///
1557    /// Defaults to `true`.
1558    pub use_shortnames: bool,
1559    /// If set to true, report all system sets the conflicting systems are part of.
1560    ///
1561    /// Defaults to `true`.
1562    pub report_sets: bool,
1563}
1564
1565impl Default for ScheduleBuildSettings {
1566    fn default() -> Self {
1567        Self::new()
1568    }
1569}
1570
1571impl ScheduleBuildSettings {
1572    /// Default build settings.
1573    /// See the field-level documentation for the default value of each field.
1574    pub const fn new() -> Self {
1575        Self {
1576            ambiguity_detection: LogLevel::Ignore,
1577            hierarchy_detection: LogLevel::Warn,
1578            auto_insert_apply_deferred: true,
1579            use_shortnames: true,
1580            report_sets: true,
1581        }
1582    }
1583}
1584
1585/// Error to denote that [`Schedule::initialize`] or [`Schedule::run`] has not yet been called for
1586/// this schedule.
1587#[derive(Error, Debug)]
1588#[error("executable schedule has not been built")]
1589pub struct ScheduleNotInitialized;
1590
1591#[cfg(test)]
1592mod tests {
1593    use alloc::{vec, vec::Vec};
1594    use core::any::TypeId;
1595
1596    use bevy_ecs_macros::ScheduleLabel;
1597
1598    use crate::{
1599        error::{ignore, panic, DefaultErrorHandler, Result},
1600        prelude::{ApplyDeferred, IntoSystemSet, Res, Resource},
1601        schedule::{
1602            passes::AutoInsertApplyDeferredPass, tests::ResMut, IntoScheduleConfigs, Schedule,
1603            ScheduleBuildPass, ScheduleBuildSettings, ScheduleCleanupPolicy, SystemSet,
1604        },
1605        system::Commands,
1606        world::World,
1607    };
1608
1609    use super::Schedules;
1610
1611    #[derive(Resource)]
1612    struct Resource1;
1613
1614    #[derive(Resource)]
1615    struct Resource2;
1616
1617    #[test]
1618    fn unchanged_auto_insert_apply_deferred_has_no_effect() {
1619        use alloc::{vec, vec::Vec};
1620
1621        #[derive(PartialEq, Debug)]
1622        enum Entry {
1623            System(usize),
1624            SyncPoint(usize),
1625        }
1626
1627        #[derive(Resource, Default)]
1628        struct Log(Vec<Entry>);
1629
1630        fn system<const N: usize>(mut res: ResMut<Log>, mut commands: Commands) {
1631            res.0.push(Entry::System(N));
1632            commands
1633                .queue(|world: &mut World| world.resource_mut::<Log>().0.push(Entry::SyncPoint(N)));
1634        }
1635
1636        let mut world = World::default();
1637        world.init_resource::<Log>();
1638        let mut schedule = Schedule::default();
1639        schedule.add_systems((system::<1>, system::<2>).chain_ignore_deferred());
1640        schedule.set_build_settings(ScheduleBuildSettings {
1641            auto_insert_apply_deferred: true,
1642            ..Default::default()
1643        });
1644        schedule.run(&mut world);
1645        let actual = world.remove_resource::<Log>().unwrap().0;
1646
1647        let expected = vec![
1648            Entry::System(1),
1649            Entry::System(2),
1650            Entry::SyncPoint(1),
1651            Entry::SyncPoint(2),
1652        ];
1653
1654        assert_eq!(actual, expected);
1655    }
1656
1657    // regression test for https://github.com/bevyengine/bevy/issues/9114
1658    #[test]
1659    fn ambiguous_with_not_breaking_run_conditions() {
1660        #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
1661        struct Set;
1662
1663        let mut world = World::new();
1664        let mut schedule = Schedule::default();
1665
1666        let system: fn() = || {
1667            panic!("This system must not run");
1668        };
1669
1670        schedule.configure_sets(Set.run_if(|| false));
1671        schedule.add_systems(system.ambiguous_with(|| ()).in_set(Set));
1672        schedule.run(&mut world);
1673    }
1674
1675    #[test]
1676    fn inserts_a_sync_point() {
1677        let mut schedule = Schedule::default();
1678        let mut world = World::default();
1679        schedule.add_systems(
1680            (
1681                |mut commands: Commands| commands.insert_resource(Resource1),
1682                |_: Res<Resource1>| {},
1683            )
1684                .chain(),
1685        );
1686        schedule.run(&mut world);
1687
1688        // inserted a sync point
1689        assert_eq!(schedule.executable.systems.len(), 3);
1690    }
1691
1692    #[test]
1693    fn explicit_sync_point_used_as_auto_sync_point() {
1694        let mut schedule = Schedule::default();
1695        let mut world = World::default();
1696        schedule.add_systems(
1697            (
1698                |mut commands: Commands| commands.insert_resource(Resource1),
1699                |_: Res<Resource1>| {},
1700            )
1701                .chain(),
1702        );
1703        schedule.add_systems((|| {}, ApplyDeferred, || {}).chain());
1704        schedule.run(&mut world);
1705
1706        // No sync point was inserted, since we can reuse the explicit sync point.
1707        assert_eq!(schedule.executable.systems.len(), 5);
1708    }
1709
1710    #[test]
1711    fn conditional_explicit_sync_point_not_used_as_auto_sync_point() {
1712        let mut schedule = Schedule::default();
1713        let mut world = World::default();
1714        schedule.add_systems(
1715            (
1716                |mut commands: Commands| commands.insert_resource(Resource1),
1717                |_: Res<Resource1>| {},
1718            )
1719                .chain(),
1720        );
1721        schedule.add_systems((|| {}, ApplyDeferred.run_if(|| false), || {}).chain());
1722        schedule.run(&mut world);
1723
1724        // A sync point was inserted, since the explicit sync point is not always run.
1725        assert_eq!(schedule.executable.systems.len(), 6);
1726    }
1727
1728    #[test]
1729    fn conditional_explicit_sync_point_not_used_as_auto_sync_point_condition_on_chain() {
1730        let mut schedule = Schedule::default();
1731        let mut world = World::default();
1732        schedule.add_systems(
1733            (
1734                |mut commands: Commands| commands.insert_resource(Resource1),
1735                |_: Res<Resource1>| {},
1736            )
1737                .chain(),
1738        );
1739        schedule.add_systems((|| {}, ApplyDeferred, || {}).chain().run_if(|| false));
1740        schedule.run(&mut world);
1741
1742        // A sync point was inserted, since the explicit sync point is not always run.
1743        assert_eq!(schedule.executable.systems.len(), 6);
1744    }
1745
1746    #[test]
1747    fn conditional_explicit_sync_point_not_used_as_auto_sync_point_condition_on_system_set() {
1748        #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
1749        struct Set;
1750
1751        let mut schedule = Schedule::default();
1752        let mut world = World::default();
1753        schedule.configure_sets(Set.run_if(|| false));
1754        schedule.add_systems(
1755            (
1756                |mut commands: Commands| commands.insert_resource(Resource1),
1757                |_: Res<Resource1>| {},
1758            )
1759                .chain(),
1760        );
1761        schedule.add_systems((|| {}, ApplyDeferred.in_set(Set), || {}).chain());
1762        schedule.run(&mut world);
1763
1764        // A sync point was inserted, since the explicit sync point is not always run.
1765        assert_eq!(schedule.executable.systems.len(), 6);
1766    }
1767
1768    #[test]
1769    fn conditional_explicit_sync_point_not_used_as_auto_sync_point_condition_on_nested_system_set()
1770    {
1771        #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
1772        struct Set1;
1773        #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
1774        struct Set2;
1775
1776        let mut schedule = Schedule::default();
1777        let mut world = World::default();
1778        schedule.configure_sets(Set2.run_if(|| false));
1779        schedule.configure_sets(Set1.in_set(Set2));
1780        schedule.add_systems(
1781            (
1782                |mut commands: Commands| commands.insert_resource(Resource1),
1783                |_: Res<Resource1>| {},
1784            )
1785                .chain(),
1786        );
1787        schedule.add_systems((|| {}, ApplyDeferred, || {}).chain().in_set(Set1));
1788        schedule.run(&mut world);
1789
1790        // A sync point was inserted, since the explicit sync point is not always run.
1791        assert_eq!(schedule.executable.systems.len(), 6);
1792    }
1793
1794    #[test]
1795    fn merges_sync_points_into_one() {
1796        let mut schedule = Schedule::default();
1797        let mut world = World::default();
1798        // insert two parallel command systems, it should only create one sync point
1799        schedule.add_systems(
1800            (
1801                (
1802                    |mut commands: Commands| commands.insert_resource(Resource1),
1803                    |mut commands: Commands| commands.insert_resource(Resource2),
1804                ),
1805                |_: Res<Resource1>, _: Res<Resource2>| {},
1806            )
1807                .chain(),
1808        );
1809        schedule.run(&mut world);
1810
1811        // inserted sync points
1812        assert_eq!(schedule.executable.systems.len(), 4);
1813
1814        // merges sync points on rebuild
1815        schedule.add_systems(((
1816            (
1817                |mut commands: Commands| commands.insert_resource(Resource1),
1818                |mut commands: Commands| commands.insert_resource(Resource2),
1819            ),
1820            |_: Res<Resource1>, _: Res<Resource2>| {},
1821        )
1822            .chain(),));
1823        schedule.run(&mut world);
1824
1825        assert_eq!(schedule.executable.systems.len(), 7);
1826    }
1827
1828    #[test]
1829    fn adds_multiple_consecutive_syncs() {
1830        let mut schedule = Schedule::default();
1831        let mut world = World::default();
1832        // insert two consecutive command systems, it should create two sync points
1833        schedule.add_systems(
1834            (
1835                |mut commands: Commands| commands.insert_resource(Resource1),
1836                |mut commands: Commands| commands.insert_resource(Resource2),
1837                |_: Res<Resource1>, _: Res<Resource2>| {},
1838            )
1839                .chain(),
1840        );
1841        schedule.run(&mut world);
1842
1843        assert_eq!(schedule.executable.systems.len(), 5);
1844    }
1845
1846    #[test]
1847    fn do_not_consider_ignore_deferred_before_exclusive_system() {
1848        let mut schedule = Schedule::default();
1849        let mut world = World::default();
1850        // chain_ignore_deferred adds no sync points usually but an exception is made for exclusive systems
1851        schedule.add_systems(
1852            (
1853                |_: Commands| {},
1854                // <- no sync point is added here because the following system is not exclusive
1855                |mut commands: Commands| commands.insert_resource(Resource1),
1856                // <- sync point is added here because the following system is exclusive which expects to see all commands to that point
1857                |world: &mut World| assert!(world.contains_resource::<Resource1>()),
1858                // <- no sync point is added here because the previous system has no deferred parameters
1859                |_: &mut World| {},
1860                // <- no sync point is added here because the following system is not exclusive
1861                |_: Commands| {},
1862            )
1863                .chain_ignore_deferred(),
1864        );
1865        schedule.run(&mut world);
1866
1867        assert_eq!(schedule.executable.systems.len(), 6); // 5 systems + 1 sync point
1868    }
1869
1870    #[test]
1871    fn bubble_sync_point_through_ignore_deferred_node() {
1872        let mut schedule = Schedule::default();
1873        let mut world = World::default();
1874
1875        let insert_resource_config = (
1876            // the first system has deferred commands
1877            |mut commands: Commands| commands.insert_resource(Resource1),
1878            // the second system has no deferred commands
1879            || {},
1880        )
1881            // the first two systems are chained without a sync point in between
1882            .chain_ignore_deferred();
1883
1884        schedule.add_systems(
1885            (
1886                insert_resource_config,
1887                // the third system would panic if the command of the first system was not applied
1888                |_: Res<Resource1>| {},
1889            )
1890                // the third system is chained after the first two, possibly with a sync point in between
1891                .chain(),
1892        );
1893
1894        // To add a sync point between the second and third system despite the second having no commands,
1895        // the first system has to signal the second system that there are unapplied commands.
1896        // With that the second system will add a sync point after it so the third system will find the resource.
1897
1898        schedule.run(&mut world);
1899
1900        assert_eq!(schedule.executable.systems.len(), 4); // 3 systems + 1 sync point
1901    }
1902
1903    #[test]
1904    fn disable_auto_sync_points() {
1905        let mut schedule = Schedule::default();
1906        schedule.set_build_settings(ScheduleBuildSettings {
1907            auto_insert_apply_deferred: false,
1908            ..Default::default()
1909        });
1910        let mut world = World::default();
1911        schedule.add_systems(
1912            (
1913                |mut commands: Commands| commands.insert_resource(Resource1),
1914                |res: Option<Res<Resource1>>| assert!(res.is_none()),
1915            )
1916                .chain(),
1917        );
1918        schedule.run(&mut world);
1919
1920        assert_eq!(schedule.executable.systems.len(), 2);
1921    }
1922
1923    mod no_sync_edges {
1924        use super::*;
1925
1926        fn insert_resource(mut commands: Commands) {
1927            commands.insert_resource(Resource1);
1928        }
1929
1930        fn resource_does_not_exist(res: Option<Res<Resource1>>) {
1931            assert!(res.is_none());
1932        }
1933
1934        #[derive(SystemSet, Hash, PartialEq, Eq, Debug, Clone)]
1935        enum Sets {
1936            A,
1937            B,
1938        }
1939
1940        fn check_no_sync_edges(add_systems: impl FnOnce(&mut Schedule)) {
1941            let mut schedule = Schedule::default();
1942            let mut world = World::default();
1943            add_systems(&mut schedule);
1944
1945            schedule.run(&mut world);
1946
1947            assert_eq!(schedule.executable.systems.len(), 2);
1948        }
1949
1950        #[test]
1951        fn system_to_system_after() {
1952            check_no_sync_edges(|schedule| {
1953                schedule.add_systems((
1954                    insert_resource,
1955                    resource_does_not_exist.after_ignore_deferred(insert_resource),
1956                ));
1957            });
1958        }
1959
1960        #[test]
1961        fn system_to_system_before() {
1962            check_no_sync_edges(|schedule| {
1963                schedule.add_systems((
1964                    insert_resource.before_ignore_deferred(resource_does_not_exist),
1965                    resource_does_not_exist,
1966                ));
1967            });
1968        }
1969
1970        #[test]
1971        fn set_to_system_after() {
1972            check_no_sync_edges(|schedule| {
1973                schedule
1974                    .add_systems((insert_resource, resource_does_not_exist.in_set(Sets::A)))
1975                    .configure_sets(Sets::A.after_ignore_deferred(insert_resource));
1976            });
1977        }
1978
1979        #[test]
1980        fn set_to_system_before() {
1981            check_no_sync_edges(|schedule| {
1982                schedule
1983                    .add_systems((insert_resource.in_set(Sets::A), resource_does_not_exist))
1984                    .configure_sets(Sets::A.before_ignore_deferred(resource_does_not_exist));
1985            });
1986        }
1987
1988        #[test]
1989        fn set_to_set_after() {
1990            check_no_sync_edges(|schedule| {
1991                schedule
1992                    .add_systems((
1993                        insert_resource.in_set(Sets::A),
1994                        resource_does_not_exist.in_set(Sets::B),
1995                    ))
1996                    .configure_sets(Sets::B.after_ignore_deferred(Sets::A));
1997            });
1998        }
1999
2000        #[test]
2001        fn set_to_set_before() {
2002            check_no_sync_edges(|schedule| {
2003                schedule
2004                    .add_systems((
2005                        insert_resource.in_set(Sets::A),
2006                        resource_does_not_exist.in_set(Sets::B),
2007                    ))
2008                    .configure_sets(Sets::A.before_ignore_deferred(Sets::B));
2009            });
2010        }
2011    }
2012
2013    mod no_sync_chain {
2014        use super::*;
2015
2016        #[derive(Resource)]
2017        struct Ra;
2018
2019        #[derive(Resource)]
2020        struct Rb;
2021
2022        #[derive(Resource)]
2023        struct Rc;
2024
2025        fn run_schedule(expected_num_systems: usize, add_systems: impl FnOnce(&mut Schedule)) {
2026            let mut schedule = Schedule::default();
2027            let mut world = World::default();
2028            add_systems(&mut schedule);
2029
2030            schedule.run(&mut world);
2031
2032            assert_eq!(schedule.executable.systems.len(), expected_num_systems);
2033        }
2034
2035        #[test]
2036        fn only_chain_outside() {
2037            run_schedule(5, |schedule: &mut Schedule| {
2038                schedule.add_systems(
2039                    (
2040                        (
2041                            |mut commands: Commands| commands.insert_resource(Ra),
2042                            |mut commands: Commands| commands.insert_resource(Rb),
2043                        ),
2044                        (
2045                            |res_a: Option<Res<Ra>>, res_b: Option<Res<Rb>>| {
2046                                assert!(res_a.is_some());
2047                                assert!(res_b.is_some());
2048                            },
2049                            |res_a: Option<Res<Ra>>, res_b: Option<Res<Rb>>| {
2050                                assert!(res_a.is_some());
2051                                assert!(res_b.is_some());
2052                            },
2053                        ),
2054                    )
2055                        .chain(),
2056                );
2057            });
2058
2059            run_schedule(4, |schedule: &mut Schedule| {
2060                schedule.add_systems(
2061                    (
2062                        (
2063                            |mut commands: Commands| commands.insert_resource(Ra),
2064                            |mut commands: Commands| commands.insert_resource(Rb),
2065                        ),
2066                        (
2067                            |res_a: Option<Res<Ra>>, res_b: Option<Res<Rb>>| {
2068                                assert!(res_a.is_none());
2069                                assert!(res_b.is_none());
2070                            },
2071                            |res_a: Option<Res<Ra>>, res_b: Option<Res<Rb>>| {
2072                                assert!(res_a.is_none());
2073                                assert!(res_b.is_none());
2074                            },
2075                        ),
2076                    )
2077                        .chain_ignore_deferred(),
2078                );
2079            });
2080        }
2081
2082        #[test]
2083        fn chain_first() {
2084            run_schedule(6, |schedule: &mut Schedule| {
2085                schedule.add_systems(
2086                    (
2087                        (
2088                            |mut commands: Commands| commands.insert_resource(Ra),
2089                            |mut commands: Commands, res_a: Option<Res<Ra>>| {
2090                                commands.insert_resource(Rb);
2091                                assert!(res_a.is_some());
2092                            },
2093                        )
2094                            .chain(),
2095                        (
2096                            |res_a: Option<Res<Ra>>, res_b: Option<Res<Rb>>| {
2097                                assert!(res_a.is_some());
2098                                assert!(res_b.is_some());
2099                            },
2100                            |res_a: Option<Res<Ra>>, res_b: Option<Res<Rb>>| {
2101                                assert!(res_a.is_some());
2102                                assert!(res_b.is_some());
2103                            },
2104                        ),
2105                    )
2106                        .chain(),
2107                );
2108            });
2109
2110            run_schedule(5, |schedule: &mut Schedule| {
2111                schedule.add_systems(
2112                    (
2113                        (
2114                            |mut commands: Commands| commands.insert_resource(Ra),
2115                            |mut commands: Commands, res_a: Option<Res<Ra>>| {
2116                                commands.insert_resource(Rb);
2117                                assert!(res_a.is_some());
2118                            },
2119                        )
2120                            .chain(),
2121                        (
2122                            |res_a: Option<Res<Ra>>, res_b: Option<Res<Rb>>| {
2123                                assert!(res_a.is_some());
2124                                assert!(res_b.is_none());
2125                            },
2126                            |res_a: Option<Res<Ra>>, res_b: Option<Res<Rb>>| {
2127                                assert!(res_a.is_some());
2128                                assert!(res_b.is_none());
2129                            },
2130                        ),
2131                    )
2132                        .chain_ignore_deferred(),
2133                );
2134            });
2135        }
2136
2137        #[test]
2138        fn chain_second() {
2139            run_schedule(6, |schedule: &mut Schedule| {
2140                schedule.add_systems(
2141                    (
2142                        (
2143                            |mut commands: Commands| commands.insert_resource(Ra),
2144                            |mut commands: Commands| commands.insert_resource(Rb),
2145                        ),
2146                        (
2147                            |mut commands: Commands,
2148                             res_a: Option<Res<Ra>>,
2149                             res_b: Option<Res<Rb>>| {
2150                                commands.insert_resource(Rc);
2151                                assert!(res_a.is_some());
2152                                assert!(res_b.is_some());
2153                            },
2154                            |res_a: Option<Res<Ra>>,
2155                             res_b: Option<Res<Rb>>,
2156                             res_c: Option<Res<Rc>>| {
2157                                assert!(res_a.is_some());
2158                                assert!(res_b.is_some());
2159                                assert!(res_c.is_some());
2160                            },
2161                        )
2162                            .chain(),
2163                    )
2164                        .chain(),
2165                );
2166            });
2167
2168            run_schedule(5, |schedule: &mut Schedule| {
2169                schedule.add_systems(
2170                    (
2171                        (
2172                            |mut commands: Commands| commands.insert_resource(Ra),
2173                            |mut commands: Commands| commands.insert_resource(Rb),
2174                        ),
2175                        (
2176                            |mut commands: Commands,
2177                             res_a: Option<Res<Ra>>,
2178                             res_b: Option<Res<Rb>>| {
2179                                commands.insert_resource(Rc);
2180                                assert!(res_a.is_none());
2181                                assert!(res_b.is_none());
2182                            },
2183                            |res_a: Option<Res<Ra>>,
2184                             res_b: Option<Res<Rb>>,
2185                             res_c: Option<Res<Rc>>| {
2186                                assert!(res_a.is_some());
2187                                assert!(res_b.is_some());
2188                                assert!(res_c.is_some());
2189                            },
2190                        )
2191                            .chain(),
2192                    )
2193                        .chain_ignore_deferred(),
2194                );
2195            });
2196        }
2197
2198        #[test]
2199        fn chain_all() {
2200            run_schedule(7, |schedule: &mut Schedule| {
2201                schedule.add_systems(
2202                    (
2203                        (
2204                            |mut commands: Commands| commands.insert_resource(Ra),
2205                            |mut commands: Commands, res_a: Option<Res<Ra>>| {
2206                                commands.insert_resource(Rb);
2207                                assert!(res_a.is_some());
2208                            },
2209                        )
2210                            .chain(),
2211                        (
2212                            |mut commands: Commands,
2213                             res_a: Option<Res<Ra>>,
2214                             res_b: Option<Res<Rb>>| {
2215                                commands.insert_resource(Rc);
2216                                assert!(res_a.is_some());
2217                                assert!(res_b.is_some());
2218                            },
2219                            |res_a: Option<Res<Ra>>,
2220                             res_b: Option<Res<Rb>>,
2221                             res_c: Option<Res<Rc>>| {
2222                                assert!(res_a.is_some());
2223                                assert!(res_b.is_some());
2224                                assert!(res_c.is_some());
2225                            },
2226                        )
2227                            .chain(),
2228                    )
2229                        .chain(),
2230                );
2231            });
2232
2233            run_schedule(6, |schedule: &mut Schedule| {
2234                schedule.add_systems(
2235                    (
2236                        (
2237                            |mut commands: Commands| commands.insert_resource(Ra),
2238                            |mut commands: Commands, res_a: Option<Res<Ra>>| {
2239                                commands.insert_resource(Rb);
2240                                assert!(res_a.is_some());
2241                            },
2242                        )
2243                            .chain(),
2244                        (
2245                            |mut commands: Commands,
2246                             res_a: Option<Res<Ra>>,
2247                             res_b: Option<Res<Rb>>| {
2248                                commands.insert_resource(Rc);
2249                                assert!(res_a.is_some());
2250                                assert!(res_b.is_none());
2251                            },
2252                            |res_a: Option<Res<Ra>>,
2253                             res_b: Option<Res<Rb>>,
2254                             res_c: Option<Res<Rc>>| {
2255                                assert!(res_a.is_some());
2256                                assert!(res_b.is_some());
2257                                assert!(res_c.is_some());
2258                            },
2259                        )
2260                            .chain(),
2261                    )
2262                        .chain_ignore_deferred(),
2263                );
2264            });
2265        }
2266    }
2267
2268    #[derive(ScheduleLabel, Hash, Debug, Clone, PartialEq, Eq)]
2269    struct TestSchedule;
2270
2271    #[derive(Resource)]
2272    struct CheckSystemRan(usize);
2273
2274    #[test]
2275    fn add_systems_to_existing_schedule() {
2276        let mut schedules = Schedules::default();
2277        let schedule = Schedule::new(TestSchedule);
2278
2279        schedules.insert(schedule);
2280        schedules.add_systems(TestSchedule, |mut ran: ResMut<CheckSystemRan>| ran.0 += 1);
2281
2282        let mut world = World::new();
2283
2284        world.insert_resource(CheckSystemRan(0));
2285        world.insert_resource(schedules);
2286        world.run_schedule(TestSchedule);
2287
2288        let value = world
2289            .get_resource::<CheckSystemRan>()
2290            .expect("CheckSystemRan Resource Should Exist");
2291        assert_eq!(value.0, 1);
2292    }
2293
2294    #[test]
2295    fn add_systems_to_non_existing_schedule() {
2296        let mut schedules = Schedules::default();
2297
2298        schedules.add_systems(TestSchedule, |mut ran: ResMut<CheckSystemRan>| ran.0 += 1);
2299
2300        let mut world = World::new();
2301
2302        world.insert_resource(CheckSystemRan(0));
2303        world.insert_resource(schedules);
2304        world.run_schedule(TestSchedule);
2305
2306        let value = world
2307            .get_resource::<CheckSystemRan>()
2308            .expect("CheckSystemRan Resource Should Exist");
2309        assert_eq!(value.0, 1);
2310    }
2311
2312    #[derive(SystemSet, Debug, Hash, Clone, PartialEq, Eq)]
2313    enum TestSet {
2314        First,
2315        Second,
2316    }
2317
2318    #[test]
2319    fn configure_set_on_existing_schedule() {
2320        let mut schedules = Schedules::default();
2321        let schedule = Schedule::new(TestSchedule);
2322
2323        schedules.insert(schedule);
2324
2325        schedules.configure_sets(TestSchedule, (TestSet::First, TestSet::Second).chain());
2326        schedules.add_systems(
2327            TestSchedule,
2328            (|mut ran: ResMut<CheckSystemRan>| {
2329                assert_eq!(ran.0, 0);
2330                ran.0 += 1;
2331            })
2332            .in_set(TestSet::First),
2333        );
2334
2335        schedules.add_systems(
2336            TestSchedule,
2337            (|mut ran: ResMut<CheckSystemRan>| {
2338                assert_eq!(ran.0, 1);
2339                ran.0 += 1;
2340            })
2341            .in_set(TestSet::Second),
2342        );
2343
2344        let mut world = World::new();
2345
2346        world.insert_resource(CheckSystemRan(0));
2347        world.insert_resource(schedules);
2348        world.run_schedule(TestSchedule);
2349
2350        let value = world
2351            .get_resource::<CheckSystemRan>()
2352            .expect("CheckSystemRan Resource Should Exist");
2353        assert_eq!(value.0, 2);
2354    }
2355
2356    #[test]
2357    fn configure_set_on_new_schedule() {
2358        let mut schedules = Schedules::default();
2359
2360        schedules.configure_sets(TestSchedule, (TestSet::First, TestSet::Second).chain());
2361        schedules.add_systems(
2362            TestSchedule,
2363            (|mut ran: ResMut<CheckSystemRan>| {
2364                assert_eq!(ran.0, 0);
2365                ran.0 += 1;
2366            })
2367            .in_set(TestSet::First),
2368        );
2369
2370        schedules.add_systems(
2371            TestSchedule,
2372            (|mut ran: ResMut<CheckSystemRan>| {
2373                assert_eq!(ran.0, 1);
2374                ran.0 += 1;
2375            })
2376            .in_set(TestSet::Second),
2377        );
2378
2379        let mut world = World::new();
2380
2381        world.insert_resource(CheckSystemRan(0));
2382        world.insert_resource(schedules);
2383        world.run_schedule(TestSchedule);
2384
2385        let value = world
2386            .get_resource::<CheckSystemRan>()
2387            .expect("CheckSystemRan Resource Should Exist");
2388        assert_eq!(value.0, 2);
2389    }
2390
2391    #[test]
2392    fn test_default_error_handler() {
2393        #[derive(Resource, Default)]
2394        struct Ran(bool);
2395
2396        fn system(mut ran: ResMut<Ran>) -> Result {
2397            ran.0 = true;
2398            Err("I failed!".into())
2399        }
2400
2401        // Test that the default error handler is used
2402        let mut world = World::default();
2403        world.init_resource::<Ran>();
2404        world.insert_resource(DefaultErrorHandler(ignore));
2405        let mut schedule = Schedule::default();
2406        schedule.add_systems(system).run(&mut world);
2407        assert!(world.resource::<Ran>().0);
2408
2409        // Test that the handler doesn't change within the schedule
2410        schedule.add_systems(
2411            (|world: &mut World| {
2412                world.insert_resource(DefaultErrorHandler(panic));
2413            })
2414            .before(system),
2415        );
2416        schedule.run(&mut world);
2417    }
2418
2419    #[test]
2420    fn get_a_system_key() {
2421        fn test_system() {}
2422
2423        let mut schedule = Schedule::default();
2424        schedule.add_systems(test_system);
2425        let mut world = World::default();
2426        let _ = schedule.initialize(&mut world);
2427
2428        let keys = schedule
2429            .graph()
2430            .systems_in_set(test_system.into_system_set().intern())
2431            .unwrap();
2432        assert_eq!(keys.len(), 1);
2433    }
2434
2435    #[test]
2436    fn get_system_keys_in_set() {
2437        fn system_1() {}
2438        fn system_2() {}
2439
2440        let mut schedule = Schedule::default();
2441        schedule.add_systems((system_1, system_2).in_set(TestSet::First));
2442        let mut world = World::default();
2443        let _ = schedule.initialize(&mut world);
2444
2445        let keys = schedule
2446            .graph()
2447            .systems_in_set(TestSet::First.into_system_set().intern())
2448            .unwrap();
2449        assert_eq!(keys.len(), 2);
2450    }
2451
2452    #[test]
2453    fn get_system_keys_with_same_name() {
2454        fn test_system() {}
2455
2456        let mut schedule = Schedule::default();
2457        schedule.add_systems((test_system, test_system));
2458        let mut world = World::default();
2459        let _ = schedule.initialize(&mut world);
2460
2461        let keys = schedule
2462            .graph()
2463            .systems_in_set(test_system.into_system_set().intern())
2464            .unwrap();
2465        assert_eq!(keys.len(), 2);
2466    }
2467
2468    #[test]
2469    fn remove_a_system() {
2470        fn system() {}
2471
2472        let mut schedule = Schedule::default();
2473        schedule.add_systems(system);
2474        let mut world = World::default();
2475
2476        let remove_count = schedule.remove_systems_in_set(
2477            system,
2478            &mut world,
2479            ScheduleCleanupPolicy::RemoveSetAndSystemsAllowBreakages,
2480        );
2481        assert_eq!(remove_count.unwrap(), 1);
2482
2483        // schedule has changed, so we check initializing again
2484        schedule.initialize(&mut world).unwrap();
2485        assert_eq!(schedule.graph().systems.len(), 0);
2486    }
2487
2488    #[test]
2489    fn remove_multiple_systems() {
2490        fn system() {}
2491
2492        let mut schedule = Schedule::default();
2493        schedule.add_systems((system, system));
2494        let mut world = World::default();
2495
2496        let remove_count = schedule.remove_systems_in_set(
2497            system,
2498            &mut world,
2499            ScheduleCleanupPolicy::RemoveSetAndSystemsAllowBreakages,
2500        );
2501        assert_eq!(remove_count.unwrap(), 2);
2502
2503        // schedule has changed, so we check initializing again
2504        schedule.initialize(&mut world).unwrap();
2505        assert_eq!(schedule.graph().systems.len(), 0);
2506    }
2507
2508    #[test]
2509    fn remove_a_system_with_dependencies() {
2510        fn system_1() {}
2511        fn system_2() {}
2512
2513        let mut schedule = Schedule::default();
2514        schedule.add_systems((system_1, system_2).chain());
2515        let mut world = World::default();
2516
2517        let remove_count = schedule.remove_systems_in_set(
2518            system_1,
2519            &mut world,
2520            ScheduleCleanupPolicy::RemoveSetAndSystemsAllowBreakages,
2521        );
2522        assert_eq!(remove_count.unwrap(), 1);
2523
2524        // schedule has changed, so we check initializing again
2525        schedule.initialize(&mut world).unwrap();
2526        assert_eq!(schedule.graph().systems.len(), 1);
2527    }
2528
2529    #[test]
2530    fn remove_a_system_and_still_ordered() {
2531        #[derive(Resource)]
2532        struct A;
2533
2534        fn system_1(_: ResMut<A>) {}
2535        fn system_2() {}
2536        fn system_3(_: ResMut<A>) {}
2537
2538        let mut schedule = Schedule::default();
2539        schedule.add_systems((system_1, system_2, system_3).chain());
2540        let mut world = World::new();
2541
2542        let _ = schedule.remove_systems_in_set(
2543            system_2,
2544            &mut world,
2545            ScheduleCleanupPolicy::RemoveSetAndSystems,
2546        );
2547
2548        let result = schedule.initialize(&mut world);
2549        assert!(result.is_ok());
2550        let conflicts = schedule.graph().conflicting_systems();
2551        assert!(conflicts.is_empty());
2552    }
2553
2554    #[test]
2555    fn remove_a_set_and_still_ordered() {
2556        #[derive(Resource)]
2557        struct A;
2558
2559        #[derive(SystemSet, Hash, PartialEq, Eq, Clone, Debug)]
2560        struct B;
2561
2562        fn system_1(_: ResMut<A>) {}
2563        fn system_2() {}
2564        fn system_3(_: ResMut<A>) {}
2565
2566        let mut schedule = Schedule::default();
2567        schedule.add_systems((system_1.before(B), system_2, system_3.after(B)));
2568        let mut world = World::new();
2569
2570        let _ = schedule.remove_systems_in_set(
2571            B,
2572            &mut world,
2573            ScheduleCleanupPolicy::RemoveSetAndSystems,
2574        );
2575
2576        let result = schedule.initialize(&mut world);
2577        assert!(result.is_ok());
2578        let conflicts = schedule.graph().conflicting_systems();
2579        assert!(conflicts.is_empty());
2580    }
2581
2582    #[test]
2583    fn build_pass_iteration_order() {
2584        #[derive(Debug)]
2585        struct Pass<const N: usize>;
2586
2587        impl<const N: usize> ScheduleBuildPass for Pass<N> {
2588            type EdgeOptions = ();
2589            fn add_dependency(
2590                &mut self,
2591                _from: crate::schedule::NodeId,
2592                _to: crate::schedule::NodeId,
2593                _options: Option<&Self::EdgeOptions>,
2594            ) {
2595            }
2596            fn build(
2597                &mut self,
2598                _world: &mut World,
2599                _graph: &mut super::ScheduleGraph,
2600                _dependency_flattened: &mut crate::schedule::graph::Dag<crate::schedule::SystemKey>,
2601            ) -> core::result::Result<(), crate::schedule::ScheduleBuildError> {
2602                Ok(())
2603            }
2604            fn collapse_set(
2605                &mut self,
2606                _set: crate::schedule::SystemSetKey,
2607                _systems: &indexmap::IndexSet<
2608                    crate::schedule::SystemKey,
2609                    bevy_platform::hash::FixedHasher,
2610                >,
2611                _dependency_flattening: &crate::schedule::graph::DiGraph<crate::schedule::NodeId>,
2612            ) -> impl Iterator<Item = (crate::schedule::NodeId, crate::schedule::NodeId)>
2613            {
2614                core::iter::empty()
2615            }
2616        }
2617
2618        let mut schedule = Schedule::default();
2619        schedule.add_build_pass(Pass::<0>);
2620        schedule.add_build_pass(Pass::<1>);
2621        schedule.add_build_pass(Pass::<2>);
2622
2623        let pass_order: Vec<TypeId> = schedule.graph().passes.keys().cloned().collect();
2624
2625        assert_eq!(
2626            pass_order,
2627            vec![
2628                TypeId::of::<AutoInsertApplyDeferredPass>(),
2629                TypeId::of::<Pass<0>>(),
2630                TypeId::of::<Pass<1>>(),
2631                TypeId::of::<Pass<2>>()
2632            ]
2633        );
2634    }
2635}