1use std::collections::VecDeque;
4
5use crate::prelude::*;
6
7#[derive(Deref, DerefMut, Clone, Copy, HasSchema, Default)]
12pub struct CurrentSystemStage(pub Ulid);
13
14pub struct SystemStagesBuilder {
16 stages: Vec<Box<dyn SystemStage>>,
18 startup_systems: Vec<StaticSystem<(), ()>>,
21
22 startup_resources: UntypedResourceSet,
24
25 single_success_systems: Vec<StaticSystem<(), Option<()>>>,
27}
28
29impl Default for SystemStagesBuilder {
30 fn default() -> Self {
31 Self::with_core_stages()
32 }
33}
34
35impl SystemStagesBuilder {
36 pub fn with_core_stages() -> Self {
38 Self {
39 stages: vec![
40 Box::new(SimpleSystemStage::new(CoreStage::First)),
41 Box::new(SimpleSystemStage::new(CoreStage::PreUpdate)),
42 Box::new(SimpleSystemStage::new(CoreStage::Update)),
43 Box::new(SimpleSystemStage::new(CoreStage::PostUpdate)),
44 Box::new(SimpleSystemStage::new(CoreStage::Last)),
45 ],
46 startup_resources: default(),
47 startup_systems: default(),
48 single_success_systems: Vec::new(),
49 }
50 }
51
52 pub fn finish(self) -> SystemStages {
54 SystemStages {
55 stages: self.stages,
56 startup_systems: self.startup_systems,
57 startup_resources: self.startup_resources,
58 single_success_systems: self.single_success_systems,
59 }
60 }
61
62 pub fn from_stages(stages: SystemStages) -> Self {
64 Self {
65 stages: stages.stages,
66 startup_systems: stages.startup_systems,
67 startup_resources: stages.startup_resources,
68 single_success_systems: stages.single_success_systems,
69 }
70 }
71
72 pub fn add_startup_system<Args, S>(&mut self, system: S) -> &mut Self
75 where
76 S: IntoSystem<Args, (), (), Sys = StaticSystem<(), ()>>,
77 {
78 self.startup_systems.push(system.system());
79 self
80 }
81
82 pub fn add_single_success_system<Args, S>(&mut self, system: S) -> &mut Self
84 where
85 S: IntoSystem<Args, (), Option<()>, Sys = StaticSystem<(), Option<()>>>,
86 {
87 self.single_success_systems.push(system.system());
88 self
89 }
90
91 pub fn add_system_to_stage<Args, S>(&mut self, label: impl StageLabel, system: S) -> &mut Self
93 where
94 S: IntoSystem<Args, (), (), Sys = StaticSystem<(), ()>>,
95 {
96 let name = label.name();
97 let id = label.id();
98 let mut stage = None;
99
100 for st in &mut self.stages {
101 if st.id() == id {
102 stage = Some(st);
103 }
104 }
105
106 let Some(stage) = stage else {
107 panic!("Stage with label `{}` ( {} ) doesn't exist.", name, id);
108 };
109
110 stage.add_system(system.system());
111
112 self
113 }
114
115 #[track_caller]
117 pub fn insert_stage_before<L: StageLabel, S: SystemStage + 'static>(
118 &mut self,
119 label: L,
120 stage: S,
121 ) -> &mut Self {
122 let stage_idx = self
123 .stages
124 .iter()
125 .position(|x| x.id() == label.id())
126 .unwrap_or_else(|| panic!("Could not find stage with label `{}`", label.name()));
127 self.stages.insert(stage_idx, Box::new(stage));
128
129 self
130 }
131
132 #[track_caller]
134 pub fn insert_stage_after<L: StageLabel, S: SystemStage + 'static>(
135 &mut self,
136 label: L,
137 stage: S,
138 ) -> &mut Self {
139 let stage_idx = self
140 .stages
141 .iter()
142 .position(|x| x.id() == label.id())
143 .unwrap_or_else(|| panic!("Could not find stage with label `{}`", label.name()));
144 self.stages.insert(stage_idx + 1, Box::new(stage));
145
146 self
147 }
148
149 pub fn insert_startup_resource<T: HasSchema>(&mut self, resource: T) {
153 self.startup_resources.insert_resource(resource);
154 }
155
156 pub fn init_startup_resource<T: HasSchema + Default>(&mut self) -> RefMut<'_, T> {
159 self.startup_resources.init_resource::<T>()
160 }
161
162 #[track_caller]
164 pub fn startup_resource_mut<T: HasSchema>(&self) -> Option<RefMut<'_, T>> {
165 self.startup_resources.resource_mut()
166 }
167}
168
169pub struct SystemStages {
171 stages: Vec<Box<dyn SystemStage>>,
173
174 startup_systems: Vec<StaticSystem<(), ()>>,
177
178 startup_resources: UntypedResourceSet,
180
181 single_success_systems: Vec<StaticSystem<(), Option<()>>>,
183}
184
185impl std::fmt::Debug for SystemStages {
186 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
187 f.debug_struct("SystemStages")
188 .finish()
192 }
193}
194
195impl Default for SystemStages {
196 fn default() -> Self {
197 SystemStagesBuilder::default().finish()
198 }
199}
200
201impl SystemStages {
202 pub fn builder() -> SystemStagesBuilder {
204 SystemStagesBuilder::default()
205 }
206
207 pub fn run(&mut self, world: &mut World) {
209 self.handle_startup(world);
211
212 for (index, system) in self.single_success_systems.iter_mut().enumerate() {
214 let should_run = !Self::has_single_success_system_succeeded(index, world);
215
216 if should_run && system.run(world, ()).is_some() {
217 Self::mark_single_success_system_succeeded(index, world);
218 }
219 }
220
221 for stage in &mut self.stages {
223 world.insert_resource(CurrentSystemStage(stage.id()));
225
226 stage.run(world);
228 }
229
230 world.maintain();
232
233 world.resources.remove::<CurrentSystemStage>();
235 }
236
237 pub fn handle_startup(&mut self, world: &mut World) {
251 self.handle_startup_resources(world);
252 self.handle_startup_systems(world);
253 }
254
255 pub fn handle_startup_resources(&mut self, world: &mut World) {
264 let (mut resources_inserted, systems_run) = match world.get_resource_mut::<SessionStarted>()
265 {
266 Some(session_started) => (
267 session_started.startup_resources_inserted,
268 session_started.startup_systems_executed,
269 ),
270 None => (false, false),
271 };
272
273 if !resources_inserted {
274 self.insert_startup_resources(world);
275 resources_inserted = true;
276
277 world.insert_resource(SessionStarted {
278 startup_systems_executed: systems_run,
279 startup_resources_inserted: resources_inserted,
280 });
281 }
282 }
283
284 pub fn handle_startup_systems(&mut self, world: &mut World) {
293 let (resources_inserted, mut systems_run) = match world.get_resource_mut::<SessionStarted>()
294 {
295 Some(session_started) => (
296 session_started.startup_resources_inserted,
297 session_started.startup_systems_executed,
298 ),
299 None => (false, false),
300 };
301
302 if !systems_run {
303 world.insert_resource(CurrentSystemStage(Ulid(0)));
305
306 for system in &mut self.startup_systems {
308 system.run(world, ());
310 }
311 systems_run = true;
312
313 world.resources.remove::<CurrentSystemStage>();
314 world.insert_resource(SessionStarted {
315 startup_systems_executed: systems_run,
316 startup_resources_inserted: resources_inserted,
317 });
318 }
319 }
320
321 fn has_single_success_system_succeeded(system_index: usize, world: &World) -> bool {
323 if let Some(system_success) = world.get_resource::<SingleSuccessSystems>() {
324 return system_success.has_system_succeeded(system_index);
325 }
326
327 false
328 }
329
330 fn mark_single_success_system_succeeded(system_index: usize, world: &mut World) {
332 if let Some(mut system_succes) = world.get_resource_mut::<SingleSuccessSystems>() {
333 system_succes.set_system_completed(system_index);
334 return;
335 }
336
337 world
339 .init_resource::<SingleSuccessSystems>()
340 .set_system_completed(system_index);
341 }
342
343 fn insert_startup_resources(&self, world: &mut World) {
347 for resource in self.startup_resources.resources().iter() {
348 let resource_copy = resource.clone_data().unwrap();
350 let resource_cell = world.resources.untyped().get_cell(resource.schema());
351 let prev_val = resource_cell.insert(resource_copy).unwrap();
352
353 if prev_val.is_some() {
355 let schema_name = resource.schema().full_name;
356 tracing::warn!("SystemStages` attempted to inserted resource {schema_name} on startup that already exists in world - startup resource not inserted.
357 When building new session, startup resources should be initialized on `SessionBuilder`.");
358 }
359 }
360 }
361}
362
363pub trait SystemStage: Sync + Send {
365 fn id(&self) -> Ulid;
367 fn name(&self) -> String;
369 fn run(&mut self, world: &World);
371
372 fn add_system(&mut self, system: StaticSystem<(), ()>);
374 fn remove_all_systems(&mut self);
376}
377
378pub struct SimpleSystemStage {
380 pub id: Ulid,
382 pub name: String,
384 pub systems: Vec<StaticSystem<(), ()>>,
388}
389
390impl SimpleSystemStage {
391 pub fn new<L: StageLabel>(label: L) -> Self {
393 Self {
394 id: label.id(),
395 name: label.name(),
396 systems: Default::default(),
397 }
398 }
399}
400
401impl SystemStage for SimpleSystemStage {
402 fn id(&self) -> Ulid {
403 self.id
404 }
405
406 fn name(&self) -> String {
407 self.name.clone()
408 }
409
410 fn run(&mut self, world: &World) {
411 for system in &mut self.systems {
413 system.run(world, ());
414 }
415
416 let queue = world.resources.get_mut::<CommandQueue>();
418 if let Some(mut command_queue) = queue {
419 for mut system in command_queue.queue.drain(..) {
420 system.run(world, ());
421 }
422 }
423 }
424
425 fn add_system(&mut self, system: StaticSystem<(), ()>) {
426 self.systems.push(system);
427 }
428
429 fn remove_all_systems(&mut self) {
430 self.systems.clear();
431 }
432}
433
434pub trait StageLabel {
436 fn name(&self) -> String;
438 fn id(&self) -> Ulid;
440}
441
442#[derive(Copy, Clone, Debug, PartialEq, Eq)]
444pub enum CoreStage {
445 First,
447 PreUpdate,
449 Update,
451 PostUpdate,
453 Last,
455}
456
457impl StageLabel for CoreStage {
458 fn name(&self) -> String {
459 format!("{:?}", self)
460 }
461
462 fn id(&self) -> Ulid {
463 match self {
464 CoreStage::First => Ulid(2021715391084198804812356024998495966),
465 CoreStage::PreUpdate => Ulid(2021715401330719559452824437611089988),
466 CoreStage::Update => Ulid(2021715410160177201728645950400543948),
467 CoreStage::PostUpdate => Ulid(2021715423103233646561968734173322317),
468 CoreStage::Last => Ulid(2021715433398666914977687392909851554),
469 }
470 }
471}
472
473#[derive(HasSchema, Default)]
477pub struct CommandQueue {
478 pub queue: VecDeque<StaticSystem<(), ()>>,
480}
481
482impl Clone for CommandQueue {
483 fn clone(&self) -> Self {
484 if self.queue.is_empty() {
485 Self {
486 queue: VecDeque::with_capacity(self.queue.capacity()),
487 }
488 } else {
489 panic!(
490 "Cannot clone CommandQueue. This probably happened because you are \
491 trying to clone a World while a system stage is still executing."
492 )
493 }
494 }
495}
496
497impl CommandQueue {
498 pub fn add<Args, S>(&mut self, system: S)
500 where
501 S: IntoSystem<Args, (), (), Sys = StaticSystem<(), ()>>,
502 {
503 self.queue.push_back(system.system());
504 }
505}
506
507#[derive(Deref, DerefMut)]
512pub struct Commands<'a>(RefMut<'a, CommandQueue>);
513
514impl<'a> SystemParam for Commands<'a> {
515 type State = AtomicResource<CommandQueue>;
516 type Param<'s> = Commands<'s>;
517
518 fn get_state(world: &World) -> Self::State {
519 let cell = world.resources.get_cell::<CommandQueue>();
520 cell.init(world);
521 cell
522 }
523
524 fn borrow<'s>(_world: &'s World, state: &'s mut Self::State) -> Self::Param<'s> {
525 Commands(state.borrow_mut().unwrap())
526 }
527}
528
529#[derive(Copy, Clone, HasSchema, Default)]
533pub struct SessionStarted {
534 pub startup_systems_executed: bool,
536
537 pub startup_resources_inserted: bool,
539}
540
541#[derive(HasSchema, Clone, Default)]
544pub struct SingleSuccessSystems {
545 pub systems_succeeded: HashSet<usize>,
547}
548
549impl SingleSuccessSystems {
550 #[allow(dead_code)]
552 pub fn reset(&mut self) {
553 self.systems_succeeded.clear();
554 }
555
556 pub fn has_system_succeeded(&self, index: usize) -> bool {
558 self.systems_succeeded.contains(&index)
559 }
560
561 pub fn set_system_completed(&mut self, index: usize) {
563 self.systems_succeeded.insert(index);
564 }
565}