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