1use crate::{
2 assets::required::RequiredResources,
3 ecs::{
4 plugin::Plugin,
5 resources::Resources,
6 system::{IntoSystem, System},
7 system_set::IntoSystemSet,
8 },
9};
10use std::collections::BTreeMap;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
17pub enum SystemStage {
18 Startup,
20 AssetSync,
22 AssetSyncDeps,
24 PreUpdate,
26 Update,
28 PostUpdate,
30 PreRender,
32 Render,
34 PostRender,
36}
37
38pub type AppRunner = Box<dyn FnOnce(App)>;
43
44pub struct App {
55 pub(crate) world: hecs::World,
56 pub(crate) resources: Resources,
57 plugins: Vec<Box<dyn Plugin>>,
58 systems: BTreeMap<SystemStage, Vec<Box<dyn System>>>,
59 runner: Option<AppRunner>,
60 pub(crate) required: RequiredResources,
61}
62
63impl Default for App {
64 fn default() -> Self {
65 Self::new()
66 }
67}
68
69impl App {
70 pub fn new() -> Self {
72 let mut world = hecs::World::default();
73 let mut resources = Resources::new(&mut world);
74 resources.insert_resource(&mut world, ());
75
76 Self {
77 world: world,
78 resources: resources,
79 plugins: Vec::new(),
80 systems: BTreeMap::new(),
81 runner: Some(Box::new(|mut app| {
82 loop {
83 app.update();
84 }
85 })),
86 required: RequiredResources::new(),
87 }
88 }
89
90 pub fn add_plugin(&mut self, plugin: impl Plugin) -> &mut Self {
92 self.plugins.push(Box::new(plugin));
93 self
94 }
95
96 pub fn add_resource(&mut self, res: impl hecs::Component) -> &mut Self {
98 self.resources.insert_resource(&mut self.world, res);
99 self
100 }
101
102 pub fn get_resource<'a, T: hecs::Component>(&'a self) -> hecs::Ref<'a, T> {
104 self.resources.get_resource(&self.world)
105 }
106
107 pub fn get_resource_mut<'a, T: hecs::Component>(&'a self) -> hecs::RefMut<'a, T> {
109 self.resources.get_resource_mut(&self.world)
110 }
111
112 pub fn try_insert_resource<T: hecs::Component>(&mut self, res: T) -> bool {
116 self.resources.try_insert(&mut self.world, res)
117 }
118
119 pub fn add_system<Marker>(
121 &mut self,
122 stage: SystemStage,
123 system: impl IntoSystem<Marker> + 'static,
124 ) -> &mut Self {
125 self.systems
126 .entry(stage)
127 .or_default()
128 .push(Box::new(system.into_system()));
129 self
130 }
131
132 pub fn add_systems<Marker>(
136 &mut self,
137 stage: SystemStage,
138 systems: impl IntoSystemSet<Marker>,
139 ) -> &mut Self {
140 let entry = self.systems.entry(stage).or_default();
141 entry.extend(systems.into_system_set());
142 self
143 }
144
145 pub fn build(&mut self) -> &mut Self {
151 let mut iterations = 0;
152 const MAX_PLUGIN_BUILD_ITERATIONS: u32 = 64;
153
154 while !self.plugins.is_empty() {
155 iterations += 1;
156 if iterations > MAX_PLUGIN_BUILD_ITERATIONS {
157 panic!(
158 "App::build() exceeded {MAX_PLUGIN_BUILD_ITERATIONS} plugin-registration passes — \
159 likely a cycle where plugins keep registering each other. Check for a plugin whose \
160 build() unconditionally re-adds itself or another plugin that re-adds it."
161 );
162 }
163 let plugins: Vec<_> = self.plugins.drain(..).collect();
164 for plugin in plugins {
165 plugin.build(self);
166 }
167 }
168
169 self.required.validate();
170
171 if let Some(systems) = self.systems.remove(&SystemStage::Startup) {
172 for mut system in systems {
173 system.run(&self.world, &self.resources);
174 }
175 self.resources.get_command_buffer().run_on(&mut self.world);
176 }
177 self
178 }
179
180 pub fn update(&mut self) {
182 for systems in self.systems.values_mut() {
183 for system in systems {
184 system.run(&self.world, &self.resources);
185 }
186 self.resources.get_command_buffer().run_on(&mut self.world);
187 }
188 }
189
190 pub fn set_runner<F>(&mut self, runner: F) -> &mut Self
196 where
197 F: FnOnce(App) + 'static,
198 {
199 self.runner = Some(Box::new(runner));
200 self
201 }
202
203 pub fn run(&mut self) {
207 let mut owned_app = std::mem::take(self);
208 let runner = owned_app.runner.take().expect("No runner found!");
209 runner(owned_app);
210 }
211}