use crate::function_system::{IntoSystem, System};
use crate::world::World;
struct SystemEntry {
stage: &'static str,
system: Box<dyn System>,
}
pub struct Schedule {
systems: Vec<SystemEntry>,
stage_order: Vec<&'static str>,
}
impl Schedule {
pub fn new() -> Self {
Self {
systems: Vec::new(),
stage_order: Vec::new(),
}
}
pub fn add_system<P>(
&mut self,
stage: &'static str,
name: &'static str,
func: impl IntoSystem<P>,
) -> &mut Self {
if !self.stage_order.contains(&stage) {
self.stage_order.push(stage);
}
self.systems.push(SystemEntry {
stage,
system: func.into_system(name),
});
self
}
pub fn run(&mut self, world: &mut World) {
world.drain_all_deadlines();
world.update_events();
for stage_idx in 0..self.stage_order.len() {
let stage = self.stage_order[stage_idx];
for entry in &mut self.systems {
if entry.stage == stage {
entry.system.run(world);
}
}
world.apply_commands();
}
}
pub fn system_count(&self) -> usize {
self.systems.len()
}
pub fn stages(&self) -> &[&'static str] {
&self.stage_order
}
}
impl Default for Schedule {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::component::Component;
use crate::system_param::{QueryMut, Res, ResMut};
#[derive(Debug)]
struct Counter(u32);
impl Component for Counter {}
fn increment_system(mut counters: QueryMut<'_, Counter>) {
for (_, counter) in counters.iter_mut() {
counter.0 += 1;
}
}
fn double_system(mut counters: QueryMut<'_, Counter>) {
for (_, counter) in counters.iter_mut() {
counter.0 *= 2;
}
}
#[test]
fn schedule_runs_systems_in_stage_order() {
let mut world = World::new();
world.spawn((Counter(1),));
let mut schedule = Schedule::new();
schedule.add_system::<(QueryMut<'_, Counter>,)>("simulate", "increment", increment_system);
schedule.add_system::<(QueryMut<'_, Counter>,)>("post", "double", double_system);
schedule.run(&mut world);
let val: Vec<u32> = world.query::<&Counter>().map(|(_, c)| c.0).collect();
assert_eq!(val, vec![4]);
}
#[test]
fn schedule_systems_within_stage_run_in_order() {
let mut world = World::new();
world.spawn((Counter(1),));
let mut schedule = Schedule::new();
schedule.add_system::<(QueryMut<'_, Counter>,)>("simulate", "increment", increment_system);
schedule.add_system::<(QueryMut<'_, Counter>,)>("simulate", "double", double_system);
schedule.run(&mut world);
let val: Vec<u32> = world.query::<&Counter>().map(|(_, c)| c.0).collect();
assert_eq!(val, vec![4]);
}
#[test]
fn schedule_stage_order_matters() {
let mut world = World::new();
world.spawn((Counter(1),));
let mut schedule = Schedule::new();
schedule.add_system::<(QueryMut<'_, Counter>,)>("pre", "double", double_system);
schedule.add_system::<(QueryMut<'_, Counter>,)>("simulate", "increment", increment_system);
schedule.run(&mut world);
let val: Vec<u32> = world.query::<&Counter>().map(|(_, c)| c.0).collect();
assert_eq!(val, vec![3]);
}
#[test]
fn empty_schedule_is_safe() {
let mut world = World::new();
let mut schedule = Schedule::new();
schedule.run(&mut world); }
fn param_increment(mut counters: QueryMut<'_, Counter>) {
for (_, c) in counters.iter_mut() {
c.0 += 1;
}
}
#[test]
fn schedule_accepts_parameterized_system() {
let mut world = World::new();
world.spawn((Counter(0),));
let mut schedule = Schedule::new();
schedule.add_system::<(QueryMut<'_, Counter>,)>(
"update",
"param_increment",
param_increment,
);
schedule.run(&mut world);
let val: Vec<u32> = world.query::<&Counter>().map(|(_, c)| c.0).collect();
assert_eq!(val, vec![1]);
}
struct Speed(f32);
fn apply_speed(speed: Res<'_, Speed>, mut counters: QueryMut<'_, Counter>) {
for (_, c) in counters.iter_mut() {
c.0 += speed.0 as u32;
}
}
#[test]
fn schedule_multi_param_systems_across_stages() {
let mut world = World::new();
world.insert_resource(Speed(10.0));
world.spawn((Counter(0),));
let mut schedule = Schedule::new();
schedule.add_system::<(QueryMut<'_, Counter>,)>("pre", "increment", increment_system);
schedule.add_system::<(Res<'_, Speed>, QueryMut<'_, Counter>)>(
"post",
"apply_speed",
apply_speed,
);
schedule.run(&mut world);
let val: Vec<u32> = world.query::<&Counter>().map(|(_, c)| c.0).collect();
assert_eq!(val, vec![11]);
}
fn increment_speed(mut speed: ResMut<'_, Speed>) {
speed.0 += 1.0;
}
#[test]
fn schedule_res_mut_persists_across_runs() {
let mut world = World::new();
world.insert_resource(Speed(0.0));
let mut schedule = Schedule::new();
schedule.add_system::<(ResMut<'_, Speed>,)>("update", "inc_speed", increment_speed);
schedule.run(&mut world);
schedule.run(&mut world);
assert!((world.resource::<Speed>().0 - 2.0).abs() < f32::EPSILON);
}
use crate::commands::Commands;
fn spawn_via_commands(mut cmds: Commands<'_>) {
cmds.spawn((Counter(100),));
}
#[test]
fn schedule_applies_commands_between_stages() {
let mut world = World::new();
let mut schedule = Schedule::new();
schedule.add_system::<(Commands<'_>,)>("spawn", "spawner", spawn_via_commands);
schedule.add_system::<(QueryMut<'_, Counter>,)>("read", "increment", increment_system);
schedule.run(&mut world);
let vals: Vec<u32> = world.query::<&Counter>().map(|(_, c)| c.0).collect();
assert_eq!(vals, vec![101]);
}
fn despawn_all_via_commands(
counters: crate::system_param::Query<'_, Counter>,
mut cmds: Commands<'_>,
) {
for (entity, _) in counters.iter() {
cmds.despawn(entity);
}
}
#[test]
fn schedule_commands_despawn_visible_to_next_stage() {
let mut world = World::new();
world.spawn((Counter(1),));
world.spawn((Counter(2),));
let mut schedule = Schedule::new();
schedule.add_system::<(crate::system_param::Query<'_, Counter>, Commands<'_>)>(
"cleanup",
"despawn_all",
despawn_all_via_commands,
);
schedule.add_system::<(QueryMut<'_, Counter>,)>("post", "increment", increment_system);
schedule.run(&mut world);
assert_eq!(world.entity_count(), 0);
}
use crate::event::{EventReader, EventWriter};
#[derive(Debug, PartialEq)]
struct ScoreEvent {
points: u32,
}
fn produce_event(mut writer: EventWriter<'_, ScoreEvent>) {
writer.send(ScoreEvent { points: 10 });
}
fn consume_event(reader: EventReader<'_, ScoreEvent>, mut counters: QueryMut<'_, Counter>) {
let total: u32 = reader.read().map(|e| e.points).sum();
for (_, counter) in counters.iter_mut() {
counter.0 += total;
}
}
#[test]
fn schedule_event_writer_reader_cross_tick() {
let mut world = World::new();
world.add_event::<ScoreEvent>();
world.spawn((Counter(0),));
let mut schedule = Schedule::new();
schedule.add_system::<(EventWriter<'_, ScoreEvent>,)>("produce", "produce", produce_event);
schedule.add_system::<(EventReader<'_, ScoreEvent>, QueryMut<'_, Counter>)>(
"consume",
"consume",
consume_event,
);
schedule.run(&mut world);
let val: Vec<u32> = world.query::<&Counter>().map(|(_, c)| c.0).collect();
assert_eq!(val, vec![0]);
schedule.run(&mut world);
let val: Vec<u32> = world.query::<&Counter>().map(|(_, c)| c.0).collect();
assert_eq!(val, vec![10]);
}
#[test]
fn schedule_events_cleared_after_two_ticks() {
let mut world = World::new();
world.add_event::<ScoreEvent>();
world.spawn((Counter(0),));
let mut schedule = Schedule::new();
schedule.add_system::<(EventWriter<'_, ScoreEvent>,)>("produce", "produce", produce_event);
schedule.add_system::<(EventReader<'_, ScoreEvent>, QueryMut<'_, Counter>)>(
"consume",
"consume",
consume_event,
);
schedule.run(&mut world);
schedule.run(&mut world);
schedule.run(&mut world);
let val: Vec<u32> = world.query::<&Counter>().map(|(_, c)| c.0).collect();
assert_eq!(val, vec![20]);
}
}