yog_api/mods/storage/kept/
owned.rs1use 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
35struct Owned<K: 'static, T> {
39 key: &'static str,
40 slots: Mutex<Option<HashMap<K, Slot<T>>>>,
41 owners: &'static Owners<K>,
42 synced: Option<Audience>,
43}
44
45impl<K: Hash + Eq + Clone + Send + Sync + 'static, T: Schema + Default + Send + 'static>
46 Owned<K, T>
47{
48 const fn new(key: &'static str, owners: &'static Owners<K>, synced: Option<Audience>) -> Self {
49 Owned {
50 key,
51 slots: Mutex::new(None),
52 owners,
53 synced,
54 }
55 }
56
57 fn with<R>(&'static self, owner: K, read: impl FnOnce(&T) -> R) -> R {
58 self.at(owner, |slot| read(&slot.value))
59 }
60
61 fn with_mut<R>(&'static self, owner: K, change: impl FnOnce(&mut T) -> R) -> R {
62 self.at(owner, |slot| {
63 slot.changed = true;
64 slot.unsent = true;
65 change(&mut slot.value)
66 })
67 }
68
69 fn at<R>(&'static self, owner: K, f: impl FnOnce(&mut Slot<T>) -> R) -> R {
70 register(self);
71 let mut slots = self.slots.lock();
72 let slot = slots
73 .get_or_insert_with(HashMap::new)
74 .entry(owner)
75 .or_insert_with_key(|owner| Slot {
76 value: self.first(owner),
77 changed: false,
78 unsent: false,
79 });
80 f(slot)
81 }
82
83 fn first(&self, owner: &K) -> T {
86 let arrived = self
87 .synced
88 .and_then(|_| synced::pending(self.key, &(self.owners.text)(owner)))
89 .and_then(|bytes| yog_data::read_bytes(&bytes).ok());
90 arrived.unwrap_or_else(|| {
91 (self.owners.place)(owner, self.key)
92 .and_then(|(store, key)| store.get(&key))
93 .unwrap_or_default()
94 })
95 }
96}
97
98impl<K: Hash + Eq + Clone + Send + Sync + 'static, T: Schema + Default + Send + 'static> Kept
99 for Owned<K, T>
100{
101 fn keep(&self) {
102 let mut slots = self.slots.lock();
103 for (owner, slot) in slots.iter_mut().flatten().filter(|(_, s)| s.changed) {
104 if let Some((store, key)) = (self.owners.place)(owner, self.key) {
105 store.set(&key, &slot.value);
106 slot.changed = false;
107 }
108 }
109 }
110
111 fn forget(&self) {
112 *self.slots.lock() = None;
113 }
114
115 fn gone(&self, owner: &Gone<'_>) {
116 let Some(owner) = (self.owners.whose)(owner) else {
117 return;
118 };
119 if let Some(slots) = self.slots.lock().as_mut() {
120 slots.remove(&owner);
121 }
122 if let Some((store, key)) = (self.owners.place)(&owner, self.key) {
123 store.remove(&key);
124 }
125 }
126
127 fn sync(&self, every: bool, out: &mut dyn FnMut(Outgoing)) {
128 let Some(audience) = self.synced else {
129 return;
130 };
131 let mut slots = self.slots.lock();
132 for (owner, slot) in slots
133 .iter_mut()
134 .flatten()
135 .filter(|(_, s)| every || s.unsent)
136 {
137 if !every {
138 slot.unsent = false;
139 }
140 let owner = (self.owners.text)(owner);
141 out(Outgoing {
142 to: matches!(audience, Audience::Owner).then(|| owner.clone()),
143 owner,
144 key: self.key,
145 bytes: yog_data::to_bytes(&slot.value),
146 });
147 }
148 }
149
150 fn take(&self, key: &str, owner: &str, bytes: &[u8]) -> bool {
151 if self.synced.is_none() || key != self.key {
152 return false;
153 }
154 let Ok(value) = yog_data::read_bytes::<T>(bytes) else {
155 return true;
156 };
157 let mut slots = self.slots.lock();
158 let slot = slots
159 .iter_mut()
160 .flatten()
161 .find(|(k, _)| (self.owners.text)(k) == owner);
162 match slot {
163 Some((_, slot)) => slot.value = value,
164 None => return false,
165 }
166 true
167 }
168}
169
170macro_rules! owned {
173 ($(#[$doc:meta])* $name:ident, $key:ty, $owner:ty, $take:expr, $owners:expr, $audience:expr) => {
174 $(#[$doc])*
175 pub struct $name<T>(Owned<$key, T>);
176
177 impl<T: Schema + Default + Send + 'static> $name<T> {
178 pub const fn new(key: &'static str) -> Self {
179 $name(Owned::new(key, $owners, None))
180 }
181
182 pub const fn synced(key: &'static str) -> Self {
186 $name(Owned::new(key, $owners, Some($audience)))
187 }
188
189 pub fn with<R>(&'static self, owner: $owner, read: impl FnOnce(&T) -> R) -> R {
191 self.0.with($take(owner), read)
192 }
193
194 pub fn with_mut<R>(&'static self, owner: $owner, change: impl FnOnce(&mut T) -> R) -> R {
196 self.0.with_mut($take(owner), change)
197 }
198 }
199 };
200}
201
202owned!(
203 PlayerData, String, &str, str::to_owned,
207 &Owners {
208 place: |uuid, key| Some((crate::storage::player(uuid)?, key.to_owned())),
209 whose: |_| None,
210 text: String::clone,
211 },
212 Audience::Owner
213);
214
215owned!(
216 BlockData, (String, [i32; 3]), (&str, BlockPos),
220 |(dim, pos): (&str, BlockPos)| (dim.to_owned(), [pos.x, pos.y, pos.z]),
221 &Owners {
222 place: block_place,
223 whose: |Gone::Block(dim, pos)| Some(((*dim).to_owned(), [pos.x, pos.y, pos.z])),
224 text: |(dim, [x, y, z])| format!("{dim}{SEP}{x}{SEP}{y}{SEP}{z}"),
225 },
226 Audience::All
227);
228
229fn block_place((dim, [x, y, z]): &(String, [i32; 3]), key: &str) -> Option<(Store, String)> {
232 let store = crate::storage::chunk(dim, x >> CHUNK_BITS, z >> CHUNK_BITS)?;
233 let owner = format!("{}{SEP}{y}{SEP}{}", x & CHUNK_MASK, z & CHUNK_MASK);
234 Some((store, yog_event::storage::record_key(&owner, key)))
235}
236
237static WORLD: Owners<()> = Owners {
239 place: |(), key| Some((crate::storage::world()?, key.to_owned())),
240 whose: |_| None,
241 text: |()| String::new(),
242};
243
244pub struct WorldData<T>(Owned<(), T>);
248
249impl<T: Schema + Default + Send + 'static> WorldData<T> {
250 pub const fn new(key: &'static str) -> Self {
251 WorldData(Owned::new(key, &WORLD, None))
252 }
253
254 pub const fn synced(key: &'static str) -> Self {
256 WorldData(Owned::new(key, &WORLD, Some(Audience::All)))
257 }
258
259 pub fn with<R>(&'static self, read: impl FnOnce(&T) -> R) -> R {
261 self.0.with((), read)
262 }
263
264 pub fn with_mut<R>(&'static self, change: impl FnOnce(&mut T) -> R) -> R {
266 self.0.with_mut((), change)
267 }
268}