1use crate::{
2 ecs::{
3 commands::UniverseCommands,
4 hierarchy::{hierarchy_system, Hierarchy, HierarchySystemResources},
5 life_cycle::EntityChanges,
6 pipeline::{PipelineBuilder, PipelineBuilderError, PipelineEngine, PipelineLayer},
7 AccessType, Multiverse, System,
8 },
9 state::{State, StateToken},
10 Scalar,
11};
12use std::{
13 any::{Any, TypeId},
14 cell::RefCell,
15 collections::HashMap,
16 rc::Rc,
17 time::{Duration, Instant},
18};
19
20pub trait AppTimer: Send + Sync {
21 fn tick(&mut self);
22 fn time(&self) -> Duration;
23 fn time_seconds(&self) -> Scalar;
24 fn delta_time(&self) -> Duration;
25 fn delta_time_seconds(&self) -> Scalar;
26 fn ticks(&self) -> usize;
27}
28
29pub struct StandardAppTimer {
30 timer: Instant,
31 last_timer: Instant,
32 time: Duration,
33 time_seconds: Scalar,
34 delta_time: Duration,
35 delta_time_seconds: Scalar,
36 ticks: usize,
37}
38
39impl Default for StandardAppTimer {
40 fn default() -> Self {
41 Self {
42 timer: Instant::now(),
43 last_timer: Instant::now(),
44 time: Duration::default(),
45 time_seconds: 0.0,
46 delta_time: Duration::default(),
47 delta_time_seconds: 0.0,
48 ticks: 0,
49 }
50 }
51}
52
53impl AppTimer for StandardAppTimer {
54 fn tick(&mut self) {
55 self.delta_time = self.last_timer.elapsed();
56 #[cfg(feature = "scalar64")]
57 let d = self.delta_time.as_secs_f64();
58 #[cfg(not(feature = "scalar64"))]
59 let d = self.delta_time.as_secs_f32();
60 self.delta_time_seconds = d;
61 self.time = self.timer.elapsed();
62 #[cfg(feature = "scalar64")]
63 let d = self.time.as_secs_f64();
64 #[cfg(not(feature = "scalar64"))]
65 let d = self.time.as_secs_f32();
66 self.time_seconds = d;
67 self.ticks = self.ticks.wrapping_add(1);
68 self.last_timer = Instant::now();
69 }
70
71 fn time(&self) -> Duration {
72 self.time
73 }
74
75 fn time_seconds(&self) -> Scalar {
76 self.time_seconds
77 }
78
79 fn delta_time(&self) -> Duration {
80 self.delta_time
81 }
82
83 fn delta_time_seconds(&self) -> Scalar {
84 self.delta_time_seconds
85 }
86
87 fn ticks(&self) -> usize {
88 self.ticks
89 }
90}
91
92pub struct AppParams(HashMap<String, String>);
93
94impl AppParams {
95 pub fn new(data: HashMap<String, String>) -> Self {
96 Self(data)
97 }
98
99 pub fn params(&self) -> &HashMap<String, String> {
100 &self.0
101 }
102
103 pub fn get(&self, name: &str) -> Option<&str> {
104 self.0.get(name).map(|v| v.as_str())
105 }
106
107 pub fn has(&self, name: &str) -> bool {
108 self.0.contains_key(name)
109 }
110}
111
112pub struct AppLifeCycle {
113 pub running: bool,
114 pub delta_time_limit: Option<Duration>,
115 pub(crate) timer: Box<dyn AppTimer>,
116 pub(crate) states_tokens: Vec<StateToken>,
117}
118
119impl AppLifeCycle {
120 pub fn new(timer: Box<dyn AppTimer>) -> Self {
121 Self::with_limit(timer, None)
122 }
123
124 pub fn with_limit(timer: Box<dyn AppTimer>, delta_time_limit: Option<Duration>) -> Self {
125 Self {
126 running: true,
127 timer,
128 states_tokens: vec![StateToken::new()],
129 delta_time_limit,
130 }
131 }
132
133 pub fn time(&self) -> Duration {
134 self.timer.time()
135 }
136
137 pub fn time_seconds(&self) -> Scalar {
138 self.timer.time_seconds()
139 }
140
141 pub fn delta_time(&self) -> Duration {
142 let dt = self.timer.delta_time();
143 match self.delta_time_limit {
144 Some(limit) => dt.min(limit),
145 None => dt,
146 }
147 }
148
149 pub fn delta_time_seconds(&self) -> Scalar {
150 let dt = self.timer.delta_time_seconds();
151 match self.delta_time_limit {
152 Some(limit) => {
153 #[cfg(feature = "scalar64")]
154 let limit = limit.as_secs_f64();
155 #[cfg(not(feature = "scalar64"))]
156 let limit = limit.as_secs_f32();
157 dt.min(limit)
158 }
159 None => dt,
160 }
161 }
162
163 pub fn ticks(&self) -> usize {
164 self.timer.ticks()
165 }
166
167 pub fn current_state_token(&self) -> StateToken {
168 if let Some(token) = self.states_tokens.last() {
169 *token
170 } else {
171 StateToken::new()
172 }
173 }
174}
175
176impl<AT> From<AT> for AppLifeCycle
177where
178 AT: AppTimer + 'static,
179{
180 fn from(timer: AT) -> Self {
181 AppLifeCycle::new(Box::new(timer))
182 }
183}
184
185impl<AT> From<(AT, Duration)> for AppLifeCycle
186where
187 AT: AppTimer + 'static,
188{
189 fn from((timer, delta_time_limit): (AT, Duration)) -> Self {
190 AppLifeCycle::with_limit(Box::new(timer), Some(delta_time_limit))
191 }
192}
193
194pub struct AppRunner {
195 pub app: Rc<RefCell<App>>,
196}
197
198impl AppRunner {
199 pub fn new(app: App) -> Self {
200 Self {
201 app: Rc::new(RefCell::new(app)),
202 }
203 }
204
205 pub fn run<BAR, E>(&mut self, mut backend_app_runner: BAR) -> Result<(), E>
206 where
207 BAR: BackendAppRunner<E>,
208 {
209 backend_app_runner.run(self.app.clone())
210 }
211}
212
213pub trait BackendAppRunner<E> {
214 fn run(&mut self, app: Rc<RefCell<App>>) -> Result<(), E>;
215}
216
217#[derive(Default)]
218pub struct StandardAppRunner {
219 pub sleep_time: Option<Duration>,
220}
221
222impl StandardAppRunner {
223 pub fn with_sleep_time(value: Duration) -> Self {
224 Self {
225 sleep_time: Some(value),
226 }
227 }
228}
229
230impl BackendAppRunner<()> for StandardAppRunner {
231 fn run(&mut self, app: Rc<RefCell<App>>) -> Result<(), ()> {
232 while app.borrow().multiverse.is_running() {
233 app.borrow_mut().process();
234 if let Some(sleep_time) = self.sleep_time {
235 std::thread::sleep(sleep_time);
236 }
237 }
238 Ok(())
239 }
240}
241
242pub struct App {
243 pub multiverse: Multiverse,
244}
245
246impl App {
247 #[inline]
248 pub fn build<PB>() -> AppBuilder<PB>
249 where
250 PB: PipelineBuilder + Default,
251 {
252 AppBuilder::<PB>::default()
253 }
254
255 #[inline]
256 pub fn process(&mut self) {
257 self.multiverse.process();
258 }
259}
260
261pub struct AppBuilder<PB>
262where
263 PB: PipelineBuilder,
264{
265 resources: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
266 pipeline_builder: PB,
267}
268
269impl<PB> Default for AppBuilder<PB>
270where
271 PB: PipelineBuilder + Default,
272{
273 fn default() -> Self {
274 Self::new(PB::default())
275 }
276}
277
278impl<PB> AppBuilder<PB>
279where
280 PB: PipelineBuilder,
281{
282 #[inline]
283 pub fn new(pipeline_builder: PB) -> Self {
284 Self {
285 resources: Default::default(),
286 pipeline_builder,
287 }
288 .with_resource(UniverseCommands::default())
289 .with_resource(EntityChanges::default())
290 .with_resource(Hierarchy::default())
291 .with_system_on_layer::<HierarchySystemResources>(
292 "hierarchy",
293 hierarchy_system,
294 &[],
295 PipelineLayer::Pre,
296 false,
297 )
298 .expect("Could not install hierarchy system!")
299 }
300
301 #[inline]
302 pub fn pipeline_builder_mut(&mut self) -> &mut PB {
303 &mut self.pipeline_builder
304 }
305
306 #[inline]
307 pub fn install_bundle<ABI, D>(
308 &mut self,
309 mut installer: ABI,
310 data: D,
311 ) -> Result<(), PipelineBuilderError>
312 where
313 ABI: FnMut(&mut AppBuilder<PB>, D) -> Result<(), PipelineBuilderError>,
314 {
315 installer(self, data)?;
316 Ok(())
317 }
318
319 #[inline]
320 pub fn with_bundle<ABI, D>(
321 mut self,
322 installer: ABI,
323 data: D,
324 ) -> Result<Self, PipelineBuilderError>
325 where
326 ABI: FnMut(&mut AppBuilder<PB>, D) -> Result<(), PipelineBuilderError>,
327 {
328 self.install_bundle(installer, data)?;
329 Ok(self)
330 }
331
332 #[inline]
333 pub fn install_resource<T>(&mut self, resource: T)
334 where
335 T: 'static + Send + Sync,
336 {
337 self.resources.insert(TypeId::of::<T>(), Box::new(resource));
338 }
339
340 #[inline]
341 pub fn with_resource<T>(mut self, resource: T) -> Self
342 where
343 T: 'static + Send + Sync,
344 {
345 self.install_resource(resource);
346 self
347 }
348
349 #[inline]
350 pub fn install_system_on_layer<AT: AccessType>(
351 &mut self,
352 name: &str,
353 system: System,
354 dependencies: &[&str],
355 layer: PipelineLayer,
356 lock_on_single_thread: bool,
357 ) -> Result<(), PipelineBuilderError> {
358 self.pipeline_builder.add_system_on_layer::<AT>(
359 name,
360 system,
361 dependencies,
362 layer,
363 lock_on_single_thread,
364 )?;
365 Ok(())
366 }
367
368 #[inline]
369 pub fn install_system<AT: AccessType>(
370 &mut self,
371 name: &str,
372 system: System,
373 dependencies: &[&str],
374 ) -> Result<(), PipelineBuilderError> {
375 self.pipeline_builder
376 .add_system::<AT>(name, system, dependencies)?;
377 Ok(())
378 }
379
380 #[inline]
381 pub fn install_system_on_single_thread<AT: AccessType>(
382 &mut self,
383 name: &str,
384 system: System,
385 dependencies: &[&str],
386 ) -> Result<(), PipelineBuilderError> {
387 self.pipeline_builder
388 .add_system_on_single_thread::<AT>(name, system, dependencies)?;
389 Ok(())
390 }
391
392 #[inline]
393 pub fn with_system_on_layer<AT: AccessType>(
394 mut self,
395 name: &str,
396 system: System,
397 dependencies: &[&str],
398 layer: PipelineLayer,
399 lock_on_single_thread: bool,
400 ) -> Result<Self, PipelineBuilderError> {
401 self.install_system_on_layer::<AT>(
402 name,
403 system,
404 dependencies,
405 layer,
406 lock_on_single_thread,
407 )?;
408 Ok(self)
409 }
410
411 #[inline]
412 pub fn with_system<AT: AccessType>(
413 mut self,
414 name: &str,
415 system: System,
416 dependencies: &[&str],
417 ) -> Result<Self, PipelineBuilderError> {
418 self.install_system::<AT>(name, system, dependencies)?;
419 Ok(self)
420 }
421
422 #[inline]
423 pub fn with_system_on_single_thread<AT: AccessType>(
424 mut self,
425 name: &str,
426 system: System,
427 dependencies: &[&str],
428 ) -> Result<Self, PipelineBuilderError> {
429 self.install_system_on_single_thread::<AT>(name, system, dependencies)?;
430 Ok(self)
431 }
432
433 #[inline]
434 pub fn build<P, S, AL>(self, state: S, life_cycle: AL) -> App
435 where
436 P: PipelineEngine + Send + Sync + Default + 'static,
437 S: State + 'static,
438 AL: Into<AppLifeCycle>,
439 {
440 self.build_with_engine(P::default(), state, life_cycle)
441 }
442
443 pub fn build_with_engine<P, S, AL>(mut self, engine: P, state: S, life_cycle: AL) -> App
444 where
445 P: PipelineEngine + Send + Sync + 'static,
446 S: State + 'static,
447 AL: Into<AppLifeCycle>,
448 {
449 self.install_resource(life_cycle.into());
450 let mut multiverse =
451 Multiverse::new(self.pipeline_builder.build_with_engine(engine), state);
452 if let Some(universe) = multiverse.default_universe_mut() {
453 for (as_type, resource) in self.resources {
454 unsafe {
455 universe.insert_resource_raw(as_type, resource);
456 }
457 }
458 }
459 App { multiverse }
460 }
461
462 #[inline]
463 pub fn build_empty<P, AL>(self, life_cycle: AL) -> App
464 where
465 P: PipelineEngine + Send + Sync + Default + 'static,
466 AL: Into<AppLifeCycle>,
467 {
468 self.build::<P, _, _>((), life_cycle)
469 }
470
471 #[inline]
472 pub fn build_empty_with_engine<P, AL>(self, engine: P, life_cycle: AL) -> App
473 where
474 P: PipelineEngine + Send + Sync + 'static,
475 AL: Into<AppLifeCycle>,
476 {
477 self.build_with_engine::<P, _, _>(engine, (), life_cycle)
478 }
479}
480
481#[cfg(test)]
482mod tests {
483 use super::*;
484 use crate::ecs::{
485 pipeline::{engines::jobs::JobsPipelineEngine, ParallelPipelineBuilder},
486 Universe,
487 };
488
489 #[test]
490 fn test_app_builder() {
491 struct A;
492 struct B;
493 struct C;
494
495 fn system(_: &mut Universe) {
496 println!("******");
497 }
498
499 fn system_a(_: &mut Universe) {
500 println!("* Start System A");
501 std::thread::sleep(std::time::Duration::from_millis(100));
502 println!("* Stop System A");
503 }
504
505 fn system_b(_: &mut Universe) {
506 println!("* Start System B");
507 std::thread::sleep(std::time::Duration::from_millis(150));
508 println!("* Stop System B");
509 }
510
511 fn system_c(_: &mut Universe) {
512 println!("* Start System C");
513 std::thread::sleep(std::time::Duration::from_millis(50));
514 println!("* Stop System C");
515 }
516
517 let app = App::build::<ParallelPipelineBuilder>()
518 .with_system::<()>("", system, &[])
519 .unwrap()
520 .with_bundle(
521 |builder, _| {
522 builder.install_resource(A);
523 builder.install_resource(B);
524 builder.install_system::<&mut A>("a", system_a, &[])?;
525 builder.install_system::<&mut B>("b", system_b, &[])?;
526 Ok(())
527 },
528 (),
529 )
530 .unwrap()
531 .with_system::<(&mut C, &A, &B)>("c", system_c, &[])
532 .unwrap()
533 .with_resource(C)
534 .build::<JobsPipelineEngine, _, _>(3, StandardAppTimer::default());
535
536 AppRunner::new(app)
537 .run(StandardAppRunner::default())
538 .unwrap();
539 }
540}