Skip to main content

bevy_ecs/schedule/
node.rs

1use alloc::{boxed::Box, collections::BTreeSet, string::String, vec::Vec};
2use core::{
3    any::TypeId,
4    fmt::{self, Debug},
5    ops::{Deref, Index, IndexMut, Range},
6};
7
8use bevy_platform::collections::{HashMap, HashSet};
9use bevy_utils::prelude::DebugName;
10use slotmap::{new_key_type, Key, KeyData, SecondaryMap, SlotMap};
11use thiserror::Error;
12
13use crate::{
14    change_detection::{CheckChangeTicks, Tick},
15    component::{ComponentId, Components},
16    prelude::{SystemIn, SystemSet},
17    query::AccessConflicts,
18    schedule::{
19        graph::{
20            DagAnalysis, DagGroups, DiGraph,
21            Direction::{self, Incoming, Outgoing},
22            GraphNodeId, UnGraph,
23        },
24        BoxedCondition, InternedSystemSet, ScheduleGraph,
25    },
26    system::{
27        ReadOnlySystem, RunSystemError, ScheduleSystem, System, SystemAccess, SystemStateFlags,
28    },
29    world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, World},
30};
31
32/// A [`SystemWithAccess`] stored in a [`ScheduleGraph`].
33pub(crate) struct SystemNode {
34    pub(crate) inner: Option<SystemWithAccess>,
35}
36
37/// A [`ScheduleSystem`] stored alongside the access returned from [`System::initialize`].
38pub struct SystemWithAccess {
39    /// The system itself.
40    pub(crate) system: ScheduleSystem,
41    /// The access returned by [`System::initialize`].
42    /// This will be empty if the system has not been initialized yet.
43    pub(crate) access: SystemAccess,
44}
45
46impl SystemWithAccess {
47    /// Constructs a new [`SystemWithAccess`] from a [`ScheduleSystem`].
48    /// The `access` will initially be empty.
49    pub fn new(system: ScheduleSystem) -> Self {
50        Self {
51            system,
52            access: SystemAccess::default(),
53        }
54    }
55
56    /// Returns the underlying [`ScheduleSystem`]
57    pub fn system(&self) -> &ScheduleSystem {
58        &self.system
59    }
60
61    /// Returns the underlying [`SystemAccess`]
62    pub fn access(&self) -> &SystemAccess {
63        &self.access
64    }
65}
66
67impl System for SystemWithAccess {
68    type In = ();
69    type Out = ();
70
71    #[inline]
72    fn name(&self) -> DebugName {
73        self.system.name()
74    }
75
76    #[inline]
77    fn system_type(&self) -> TypeId {
78        self.system.system_type()
79    }
80
81    #[inline]
82    fn flags(&self) -> SystemStateFlags {
83        self.system.flags()
84    }
85
86    #[inline]
87    unsafe fn run_unsafe(
88        &mut self,
89        input: SystemIn<'_, Self>,
90        world: UnsafeWorldCell,
91    ) -> Result<Self::Out, RunSystemError> {
92        // SAFETY: Caller ensures the same safety requirements.
93        unsafe { self.system.run_unsafe(input, world) }
94    }
95
96    #[cfg(feature = "hotpatching")]
97    #[inline]
98    fn refresh_hotpatch(&mut self) {
99        self.system.refresh_hotpatch();
100    }
101
102    #[inline]
103    fn apply_deferred(&mut self, world: &mut World) {
104        self.system.apply_deferred(world);
105    }
106
107    #[inline]
108    fn queue_deferred(&mut self, world: DeferredWorld) {
109        self.system.queue_deferred(world);
110    }
111
112    #[inline]
113    fn initialize(&mut self, world: &mut World) -> SystemAccess {
114        self.system.initialize(world)
115    }
116
117    #[inline]
118    fn check_change_tick(&mut self, check: CheckChangeTicks) {
119        self.system.check_change_tick(check);
120    }
121
122    #[inline]
123    fn default_system_sets(&self) -> Vec<InternedSystemSet> {
124        self.system.default_system_sets()
125    }
126
127    #[inline]
128    fn get_last_run(&self) -> Tick {
129        self.system.get_last_run()
130    }
131
132    #[inline]
133    fn set_last_run(&mut self, last_run: Tick) {
134        self.system.set_last_run(last_run);
135    }
136}
137
138/// A [`BoxedCondition`] stored alongside the access returned from [`System::initialize`].
139pub struct ConditionWithAccess {
140    /// The condition itself.
141    pub condition: BoxedCondition,
142    /// The access returned by [`System::initialize`].
143    /// This will be empty if the system has not been initialized yet.
144    pub access: SystemAccess,
145}
146
147impl ConditionWithAccess {
148    /// Constructs a new [`ConditionWithAccess`] from a [`BoxedCondition`].
149    /// The `access` will initially be empty.
150    pub fn new(condition: BoxedCondition) -> Self {
151        Self {
152            condition,
153            access: SystemAccess::default(),
154        }
155    }
156}
157
158impl System for ConditionWithAccess {
159    type In = ();
160    type Out = bool;
161
162    #[inline]
163    fn name(&self) -> DebugName {
164        self.condition.name()
165    }
166
167    #[inline]
168    fn system_type(&self) -> TypeId {
169        self.condition.system_type()
170    }
171
172    #[inline]
173    fn flags(&self) -> SystemStateFlags {
174        self.condition.flags()
175    }
176
177    #[inline]
178    unsafe fn run_unsafe(
179        &mut self,
180        input: SystemIn<'_, Self>,
181        world: UnsafeWorldCell,
182    ) -> Result<Self::Out, RunSystemError> {
183        // SAFETY: Caller ensures the same safety requirements.
184        unsafe { self.condition.run_unsafe(input, world) }
185    }
186
187    #[cfg(feature = "hotpatching")]
188    #[inline]
189    fn refresh_hotpatch(&mut self) {
190        self.condition.refresh_hotpatch();
191    }
192
193    #[inline]
194    fn apply_deferred(&mut self, world: &mut World) {
195        self.condition.apply_deferred(world);
196    }
197
198    #[inline]
199    fn queue_deferred(&mut self, world: DeferredWorld) {
200        self.condition.queue_deferred(world);
201    }
202
203    #[inline]
204    fn initialize(&mut self, world: &mut World) -> SystemAccess {
205        self.condition.initialize(world)
206    }
207
208    #[inline]
209    fn check_change_tick(&mut self, check: CheckChangeTicks) {
210        self.condition.check_change_tick(check);
211    }
212
213    #[inline]
214    fn default_system_sets(&self) -> Vec<InternedSystemSet> {
215        self.condition.default_system_sets()
216    }
217
218    #[inline]
219    fn get_last_run(&self) -> Tick {
220        self.condition.get_last_run()
221    }
222
223    #[inline]
224    fn set_last_run(&mut self, last_run: Tick) {
225        self.condition.set_last_run(last_run);
226    }
227}
228
229impl SystemNode {
230    /// Create a new [`SystemNode`]
231    pub fn new(system: ScheduleSystem) -> Self {
232        Self {
233            inner: Some(SystemWithAccess::new(system)),
234        }
235    }
236
237    /// Obtain a reference to the [`SystemWithAccess`] represented by this node.
238    pub fn get(&self) -> Option<&SystemWithAccess> {
239        self.inner.as_ref()
240    }
241
242    /// Obtain a mutable reference to the [`SystemWithAccess`] represented by this node.
243    pub fn get_mut(&mut self) -> Option<&mut SystemWithAccess> {
244        self.inner.as_mut()
245    }
246}
247
248new_key_type! {
249    /// A unique identifier for a system in a [`ScheduleGraph`].
250    pub struct SystemKey;
251    /// A unique identifier for a system set in a [`ScheduleGraph`].
252    pub struct SystemSetKey;
253}
254
255impl GraphNodeId for SystemKey {
256    type Adjacent = (SystemKey, Direction);
257    type Edge = (SystemKey, SystemKey);
258
259    fn kind(&self) -> &'static str {
260        "system"
261    }
262}
263
264impl GraphNodeId for SystemSetKey {
265    type Adjacent = (SystemSetKey, Direction);
266    type Edge = (SystemSetKey, SystemSetKey);
267
268    fn kind(&self) -> &'static str {
269        "system set"
270    }
271}
272
273impl TryFrom<NodeId> for SystemKey {
274    type Error = SystemSetKey;
275
276    fn try_from(value: NodeId) -> Result<Self, Self::Error> {
277        match value {
278            NodeId::System(key) => Ok(key),
279            NodeId::Set(key) => Err(key),
280        }
281    }
282}
283
284impl TryFrom<NodeId> for SystemSetKey {
285    type Error = SystemKey;
286
287    fn try_from(value: NodeId) -> Result<Self, Self::Error> {
288        match value {
289            NodeId::System(key) => Err(key),
290            NodeId::Set(key) => Ok(key),
291        }
292    }
293}
294
295/// Unique identifier for a system or system set stored in a [`ScheduleGraph`].
296///
297/// [`ScheduleGraph`]: crate::schedule::ScheduleGraph
298#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
299pub enum NodeId {
300    /// Identifier for a system.
301    System(SystemKey),
302    /// Identifier for a system set.
303    Set(SystemSetKey),
304}
305
306impl NodeId {
307    /// Returns `true` if the identified node is a system.
308    pub const fn is_system(&self) -> bool {
309        matches!(self, NodeId::System(_))
310    }
311
312    /// Returns `true` if the identified node is a system set.
313    pub const fn is_set(&self) -> bool {
314        matches!(self, NodeId::Set(_))
315    }
316
317    /// Returns the system key if the node is a system, otherwise `None`.
318    pub const fn as_system(&self) -> Option<SystemKey> {
319        match self {
320            NodeId::System(system) => Some(*system),
321            NodeId::Set(_) => None,
322        }
323    }
324
325    /// Returns the system set key if the node is a system set, otherwise `None`.
326    pub const fn as_set(&self) -> Option<SystemSetKey> {
327        match self {
328            NodeId::System(_) => None,
329            NodeId::Set(set) => Some(*set),
330        }
331    }
332}
333
334impl GraphNodeId for NodeId {
335    type Adjacent = CompactNodeIdAndDirection;
336    type Edge = CompactNodeIdPair;
337
338    fn kind(&self) -> &'static str {
339        match self {
340            NodeId::System(n) => n.kind(),
341            NodeId::Set(n) => n.kind(),
342        }
343    }
344}
345
346impl From<SystemKey> for NodeId {
347    fn from(system: SystemKey) -> Self {
348        NodeId::System(system)
349    }
350}
351
352impl From<SystemSetKey> for NodeId {
353    fn from(set: SystemSetKey) -> Self {
354        NodeId::Set(set)
355    }
356}
357
358/// Compact storage of a [`NodeId`] and a [`Direction`].
359#[derive(Clone, Copy)]
360pub struct CompactNodeIdAndDirection {
361    key: KeyData,
362    is_system: bool,
363    direction: Direction,
364}
365
366impl Debug for CompactNodeIdAndDirection {
367    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
368        let tuple: (_, _) = (*self).into();
369        tuple.fmt(f)
370    }
371}
372
373impl From<(NodeId, Direction)> for CompactNodeIdAndDirection {
374    fn from((id, direction): (NodeId, Direction)) -> Self {
375        let key = match id {
376            NodeId::System(key) => key.data(),
377            NodeId::Set(key) => key.data(),
378        };
379        let is_system = id.is_system();
380
381        Self {
382            key,
383            is_system,
384            direction,
385        }
386    }
387}
388
389impl From<CompactNodeIdAndDirection> for (NodeId, Direction) {
390    fn from(value: CompactNodeIdAndDirection) -> Self {
391        let node = match value.is_system {
392            true => NodeId::System(value.key.into()),
393            false => NodeId::Set(value.key.into()),
394        };
395
396        (node, value.direction)
397    }
398}
399
400/// Compact storage of a [`NodeId`] pair.
401#[derive(Clone, Copy, Hash, PartialEq, Eq)]
402pub struct CompactNodeIdPair {
403    key_a: KeyData,
404    key_b: KeyData,
405    is_system_a: bool,
406    is_system_b: bool,
407}
408
409impl Debug for CompactNodeIdPair {
410    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
411        let tuple: (_, _) = (*self).into();
412        tuple.fmt(f)
413    }
414}
415
416impl From<(NodeId, NodeId)> for CompactNodeIdPair {
417    fn from((a, b): (NodeId, NodeId)) -> Self {
418        let key_a = match a {
419            NodeId::System(index) => index.data(),
420            NodeId::Set(index) => index.data(),
421        };
422        let is_system_a = a.is_system();
423
424        let key_b = match b {
425            NodeId::System(index) => index.data(),
426            NodeId::Set(index) => index.data(),
427        };
428        let is_system_b = b.is_system();
429
430        Self {
431            key_a,
432            key_b,
433            is_system_a,
434            is_system_b,
435        }
436    }
437}
438
439impl From<CompactNodeIdPair> for (NodeId, NodeId) {
440    fn from(value: CompactNodeIdPair) -> Self {
441        let a = match value.is_system_a {
442            true => NodeId::System(value.key_a.into()),
443            false => NodeId::Set(value.key_a.into()),
444        };
445
446        let b = match value.is_system_b {
447            true => NodeId::System(value.key_b.into()),
448            false => NodeId::Set(value.key_b.into()),
449        };
450
451        (a, b)
452    }
453}
454
455/// Container for systems in a schedule.
456#[derive(Default)]
457pub struct Systems {
458    /// List of systems in the schedule.
459    nodes: SlotMap<SystemKey, SystemNode>,
460    /// List of conditions for each system, in the same order as `nodes`.
461    conditions: SecondaryMap<SystemKey, Vec<ConditionWithAccess>>,
462    /// Systems and their conditions that have not been initialized yet.
463    uninit: Vec<SystemKey>,
464}
465
466impl Systems {
467    /// Returns the number of systems in this container.
468    pub fn len(&self) -> usize {
469        self.nodes.len()
470    }
471
472    /// Returns `true` if this container is empty.
473    pub fn is_empty(&self) -> bool {
474        self.nodes.is_empty()
475    }
476
477    /// Returns a reference to the system with the given key, if it exists.
478    pub fn get(&self, key: SystemKey) -> Option<&SystemWithAccess> {
479        self.nodes.get(key).and_then(|node| node.get())
480    }
481
482    /// Returns a mutable reference to the system with the given key, if it exists.
483    pub fn get_mut(&mut self, key: SystemKey) -> Option<&mut SystemWithAccess> {
484        self.nodes.get_mut(key).and_then(|node| node.get_mut())
485    }
486
487    /// Returns a mutable reference to the system with the given key. Will return
488    /// `None` if the key does not exist.
489    pub(crate) fn node_mut(&mut self, key: SystemKey) -> Option<&mut SystemNode> {
490        self.nodes.get_mut(key)
491    }
492
493    /// Returns `true` if the system with the given key has conditions.
494    pub fn has_conditions(&self, key: SystemKey) -> bool {
495        self.conditions
496            .get(key)
497            .is_some_and(|conditions| !conditions.is_empty())
498    }
499
500    /// Returns a reference to the conditions for the system with the given key, if it exists.
501    pub fn get_conditions(&self, key: SystemKey) -> Option<&[ConditionWithAccess]> {
502        self.conditions.get(key).map(Vec::as_slice)
503    }
504
505    /// Returns a mutable reference to the conditions for the system with the given key, if it exists.
506    pub fn get_conditions_mut(&mut self, key: SystemKey) -> Option<&mut Vec<ConditionWithAccess>> {
507        self.conditions.get_mut(key)
508    }
509
510    /// Returns an iterator over all systems and their conditions in this
511    /// container.
512    pub fn iter(
513        &self,
514    ) -> impl Iterator<Item = (SystemKey, &ScheduleSystem, &[ConditionWithAccess])> + '_ {
515        self.nodes.iter().filter_map(|(key, node)| {
516            let system = &node.get()?.system;
517            let conditions = self
518                .conditions
519                .get(key)
520                .map(Vec::as_slice)
521                .unwrap_or_default();
522            Some((key, system, conditions))
523        })
524    }
525
526    /// Inserts a new system into the container, along with its conditions,
527    /// and queues it to be initialized later in [`Systems::initialize`].
528    ///
529    /// We have to defer initialization of systems in the container until we have
530    /// `&mut World` access, so we store these in a list until
531    /// [`Systems::initialize`] is called. This is usually done upon the first
532    /// run of the schedule.
533    pub fn insert(
534        &mut self,
535        system: ScheduleSystem,
536        conditions: Vec<Box<dyn ReadOnlySystem<In = (), Out = bool>>>,
537    ) -> SystemKey {
538        let key = self.nodes.insert(SystemNode::new(system));
539        self.conditions.insert(
540            key,
541            conditions
542                .into_iter()
543                .map(ConditionWithAccess::new)
544                .collect(),
545        );
546        self.uninit.push(key);
547        key
548    }
549
550    /// Remove a system with [`SystemKey`]
551    pub(crate) fn remove(&mut self, key: SystemKey) -> bool {
552        let mut found = false;
553        if self.nodes.remove(key).is_some() {
554            found = true;
555        }
556
557        if self.conditions.remove(key).is_some() {
558            found = true;
559        }
560
561        if let Some(index) = self.uninit.iter().position(|value| *value == key) {
562            self.uninit.remove(index);
563            found = true;
564        }
565
566        found
567    }
568
569    /// Returns `true` if all systems in this container have been initialized.
570    pub fn is_initialized(&self) -> bool {
571        self.uninit.is_empty()
572    }
573
574    /// Initializes all systems and their conditions that have not been
575    /// initialized yet.
576    pub fn initialize(&mut self, world: &mut World) {
577        for key in self.uninit.drain(..) {
578            let Some(system) = self.nodes.get_mut(key).and_then(|node| node.get_mut()) else {
579                continue;
580            };
581            system.access = system.system.initialize(world);
582            let Some(conditions) = self.conditions.get_mut(key) else {
583                continue;
584            };
585            for condition in conditions {
586                condition.access = condition.condition.initialize(world);
587            }
588        }
589    }
590
591    /// Calculates the list of systems that conflict with each other based on
592    /// their access patterns.
593    ///
594    /// If the `Box<[ComponentId]>` is empty for a given pair of systems, then the
595    /// systems conflict on [`World`] access in general (e.g. one of them is
596    /// exclusive, or both systems have `Query<EntityMut>`).
597    pub fn get_conflicting_systems(
598        &self,
599        flat_dependency_analysis: &DagAnalysis<SystemKey>,
600        flat_ambiguous_with: &UnGraph<SystemKey>,
601        ambiguous_with_all: &HashSet<NodeId>,
602        ignored_ambiguities: &BTreeSet<ComponentId>,
603    ) -> ConflictingSystems {
604        let mut conflicting_systems: Vec<(_, _, Box<[_]>)> = Vec::new();
605        for &(a, b) in flat_dependency_analysis.disconnected() {
606            if flat_ambiguous_with.contains_edge(a, b)
607                || ambiguous_with_all.contains(&NodeId::System(a))
608                || ambiguous_with_all.contains(&NodeId::System(b))
609            {
610                continue;
611            }
612
613            let access_a = &self[a].access;
614            let access_b = &self[b].access;
615            if access_a.is_exclusive() || access_b.is_exclusive() {
616                conflicting_systems.push((a, b, Box::new([])));
617            } else {
618                if !access_a.is_compatible(access_b) {
619                    match access_a.get_conflicts(access_b) {
620                        AccessConflicts::Individual(conflicts) => {
621                            let conflicts: Box<[_]> = conflicts
622                                .iter()
623                                .filter(|id| !ignored_ambiguities.contains(id))
624                                .collect();
625                            if !conflicts.is_empty() {
626                                conflicting_systems.push((a, b, conflicts));
627                            }
628                        }
629                        AccessConflicts::All => {
630                            // there is no specific component conflicting, but the systems are overall incompatible
631                            // for example 2 systems with `Query<EntityMut>`
632                            conflicting_systems.push((a, b, Box::new([])));
633                        }
634                    }
635                }
636            }
637        }
638
639        ConflictingSystems(conflicting_systems)
640    }
641}
642
643impl Index<SystemKey> for Systems {
644    type Output = SystemWithAccess;
645
646    #[track_caller]
647    fn index(&self, key: SystemKey) -> &Self::Output {
648        self.get(key)
649            .unwrap_or_else(|| panic!("System with key {:?} does not exist in the schedule", key))
650    }
651}
652
653impl IndexMut<SystemKey> for Systems {
654    #[track_caller]
655    fn index_mut(&mut self, key: SystemKey) -> &mut Self::Output {
656        self.get_mut(key)
657            .unwrap_or_else(|| panic!("System with key {:?} does not exist in the schedule", key))
658    }
659}
660
661/// Pairs of systems that conflict with each other along with the components
662/// they conflict on, which prevents them from running in parallel. If the
663/// component list is empty, the systems conflict on [`World`] access in general
664/// (e.g. one of them is exclusive, or both systems have `Query<EntityMut>`).
665#[derive(Clone, Debug, Default)]
666pub struct ConflictingSystems(pub Vec<(SystemKey, SystemKey, Box<[ComponentId]>)>);
667
668impl ConflictingSystems {
669    /// Checks if there are any conflicting systems, returning [`Ok`] if there
670    /// are none, or an [`AmbiguousSystemConflictsWarning`] if there are.
671    pub fn check_if_not_empty(&self) -> Result<(), AmbiguousSystemConflictsWarning> {
672        if self.0.is_empty() {
673            Ok(())
674        } else {
675            Err(AmbiguousSystemConflictsWarning(self.clone()))
676        }
677    }
678
679    /// Converts the conflicting systems into an iterator of their system names
680    /// and the names of the components they conflict on.
681    pub fn to_string(
682        &self,
683        graph: &ScheduleGraph,
684        components: &Components,
685    ) -> impl Iterator<Item = (String, String, Box<[DebugName]>)> {
686        self.iter().map(move |(system_a, system_b, conflicts)| {
687            let name_a = graph.get_node_name(&NodeId::System(*system_a));
688            let name_b = graph.get_node_name(&NodeId::System(*system_b));
689
690            let conflict_names: Box<[_]> = conflicts
691                .iter()
692                .map(|id| components.get_name(*id).unwrap())
693                .collect();
694
695            (name_a, name_b, conflict_names)
696        })
697    }
698}
699
700impl Deref for ConflictingSystems {
701    type Target = Vec<(SystemKey, SystemKey, Box<[ComponentId]>)>;
702
703    fn deref(&self) -> &Self::Target {
704        &self.0
705    }
706}
707
708/// Error returned when there are ambiguous system conflicts detected.
709#[derive(Error, Debug)]
710#[error("Systems with conflicting access have indeterminate run order: {:?}", .0.0)]
711pub struct AmbiguousSystemConflictsWarning(pub ConflictingSystems);
712
713/// Container for system sets in a schedule.
714#[derive(Default)]
715pub struct SystemSets {
716    /// List of system sets in the schedule.
717    sets: SlotMap<SystemSetKey, InternedSystemSet>,
718    /// List of conditions for each system set, in the same order as `sets`.
719    conditions: SecondaryMap<SystemSetKey, Vec<ConditionWithAccess>>,
720    /// Map from system sets to their keys.
721    ids: HashMap<InternedSystemSet, SystemSetKey>,
722    /// System sets that have not been initialized yet.
723    uninit: Vec<UninitializedSet>,
724}
725
726/// A system set's conditions that have not been initialized yet.
727struct UninitializedSet {
728    key: SystemSetKey,
729    /// The range of indices in [`SystemSets::conditions`] that correspond
730    /// to conditions that have not been initialized yet.
731    ///
732    /// [`SystemSets::conditions`] for a given set may be appended to
733    /// multiple times (e.g. when `configure_sets` is called multiple with
734    /// the same set), so we need to track which conditions in that list
735    /// are newly added and not yet initialized.
736    ///
737    /// Systems don't need this tracking because each `add_systems` call
738    /// creates separate nodes in the graph with their own conditions,
739    /// so all conditions are initialized together.
740    uninitialized_conditions: Range<usize>,
741}
742
743impl SystemSets {
744    /// Returns the number of system sets in this container.
745    pub fn len(&self) -> usize {
746        self.sets.len()
747    }
748
749    /// Returns `true` if this container is empty.
750    pub fn is_empty(&self) -> bool {
751        self.sets.is_empty()
752    }
753
754    /// Returns `true` if the given set is present in this container.
755    pub fn contains(&self, set: impl SystemSet) -> bool {
756        self.ids.contains_key(&set.intern())
757    }
758
759    /// Returns a reference to the system set with the given key, if it exists.
760    pub fn get(&self, key: SystemSetKey) -> Option<&dyn SystemSet> {
761        self.sets.get(key).map(|set| &**set)
762    }
763
764    /// Returns the key for the given system set, returns None if it does not exist.
765    pub fn get_key(&self, set: InternedSystemSet) -> Option<SystemSetKey> {
766        self.ids.get(&set).copied()
767    }
768
769    /// Returns the key for the given system set, inserting it into this
770    /// container if it does not already exist.
771    pub fn get_key_or_insert(&mut self, set: InternedSystemSet) -> SystemSetKey {
772        *self.ids.entry(set).or_insert_with(|| {
773            let key = self.sets.insert(set);
774            self.conditions.insert(key, Vec::new());
775            key
776        })
777    }
778
779    /// Returns `true` if the system set with the given key has conditions.
780    pub fn has_conditions(&self, key: SystemSetKey) -> bool {
781        self.conditions
782            .get(key)
783            .is_some_and(|conditions| !conditions.is_empty())
784    }
785
786    /// Returns a reference to the conditions for the system set with the given
787    /// key, if it exists.
788    pub fn get_conditions(&self, key: SystemSetKey) -> Option<&[ConditionWithAccess]> {
789        self.conditions.get(key).map(Vec::as_slice)
790    }
791
792    /// Returns a mutable reference to the conditions for the system set with
793    /// the given key, if it exists.
794    pub fn get_conditions_mut(
795        &mut self,
796        key: SystemSetKey,
797    ) -> Option<&mut Vec<ConditionWithAccess>> {
798        self.conditions.get_mut(key)
799    }
800
801    /// Returns an iterator over all system sets in this container, along with
802    /// their conditions.
803    pub fn iter(
804        &self,
805    ) -> impl Iterator<Item = (SystemSetKey, &dyn SystemSet, &[ConditionWithAccess])> {
806        self.sets.iter().filter_map(|(key, set)| {
807            let conditions = self.conditions.get(key)?.as_slice();
808            Some((key, &**set, conditions))
809        })
810    }
811
812    /// Inserts conditions for a system set into the container, and queues the
813    /// newly added conditions to be initialized later in [`SystemSets::initialize`].
814    ///
815    /// If the set was not already present in the container, it is added automatically.
816    ///
817    /// We have to defer initialization of system set conditions in the container
818    /// until we have `&mut World` access, so we store these in a list until
819    /// [`SystemSets::initialize`] is called. This is usually done upon the
820    /// first run of the schedule.
821    pub fn insert(
822        &mut self,
823        set: InternedSystemSet,
824        new_conditions: Vec<Box<dyn ReadOnlySystem<In = (), Out = bool>>>,
825    ) -> SystemSetKey {
826        let key = self.get_key_or_insert(set);
827        if !new_conditions.is_empty() {
828            let current_conditions = &mut self.conditions[key];
829            let start = current_conditions.len();
830            self.uninit.push(UninitializedSet {
831                key,
832                uninitialized_conditions: start..(start + new_conditions.len()),
833            });
834            current_conditions.extend(new_conditions.into_iter().map(ConditionWithAccess::new));
835        }
836        key
837    }
838
839    /// Remove a set with a [`SystemSetKey`]
840    pub(crate) fn remove(&mut self, key: SystemSetKey) -> bool {
841        self.sets.remove(key);
842        self.conditions.remove(key);
843        self.uninit.retain(|uninit| uninit.key != key);
844        true
845    }
846
847    /// Returns `true` if all system sets' conditions in this container have
848    /// been initialized.
849    pub fn is_initialized(&self) -> bool {
850        self.uninit.is_empty()
851    }
852
853    /// Initializes all system sets' conditions that have not been
854    /// initialized yet. Because a system set's conditions may be appended to
855    /// multiple times, we track which conditions were added since the last
856    /// initialization and only initialize those.
857    pub fn initialize(&mut self, world: &mut World) {
858        for uninit in self.uninit.drain(..) {
859            let Some(conditions) = self.conditions.get_mut(uninit.key) else {
860                continue;
861            };
862            for condition in &mut conditions[uninit.uninitialized_conditions] {
863                condition.access = condition.initialize(world);
864            }
865        }
866    }
867
868    /// Ensures that there are no edges to system-type sets that have multiple
869    /// instances.
870    pub fn check_type_set_ambiguity(
871        &self,
872        set_systems: &DagGroups<SystemSetKey, SystemKey>,
873        ambiguous_with: &UnGraph<NodeId>,
874        dependency: &DiGraph<NodeId>,
875    ) -> Result<(), SystemTypeSetAmbiguityError> {
876        for (&key, systems) in set_systems.iter() {
877            let set = &self[key];
878            if set.system_type().is_some() {
879                let instances = systems.len();
880                let ambiguous_with = ambiguous_with.edges(NodeId::Set(key));
881                let before = dependency.edges_directed(NodeId::Set(key), Incoming);
882                let after = dependency.edges_directed(NodeId::Set(key), Outgoing);
883                let relations = before.count() + after.count() + ambiguous_with.count();
884                if instances > 1 && relations > 0 {
885                    return Err(SystemTypeSetAmbiguityError(key));
886                }
887            }
888        }
889        Ok(())
890    }
891}
892
893impl Index<SystemSetKey> for SystemSets {
894    type Output = dyn SystemSet;
895
896    #[track_caller]
897    fn index(&self, key: SystemSetKey) -> &Self::Output {
898        self.get(key).unwrap_or_else(|| {
899            panic!(
900                "System set with key {:?} does not exist in the schedule",
901                key
902            )
903        })
904    }
905}
906
907/// Error returned when calling [`SystemSets::check_type_set_ambiguity`].
908#[derive(Error, Debug)]
909#[error("Tried to order against `{0:?}` in a schedule that has more than one `{0:?}` instance. `{0:?}` is a `SystemTypeSet` and cannot be used for ordering if ambiguous. Use a different set without this restriction.")]
910pub struct SystemTypeSetAmbiguityError(pub SystemSetKey);
911
912#[cfg(test)]
913mod tests {
914    use alloc::{boxed::Box, vec};
915
916    use crate::{
917        prelude::SystemSet,
918        schedule::{SystemSets, Systems},
919        system::IntoSystem,
920        world::World,
921    };
922
923    #[derive(SystemSet, Clone, Copy, PartialEq, Eq, Debug, Hash)]
924    pub struct TestSet;
925
926    #[test]
927    fn systems() {
928        fn empty_system() {}
929
930        let mut systems = Systems::default();
931        assert!(systems.is_empty());
932        assert_eq!(systems.len(), 0);
933
934        let system = Box::new(IntoSystem::into_system(empty_system));
935        let key = systems.insert(system, vec![]);
936
937        assert!(!systems.is_empty());
938        assert_eq!(systems.len(), 1);
939        assert!(systems.get(key).is_some());
940        assert!(systems.get_conditions(key).is_some());
941        assert!(systems.get_conditions(key).unwrap().is_empty());
942        assert!(systems.get_mut(key).is_some());
943        assert!(!systems.is_initialized());
944        assert!(systems.iter().next().is_some());
945
946        let mut world = World::new();
947        systems.initialize(&mut world);
948        assert!(systems.is_initialized());
949    }
950
951    #[test]
952    fn system_sets() {
953        fn always_true() -> bool {
954            true
955        }
956
957        let mut sets = SystemSets::default();
958        assert!(sets.is_empty());
959        assert_eq!(sets.len(), 0);
960
961        let condition = Box::new(IntoSystem::into_system(always_true));
962        let key = sets.insert(TestSet.intern(), vec![condition]);
963
964        assert!(!sets.is_empty());
965        assert_eq!(sets.len(), 1);
966        assert!(sets.get(key).is_some());
967        assert!(sets.get_conditions(key).is_some());
968        assert!(!sets.get_conditions(key).unwrap().is_empty());
969        assert!(!sets.is_initialized());
970        assert!(sets.iter().next().is_some());
971
972        let mut world = World::new();
973        sets.initialize(&mut world);
974        assert!(sets.is_initialized());
975    }
976}