1use crate::{
2 ecs::{
3 events::{AsyncEventChannel, Events, drain_async_events},
4 plugin::Plugin,
5 resources::Resources,
6 system::{IntoSystem, System},
7 system_set::IntoSystemSet,
8 },
9};
10use std::collections::{BTreeMap, BinaryHeap, HashMap};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
18pub enum SystemStage {
19 Startup,
23 AssetSync,
25 AssetSyncDeps,
28 PreUpdate,
30 Update,
32 PostUpdate,
34 PreRender,
36 Render,
38 PostRender,
40}
41
42const ALL_STAGES: [SystemStage; 9] = [
44 SystemStage::Startup,
45 SystemStage::AssetSync,
46 SystemStage::AssetSyncDeps,
47 SystemStage::PreUpdate,
48 SystemStage::Update,
49 SystemStage::PostUpdate,
50 SystemStage::PreRender,
51 SystemStage::Render,
52 SystemStage::PostRender,
53];
54
55pub type AppRunner = Box<dyn FnOnce(App)>;
60
61pub struct App {
71 pub(crate) world: hecs::World,
72 pub(crate) resources: Resources,
73 plugins: Vec<Box<dyn Plugin>>,
74 systems: BTreeMap<SystemStage, Vec<Box<dyn System>>>,
75 runner: Option<AppRunner>,
76 event_updaters: Vec<Box<dyn FnMut(&hecs::World, &Resources)>>,
81}
82
83impl Default for App {
84 fn default() -> Self {
85 Self::new()
86 }
87}
88
89impl App {
90 pub fn new() -> Self {
97 let mut world = hecs::World::default();
98 let mut resources = Resources::new(&mut world);
99 resources.insert_resource(&mut world, ());
100
101 let mut app = Self {
102 world: world,
103 resources: resources,
104 plugins: Vec::new(),
105 systems: BTreeMap::new(),
106 runner: Some(Box::new(|mut app| {
107 loop {
108 app.update();
109 }
110 })),
111 event_updaters: Vec::new(),
112 };
113 app.add_plugin(crate::time::TimePlugin);
114 app.add_plugin(crate::gamepad::GamepadPlugin);
115 app.add_plugin(crate::audio::AudioPlugin);
116 app
117 }
118
119 fn run_stage_once(&mut self, stage: SystemStage) {
121 if let Some(systems) = self.systems.get_mut(&stage) {
122 for system in systems.iter_mut() {
123 let _guard = crate::ecs::resources::set_current_system(system.name());
124 system.run(&self.world, &self.resources);
125 }
126 }
127 self.resources.get_command_buffer().run_on(&mut self.world);
128 }
129
130 pub fn add_plugin(&mut self, plugin: impl Plugin) -> &mut Self {
132 self.plugins.push(Box::new(plugin));
133 self
134 }
135
136 pub fn add_resource(&mut self, res: impl hecs::Component) -> &mut Self {
138 self.resources.insert_resource(&mut self.world, res);
139 self
140 }
141
142 pub fn get_resource<'a, T: hecs::Component>(&'a self) -> hecs::Ref<'a, T> {
144 self.resources.get_resource(&self.world)
145 }
146
147 pub fn get_resource_mut<'a, T: hecs::Component>(&'a self) -> hecs::RefMut<'a, T> {
149 self.resources.get_resource_mut(&self.world)
150 }
151
152 pub fn try_insert_resource<T: hecs::Component>(&mut self, res: T) -> bool {
156 self.resources.try_insert(&mut self.world, res)
157 }
158
159 pub fn add_event<T: hecs::Component>(&mut self) -> &mut Self {
163 self.try_insert_resource(Events::<T>::default());
164 self.event_updaters.push(Box::new(|world, resources| {
165 resources.get_resource_mut::<Events<T>>(world).update();
166 }));
167 self
168 }
169
170 pub fn add_async_event<T: hecs::Component>(&mut self) -> &mut Self {
174 self.add_event::<T>();
175 self.try_insert_resource(AsyncEventChannel::<T>::new());
176 self.add_system(SystemStage::PreUpdate, drain_async_events::<T>);
177 self
178 }
179
180 pub fn add_system<Marker>(
182 &mut self,
183 stage: SystemStage,
184 system: impl IntoSystem<Marker> + 'static,
185 ) -> &mut Self {
186 self.systems
187 .entry(stage)
188 .or_default()
189 .push(Box::new(system.into_system()));
190 self
191 }
192
193 pub fn add_systems<Marker>(
197 &mut self,
198 stage: SystemStage,
199 systems: impl IntoSystemSet<Marker>,
200 ) -> &mut Self {
201 let entry = self.systems.entry(stage).or_default();
202 entry.extend(systems.into_system_set());
203 self
204 }
205
206 fn sort_stage(stage: SystemStage, systems: &mut Vec<Box<dyn System>>) {
211 let id_index: HashMap<std::any::TypeId, usize> = systems
212 .iter()
213 .enumerate()
214 .map(|(i, s)| (s.ordering_id(), i))
215 .collect();
216
217 let n = systems.len();
218 let mut adjacency: Vec<Vec<usize>> = vec![Vec::new(); n];
219 let mut in_degree = vec![0usize; n];
220
221 for (i, system) in systems.iter().enumerate() {
222 for id in system.after_ids() {
223 if let Some(&dep) = id_index.get(id) {
224 adjacency[dep].push(i);
225 in_degree[i] += 1;
226 }
227 }
228 for id in system.before_ids() {
229 if let Some(&dependent) = id_index.get(id) {
230 adjacency[i].push(dependent);
231 in_degree[dependent] += 1;
232 }
233 }
234 }
235
236 let mut ready: BinaryHeap<std::cmp::Reverse<usize>> = in_degree
238 .iter()
239 .enumerate()
240 .filter(|(_, d)| **d == 0)
241 .map(|(i, _)| std::cmp::Reverse(i))
242 .collect();
243
244 let mut order = Vec::with_capacity(n);
245 while let Some(std::cmp::Reverse(u)) = ready.pop() {
246 order.push(u);
247 for &v in &adjacency[u] {
248 in_degree[v] -= 1;
249 if in_degree[v] == 0 {
250 ready.push(std::cmp::Reverse(v));
251 }
252 }
253 }
254
255 if order.len() != n {
256 let stuck: Vec<&'static str> = (0..n)
257 .filter(|i| in_degree[*i] > 0)
258 .map(|i| systems[i].name())
259 .collect();
260 panic!(
261 "{stage:?}: system ordering constraints form a cycle among: {stuck:?}"
262 );
263 }
264
265 let mut taken: Vec<Option<Box<dyn System>>> = systems.drain(..).map(Some).collect();
266 for i in order {
267 systems.push(taken[i].take().unwrap());
268 }
269 }
270
271 pub fn build(&mut self) -> &mut Self {
277 let mut iterations = 0;
278 const MAX_PLUGIN_BUILD_ITERATIONS: u32 = 64;
279
280 while !self.plugins.is_empty() {
281 iterations += 1;
282 if iterations > MAX_PLUGIN_BUILD_ITERATIONS {
283 panic!(
284 "App::build() exceeded {MAX_PLUGIN_BUILD_ITERATIONS} plugin-registration passes — \
285 likely a cycle where plugins keep registering each other. Check for a plugin whose \
286 build() unconditionally re-adds itself or another plugin that re-adds it."
287 );
288 }
289 let plugins: Vec<_> = self.plugins.drain(..).collect();
290 for plugin in plugins {
291 plugin.build(self);
292 }
293 }
294
295 for (stage, systems) in self.systems.iter_mut() {
296 Self::sort_stage(*stage, systems);
297 }
298
299 self
300 }
301
302 pub fn update(&mut self) {
304 for updater in self.event_updaters.iter_mut() {
305 updater(&self.world, &self.resources);
306 }
307 for stage in ALL_STAGES {
308 self.run_stage_once(stage);
309 }
310 }
311
312 pub fn set_runner<F>(&mut self, runner: F) -> &mut Self
318 where
319 F: FnOnce(App) + 'static,
320 {
321 self.runner = Some(Box::new(runner));
322 self
323 }
324
325 pub fn run(&mut self) {
329 let mut owned_app = std::mem::take(self);
330 let runner = owned_app.runner.take().expect("No runner found!");
331 runner(owned_app);
332 }
333}
334
335#[cfg(test)]
336mod tests {
337 use super::*;
338 use crate::ecs::system::{ResMut, SystemOrderingExt};
339
340 struct Order(Vec<&'static str>);
341
342 fn sys_a(mut o: ResMut<Order>) {
343 o.0.push("a");
344 }
345 fn sys_b(mut o: ResMut<Order>) {
346 o.0.push("b");
347 }
348 fn sys_c(mut o: ResMut<Order>) {
349 o.0.push("c");
350 }
351
352 #[test]
353 fn systems_run_in_declared_order() {
354 let mut app = App::new();
355 app.add_resource(Order(Vec::new()));
356
357 app.add_system(SystemStage::Update, sys_a.after(sys_c));
361 app.add_system(SystemStage::Update, sys_b.after(sys_c));
362 app.add_system(SystemStage::Update, sys_c);
363
364 app.build();
365 app.update();
366
367 let order = app.get_resource::<Order>();
368 assert_eq!(order.0, vec!["c", "a", "b"]);
369 }
370
371 #[test]
372 #[should_panic(expected = "cycle")]
373 fn cyclic_ordering_constraints_panic() {
374 let mut app = App::new();
375 app.add_resource(Order(Vec::new()));
376
377 app.add_system(SystemStage::Update, sys_a.after(sys_b));
378 app.add_system(SystemStage::Update, sys_b.after(sys_a));
379
380 app.build();
381 }
382
383 #[test]
384 fn time_plugin_is_already_built_into_a_fresh_app() {
385 let mut app = App::new();
386 app.build();
387
388 let _ = app.get_resource::<crate::time::Time>();
391 }
392
393 #[test]
394 fn registering_time_plugin_again_does_not_double_register_its_system() {
395 let mut baseline = App::new();
399 baseline.build();
400 let baseline_count = baseline.systems.get(&SystemStage::PreUpdate).map_or(0, Vec::len);
401
402 let mut app = App::new();
403 app.add_plugin(crate::time::TimePlugin); app.build();
405
406 let tick_systems = app.systems.get(&SystemStage::PreUpdate).map_or(0, Vec::len);
408 assert_eq!(tick_systems, baseline_count);
409 }
410}