azalea_inventory/
slot.rs

1use std::{
2    any::Any,
3    fmt,
4    io::{Cursor, Write},
5};
6
7use azalea_buf::{AzaleaRead, AzaleaReadVar, AzaleaWrite, AzaleaWriteVar, BufReadError};
8use azalea_registry::DataComponentKind;
9use indexmap::IndexMap;
10
11use crate::components::{self};
12
13/// Either an item in an inventory or nothing.
14#[derive(Debug, Clone, Default, PartialEq)]
15pub enum ItemStack {
16    #[default]
17    Empty,
18    Present(ItemStackData),
19}
20
21impl ItemStack {
22    /// Check if the slot is ItemStack::Empty, if the count is <= 0, or if the
23    /// item is air.
24    ///
25    /// This is the opposite of [`ItemStack::is_present`].
26    pub fn is_empty(&self) -> bool {
27        match self {
28            ItemStack::Empty => true,
29            ItemStack::Present(item) => item.is_empty(),
30        }
31    }
32    /// Check if the slot is not ItemStack::Empty, if the count is > 0, and if
33    /// the item is not air.
34    ///
35    /// This is the opposite of [`ItemStack::is_empty`].
36    pub fn is_present(&self) -> bool {
37        !self.is_empty()
38    }
39
40    /// Return the amount of the item in the slot, or 0 if the slot is empty.
41    ///
42    /// Note that it's possible for the count to be zero or negative when the
43    /// slot is present.
44    pub fn count(&self) -> i32 {
45        match self {
46            ItemStack::Empty => 0,
47            ItemStack::Present(i) => i.count,
48        }
49    }
50
51    /// Remove `count` items from this slot, returning the removed items.
52    pub fn split(&mut self, count: u32) -> ItemStack {
53        match self {
54            ItemStack::Empty => ItemStack::Empty,
55            ItemStack::Present(i) => {
56                let returning = i.split(count);
57                if i.is_empty() {
58                    *self = ItemStack::Empty;
59                }
60                ItemStack::Present(returning)
61            }
62        }
63    }
64
65    /// Get the `kind` of the item in this slot, or
66    /// [`azalea_registry::Item::Air`]
67    pub fn kind(&self) -> azalea_registry::Item {
68        match self {
69            ItemStack::Empty => azalea_registry::Item::Air,
70            ItemStack::Present(i) => i.kind,
71        }
72    }
73
74    /// Update whether this slot is empty, based on the count.
75    pub fn update_empty(&mut self) {
76        if let ItemStack::Present(i) = self {
77            if i.is_empty() {
78                *self = ItemStack::Empty;
79            }
80        }
81    }
82
83    /// Convert this slot into an [`ItemStackData`], if it's present.
84    pub fn as_present(&self) -> Option<&ItemStackData> {
85        match self {
86            ItemStack::Empty => None,
87            ItemStack::Present(i) => Some(i),
88        }
89    }
90}
91
92/// An item in an inventory, with a count and NBT. Usually you want
93/// [`ItemStack`] or [`azalea_registry::Item`] instead.
94#[derive(Debug, Clone, PartialEq)]
95pub struct ItemStackData {
96    /// The amount of the item in this slot.
97    ///
98    /// The count can be zero or negative, but this is rare.
99    pub count: i32,
100    pub kind: azalea_registry::Item,
101    pub components: DataComponentPatch,
102}
103
104impl ItemStackData {
105    /// Remove `count` items from this slot, returning the removed items.
106    pub fn split(&mut self, count: u32) -> ItemStackData {
107        let returning_count = i32::min(count as i32, self.count);
108        let mut returning = self.clone();
109        returning.count = returning_count;
110        self.count -= returning_count;
111        returning
112    }
113
114    /// Check if the count of the item is <= 0 or if the item is air.
115    pub fn is_empty(&self) -> bool {
116        self.count <= 0 || self.kind == azalea_registry::Item::Air
117    }
118
119    /// Whether this item is the same as another item, ignoring the count.
120    ///
121    /// ```
122    /// # use azalea_inventory::ItemStackData;
123    /// # use azalea_registry::Item;
124    /// let mut a = ItemStackData {
125    ///    kind: Item::Stone,
126    ///    count: 1,
127    ///    components: Default::default(),
128    /// };
129    /// let mut b = ItemStackData {
130    ///   kind: Item::Stone,
131    ///   count: 2,
132    ///   components: Default::default(),
133    /// };
134    /// assert!(a.is_same_item_and_components(&b));
135    ///
136    /// b.kind = Item::Dirt;
137    /// assert!(!a.is_same_item_and_components(&b));
138    /// ```
139    pub fn is_same_item_and_components(&self, other: &ItemStackData) -> bool {
140        self.kind == other.kind && self.components == other.components
141    }
142}
143
144impl AzaleaRead for ItemStack {
145    fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
146        let count = i32::azalea_read_var(buf)?;
147        if count <= 0 {
148            Ok(ItemStack::Empty)
149        } else {
150            let kind = azalea_registry::Item::azalea_read(buf)?;
151            let components = DataComponentPatch::azalea_read(buf)?;
152            Ok(ItemStack::Present(ItemStackData {
153                count,
154                kind,
155                components,
156            }))
157        }
158    }
159}
160
161impl AzaleaWrite for ItemStack {
162    fn azalea_write(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
163        match self {
164            ItemStack::Empty => 0_i32.azalea_write_var(buf)?,
165            ItemStack::Present(i) => {
166                i.count.azalea_write_var(buf)?;
167                i.kind.azalea_write(buf)?;
168                i.components.azalea_write(buf)?;
169            }
170        };
171        Ok(())
172    }
173}
174
175/// An update to an item's data components.
176///
177/// Note that in vanilla items come with their own set of default components,
178/// and Azalea does not implement that yet.
179#[derive(Default)]
180pub struct DataComponentPatch {
181    components: IndexMap<DataComponentKind, Option<Box<dyn components::EncodableDataComponent>>>,
182}
183
184impl DataComponentPatch {
185    /// Returns the value of the component in the generic argument for this
186    /// item.
187    ///
188    /// ```
189    /// # use azalea_inventory::{ItemStackData, DataComponentPatch, components};
190    /// # use azalea_registry::Item;
191    /// # fn example(item: &ItemStackData) -> Option<()> {
192    /// let item_nutrition = item.components.get::<components::Food>()?.nutrition;
193    /// # Some(())
194    /// # }
195    /// ```
196    pub fn get<T: components::DataComponent>(&self) -> Option<&T> {
197        let component = self.components.get(&T::KIND).and_then(|c| c.as_deref())?;
198        let component_any = component as &dyn Any;
199        component_any.downcast_ref::<T>()
200    }
201
202    pub fn get_kind(
203        &self,
204        kind: DataComponentKind,
205    ) -> Option<&dyn components::EncodableDataComponent> {
206        self.components.get(&kind).and_then(|c| c.as_deref())
207    }
208
209    /// Returns whether the component in the generic argument is present for
210    /// this item.
211    ///
212    /// ```
213    /// # use azalea_inventory::{ItemStackData, DataComponentPatch, components};
214    /// # use azalea_registry::Item;
215    /// # let item = ItemStackData {
216    /// #     kind: Item::Stone,
217    /// #     count: 1,
218    /// #     components: Default::default(),
219    /// # };
220    /// let is_edible = item.components.has::<components::Food>();
221    /// # assert!(!is_edible);
222    /// ```
223    pub fn has<T: components::DataComponent>(&self) -> bool {
224        self.has_kind(T::KIND)
225    }
226
227    pub fn has_kind(&self, kind: DataComponentKind) -> bool {
228        self.get_kind(kind).is_some()
229    }
230}
231
232impl AzaleaRead for DataComponentPatch {
233    fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
234        let components_with_data_count = u32::azalea_read_var(buf)?;
235        let components_without_data_count = u32::azalea_read_var(buf)?;
236
237        if components_without_data_count == 0 && components_with_data_count == 0 {
238            return Ok(DataComponentPatch::default());
239        }
240
241        let mut components = IndexMap::new();
242        for _ in 0..components_with_data_count {
243            let component_kind = DataComponentKind::azalea_read(buf)?;
244            let component_data = components::from_kind(component_kind, buf)?;
245            components.insert(component_kind, Some(component_data));
246        }
247
248        for _ in 0..components_without_data_count {
249            let component_kind = DataComponentKind::azalea_read(buf)?;
250            components.insert(component_kind, None);
251        }
252
253        Ok(DataComponentPatch { components })
254    }
255}
256
257impl AzaleaWrite for DataComponentPatch {
258    fn azalea_write(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
259        let mut components_with_data_count: u32 = 0;
260        let mut components_without_data_count: u32 = 0;
261        for component in self.components.values() {
262            if component.is_some() {
263                components_with_data_count += 1;
264            } else {
265                components_without_data_count += 1;
266            }
267        }
268
269        components_with_data_count.azalea_write_var(buf)?;
270        components_without_data_count.azalea_write_var(buf)?;
271
272        let mut component_buf = Vec::new();
273        for (kind, component) in &self.components {
274            if let Some(component) = component {
275                kind.azalea_write(buf)?;
276
277                component_buf.clear();
278                component.encode(&mut component_buf)?;
279                buf.write_all(&component_buf)?;
280            }
281        }
282
283        for (kind, component) in &self.components {
284            if component.is_none() {
285                kind.azalea_write(buf)?;
286            }
287        }
288
289        Ok(())
290    }
291}
292
293impl Clone for DataComponentPatch {
294    fn clone(&self) -> Self {
295        let mut components = IndexMap::with_capacity(self.components.len());
296        for (kind, component) in &self.components {
297            components.insert(*kind, component.as_ref().map(|c| (*c).clone()));
298        }
299        DataComponentPatch { components }
300    }
301}
302impl fmt::Debug for DataComponentPatch {
303    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
304        f.debug_set().entries(self.components.keys()).finish()
305    }
306}
307impl PartialEq for DataComponentPatch {
308    fn eq(&self, other: &Self) -> bool {
309        if self.components.len() != other.components.len() {
310            return false;
311        }
312        for (kind, component) in &self.components {
313            if let Some(other_component) = other.components.get(kind) {
314                // we can't use PartialEq, but we can use our own eq method
315                if let Some(component) = component {
316                    if let Some(other_component) = other_component {
317                        if !component.eq((*other_component).clone()) {
318                            return false;
319                        }
320                    } else {
321                        return false;
322                    }
323                } else if other_component.is_some() {
324                    return false;
325                }
326            } else {
327                return false;
328            }
329        }
330        true
331    }
332}