bones_ecs/
stage.rs

1//! Implementation of stage abstraction for running collections of systems over a [`World`].
2
3use std::collections::VecDeque;
4
5use crate::prelude::*;
6
7/// Resource that is automatically added to the world while a system stage is being run
8/// that specifies the unique ID of the stage that being run.
9///
10/// If the stage is `Ulid(0)`, the default ID, then that means the startup stage is being run.
11#[derive(Deref, DerefMut, Clone, Copy, HasSchema, Default)]
12pub struct CurrentSystemStage(pub Ulid);
13
14/// An ordered collection of [`SystemStage`]s.
15pub struct SystemStages {
16    /// The stages in the collection, in the order that they will be run.
17    pub stages: Vec<Box<dyn SystemStage>>,
18    /// Whether or not the startup systems have been run yet.
19    pub has_started: bool,
20    /// The systems that should run at startup.
21    pub startup_systems: Vec<StaticSystem<(), ()>>,
22    /// Systems that are continously run until they succeed(return Some). These run before all stages. Uses Option to allow for easy usage of `?`.
23    pub single_success_systems: Vec<StaticSystem<(), Option<()>>>,
24}
25
26impl std::fmt::Debug for SystemStages {
27    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28        f.debug_struct("SystemStages")
29            // TODO: Add list of stages to the debug render for `SystemStages`.
30            // We can at least list the names of each stage for `SystemStages` debug
31            // implementation.
32            .finish()
33    }
34}
35
36impl Default for SystemStages {
37    fn default() -> Self {
38        Self::with_core_stages()
39    }
40}
41
42impl SystemStages {
43    /// Execute the systems on the given `world`.
44    pub fn run(&mut self, world: &mut World) {
45        // If we haven't run our startup systems yet
46        if !self.has_started {
47            // Set the current stage resource
48            world.insert_resource(CurrentSystemStage(Ulid(0)));
49
50            // For each startup system
51            for system in &mut self.startup_systems {
52                // Run the system
53                system.run(world, ());
54            }
55
56            // Don't run startup systems again
57            self.has_started = true;
58        }
59
60        // Run single success systems
61        self.single_success_systems.retain_mut(|system| {
62            let result = system.run(world, ());
63            result.is_none() // Keep the system if it didn't succeed (returned None)
64        });
65
66        // Run each stage
67        for stage in &mut self.stages {
68            // Set the current stage resource
69            world.insert_resource(CurrentSystemStage(stage.id()));
70
71            // Run the stage
72            stage.run(world);
73        }
74
75        // Cleanup killed entities
76        world.maintain();
77
78        // Remove the current system stage resource
79        world.resources.remove::<CurrentSystemStage>();
80    }
81
82    /// Create a [`SystemStages`] collection, initialized with a stage for each [`CoreStage`].
83    pub fn with_core_stages() -> Self {
84        Self {
85            stages: vec![
86                Box::new(SimpleSystemStage::new(CoreStage::First)),
87                Box::new(SimpleSystemStage::new(CoreStage::PreUpdate)),
88                Box::new(SimpleSystemStage::new(CoreStage::Update)),
89                Box::new(SimpleSystemStage::new(CoreStage::PostUpdate)),
90                Box::new(SimpleSystemStage::new(CoreStage::Last)),
91            ],
92            has_started: false,
93            startup_systems: default(),
94            single_success_systems: Vec::new(),
95        }
96    }
97
98    /// Add a system that will run only once, before all of the other non-startup systems.
99    pub fn add_startup_system<Args, S>(&mut self, system: S) -> &mut Self
100    where
101        S: IntoSystem<Args, (), (), Sys = StaticSystem<(), ()>>,
102    {
103        self.startup_systems.push(system.system());
104        self
105    }
106
107    /// Add a system that will run each frame until it succeeds (returns Some). Runs before all stages. Uses Option to allow for easy usage of `?`.
108    pub fn add_single_success_system<Args, S>(&mut self, system: S) -> &mut Self
109    where
110        S: IntoSystem<Args, (), Option<()>, Sys = StaticSystem<(), Option<()>>>,
111    {
112        self.single_success_systems.push(system.system());
113        self
114    }
115
116    /// Add a [`System`] to the stage with the given label.
117    pub fn add_system_to_stage<Args, S>(&mut self, label: impl StageLabel, system: S) -> &mut Self
118    where
119        S: IntoSystem<Args, (), (), Sys = StaticSystem<(), ()>>,
120    {
121        let name = label.name();
122        let id = label.id();
123        let mut stage = None;
124
125        for st in &mut self.stages {
126            if st.id() == id {
127                stage = Some(st);
128            }
129        }
130
131        let Some(stage) = stage else {
132            panic!("Stage with label `{}` ( {} ) doesn't exist.", name, id);
133        };
134
135        stage.add_system(system.system());
136
137        self
138    }
139
140    /// Insert a new stage, before another existing stage
141    #[track_caller]
142    pub fn insert_stage_before<L: StageLabel, S: SystemStage + 'static>(
143        &mut self,
144        label: L,
145        stage: S,
146    ) -> &mut Self {
147        let stage_idx = self
148            .stages
149            .iter()
150            .position(|x| x.id() == label.id())
151            .unwrap_or_else(|| panic!("Could not find stage with label `{}`", label.name()));
152        self.stages.insert(stage_idx, Box::new(stage));
153
154        self
155    }
156
157    /// Insert a new stage, after another existing stage
158    #[track_caller]
159    pub fn insert_stage_after<L: StageLabel, S: SystemStage + 'static>(
160        &mut self,
161        label: L,
162        stage: S,
163    ) -> &mut Self {
164        let stage_idx = self
165            .stages
166            .iter()
167            .position(|x| x.id() == label.id())
168            .unwrap_or_else(|| panic!("Could not find stage with label `{}`", label.name()));
169        self.stages.insert(stage_idx + 1, Box::new(stage));
170
171        self
172    }
173
174    /// Remove all systems from all stages, including startup and single success systems. Resets has_started as well, allowing for startup systems to run once again.
175    pub fn reset_remove_all_systems(&mut self) {
176        // Reset the has_started flag
177        self.has_started = false;
178        self.remove_all_systems();
179    }
180
181    /// Remove all systems from all stages, including startup and single success systems. Does not reset has_started.
182    pub fn remove_all_systems(&mut self) {
183        // Clear startup systems
184        self.startup_systems.clear();
185
186        // Clear single success systems
187        self.single_success_systems.clear();
188
189        // Clear systems from each stage
190        for stage in &mut self.stages {
191            stage.remove_all_systems();
192        }
193    }
194}
195
196/// Trait for system stages. A stage is a
197pub trait SystemStage: Sync + Send {
198    /// The unique identifier for the stage.
199    fn id(&self) -> Ulid;
200    /// The human-readable name for the stage, used for error messages when something goes wrong.
201    fn name(&self) -> String;
202    /// Execute the systems on the given `world`.
203    fn run(&mut self, world: &World);
204
205    /// Add a system to this stage.
206    fn add_system(&mut self, system: StaticSystem<(), ()>);
207    /// Remove all systems from this stage.
208    fn remove_all_systems(&mut self);
209}
210
211/// A collection of systems that will be run in order.
212pub struct SimpleSystemStage {
213    /// The unique identifier for the stage.
214    pub id: Ulid,
215    /// The human-readable name for the stage, used for error messages when something goes wrong.
216    pub name: String,
217    /// The list of systems in the stage.
218    ///
219    /// Each system will be run in the order that they are in in this list.
220    pub systems: Vec<StaticSystem<(), ()>>,
221}
222
223impl SimpleSystemStage {
224    /// Create a new, empty stage, for the given label.
225    pub fn new<L: StageLabel>(label: L) -> Self {
226        Self {
227            id: label.id(),
228            name: label.name(),
229            systems: Default::default(),
230        }
231    }
232}
233
234impl SystemStage for SimpleSystemStage {
235    fn id(&self) -> Ulid {
236        self.id
237    }
238
239    fn name(&self) -> String {
240        self.name.clone()
241    }
242
243    fn run(&mut self, world: &World) {
244        // Run the systems
245        for system in &mut self.systems {
246            system.run(world, ());
247        }
248
249        // Drain the command queue
250        let queue = world.resources.get_mut::<CommandQueue>();
251        if let Some(mut command_queue) = queue {
252            for mut system in command_queue.queue.drain(..) {
253                system.run(world, ());
254            }
255        }
256    }
257
258    fn add_system(&mut self, system: StaticSystem<(), ()>) {
259        self.systems.push(system);
260    }
261
262    fn remove_all_systems(&mut self) {
263        self.systems.clear();
264    }
265}
266
267/// Trait for things that may be used to identify a system stage.
268pub trait StageLabel {
269    /// Returns the human-readable name of the label, used in error messages.
270    fn name(&self) -> String;
271    /// Returns a unique identifier for the stage.
272    fn id(&self) -> Ulid;
273}
274
275/// A [`StageLabel`] for the 5 core stages.
276#[derive(Copy, Clone, Debug, PartialEq, Eq)]
277pub enum CoreStage {
278    /// The first stage
279    First,
280    /// The second stage
281    PreUpdate,
282    /// The third stage
283    Update,
284    /// The fourth stage
285    PostUpdate,
286    /// The fifth stage
287    Last,
288}
289
290impl StageLabel for CoreStage {
291    fn name(&self) -> String {
292        format!("{:?}", self)
293    }
294
295    fn id(&self) -> Ulid {
296        match self {
297            CoreStage::First => Ulid(2021715391084198804812356024998495966),
298            CoreStage::PreUpdate => Ulid(2021715401330719559452824437611089988),
299            CoreStage::Update => Ulid(2021715410160177201728645950400543948),
300            CoreStage::PostUpdate => Ulid(2021715423103233646561968734173322317),
301            CoreStage::Last => Ulid(2021715433398666914977687392909851554),
302        }
303    }
304}
305
306/// A resource containing the [`Commands`] command queue.
307///
308/// You can use [`Commands`] as a [`SystemParam`] as a shortcut to [`ResMut<CommandQueue>`].
309#[derive(HasSchema, Default)]
310pub struct CommandQueue {
311    /// The system queue that will be run at the end of the stage
312    pub queue: VecDeque<StaticSystem<(), ()>>,
313}
314
315impl Clone for CommandQueue {
316    fn clone(&self) -> Self {
317        if self.queue.is_empty() {
318            Self {
319                queue: VecDeque::with_capacity(self.queue.capacity()),
320            }
321        } else {
322            panic!(
323                "Cannot clone CommandQueue. This probably happened because you are \
324                trying to clone a World while a system stage is still executing."
325            )
326        }
327    }
328}
329
330impl CommandQueue {
331    /// Add a system to be run at the end of the stage.
332    pub fn add<Args, S>(&mut self, system: S)
333    where
334        S: IntoSystem<Args, (), (), Sys = StaticSystem<(), ()>>,
335    {
336        self.queue.push_back(system.system());
337    }
338}
339
340/// A [`SystemParam`] that can be used to schedule systems that will be run at the end of the
341/// current [`SystemStage`].
342///
343/// This is a shortcut for [`ResMut<CommandQueue>`].
344#[derive(Deref, DerefMut)]
345pub struct Commands<'a>(RefMut<'a, CommandQueue>);
346
347impl<'a> SystemParam for Commands<'a> {
348    type State = AtomicResource<CommandQueue>;
349    type Param<'s> = Commands<'s>;
350
351    fn get_state(world: &World) -> Self::State {
352        let cell = world.resources.get_cell::<CommandQueue>();
353        cell.init(world);
354        cell
355    }
356
357    fn borrow<'s>(_world: &'s World, state: &'s mut Self::State) -> Self::Param<'s> {
358        Commands(state.borrow_mut().unwrap())
359    }
360}