oxygengine_core/
app.rs

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}