Skip to main content

tinygame_core/core/
world.rs

1use sdl3_main::AppResult;
2use std::{
3    any::{Any, TypeId, type_name},
4    cell::{Ref, RefCell, RefMut},
5    collections::{HashMap, HashSet},
6    thread::{self, ThreadId},
7};
8
9use crate::core::plugins::Plugin;
10
11pub struct World {
12    thread_id: ThreadId,
13    plugins: HashSet<String>,
14    resources: HashMap<TypeId, RefCell<Box<dyn Any>>>,
15    init_hooks: Vec<fn(&mut Self) -> AppResult>,
16    step_hooks: Vec<fn(&Self) -> AppResult>,
17    pre_step_hooks: Vec<fn(&Self) -> AppResult>,
18    post_step_hooks: Vec<fn(&Self) -> AppResult>,
19    exit_hooks: Vec<fn(&Self) -> ()>,
20}
21
22impl World {
23    pub fn new() -> Self {
24        Self {
25            thread_id: thread::current().id(),
26            plugins: HashSet::new(),
27            resources: HashMap::new(),
28            init_hooks: Vec::new(),
29            step_hooks: Vec::new(),
30            pre_step_hooks: Vec::new(),
31            post_step_hooks: Vec::new(),
32            exit_hooks: Vec::new(),
33        }
34    }
35    pub fn run_init(&mut self) -> AppResult {
36        if self.thread_id != thread::current().id() {
37            unreachable!("World run_init called from another thread than it was created")
38        }
39        for hook in self.init_hooks.clone() {
40            match hook(self) {
41                AppResult::Continue => (),
42                AppResult::Success => return AppResult::Continue,
43                AppResult::Failure => return AppResult::Failure,
44            }
45        }
46        AppResult::Continue
47    }
48    pub fn run_step(&self) -> AppResult {
49        if self.thread_id != thread::current().id() {
50            unreachable!("World run_step called from another thread than it was created")
51        }
52        for hook in self.pre_step_hooks.iter() {
53            match hook(self) {
54                AppResult::Continue => (),
55                AppResult::Success => return AppResult::Continue,
56                AppResult::Failure => return AppResult::Failure,
57            }
58        }
59        for hook in self.step_hooks.iter() {
60            match hook(self) {
61                AppResult::Continue => (),
62                AppResult::Success => return AppResult::Continue,
63                AppResult::Failure => return AppResult::Failure,
64            }
65        }
66        for hook in self.post_step_hooks.iter() {
67            match hook(self) {
68                AppResult::Continue => (),
69                AppResult::Success => return AppResult::Continue,
70                AppResult::Failure => return AppResult::Failure,
71            }
72        }
73        AppResult::Continue
74    }
75    pub fn run_exit(&self) {
76        if self.thread_id != thread::current().id() {
77            unreachable!("World run_exit called from another thread than it was created")
78        }
79        for hook in self.exit_hooks.iter().rev() {
80            hook(self)
81        }
82    }
83
84    pub fn add_plugin<T: Plugin>(&mut self) {
85        if self.thread_id != thread::current().id() {
86            unreachable!("World add_plugin called from another thread than it was created")
87        }
88
89        let meta = T::get_meta();
90        if self.plugins.contains(meta.name) {
91            return;
92        }
93        for dep in meta.depends_on.iter() {
94            if !self.plugins.contains(*dep) {
95                panic!(
96                    "Plugin {} depends on {}, but it isn't installed",
97                    meta.name, dep
98                )
99            }
100        }
101
102        self.plugins.insert(meta.name.to_owned());
103        self.init_hooks.push(T::init);
104        self.step_hooks.push(T::step);
105        self.pre_step_hooks.push(T::pre_step);
106        self.post_step_hooks.push(T::post_step);
107        self.exit_hooks.push(T::exit);
108    }
109
110    pub fn insert<T: 'static>(&mut self, value: T) {
111        if self.resources.contains_key(&TypeId::of::<T>()) {
112            return;
113        }
114        self.resources
115            .insert(TypeId::of::<T>(), RefCell::new(Box::new(value)));
116    }
117
118    pub fn try_get<'a, T: 'static>(&'a self) -> Option<Ref<'a, T>> {
119        self.resources
120            .get(&TypeId::of::<T>())
121            .map(|resource| Ref::map(resource.borrow(), |r| r.downcast_ref::<T>().unwrap()))
122    }
123
124    pub fn get<'a, T: 'static>(&'a self) -> Ref<'a, T> {
125        match self.try_get::<T>() {
126            Some(resource) => resource,
127            None => panic!(
128                "Cannot get resource {}, is corresponding plugin initialized?",
129                type_name::<T>()
130            ),
131        }
132    }
133
134    pub fn try_get_mut<'a, T: 'static>(&'a self) -> Option<RefMut<'a, T>> {
135        self.resources
136            .get(&TypeId::of::<T>())
137            .map(|resource| RefMut::map(resource.borrow_mut(), |r| r.downcast_mut::<T>().unwrap()))
138    }
139
140    pub fn get_mut<'a, T: 'static>(&'a self) -> RefMut<'a, T> {
141        match self.try_get_mut::<T>() {
142            Some(resource) => resource,
143            None => panic!(
144                "Cannot get resource {}, is corresponding plugin initialized?",
145                type_name::<T>()
146            ),
147        }
148    }
149}