Skip to main content

xuko_ecs/
schedule.rs

1use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
2
3use super::World;
4use std::sync::{Arc, RwLock};
5
6/// A system schedule for the [`Scheduler`]
7#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9pub struct Schedule(usize);
10
11impl From<usize> for Schedule {
12    fn from(value: usize) -> Self {
13        Self(value)
14    }
15}
16
17/// Pre-defined schedule keys
18pub mod schedules {
19    use super::Schedule;
20
21    /// Schedule that can be used for the initialization of a game
22    pub const INIT: Schedule = Schedule(0);
23
24    /// Schedule that can be used for the main loop of a game
25    pub const UPDATE: Schedule = Schedule(1);
26
27    /// Schedule that can be used for the deinitialization of a game
28    pub const EXIT: Schedule = Schedule(2);
29
30    /// Schedule that can be used for the render step of a game
31    pub const RENDER: Schedule = Schedule(3);
32
33    /// Schedule that can be used after the render step of a game
34    pub const POST_RENDER: Schedule = Schedule(4);
35}
36
37/// System trait
38pub trait System: Send + Sync + 'static {
39    /// Whether this [`System`] takes a mutable reference to the [`World`] or not
40    fn is_mut(&self) -> bool;
41
42    /// Apply the system on a reference to [`World`]
43    fn apply(&self, world: &World) {
44        let _ = world;
45    }
46
47    /// Apply the system on a mutable reference to [`World`]
48    fn apply_mut(&self, world: &mut World) {
49        let _ = world;
50    }
51}
52
53/// System that takes an immutable reference to the [`World`]
54pub type FnSystem = fn(&World);
55
56/// System that takes an mutable reference to the [`World`]
57pub type FnMutSystem = fn(&mut World);
58
59impl System for FnSystem {
60    fn is_mut(&self) -> bool {
61        false
62    }
63
64    fn apply(&self, world: &World) {
65        self(world);
66    }
67}
68
69impl System for FnMutSystem {
70    fn is_mut(&self) -> bool {
71        true
72    }
73
74    fn apply_mut(&self, world: &mut World) {
75        self(world);
76    }
77}
78
79type SchedulerWorld = Arc<RwLock<World>>;
80
81/// The [`Scheduler`]
82///
83/// Defines and runs systems and applies them to a [`World`]
84///
85/// # Examples
86///
87/// ```
88/// use xuko_ecs::{World, schedule::{Scheduler, FnMutSystem, schedules}};
89///
90/// let mut scheduler = Scheduler::new(World::new());
91///
92/// struct NumberName(String);
93///
94/// fn add_numbers(world: &mut World) {
95///     let numbers = [
96///         (NumberName("PI".to_string()), 3.14159f32),
97///         (NumberName("Nine".to_string()), 9f32),
98///     ];
99///
100///     for (name, value) in numbers {
101///         let entity = world.spawn();
102///         let entity = world.get_entity_mut(entity).unwrap();
103///
104///         entity.add(name);
105///         entity.add(value);
106///     }
107/// }
108///
109/// scheduler.add_system(schedules::INIT, add_numbers as FnMutSystem);
110/// scheduler.run_schedule(schedules::INIT);
111/// ```
112pub struct Scheduler {
113    world: SchedulerWorld,
114    schedules: indexmap::IndexMap<Schedule, Vec<Box<dyn System>>>,
115}
116
117impl Scheduler {
118    /// Create a new [Scheduler]
119    pub fn new(world: World) -> Self {
120        Self {
121            world: Arc::new(RwLock::new(world)),
122            schedules: Default::default(),
123        }
124    }
125
126    /// Add a system to schedule `schedule`
127    pub fn add_system(&mut self, schedule: Schedule, system: impl System) {
128        // TODO: add IDs to systems
129        self.schedules
130            .entry(schedule)
131            .or_default()
132            .push(Box::new(system));
133    }
134
135    /// Run schedule  `schedule`
136    pub fn run_schedule(&self, schedule: Schedule) {
137        let world = Arc::clone(&self.world);
138
139        if let Some(systems) = self.schedules.get(&schedule) {
140            systems.par_iter().for_each(|s| {
141                let rc = world.clone();
142                if s.is_mut() {
143                    let mut lock = rc.write().unwrap();
144                    s.apply_mut(&mut lock);
145                } else {
146                    let lock = rc.read().unwrap();
147                    s.apply(&lock);
148                }
149            });
150        }
151    }
152}