Skip to main content

yog_api/mods/storage/
kept.rs

1//! Values the mod keeps: loaded from their store when first used, written
2//! back as the world is saved — no loading and saving by hand.
3
4mod owned;
5pub(super) mod synced;
6
7use yog_core::BlockPos;
8use yog_data::Schema;
9use yog_sync::Mutex;
10
11pub use owned::{BlockData, PlayerData, WorldData};
12
13/// What's kept, as the saving sees it.
14trait Kept: Sync {
15    /// Puts what changed into its store.
16    fn keep(&self);
17    /// The world closed: what belongs to it is forgotten (the next world loads its own).
18    fn forget(&self);
19    /// `owner` is gone for good: what it kept removed.
20    fn gone(&self, _owner: &Gone<'_>) {}
21    /// Its `synced` values to send: the changed ones, or `every` one (a
22    /// player joined).
23    fn sync(&self, _every: bool, _out: &mut dyn FnMut(synced::Outgoing)) {}
24    /// A synced value arrived (a client): taken when this keeps `key`.
25    fn take(&self, _key: &str, _owner: &str, _bytes: &[u8]) -> bool {
26        false
27    }
28}
29
30/// An owner gone for good: what it kept goes with it.
31pub(super) enum Gone<'a> {
32    /// The block at a place broken, or another put there: its dimension, its place.
33    Block(&'a str, BlockPos),
34}
35
36/// Every kept value used so far.
37static KEPT: Mutex<Vec<&'static dyn Kept>> = Mutex::new(Vec::new());
38
39fn register(kept: &'static dyn Kept) {
40    let mut all = KEPT.lock();
41    if !all.iter().any(|k| std::ptr::addr_eq(*k, kept)) {
42        all.push(kept);
43    }
44}
45
46pub(super) fn keep_all() {
47    for kept in KEPT.lock().iter() {
48        kept.keep();
49    }
50}
51
52pub(super) fn gone(owner: &Gone<'_>) {
53    for kept in KEPT.lock().iter() {
54        kept.gone(owner);
55    }
56}
57
58pub(super) fn sync(every: bool, out: &mut dyn FnMut(synced::Outgoing)) {
59    for kept in KEPT.lock().iter() {
60        kept.sync(every, out);
61    }
62}
63
64pub(super) fn take(key: &str, owner: &str, bytes: &[u8]) -> bool {
65    KEPT.lock().iter().any(|kept| kept.take(key, owner, bytes))
66}
67
68pub(super) fn forget_world() {
69    for kept in KEPT.lock().iter() {
70        kept.forget();
71    }
72}
73
74/// A value kept for the install, whatever world (`<game>/yog-data/<mod>/game.ydata`):
75/// written as it changes.
76pub struct GameData<T> {
77    key: &'static str,
78    slot: Mutex<Option<T>>,
79}
80
81impl<T: Schema + Default + Send + 'static> GameData<T> {
82    pub const fn new(key: &'static str) -> Self {
83        GameData {
84            key,
85            slot: Mutex::new(None),
86        }
87    }
88
89    /// What `read` makes of the value.
90    pub fn with<R>(&'static self, read: impl FnOnce(&T) -> R) -> R {
91        let mut slot = self.slot.lock();
92        read(slot.get_or_insert_with(|| super::game().get_or_default(self.key)))
93    }
94
95    /// The value changed by `change`, and written.
96    pub fn with_mut<R>(&'static self, change: impl FnOnce(&mut T) -> R) -> R {
97        let mut slot = self.slot.lock();
98        let value = slot.get_or_insert_with(|| super::game().get_or_default(self.key));
99        let out = change(value);
100        let store = super::game();
101        store.set(self.key, value);
102        if let Err(e) = store.flush() {
103            crate::error!("[{}] couldn't save its data: {e}", crate::mod_id());
104        }
105        out
106    }
107}