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