1mod groups;
2mod resources;
3#[cfg(test)]
4mod tests;
5
6use crate::hooks::{HookRegistry, OnAddHook, OnRemoveHook};
7use crate::resource::{Resource, ResourceTrait};
8use crate::scheduler::{ScheduleError, ScheduleStage, SimpleScheduler, SystemConfig, SystemGroup};
9use crate::simple_world::SimpleWorld;
10use crate::system::{AppSystem, SystemContext};
11use alloc::vec::Vec;
12use soroban_sdk::{Env, Symbol};
13
14pub trait Plugin {
37 fn name(&self) -> &'static str;
38 fn build(&self, app: &mut GameApp);
39}
40
41pub trait PluginGroup {
43 fn build(self, app: &mut GameApp);
44}
45
46pub struct GameApp {
85 world: SimpleWorld,
86 scheduler: SimpleScheduler,
87 hooks: HookRegistry,
88 resources: Vec<Resource>,
89 plugins_registered: Vec<&'static str>,
90 startup_ran: bool,
91}
92
93impl GameApp {
94 pub fn new(env: &Env) -> Self {
95 Self {
96 world: SimpleWorld::new(env),
97 scheduler: SimpleScheduler::new(),
98 hooks: HookRegistry::new(),
99 resources: Vec::new(),
100 plugins_registered: Vec::new(),
101 startup_ran: false,
102 }
103 }
104
105 pub fn with_world(world: SimpleWorld) -> Self {
106 Self {
107 world,
108 scheduler: SimpleScheduler::new(),
109 hooks: HookRegistry::new(),
110 resources: Vec::new(),
111 plugins_registered: Vec::new(),
112 startup_ran: false,
113 }
114 }
115
116 pub fn add_plugin<P: Plugin>(&mut self, plugin: P) -> &mut Self {
117 let name = Plugin::name(&plugin);
118 if !self.has_plugin(name) {
119 self.plugins_registered.push(name);
120 Plugin::build(&plugin, self);
121 }
122 self
123 }
124
125 pub fn add_plugins<G: PluginGroup>(&mut self, group: G) -> &mut Self {
126 group.build(self);
127 self
128 }
129
130 pub fn add_system<F>(&mut self, name: &'static str, system: F) -> &mut Self
132 where
133 F: FnMut(&mut SimpleWorld, &Env) + 'static,
134 {
135 self.scheduler.add_system(name, system);
136 self
137 }
138
139 pub fn add_system_with_config<F>(
141 &mut self,
142 name: &'static str,
143 system: F,
144 config: SystemConfig,
145 ) -> &mut Self
146 where
147 F: FnMut(&mut SimpleWorld, &Env) + 'static,
148 {
149 self.scheduler.add_system_with_config(name, system, config);
150 self
151 }
152
153 pub fn add_system_in_stage<F>(
155 &mut self,
156 stage: ScheduleStage,
157 name: &'static str,
158 system: F,
159 ) -> &mut Self
160 where
161 F: FnMut(&mut SimpleWorld, &Env) + 'static,
162 {
163 self.scheduler.add_system_in_stage(stage, name, system);
164 self
165 }
166
167 pub fn add_context_system<F>(&mut self, name: &'static str, system: F) -> &mut Self
169 where
170 F: for<'w, 'e, 'c> FnMut(&mut SystemContext<'w, 'e, 'c>) + 'static,
171 {
172 self.scheduler.add_context_system(name, system);
173 self
174 }
175
176 pub fn add_context_system_with_config<F>(
178 &mut self,
179 name: &'static str,
180 system: F,
181 config: SystemConfig,
182 ) -> &mut Self
183 where
184 F: for<'w, 'e, 'c> FnMut(&mut SystemContext<'w, 'e, 'c>) + 'static,
185 {
186 self.scheduler
187 .add_context_system_with_config(name, system, config);
188 self
189 }
190
191 pub fn add_context_system_in_stage<F>(
193 &mut self,
194 stage: ScheduleStage,
195 name: &'static str,
196 system: F,
197 ) -> &mut Self
198 where
199 F: for<'w, 'e, 'c> FnMut(&mut SystemContext<'w, 'e, 'c>) + 'static,
200 {
201 self.scheduler
202 .add_context_system_in_stage(stage, name, system);
203 self
204 }
205
206 pub fn add_simple_system<S>(&mut self, name: &'static str, system: S) -> &mut Self
208 where
209 S: AppSystem + 'static,
210 {
211 self.scheduler.add_simple_system(name, system);
212 self
213 }
214
215 pub fn add_simple_system_with_config<S>(
217 &mut self,
218 name: &'static str,
219 system: S,
220 config: SystemConfig,
221 ) -> &mut Self
222 where
223 S: AppSystem + 'static,
224 {
225 self.scheduler
226 .add_simple_system_with_config(name, system, config);
227 self
228 }
229
230 pub fn add_simple_system_in_stage<S>(
232 &mut self,
233 stage: ScheduleStage,
234 name: &'static str,
235 system: S,
236 ) -> &mut Self
237 where
238 S: AppSystem + 'static,
239 {
240 self.scheduler
241 .add_simple_system_in_stage(stage, name, system);
242 self
243 }
244
245 pub fn add_systems<G>(&mut self, systems: G) -> &mut Self
247 where
248 G: SystemGroup,
249 {
250 self.scheduler.add_systems(systems);
251 self
252 }
253
254 pub fn add_systems_in_stage<G>(&mut self, stage: ScheduleStage, systems: G) -> &mut Self
256 where
257 G: SystemGroup,
258 {
259 self.scheduler.add_systems_in_stage(stage, systems);
260 self
261 }
262
263 pub fn add_startup_system<F>(&mut self, name: &'static str, system: F) -> &mut Self
265 where
266 F: FnMut(&mut SimpleWorld, &Env) + 'static,
267 {
268 self.add_system_with_config(
269 name,
270 system,
271 SystemConfig::new().in_stage(ScheduleStage::Startup),
272 )
273 }
274
275 pub fn add_hook_on_add(&mut self, component_type: Symbol, hook: OnAddHook) -> &mut Self {
276 self.hooks.on_add(component_type, hook);
277 self
278 }
279
280 pub fn add_hook_on_remove(&mut self, component_type: Symbol, hook: OnRemoveHook) -> &mut Self {
281 self.hooks.on_remove(component_type, hook);
282 self
283 }
284
285 pub fn insert_resource<R: ResourceTrait>(&mut self, env: &Env, resource: &R) -> &mut Self {
286 resources::insert_resource(&mut self.resources, env, resource);
287 self
288 }
289
290 pub fn get_resource<R: ResourceTrait>(&self, env: &Env) -> Option<R> {
291 resources::get_resource(&self.resources, env)
292 }
293
294 pub fn remove_resource<R: ResourceTrait>(&mut self) -> Option<Resource> {
295 resources::remove_resource::<R>(&mut self.resources)
296 }
297
298 pub fn world(&self) -> &SimpleWorld {
299 &self.world
300 }
301
302 pub fn world_mut(&mut self) -> &mut SimpleWorld {
303 &mut self.world
304 }
305
306 pub fn scheduler(&self) -> &SimpleScheduler {
307 &self.scheduler
308 }
309
310 pub fn hooks(&self) -> &HookRegistry {
311 &self.hooks
312 }
313
314 pub fn resources(&self) -> &Vec<Resource> {
315 &self.resources
316 }
317
318 pub fn run_startup(&mut self, env: &Env) -> Result<(), ScheduleError> {
319 if !self.startup_ran {
320 self.scheduler
321 .run_stage(ScheduleStage::Startup, &mut self.world, env)?;
322 self.startup_ran = true;
323 }
324 Ok(())
325 }
326
327 pub fn run(&mut self, env: &Env) -> Result<(), ScheduleError> {
329 self.run_startup(env)?;
330 self.scheduler
331 .run_stage(ScheduleStage::PreUpdate, &mut self.world, env)?;
332 self.scheduler
333 .run_stage(ScheduleStage::Update, &mut self.world, env)?;
334 self.scheduler
335 .run_stage(ScheduleStage::PostUpdate, &mut self.world, env)?;
336 self.scheduler
337 .run_stage(ScheduleStage::Cleanup, &mut self.world, env)?;
338 Ok(())
339 }
340
341 pub fn run_stage(&mut self, stage: ScheduleStage, env: &Env) -> Result<(), ScheduleError> {
342 if stage == ScheduleStage::Startup {
343 return self.run_startup(env);
344 }
345 self.scheduler.run_stage(stage, &mut self.world, env)
346 }
347
348 pub fn configure_system(
349 &mut self,
350 name: &str,
351 config: SystemConfig,
352 ) -> Result<&mut Self, ScheduleError> {
353 self.scheduler.configure_system(name, config)?;
354 Ok(self)
355 }
356
357 pub fn into_world(self) -> SimpleWorld {
358 self.world
359 }
360
361 pub fn plugin_count(&self) -> usize {
362 self.plugins_registered.len()
363 }
364
365 pub fn has_plugin(&self, name: &str) -> bool {
366 self.plugins_registered.contains(&name)
367 }
368
369 pub fn system_count(&self) -> usize {
370 self.scheduler.system_count()
371 }
372}