1use std::collections::HashMap;
6use std::hash::Hash;
7
8use yog_core::BlockPos;
9use yog_data::Schema;
10use yog_ids::storage::SEP;
11use yog_ids::units::{CHUNK_BITS, CHUNK_MASK};
12use yog_sync::Mutex;
13
14use super::synced::{self, Audience, Outgoing};
15use super::{Gone, Kept, register};
16use crate::storage::Store;
17
18struct Owners<K> {
22 place: fn(&K, &str) -> Option<(Store, String)>,
23 whose: fn(&Gone<'_>) -> Option<K>,
24 text: fn(&K) -> String,
25}
26
27struct Slot<T> {
30 value: T,
31 changed: bool,
32 unsent: bool,
33}
34
35pub struct Was<T> {
38 pub(super) key: &'static str,
39 read: fn(&Store, &str) -> Option<T>,
40}
41
42impl<T: Schema> Was<T> {
43 pub(super) fn read(&self, store: &Store, key: &str) -> Option<T> {
45 (self.read)(store, key)
46 }
47
48 pub const fn renamed(key: &'static str) -> Self {
50 Self::from::<T>(key)
51 }
52
53 pub const fn from<Old: Schema + Into<T>>(key: &'static str) -> Self {
55 Was {
56 key,
57 read: read_as::<Old, T>,
58 }
59 }
60}
61
62fn read_as<Old: Schema + Into<T>, T>(store: &Store, key: &str) -> Option<T> {
64 store.get::<Old>(key).map(Into::into)
65}
66
67struct Owned<K: 'static, T> {
71 key: &'static str,
72 slots: Mutex<Option<HashMap<K, Slot<T>>>>,
73 owners: &'static Owners<K>,
74 synced: Option<Audience>,
75 was: Option<Was<T>>,
76}
77
78impl<K: Hash + Eq + Clone + Send + Sync + 'static, T: Schema + Default + Send + 'static>
79 Owned<K, T>
80{
81 const fn new(key: &'static str, owners: &'static Owners<K>, synced: Option<Audience>) -> Self {
82 Owned {
83 key,
84 slots: Mutex::new(None),
85 owners,
86 synced,
87 was: None,
88 }
89 }
90
91 fn with<R>(&'static self, owner: K, read: impl FnOnce(&T) -> R) -> R {
92 self.at(owner, |slot| read(&slot.value))
93 }
94
95 fn with_mut<R>(&'static self, owner: K, change: impl FnOnce(&mut T) -> R) -> R {
96 self.at(owner, |slot| {
97 slot.changed = true;
98 slot.unsent = true;
99 change(&mut slot.value)
100 })
101 }
102
103 fn at<R>(&'static self, owner: K, f: impl FnOnce(&mut Slot<T>) -> R) -> R {
104 register(self);
105 let mut slots = self.slots.lock();
106 let slot = slots
107 .get_or_insert_with(HashMap::new)
108 .entry(owner)
109 .or_insert_with_key(|owner| {
110 let (value, changed) = self.first(owner);
111 Slot {
112 value,
113 changed,
114 unsent: false,
115 }
116 });
117 f(slot)
118 }
119
120 fn first(&self, owner: &K) -> (T, bool) {
125 let arrived = self
126 .synced
127 .and_then(|_| synced::pending(self.key, &(self.owners.text)(owner)))
128 .and_then(|bytes| yog_data::read_bytes(&bytes).ok());
129 if let Some(value) = arrived {
130 return (value, false);
131 }
132 let Some((store, key)) = (self.owners.place)(owner, self.key) else {
133 return (T::default(), false);
134 };
135 if let Some(value) = store.get(&key) {
136 return (value, false);
137 }
138 let old = self.was.as_ref().and_then(|was| {
139 let (store, old_key) = (self.owners.place)(owner, was.key)?;
140 let value = was.read(&store, &old_key)?;
141 store.remove(&old_key);
142 Some(value)
143 });
144 match old {
145 Some(value) => (value, true),
146 None => (T::default(), false),
147 }
148 }
149}
150
151impl<K: Hash + Eq + Clone + Send + Sync + 'static, T: Schema + Default + Send + 'static> Kept
152 for Owned<K, T>
153{
154 fn keep(&self) {
155 let mut slots = self.slots.lock();
156 for (owner, slot) in slots.iter_mut().flatten().filter(|(_, s)| s.changed) {
157 if let Some((store, key)) = (self.owners.place)(owner, self.key) {
158 store.set(&key, &slot.value);
159 slot.changed = false;
160 }
161 }
162 }
163
164 fn forget(&self) {
165 *self.slots.lock() = None;
166 }
167
168 fn gone(&self, owner: &Gone<'_>) {
169 let Some(owner) = (self.owners.whose)(owner) else {
170 return;
171 };
172 if let Some(slots) = self.slots.lock().as_mut() {
173 slots.remove(&owner);
174 }
175 if let Some((store, key)) = (self.owners.place)(&owner, self.key) {
176 store.remove(&key);
177 }
178 }
179
180 fn sync(&self, every: bool, out: &mut dyn FnMut(Outgoing)) {
181 let Some(audience) = self.synced else {
182 return;
183 };
184 let mut slots = self.slots.lock();
185 for (owner, slot) in slots
186 .iter_mut()
187 .flatten()
188 .filter(|(_, s)| every || s.unsent)
189 {
190 if !every {
191 slot.unsent = false;
192 }
193 let owner = (self.owners.text)(owner);
194 out(Outgoing {
195 to: matches!(audience, Audience::Owner).then(|| owner.clone()),
196 owner,
197 key: self.key,
198 bytes: yog_data::to_bytes(&slot.value),
199 });
200 }
201 }
202
203 fn take(&self, key: &str, owner: &str, bytes: &[u8]) -> bool {
204 if self.synced.is_none() || key != self.key {
205 return false;
206 }
207 let Ok(value) = yog_data::read_bytes::<T>(bytes) else {
208 return true;
209 };
210 let mut slots = self.slots.lock();
211 let slot = slots
212 .iter_mut()
213 .flatten()
214 .find(|(k, _)| (self.owners.text)(k) == owner);
215 match slot {
216 Some((_, slot)) => slot.value = value,
217 None => return false,
218 }
219 true
220 }
221}
222
223macro_rules! owned {
226 ($(#[$doc:meta])* $name:ident, $key:ty, $owner:ty, $take:expr, $owners:expr, $audience:expr) => {
227 $(#[$doc])*
228 pub struct $name<T>(Owned<$key, T>);
229
230 impl<T: Schema + Default + Send + 'static> $name<T> {
231 pub const fn new(key: &'static str) -> Self {
232 $name(Owned::new(key, $owners, None))
233 }
234
235 pub const fn synced(key: &'static str) -> Self {
239 $name(Owned::new(key, $owners, Some($audience)))
240 }
241
242 pub const fn was(mut self, was: Was<T>) -> Self {
245 self.0.was = Some(was);
246 self
247 }
248
249 pub fn with<R>(&'static self, owner: $owner, read: impl FnOnce(&T) -> R) -> R {
251 self.0.with($take(owner), read)
252 }
253
254 pub fn with_mut<R>(&'static self, owner: $owner, change: impl FnOnce(&mut T) -> R) -> R {
256 self.0.with_mut($take(owner), change)
257 }
258 }
259 };
260}
261
262owned!(
263 PlayerData, String, &str, str::to_owned,
267 &Owners {
268 place: |uuid, key| Some((crate::storage::player(uuid)?, key.to_owned())),
269 whose: |_| None,
270 text: String::clone,
271 },
272 Audience::Owner
273);
274
275owned!(
276 BlockData, (String, [i32; 3]), (&str, BlockPos),
280 |(dim, pos): (&str, BlockPos)| (dim.to_owned(), [pos.x, pos.y, pos.z]),
281 &Owners {
282 place: block_place,
283 whose: |Gone::Block(dim, pos)| Some(((*dim).to_owned(), [pos.x, pos.y, pos.z])),
284 text: |(dim, [x, y, z])| format!("{dim}{SEP}{x}{SEP}{y}{SEP}{z}"),
285 },
286 Audience::All
287);
288
289fn block_place((dim, [x, y, z]): &(String, [i32; 3]), key: &str) -> Option<(Store, String)> {
292 let store = crate::storage::chunk(dim, x >> CHUNK_BITS, z >> CHUNK_BITS)?;
293 let owner = format!("{}{SEP}{y}{SEP}{}", x & CHUNK_MASK, z & CHUNK_MASK);
294 Some((store, yog_event::storage::record_key(&owner, key)))
295}
296
297static WORLD: Owners<()> = Owners {
299 place: |(), key| Some((crate::storage::world()?, key.to_owned())),
300 whose: |_| None,
301 text: |()| String::new(),
302};
303
304pub struct WorldData<T>(Owned<(), T>);
308
309impl<T: Schema + Default + Send + 'static> WorldData<T> {
310 pub const fn new(key: &'static str) -> Self {
311 WorldData(Owned::new(key, &WORLD, None))
312 }
313
314 pub const fn synced(key: &'static str) -> Self {
316 WorldData(Owned::new(key, &WORLD, Some(Audience::All)))
317 }
318
319 pub const fn was(mut self, was: Was<T>) -> Self {
322 self.0.was = Some(was);
323 self
324 }
325
326 pub fn with<R>(&'static self, read: impl FnOnce(&T) -> R) -> R {
328 self.0.with((), read)
329 }
330
331 pub fn with_mut<R>(&'static self, change: impl FnOnce(&mut T) -> R) -> R {
333 self.0.with_mut((), change)
334 }
335}