Skip to main content

nya_ecs/
system.rs

1//! Query functions, "Systems"
2
3use std::marker::PhantomData;
4
5use crate::{
6    World,
7    query::{Query, Queryable, ToFilter},
8};
9
10use indexmap::IndexMap;
11use nya_core::sparse_set::SparseSet;
12
13/// System trait
14pub trait System: 'static {
15    /// Run the system on `world`
16    fn run(&self, world: &mut World);
17}
18
19/// "Dyanmic" System trait
20pub struct QuerySystem<F: ToFilter, C: Fn(Query, &mut World) = fn(Query, &mut World)> {
21    callback: C,
22    _marker: PhantomData<F>,
23}
24
25impl<F: ToFilter, C: Fn(Query, &mut World)> QuerySystem<F, C> {
26    /// Create a new [QuerySystem]
27    pub const fn new(callback: C) -> Self {
28        Self {
29            callback,
30            _marker: PhantomData,
31        }
32    }
33}
34
35impl<F: ToFilter, C: Fn(Query, &mut World)> System for QuerySystem<F, C>
36where
37    Self: 'static,
38{
39    fn run(&self, world: &mut World) {
40        let query = world.query::<F>();
41        (self.callback)(query, world);
42    }
43}
44
45/// Manages systems for a [World] in multiple runlevels
46pub struct SystemManager {
47    /// The World
48    pub world: World,
49    runlevels: IndexMap<usize, SparseSet<Box<dyn System>>>,
50}
51
52impl SystemManager {
53    /// Create new [SystemManager]
54    pub fn new(world: World) -> Self {
55        SystemManager {
56            world,
57            runlevels: IndexMap::with_capacity(2),
58        }
59    }
60
61    /// Return whether runlevel `level` exists
62    pub fn has_runlevel(&self, level: usize) -> bool {
63        self.runlevels.contains_key(&level)
64    }
65
66    /// Add a [System] to runlevel `level`, return the system id
67    pub fn add_system<S: System>(&mut self, level: usize, system: S) -> usize {
68        self.runlevels
69            .entry(level)
70            .or_default()
71            .insert(Box::new(system))
72    }
73
74    /// Remove a [System] from runlevel `level`
75    pub fn remove_system<S: System>(&mut self, level: usize, system: usize) {
76        self.runlevels.entry(level).or_default().remove(system);
77    }
78
79    /// Run systems of in runlevel `level`
80    pub fn run_systems(&mut self, level: usize) {
81        if let Some(systems) = self.runlevels.get(&level) {
82            for s in systems {
83                s.value.run(&mut self.world);
84            }
85        }
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use crate::{
92        Exclude, World, component,
93        system::{QuerySystem, SystemManager},
94    };
95
96    #[allow(dead_code)]
97    struct Tag(i32);
98    component!(Tag);
99
100    struct Hello;
101    component!(Hello);
102
103    #[test]
104    fn basic_query() {
105        let mut manager = SystemManager::new(World::new());
106
107        for i in 0..10 {
108            let e = manager.world.spawn();
109            manager.world.add(e, Tag(32));
110
111            if i % 2 == 0 {
112                manager.world.add(e, Hello);
113            }
114        }
115
116        manager.add_system(
117            0,
118            QuerySystem::<(Tag, Exclude<(Hello,)>)>::new(|query, world| {
119                let _ = world;
120                println!("{:?}", query.entities());
121            }),
122        );
123
124        manager.run_systems(0);
125        manager.run_systems(1); // runlevel does not exist
126    }
127}