chunkedge_item/
stack.rs

1use std::fmt::Debug;
2use std::io::Write;
3
4use chunkedge_binary::{Encode, VarInt};
5use chunkedge_generated::item::ItemKind;
6
7use crate::components::{ItemComponent, Patchable};
8use crate::vanilla_components::ItemKindExt;
9use crate::NUM_ITEM_COMPONENTS;
10
11/// A stack of items in an inventory.
12#[derive(Clone, PartialEq)]
13pub struct ItemStack {
14    pub item: ItemKind,
15    pub count: i8,
16    pub(crate) components: [Patchable<Box<ItemComponent>>; NUM_ITEM_COMPONENTS],
17}
18
19impl Default for ItemStack {
20    fn default() -> Self {
21        ItemStack::EMPTY
22    }
23}
24
25impl Debug for ItemStack {
26    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27        f.debug_struct("ItemStack")
28            .field("item", &self.item)
29            .field("count", &self.count)
30            .field(
31                "components",
32                &self
33                    .components
34                    .iter()
35                    .enumerate()
36                    .filter_map(|(i, c)| c.as_option().map(|comp| (i, comp)))
37                    .collect::<Vec<_>>(),
38            )
39            .finish()
40    }
41}
42
43#[derive(Clone, PartialEq, Debug)]
44pub struct HashedItemStack {
45    pub item: ItemKind,
46    pub count: i8,
47    pub(crate) components: [Patchable<()>; NUM_ITEM_COMPONENTS],
48}
49impl HashedItemStack {
50    pub const EMPTY: Self = Self {
51        item: ItemKind::Air,
52        count: 0,
53        components: [const { Patchable::None }; NUM_ITEM_COMPONENTS],
54    };
55
56    #[must_use]
57    pub const fn new(item: ItemKind, count: i8) -> Self {
58        Self {
59            item,
60            count,
61            components: [const { Patchable::None }; NUM_ITEM_COMPONENTS],
62        }
63    }
64
65    pub const fn is_empty(&self) -> bool {
66        matches!(self.item, ItemKind::Air) || self.count <= 0
67    }
68}
69
70impl From<ItemStack> for HashedItemStack {
71    fn from(stack: ItemStack) -> Self {
72        Self {
73            item: stack.item,
74            count: stack.count,
75            components: stack.components.map(|c| match c {
76                Patchable::Default(_) => Patchable::Default(()),
77                Patchable::Added((_, h)) => Patchable::Added(((), h)),
78                Patchable::Removed => Patchable::Removed,
79                Patchable::None => Patchable::None,
80            }),
81        }
82    }
83}
84
85impl ItemStack {
86    pub const EMPTY: ItemStack = ItemStack {
87        item: ItemKind::Air,
88        count: 0,
89        components: [const { Patchable::None }; NUM_ITEM_COMPONENTS],
90    };
91
92    /// Creates a new item stack without any components.
93    #[must_use]
94    pub const fn new(item: ItemKind, count: i8) -> Self {
95        Self {
96            item,
97            count,
98            components: [const { Patchable::None }; NUM_ITEM_COMPONENTS],
99        }
100    }
101
102    /// Creates a new item stack with the vanilla default components for the
103    /// given [`ItemKind`].
104    pub fn new_vanilla(item: ItemKind, count: i8) -> Self {
105        let components = item.default_components();
106        Self {
107            item,
108            count,
109            components,
110        }
111    }
112
113    /// Read the components of the item stack.
114    pub fn components(&self) -> Vec<&ItemComponent> {
115        self.components
116            .iter()
117            .filter_map(|component| component.as_option())
118            .map(|boxed| &**boxed)
119            .collect()
120    }
121
122    /// Returns the default components for the [`ItemKind`].
123    pub fn default_components(&self) -> Vec<ItemComponent> {
124        self.item
125            .default_components()
126            .iter()
127            .filter_map(|component| component.as_option().map(|b| &**b))
128            .cloned()
129            .collect()
130    }
131
132    /// Attach a component to the item stack.
133    pub fn insert_component(&mut self, component: ItemComponent) {
134        let id = component.id() as usize;
135        if let Patchable::Default(default) = &self.components[id] {
136            // We don't need to add a components if its default for the item kind.
137            if **default == component {
138                return;
139            }
140        }
141
142        let hash = component.hash();
143        self.components[id] = Patchable::Added((Box::new(component), hash));
144    }
145
146    /// Remove a component from the item stack by its ID, see
147    /// [`ItemComponent::id`].String
148    ///
149    /// Returns the removed component if it was present, otherwise `None`.
150    pub fn remove_component<I: Into<usize>>(&mut self, id: I) -> Option<ItemComponent> {
151        let id = id.into();
152        if id < NUM_ITEM_COMPONENTS {
153            std::mem::replace(&mut self.components[id], Patchable::Removed)
154                .to_option()
155                .map(|boxed| *boxed)
156        } else {
157            None
158        }
159    }
160
161    /// Get a specific component by its ID, see [`ItemComponent::id`].
162    pub fn get_component<I: Into<usize>>(&self, id: I) -> Option<&ItemComponent> {
163        let id = id.into();
164        if id < NUM_ITEM_COMPONENTS {
165            match &self.components[id] {
166                Patchable::Added((component, _)) | Patchable::Default(component) => {
167                    Some(&**component)
168                }
169                _ => None,
170            }
171        } else {
172            None
173        }
174    }
175
176    #[must_use]
177    pub const fn with_count(mut self, count: i8) -> Self {
178        self.count = count;
179        self
180    }
181
182    #[must_use]
183    pub const fn with_item(mut self, item: ItemKind) -> Self {
184        self.item = item;
185        self
186    }
187
188    #[must_use]
189    pub fn with_components(mut self, components: Vec<ItemComponent>) -> Self {
190        for component in components {
191            self.insert_component(component);
192        }
193        self
194    }
195
196    pub const fn is_empty(&self) -> bool {
197        matches!(self.item, ItemKind::Air) || self.count <= 0
198    }
199
200    pub fn encode_recursive<W: Write>(
201        &self,
202        mut w: W,
203        prefixed: bool,
204    ) -> Result<(), anyhow::Error> {
205        if self.is_empty() {
206            VarInt(0).encode(w)
207        } else {
208            // Break recursion loop by erasing the type
209            let w: &mut dyn Write = &mut w;
210
211            VarInt(i32::from(self.count)).encode(&mut *w)?;
212            self.item.encode(&mut *w)?;
213
214            let mut added = Vec::new();
215            let mut removed = Vec::new();
216
217            for (i, patch) in self.components.iter().enumerate() {
218                match patch {
219                    Patchable::Added((comp, _)) => added.push((i, comp)),
220                    Patchable::Removed => removed.push(i),
221                    _ => {}
222                }
223            }
224
225            // Encode Added & removed
226            VarInt(added.len() as i32).encode(&mut *w)?;
227            VarInt(removed.len() as i32).encode(&mut *w)?;
228
229            for (id, comp) in added {
230                VarInt(id as i32).encode(&mut *w)?;
231                if prefixed {
232                    // We need to record the length of the component data.
233                    // Then we encode len then the data.
234                    //
235                    // We use a dummy writer to avoid allocator pressure at the cost of cpu.
236
237                    struct ByteCounter {
238                        count: usize,
239                    }
240
241                    impl Write for ByteCounter {
242                        fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
243                            self.count += buf.len();
244                            Ok(buf.len())
245                        }
246
247                        fn flush(&mut self) -> std::io::Result<()> {
248                            Ok(())
249                        }
250                    }
251
252                    // Encode to the counter to determine the length
253                    let mut counter = ByteCounter { count: 0 };
254                    comp.encode(&mut counter)?;
255
256                    // Write the length prefix
257                    VarInt(counter.count as i32).encode(&mut *w)?;
258
259                    // Real run: Encode the data to the actual writer
260                    comp.encode(&mut *w)?;
261                } else {
262                    comp.encode(&mut *w)?;
263                }
264            }
265
266            for id in removed {
267                VarInt(id as i32).encode(&mut *w)?;
268            }
269
270            Ok(())
271        }
272    }
273}