use std::any::TypeId;
use crate::ecs::{
commands::{ResourceCommandQueue, TriggerQueue},
resources::Resources,
system_param::{IntoSystem, System, SystemChain, SystemConfig},
};
#[derive(Default)]
pub struct Schedule {
systems: Vec<(TypeId, Box<dyn System>, i32)>,
constraints: Vec<(TypeId, TypeId)>,
order: Vec<usize>,
order_dirty: bool,
}
impl Schedule {
pub fn add_system<S, Params>(&mut self, system: impl Into<SystemConfig<S, Params>>) -> &mut Self
where
Params: 'static,
S: IntoSystem<Params> + 'static,
{
let (id, system, priority, constraints) = system.into().into_parts();
self.systems.push((id, system, priority));
self.constraints.extend(constraints);
self.order_dirty = true;
self
}
pub fn add_systems(&mut self, chain: SystemChain) -> &mut Self {
let (systems, constraints) = chain.into_parts();
self.systems.extend(systems);
self.constraints.extend(constraints);
self.order_dirty = true;
self
}
fn compute_order(&self) -> Vec<usize> {
let n = self.systems.len();
let index_of = |id: TypeId| self.systems.iter().position(|(sid, _, _)| *sid == id);
let mut in_degree = vec![0usize; n];
let mut dependents: Vec<Vec<usize>> = vec![Vec::new(); n];
for &(dependent_id, dependency_id) in &self.constraints {
if let (Some(dependent), Some(dependency)) =
(index_of(dependent_id), index_of(dependency_id))
&& dependent != dependency
{
dependents[dependency].push(dependent);
in_degree[dependent] += 1;
}
}
let mut remaining: Vec<usize> = (0..n).collect();
let mut order = Vec::with_capacity(n);
while !remaining.is_empty() {
let mut best: Option<(usize, i32)> = None;
for (pos, &i) in remaining.iter().enumerate() {
if in_degree[i] != 0 {
continue;
}
let priority = self.systems[i].2;
if best.is_none_or(|(_, best_priority)| priority > best_priority) {
best = Some((pos, priority));
}
}
let ready = best.map(|(pos, _)| pos).expect("system ordering constraints form a cycle");
let picked = remaining.remove(ready);
order.push(picked);
for &dependent in &dependents[picked] {
in_degree[dependent] -= 1;
}
}
order
}
pub fn run(&mut self, world: &mut hecs::World, resources: &mut Resources) {
if self.order_dirty {
self.order = self.compute_order();
self.order_dirty = false;
}
for &index in &self.order {
self.systems[index].1.run(world, &*resources);
Self::sync_commands(world, resources);
}
}
fn sync_commands(world: &mut hecs::World, resources: &mut Resources) {
resources.get_mut::<hecs::CommandBuffer>().run_on(world);
if resources.contains::<ResourceCommandQueue>() {
let commands = std::mem::take(&mut resources.get_mut::<ResourceCommandQueue>().0);
for command in commands {
command(resources);
}
}
if resources.contains::<TriggerQueue>() {
let triggers = std::mem::take(&mut resources.get_mut::<TriggerQueue>().0);
for trigger in triggers {
trigger(world, resources);
}
}
}
}