1use std::collections::HashMap;
5
6use yog_data::Schema;
7use yog_sync::Mutex;
8
9trait Kept: Sync {
11 fn keep(&self);
13 fn forget(&self);
15}
16
17static KEPT: Mutex<Vec<&'static dyn Kept>> = Mutex::new(Vec::new());
19
20fn register(kept: &'static dyn Kept) {
21 let mut all = KEPT.lock();
22 if !all.iter().any(|k| std::ptr::addr_eq(*k, kept)) {
23 all.push(kept);
24 }
25}
26
27pub(super) fn keep_all() {
28 for kept in KEPT.lock().iter() {
29 kept.keep();
30 }
31}
32
33pub(super) fn forget_world() {
34 for kept in KEPT.lock().iter() {
35 kept.forget();
36 }
37}
38
39struct Slot<T> {
41 value: T,
42 changed: bool,
43}
44
45pub struct WorldData<T> {
49 key: &'static str,
50 slot: Mutex<Option<Slot<T>>>,
51}
52
53impl<T: Schema + Default + Send + 'static> WorldData<T> {
54 pub const fn new(key: &'static str) -> Self {
55 WorldData {
56 key,
57 slot: Mutex::new(None),
58 }
59 }
60
61 pub fn with<R>(&'static self, read: impl FnOnce(&T) -> R) -> R {
63 self.at(|slot| read(&slot.value))
64 }
65
66 pub fn with_mut<R>(&'static self, change: impl FnOnce(&mut T) -> R) -> R {
68 self.at(|slot| {
69 slot.changed = true;
70 change(&mut slot.value)
71 })
72 }
73
74 fn at<R>(&'static self, f: impl FnOnce(&mut Slot<T>) -> R) -> R {
75 register(self);
76 let mut slot = self.slot.lock();
77 f(slot.get_or_insert_with(|| Slot {
78 value: super::world()
79 .and_then(|store| store.get(self.key))
80 .unwrap_or_default(),
81 changed: false,
82 }))
83 }
84}
85
86impl<T: Schema + Default + Send + 'static> Kept for WorldData<T> {
87 fn keep(&self) {
88 let mut slot = self.slot.lock();
89 if let Some(slot) = slot.as_mut().filter(|s| s.changed)
90 && let Some(store) = super::world()
91 {
92 store.set(self.key, &slot.value);
93 slot.changed = false;
94 }
95 }
96
97 fn forget(&self) {
98 *self.slot.lock() = None;
99 }
100}
101
102pub struct PlayerData<T> {
104 key: &'static str,
105 slots: Mutex<Option<HashMap<String, Slot<T>>>>,
106}
107
108impl<T: Schema + Default + Send + 'static> PlayerData<T> {
109 pub const fn new(key: &'static str) -> Self {
110 PlayerData {
111 key,
112 slots: Mutex::new(None),
113 }
114 }
115
116 pub fn with<R>(&'static self, uuid: &str, read: impl FnOnce(&T) -> R) -> R {
118 self.at(uuid, |slot| read(&slot.value))
119 }
120
121 pub fn with_mut<R>(&'static self, uuid: &str, change: impl FnOnce(&mut T) -> R) -> R {
123 self.at(uuid, |slot| {
124 slot.changed = true;
125 change(&mut slot.value)
126 })
127 }
128
129 fn at<R>(&'static self, uuid: &str, f: impl FnOnce(&mut Slot<T>) -> R) -> R {
130 register(self);
131 let mut slots = self.slots.lock();
132 let slot = slots
133 .get_or_insert_with(HashMap::new)
134 .entry(uuid.to_owned())
135 .or_insert_with(|| Slot {
136 value: super::player(uuid)
137 .and_then(|store| store.get(self.key))
138 .unwrap_or_default(),
139 changed: false,
140 });
141 f(slot)
142 }
143}
144
145impl<T: Schema + Default + Send + 'static> Kept for PlayerData<T> {
146 fn keep(&self) {
147 let mut slots = self.slots.lock();
148 for (uuid, slot) in slots.iter_mut().flatten().filter(|(_, s)| s.changed) {
149 if let Some(store) = super::player(uuid) {
150 store.set(self.key, &slot.value);
151 slot.changed = false;
152 }
153 }
154 }
155
156 fn forget(&self) {
157 *self.slots.lock() = None;
158 }
159}
160
161pub struct GameData<T> {
164 key: &'static str,
165 slot: Mutex<Option<T>>,
166}
167
168impl<T: Schema + Default + Send + 'static> GameData<T> {
169 pub const fn new(key: &'static str) -> Self {
170 GameData {
171 key,
172 slot: Mutex::new(None),
173 }
174 }
175
176 pub fn with<R>(&'static self, read: impl FnOnce(&T) -> R) -> R {
178 let mut slot = self.slot.lock();
179 read(slot.get_or_insert_with(|| super::game().get_or_default(self.key)))
180 }
181
182 pub fn with_mut<R>(&'static self, change: impl FnOnce(&mut T) -> R) -> R {
184 let mut slot = self.slot.lock();
185 let value = slot.get_or_insert_with(|| super::game().get_or_default(self.key));
186 let out = change(value);
187 let store = super::game();
188 store.set(self.key, value);
189 if let Err(e) = store.flush() {
190 crate::error!("[{}] couldn't save its data: {e}", crate::mod_id());
191 }
192 out
193 }
194}