tinygame_core/core/
world.rs1use 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}