goud_engine/ecs/app/
mod.rs1pub mod builtin_plugins;
26pub mod physics_plugins;
27pub mod plugin;
28
29pub use builtin_plugins::{DefaultPlugins, TransformPropagationPlugin};
30pub use physics_plugins::{PhysicsPlugin2D, PhysicsPlugin3D};
31pub use plugin::{Plugin, PluginGroup};
32
33use std::any::TypeId;
34use std::collections::HashSet;
35
36use crate::ecs::resource::{NonSendResource, Resource};
37use crate::ecs::schedule::{CoreStage, Stage, SystemSetConfig, SystemStage};
38use crate::ecs::system::IntoSystem;
39use crate::ecs::World;
40
41pub struct App {
51 world: World,
53 stages: Vec<(CoreStage, SystemStage)>,
55 initialized_plugins: HashSet<TypeId>,
57}
58
59impl App {
60 pub fn new_with_defaults() -> Self {
62 let mut app = Self::new();
63 app.add_plugin_group(DefaultPlugins);
64 app
65 }
66
67 pub fn new() -> Self {
69 let stages = CoreStage::all()
70 .iter()
71 .map(|&stage| (stage, SystemStage::from_core(stage)))
72 .collect();
73
74 Self {
75 world: World::new(),
76 stages,
77 initialized_plugins: HashSet::new(),
78 }
79 }
80
81 pub fn add_plugin<P: Plugin>(&mut self, plugin: P) -> &mut Self {
90 let plugin_type_id = TypeId::of::<P>();
91
92 if self.initialized_plugins.contains(&plugin_type_id) {
93 log::warn!(
94 "Plugin '{}' already added, skipping duplicate",
95 plugin.name()
96 );
97 return self;
98 }
99
100 for dep in plugin.dependencies() {
102 assert!(
103 self.initialized_plugins.contains(&dep),
104 "Plugin '{}' has an unmet dependency (TypeId: {:?}). \
105 Add the dependency plugin first.",
106 plugin.name(),
107 dep
108 );
109 }
110
111 plugin.build(self);
112 self.initialized_plugins.insert(plugin_type_id);
113 self
114 }
115
116 pub fn add_plugin_group<G: PluginGroup>(&mut self, group: G) -> &mut Self {
118 group.build(self);
119 self
120 }
121
122 pub fn insert_non_send_resource<T: NonSendResource>(&mut self, resource: T) -> &mut Self {
124 self.world.insert_non_send_resource(resource);
125 self
126 }
127
128 pub fn add_system<S, Marker>(&mut self, stage: CoreStage, system: S) -> &mut Self
130 where
131 S: IntoSystem<Marker>,
132 {
133 for (core_stage, system_stage) in &mut self.stages {
134 if *core_stage == stage {
135 system_stage.add_system(system);
136 return self;
137 }
138 }
139 self
140 }
141
142 pub fn insert_resource<R: Resource>(&mut self, resource: R) -> &mut Self {
144 self.world.insert_resource(resource);
145 self
146 }
147
148 pub fn world(&self) -> &World {
150 &self.world
151 }
152
153 pub fn world_mut(&mut self) -> &mut World {
155 &mut self.world
156 }
157
158 pub fn run_once(&mut self) {
163 for (_stage_label, stage) in &mut self.stages {
164 stage.run(&mut self.world);
165 }
166 }
167
168 pub fn update(&mut self) {
173 self.run_once();
174 }
175
176 pub fn register_set(&mut self, stage: CoreStage, name: &str) -> &mut Self {
182 for (core_stage, system_stage) in &mut self.stages {
183 if *core_stage == stage {
184 system_stage.register_set(name);
185 return self;
186 }
187 }
188 self
189 }
190
191 pub fn add_system_to_set<S, Marker>(
196 &mut self,
197 stage: CoreStage,
198 set_name: &str,
199 system: S,
200 ) -> &mut Self
201 where
202 S: IntoSystem<Marker>,
203 {
204 for (core_stage, system_stage) in &mut self.stages {
205 if *core_stage == stage {
206 assert!(
207 system_stage.get_set(set_name).is_some(),
208 "System set '{set_name}' is not registered in stage {stage:?}"
209 );
210 let id = system_stage.add_system(system);
211 system_stage.add_system_to_set(set_name, id);
212 return self;
213 }
214 }
215 self
216 }
217
218 pub fn configure_set(
220 &mut self,
221 stage: CoreStage,
222 name: &str,
223 config: SystemSetConfig,
224 ) -> &mut Self {
225 for (core_stage, system_stage) in &mut self.stages {
226 if *core_stage == stage {
227 system_stage.configure_named_set(name, config);
228 return self;
229 }
230 }
231 self
232 }
233}
234
235impl Default for App {
236 fn default() -> Self {
237 Self::new()
238 }
239}
240
241impl std::fmt::Debug for App {
242 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
243 let total_systems: usize = self.stages.iter().map(|(_, s)| s.system_count()).sum();
244 f.debug_struct("App")
245 .field("stage_count", &self.stages.len())
246 .field("total_systems", &total_systems)
247 .field("plugin_count", &self.initialized_plugins.len())
248 .finish()
249 }
250}
251
252#[cfg(test)]
253#[path = "tests.rs"]
254mod tests;
255
256#[cfg(test)]
257#[path = "tests_extended.rs"]
258mod tests_extended;