magma_app/
lib.rs

1/*!
2This crate provides basic functionality for creating and running an [`App`].
3A [`Module`] trait is also provided for implementing additional functionality.
4*/
5use std::{
6    any::{Any, TypeId},
7    collections::HashMap,
8};
9
10use magma_ecs::{
11    error::EventError,
12    rayon::iter::{IntoParallelRefIterator, ParallelIterator},
13    systems::{Systems, dispatcher::Dispatcher},
14};
15use module::Module;
16
17pub use magma_ecs;
18pub use magma_ecs::{World, entities, rayon, resources, systems};
19pub use schedule::AppSchedule;
20
21use crate::{
22    error::ScheduleError,
23    schedule::{PostUpdate, PreUpdate, Startup, Update},
24};
25
26pub mod error;
27/// Support for adding [`Module`]s
28pub mod module;
29/// The [`AppSchedule`] trait and default schedules.
30pub mod schedule;
31
32type SystemSlice = &'static [(fn(&World), &'static str, &'static [&'static str])];
33
34/// The [`App`] struct holds all the apps data and defines the necessary functions and methods to operate on it.
35pub struct App {
36    pub world: World,
37    runner: fn(App),
38    modules: Vec<TypeId>,
39    systems: HashMap<TypeId, (Systems, Dispatcher)>,
40    event_systems: HashMap<TypeId, (Systems, Dispatcher)>,
41}
42
43impl Default for App {
44    fn default() -> Self {
45        let mut app = Self {
46            world: Default::default(),
47            runner: default_runner,
48            modules: vec![],
49            systems: Default::default(),
50            event_systems: Default::default(),
51        };
52
53        app.register_schedule::<Startup>();
54        app.register_schedule::<PreUpdate>();
55        app.register_schedule::<Update>();
56        app.register_schedule::<PostUpdate>();
57
58        app
59    }
60}
61
62impl App {
63    /// Create a new [`App`]
64    pub fn new() -> Self {
65        Self::default()
66    }
67
68    /**
69    Add a [`Module`] to the [`App`]. If it already exists, nothing happens.
70
71    # Example
72
73    ```
74    use magma_app::{module::Module, App};
75
76    let mut app = App::new();
77    app.add_module(ExampleModule);
78
79    struct ExampleModule;
80
81    impl Module for ExampleModule {
82        fn setup(self, app: &mut App) {
83            // Setup the module
84            // E.g. register components to the World or add resources
85        }
86    }
87    ```
88    */
89    pub fn add_module(&mut self, module: impl Module + 'static) {
90        let type_id = module.type_id();
91        if !self.modules.contains(&type_id) {
92            self.modules.push(type_id);
93            module.setup(self);
94        }
95    }
96
97    /// Register an [`AppSchedule`].
98    pub fn register_schedule<S: AppSchedule + 'static>(&mut self) {
99        self.systems.insert(TypeId::of::<S>(), Default::default());
100    }
101
102    /// Run an [`AppSchedule`].
103    pub fn run_schedule<S: AppSchedule + 'static>(&self) -> Result<(), ScheduleError> {
104        self.systems
105            .get(&TypeId::of::<S>())
106            .ok_or(ScheduleError::ScheduleNotRegistered)?
107            .1
108            .dispatch(&self.world);
109        Ok(())
110    }
111
112    /**
113    Add systems to the [`App`]'s [`World`]. Systems must take an immutable reference to [`World`].
114
115    # Errors
116
117    Returns an error, when the schedule isn't registered.
118
119    # Example
120
121    ```
122    use magma_app::{App, World};
123    use magma_app::schedule::Startup;
124
125    let mut app = App::new();
126    app.add_systems::<Startup>(&[(example_system, "example_system", &[])]).unwrap();
127
128    fn example_system(_world: &World) {
129        // E.g. change something in the World
130    }
131    ```
132    */
133    pub fn add_systems<S: AppSchedule + 'static>(
134        &mut self,
135        systems: SystemSlice,
136    ) -> Result<(), ScheduleError> {
137        let schedule = self
138            .systems
139            .get_mut(&TypeId::of::<S>())
140            .ok_or(ScheduleError::ScheduleNotRegistered)?;
141        for (run, name, deps) in systems {
142            schedule.0.add(*run, name, deps);
143        }
144
145        schedule.1 = schedule.0.to_owned().build_dispatcher();
146        Ok(())
147    }
148
149    pub fn register_event<E: Any + Send + Sync + Clone>(&mut self) {
150        self.world.register_event::<E>();
151        self.event_systems
152            .insert(TypeId::of::<E>(), (Systems::new(), Dispatcher::default()));
153    }
154
155    pub fn add_event_systems<E: Any + Send + Sync + Clone>(
156        &mut self,
157        systems: SystemSlice,
158    ) -> Result<(), EventError> {
159        let event_systems = self
160            .event_systems
161            .get_mut(&TypeId::of::<E>())
162            .ok_or(EventError::EventNotRegistered)?;
163        for (run, name, deps) in systems {
164            event_systems.0.add(*run, name, deps);
165        }
166        event_systems.1 = event_systems.0.to_owned().build_dispatcher();
167        Ok(())
168    }
169
170    /// Set the runner of the [`App`]
171    pub fn set_runner(&mut self, runner: fn(App)) {
172        self.runner = runner;
173    }
174
175    /// Process pending events.
176    pub fn process_events(&self) {
177        let events = self.world.get_pending_events();
178        // dispatch systems for events
179        events.par_iter().for_each(|type_id| {
180            self.event_systems
181                .get(type_id)
182                .unwrap()
183                .1
184                .dispatch(&self.world);
185        });
186
187        self.world.clear_events();
188    }
189
190    /// Run the Application
191    pub fn run(self) {
192        (self.runner)(self);
193    }
194}
195
196fn default_runner(app: App) {
197    app.run_schedule::<Startup>().unwrap();
198    loop {
199        app.run_schedule::<PreUpdate>().unwrap();
200        app.run_schedule::<Update>().unwrap();
201        app.run_schedule::<PostUpdate>().unwrap();
202        app.process_events();
203    }
204}