Skip to main content

yog_api/mods/storage/
item.rs

1//! Values kept on a stack — the Rust side of a Yog Script item's `saved`
2//! and `synced` members (`docs/design/storage.md`): bytes by key in the
3//! custom data of what a player holds, so they travel with the stack and
4//! reach its clients with it.
5
6use std::marker::PhantomData;
7
8use yog_data::Schema;
9
10/// A value kept in the data of the stack a player holds, at its key (the
11/// mod's own: `<mod>:<key>`); its default where the stack has none yet.
12///
13/// ```ignore
14/// static CHARGES: ItemData<u32> = ItemData::new("charges");
15/// CHARGES.update(&use_item.player, use_item.off_hand, |n| *n = n.saturating_sub(1));
16/// ```
17pub struct ItemData<T> {
18    key: &'static str,
19    value: PhantomData<fn() -> T>,
20}
21
22impl<T: Schema + Default> ItemData<T> {
23    pub const fn new(key: &'static str) -> Self {
24        ItemData {
25            key,
26            value: PhantomData,
27        }
28    }
29
30    /// The value on the stack `player` holds (`off_hand`: in its other
31    /// hand); its default when the stack has none (or there's no stack).
32    pub fn get(&self, player: &str, off_hand: bool) -> T {
33        crate::event::player::held_item_data(player, off_hand, self.stack_key())
34            .and_then(|r| yog_data::read_bytes(&r.bytes).ok())
35            .unwrap_or_default()
36    }
37
38    /// The value on the stack this client's player holds (`off_hand`: in
39    /// its other hand), as the server last sent the stack — the game sends
40    /// stacks, so every `ItemData` is synced; its default when the stack has
41    /// none (or there's no stack).
42    pub fn held_here(&self, off_hand: bool) -> T {
43        crate::event::client::held_item_data(off_hand, self.stack_key())
44            .and_then(|r| yog_data::read_bytes(&r.bytes).ok())
45            .unwrap_or_default()
46    }
47
48    /// Puts `value` on the stack `player` holds; false when it holds nothing.
49    pub fn set(&self, player: &str, off_hand: bool, value: &T) -> bool {
50        crate::event::player::set_held_item_data(
51            player,
52            off_hand,
53            self.stack_key(),
54            yog_data::to_bytes(value),
55        )
56    }
57
58    /// The value on the stack `player` holds changed by `change`, and put
59    /// back; what `change` returns, none when it holds nothing.
60    pub fn update<R>(
61        &self,
62        player: &str,
63        off_hand: bool,
64        change: impl FnOnce(&mut T) -> R,
65    ) -> Option<R> {
66        let mut value = self.get(player, off_hand);
67        let out = change(&mut value);
68        self.set(player, off_hand, &value).then_some(out)
69    }
70
71    /// The key in a stack's data: the mod's, then its own (as Yog Script's are).
72    fn stack_key(&self) -> String {
73        format!("{}{}{}", crate::mod_id(), yog_ids::sep::NAMESPACE, self.key)
74    }
75}