Skip to main content

tinygame_core/core/
world.rs

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