1use crate::function_system::{IntoSystem, System};
4use crate::world::World;
5
6struct SystemEntry {
7 stage: &'static str,
8 system: Box<dyn System>,
9}
10
11pub struct Schedule {
19 systems: Vec<SystemEntry>,
20 stage_order: Vec<&'static str>,
21}
22
23impl Schedule {
24 pub fn new() -> Self {
25 Self {
26 systems: Vec::new(),
27 stage_order: Vec::new(),
28 }
29 }
30
31 pub fn add_system<P>(
36 &mut self,
37 stage: &'static str,
38 name: &'static str,
39 func: impl IntoSystem<P>,
40 ) -> &mut Self {
41 if !self.stage_order.contains(&stage) {
42 self.stage_order.push(stage);
43 }
44 self.systems.push(SystemEntry {
45 stage,
46 system: func.into_system(name),
47 });
48 self
49 }
50
51 pub fn run(&mut self, world: &mut World) {
60 world.drain_all_deadlines();
62
63 world.flush_render_events();
67
68 world.update_events();
72
73 for stage_idx in 0..self.stage_order.len() {
74 let stage = self.stage_order[stage_idx];
75 for entry in &mut self.systems {
76 if entry.stage == stage {
77 entry.system.run(world);
78 }
79 }
80 world.apply_commands();
81 }
82
83 world.flush_render_events();
85 }
86
87 pub fn system_count(&self) -> usize {
89 self.systems.len()
90 }
91
92 pub fn stages(&self) -> &[&'static str] {
94 &self.stage_order
95 }
96}
97
98impl Default for Schedule {
99 fn default() -> Self {
100 Self::new()
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107 use crate::component::Component;
108 use crate::system_param::{QueryMut, Res, ResMut};
109
110 #[derive(Debug)]
111 struct Counter(u32);
112 impl Component for Counter {}
113
114 fn increment_system(mut counters: QueryMut<'_, Counter>) {
115 for (_, counter) in counters.iter_mut() {
116 counter.0 += 1;
117 }
118 }
119
120 fn double_system(mut counters: QueryMut<'_, Counter>) {
121 for (_, counter) in counters.iter_mut() {
122 counter.0 *= 2;
123 }
124 }
125
126 #[test]
127 fn schedule_runs_systems_in_stage_order() {
128 let mut world = World::new();
129 world.spawn((Counter(1),));
130
131 let mut schedule = Schedule::new();
132 schedule.add_system::<(QueryMut<'_, Counter>,)>("simulate", "increment", increment_system);
133 schedule.add_system::<(QueryMut<'_, Counter>,)>("post", "double", double_system);
134
135 schedule.run(&mut world);
136
137 let val: Vec<u32> = world.query::<&Counter>().map(|(_, c)| c.0).collect();
139 assert_eq!(val, vec![4]);
140 }
141
142 #[test]
143 fn schedule_systems_within_stage_run_in_order() {
144 let mut world = World::new();
145 world.spawn((Counter(1),));
146
147 let mut schedule = Schedule::new();
148 schedule.add_system::<(QueryMut<'_, Counter>,)>("simulate", "increment", increment_system);
149 schedule.add_system::<(QueryMut<'_, Counter>,)>("simulate", "double", double_system);
150
151 schedule.run(&mut world);
152
153 let val: Vec<u32> = world.query::<&Counter>().map(|(_, c)| c.0).collect();
155 assert_eq!(val, vec![4]);
156 }
157
158 #[test]
159 fn schedule_stage_order_matters() {
160 let mut world = World::new();
161 world.spawn((Counter(1),));
162
163 let mut schedule = Schedule::new();
164 schedule.add_system::<(QueryMut<'_, Counter>,)>("pre", "double", double_system);
165 schedule.add_system::<(QueryMut<'_, Counter>,)>("simulate", "increment", increment_system);
166
167 schedule.run(&mut world);
168
169 let val: Vec<u32> = world.query::<&Counter>().map(|(_, c)| c.0).collect();
171 assert_eq!(val, vec![3]);
172 }
173
174 #[test]
175 fn empty_schedule_is_safe() {
176 let mut world = World::new();
177 let mut schedule = Schedule::new();
178 schedule.run(&mut world); }
180
181 fn param_increment(mut counters: QueryMut<'_, Counter>) {
184 for (_, c) in counters.iter_mut() {
185 c.0 += 1;
186 }
187 }
188
189 #[test]
190 fn schedule_accepts_parameterized_system() {
191 let mut world = World::new();
192 world.spawn((Counter(0),));
193
194 let mut schedule = Schedule::new();
195 schedule.add_system::<(QueryMut<'_, Counter>,)>(
196 "update",
197 "param_increment",
198 param_increment,
199 );
200
201 schedule.run(&mut world);
202
203 let val: Vec<u32> = world.query::<&Counter>().map(|(_, c)| c.0).collect();
204 assert_eq!(val, vec![1]);
205 }
206
207 struct Speed(f32);
208
209 fn apply_speed(speed: Res<'_, Speed>, mut counters: QueryMut<'_, Counter>) {
210 for (_, c) in counters.iter_mut() {
211 c.0 += speed.0 as u32;
212 }
213 }
214
215 #[test]
216 fn schedule_multi_param_systems_across_stages() {
217 let mut world = World::new();
218 world.insert_resource(Speed(10.0));
219 world.spawn((Counter(0),));
220
221 let mut schedule = Schedule::new();
222 schedule.add_system::<(QueryMut<'_, Counter>,)>("pre", "increment", increment_system);
223 schedule.add_system::<(Res<'_, Speed>, QueryMut<'_, Counter>)>(
224 "post",
225 "apply_speed",
226 apply_speed,
227 );
228
229 schedule.run(&mut world);
230
231 let val: Vec<u32> = world.query::<&Counter>().map(|(_, c)| c.0).collect();
233 assert_eq!(val, vec![11]);
234 }
235
236 fn increment_speed(mut speed: ResMut<'_, Speed>) {
237 speed.0 += 1.0;
238 }
239
240 #[test]
241 fn schedule_res_mut_persists_across_runs() {
242 let mut world = World::new();
243 world.insert_resource(Speed(0.0));
244
245 let mut schedule = Schedule::new();
246 schedule.add_system::<(ResMut<'_, Speed>,)>("update", "inc_speed", increment_speed);
247
248 schedule.run(&mut world);
249 schedule.run(&mut world);
250
251 assert!((world.resource::<Speed>().0 - 2.0).abs() < f32::EPSILON);
252 }
253
254 use crate::commands::Commands;
257
258 fn spawn_via_commands(mut cmds: Commands<'_>) {
259 cmds.spawn((Counter(100),));
260 }
261
262 #[test]
263 fn schedule_applies_commands_between_stages() {
264 let mut world = World::new();
265
266 let mut schedule = Schedule::new();
269 schedule.add_system::<(Commands<'_>,)>("spawn", "spawner", spawn_via_commands);
270 schedule.add_system::<(QueryMut<'_, Counter>,)>("read", "increment", increment_system);
271
272 schedule.run(&mut world);
273
274 let vals: Vec<u32> = world.query::<&Counter>().map(|(_, c)| c.0).collect();
276 assert_eq!(vals, vec![101]);
277 }
278
279 fn despawn_all_via_commands(
280 counters: crate::system_param::Query<'_, Counter>,
281 mut cmds: Commands<'_>,
282 ) {
283 for (entity, _) in counters.iter() {
284 cmds.despawn(entity);
285 }
286 }
287
288 #[test]
289 fn schedule_commands_despawn_visible_to_next_stage() {
290 let mut world = World::new();
291 world.spawn((Counter(1),));
292 world.spawn((Counter(2),));
293
294 let mut schedule = Schedule::new();
295 schedule.add_system::<(crate::system_param::Query<'_, Counter>, Commands<'_>)>(
296 "cleanup",
297 "despawn_all",
298 despawn_all_via_commands,
299 );
300 schedule.add_system::<(QueryMut<'_, Counter>,)>("post", "increment", increment_system);
301
302 schedule.run(&mut world);
303
304 assert_eq!(world.entity_count(), 0);
306 }
307
308 use crate::event::{EventReader, EventWriter};
313
314 #[derive(Debug, PartialEq)]
315 struct ScoreEvent {
316 points: u32,
317 }
318
319 fn produce_event(mut writer: EventWriter<'_, ScoreEvent>) {
320 writer.send(ScoreEvent { points: 10 });
321 }
322
323 fn consume_event(reader: EventReader<'_, ScoreEvent>, mut counters: QueryMut<'_, Counter>) {
324 let total: u32 = reader.read().map(|e| e.points).sum();
325 for (_, counter) in counters.iter_mut() {
326 counter.0 += total;
327 }
328 }
329
330 #[test]
331 fn schedule_event_writer_reader_cross_tick() {
332 let mut world = World::new();
333 world.add_event::<ScoreEvent>();
334 world.spawn((Counter(0),));
335
336 let mut schedule = Schedule::new();
337 schedule.add_system::<(EventWriter<'_, ScoreEvent>,)>("produce", "produce", produce_event);
339 schedule.add_system::<(EventReader<'_, ScoreEvent>, QueryMut<'_, Counter>)>(
341 "consume",
342 "consume",
343 consume_event,
344 );
345
346 schedule.run(&mut world);
349
350 let val: Vec<u32> = world.query::<&Counter>().map(|(_, c)| c.0).collect();
352 assert_eq!(val, vec![0]);
353
354 schedule.run(&mut world);
357
358 let val: Vec<u32> = world.query::<&Counter>().map(|(_, c)| c.0).collect();
359 assert_eq!(val, vec![10]);
360 }
361
362 #[test]
363 fn schedule_events_cleared_after_two_ticks() {
364 let mut world = World::new();
365 world.add_event::<ScoreEvent>();
366 world.spawn((Counter(0),));
367
368 let mut schedule = Schedule::new();
369 schedule.add_system::<(EventWriter<'_, ScoreEvent>,)>("produce", "produce", produce_event);
370 schedule.add_system::<(EventReader<'_, ScoreEvent>, QueryMut<'_, Counter>)>(
371 "consume",
372 "consume",
373 consume_event,
374 );
375
376 schedule.run(&mut world);
378 schedule.run(&mut world);
380 schedule.run(&mut world);
383
384 let val: Vec<u32> = world.query::<&Counter>().map(|(_, c)| c.0).collect();
386 assert_eq!(val, vec![20]);
387 }
388
389 use crate::render_event::{RenderEvent, RenderEventRegistry};
394
395 #[derive(Debug)]
396 struct ImpactRenderEvent {
397 entity_index: u32,
398 }
399
400 impl RenderEvent for ImpactRenderEvent {
401 const KIND: u32 = 1;
402 fn entity(&self) -> u32 {
403 self.entity_index
404 }
405 fn position(&self) -> [f32; 3] {
406 [0.0; 3]
407 }
408 }
409
410 #[test]
411 fn schedule_flush_captures_system_written_render_events() {
412 let mut world = World::new();
413 world.add_event::<ImpactRenderEvent>();
414
415 let mut registry = RenderEventRegistry::new();
416 registry.register::<ImpactRenderEvent>();
417 world.insert_resource(registry);
418
419 fn emit_impact(mut writer: EventWriter<'_, ImpactRenderEvent>) {
420 writer.send(ImpactRenderEvent { entity_index: 42 });
421 }
422
423 let mut schedule = Schedule::new();
424 schedule.add_system::<(EventWriter<'_, ImpactRenderEvent>,)>("sim", "emit", emit_impact);
425
426 schedule.run(&mut world);
427
428 let events = world.resource::<RenderEventRegistry>().drain();
430 assert_eq!(events.len(), 1);
431 assert_eq!(events[0].entity, 42);
432 }
433
434 #[test]
435 fn schedule_flush_captures_deadline_fired_render_events() {
436 use crate::deadline::{Clock, Deadlines, TestClock, Timestamp};
437
438 let mut world = World::new();
439 world.add_event::<ImpactRenderEvent>();
440 world.add_deadline_type::<ImpactRenderEvent>();
441
442 world
444 .insert_resource(Box::new(TestClock::new(Timestamp::from_micros(0))) as Box<dyn Clock>);
445
446 let mut registry = RenderEventRegistry::new();
447 registry.register::<ImpactRenderEvent>();
448 world.insert_resource(registry);
449
450 world
452 .resource_mut::<Deadlines<ImpactRenderEvent>>()
453 .schedule(
454 Timestamp::from_micros(0),
455 ImpactRenderEvent { entity_index: 99 },
456 );
457
458 let mut schedule = Schedule::new();
459 schedule.run(&mut world);
460
461 let events = world.resource::<RenderEventRegistry>().drain();
463 assert_eq!(events.len(), 1);
464 assert_eq!(events[0].entity, 99);
465 }
466
467 #[test]
468 fn multi_tick_render_events_accumulate_across_schedule_runs() {
469 let mut world = World::new();
470 world.add_event::<ImpactRenderEvent>();
471
472 let mut registry = RenderEventRegistry::new();
473 registry.register::<ImpactRenderEvent>();
474 world.insert_resource(registry);
475
476 fn emit_impact(mut writer: EventWriter<'_, ImpactRenderEvent>) {
477 writer.send(ImpactRenderEvent { entity_index: 1 });
478 }
479
480 let mut schedule = Schedule::new();
481 schedule.add_system::<(EventWriter<'_, ImpactRenderEvent>,)>("sim", "emit", emit_impact);
482
483 schedule.run(&mut world);
485 schedule.run(&mut world);
486
487 let events = world.resource::<RenderEventRegistry>().drain();
488 assert_eq!(events.len(), 2);
489 }
490}