azalea_inventory/
slot.rs

1use std::{
2    any::Any,
3    borrow::Cow,
4    fmt,
5    io::{self, Cursor, Write},
6};
7
8use azalea_buf::{AzaleaRead, AzaleaReadVar, AzaleaWrite, AzaleaWriteVar, BufReadError};
9use azalea_core::codec_utils::is_default;
10use azalea_registry::{DataComponentKind, Item};
11use indexmap::IndexMap;
12use serde::{Serialize, ser::SerializeMap};
13
14use crate::{
15    components::{self, DataComponentUnion},
16    default_components::get_default_component,
17};
18
19/// Either an item in an inventory or nothing.
20#[derive(Debug, Clone, Default, PartialEq)]
21pub enum ItemStack {
22    #[default]
23    Empty,
24    Present(ItemStackData),
25}
26
27impl ItemStack {
28    /// Create a new [`ItemStack`] with the given number of [`Item`]s.
29    ///
30    /// If item is air or the count isn't positive, then it'll be set to an
31    /// empty `ItemStack`.
32    pub fn new(item: Item, count: i32) -> Self {
33        let mut i = ItemStack::Present(ItemStackData::new(item, count));
34        // set it to Empty if the item is air or if the count isn't positive
35        i.update_empty();
36        i
37    }
38
39    /// Check if the slot is ItemStack::Empty, if the count is <= 0, or if the
40    /// item is air.
41    ///
42    /// This is the opposite of [`ItemStack::is_present`].
43    pub fn is_empty(&self) -> bool {
44        match self {
45            ItemStack::Empty => true,
46            ItemStack::Present(item) => item.is_empty(),
47        }
48    }
49    /// Check if the slot is not ItemStack::Empty, if the count is > 0, and if
50    /// the item is not air.
51    ///
52    /// This is the opposite of [`ItemStack::is_empty`].
53    pub fn is_present(&self) -> bool {
54        !self.is_empty()
55    }
56
57    /// Return the amount of the item in the slot, or 0 if the slot is empty.
58    ///
59    /// Note that it's possible for the count to be zero or negative when the
60    /// slot is present.
61    pub fn count(&self) -> i32 {
62        match self {
63            ItemStack::Empty => 0,
64            ItemStack::Present(i) => i.count,
65        }
66    }
67
68    /// Remove `count` items from this slot, returning the removed items.
69    pub fn split(&mut self, count: u32) -> ItemStack {
70        match self {
71            ItemStack::Empty => ItemStack::Empty,
72            ItemStack::Present(i) => {
73                let returning = i.split(count);
74                if i.is_empty() {
75                    *self = ItemStack::Empty;
76                }
77                ItemStack::Present(returning)
78            }
79        }
80    }
81
82    /// Get the `kind` of the item in this slot, or
83    /// [`azalea_registry::Item::Air`]
84    pub fn kind(&self) -> azalea_registry::Item {
85        match self {
86            ItemStack::Empty => azalea_registry::Item::Air,
87            ItemStack::Present(i) => i.kind,
88        }
89    }
90
91    /// Update whether this slot is empty, based on the count.
92    pub fn update_empty(&mut self) {
93        if let ItemStack::Present(i) = self
94            && i.is_empty()
95        {
96            *self = ItemStack::Empty;
97        }
98    }
99
100    /// Convert this slot into an [`ItemStackData`], if it's present.
101    pub fn as_present(&self) -> Option<&ItemStackData> {
102        match self {
103            ItemStack::Empty => None,
104            ItemStack::Present(i) => Some(i),
105        }
106    }
107
108    pub fn as_present_mut(&mut self) -> Option<&mut ItemStackData> {
109        match self {
110            ItemStack::Empty => None,
111            ItemStack::Present(i) => Some(i),
112        }
113    }
114
115    /// Get the value of a data component for this item.
116    ///
117    /// This is used for things like getting the damage of an item, or seeing
118    /// how much food it replenishes.
119    pub fn get_component<'a, T: components::DataComponentTrait>(&'a self) -> Option<Cow<'a, T>> {
120        self.as_present().and_then(|i| i.get_component::<T>())
121    }
122
123    pub fn with_component<
124        T: components::EncodableDataComponent + components::DataComponentTrait,
125    >(
126        mut self,
127        component: impl Into<Option<T>>,
128    ) -> Self {
129        if let ItemStack::Present(i) = &mut self {
130            let component: Option<T> = component.into();
131            let component: Option<DataComponentUnion> = component.map(|c| c.into());
132            i.component_patch.components.insert(T::KIND, component);
133        }
134        self
135    }
136}
137impl Serialize for ItemStack {
138    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
139    where
140        S: serde::Serializer,
141    {
142        match self {
143            ItemStack::Empty => serializer.serialize_unit(),
144            ItemStack::Present(i) => i.serialize(serializer),
145        }
146    }
147}
148
149/// An item in an inventory, with a count and a set of data components.
150///
151/// Usually you want [`ItemStack`] or [`azalea_registry::Item`] instead.
152#[derive(Debug, Clone, PartialEq, Serialize)]
153pub struct ItemStackData {
154    #[serde(rename = "id")]
155    pub kind: azalea_registry::Item,
156    /// The amount of the item in this slot.
157    ///
158    /// The count can be zero or negative, but this is rare.
159    pub count: i32,
160    /// The item's components that the server set to be different from the
161    /// defaults.
162    #[serde(rename = "components", skip_serializing_if = "is_default")]
163    pub component_patch: DataComponentPatch,
164}
165
166impl ItemStackData {
167    /// Create a new [`ItemStackData`] with the given number of [`Item`]s.
168    pub fn new(item: Item, count: i32) -> Self {
169        ItemStackData {
170            count,
171            kind: item,
172            component_patch: DataComponentPatch::default(),
173        }
174    }
175
176    /// Remove `count` items from this slot, returning the removed items.
177    pub fn split(&mut self, count: u32) -> ItemStackData {
178        let returning_count = i32::min(count as i32, self.count);
179        let mut returning = self.clone();
180        returning.count = returning_count;
181        self.count -= returning_count;
182        returning
183    }
184
185    /// Check if the count of the item is <= 0 or if the item is air.
186    pub fn is_empty(&self) -> bool {
187        self.count <= 0 || self.kind == azalea_registry::Item::Air
188    }
189
190    /// Whether this item is the same as another item, ignoring the count.
191    ///
192    /// ```
193    /// # use azalea_inventory::ItemStackData;
194    /// # use azalea_registry::Item;
195    /// let mut a = ItemStackData::from(Item::Stone);
196    /// let mut b = ItemStackData::new(Item::Stone, 2);
197    /// assert!(a.is_same_item_and_components(&b));
198    ///
199    /// b.kind = Item::Dirt;
200    /// assert!(!a.is_same_item_and_components(&b));
201    /// ```
202    pub fn is_same_item_and_components(&self, other: &ItemStackData) -> bool {
203        self.kind == other.kind && self.component_patch == other.component_patch
204    }
205
206    /// Get the value of a data component for this item.
207    ///
208    /// This is used for things like getting the damage of an item, or seeing
209    /// how much food it replenishes.
210    pub fn get_component<'a, T: components::DataComponentTrait>(&'a self) -> Option<Cow<'a, T>> {
211        if let Some(c) = self.component_patch.get::<T>() {
212            Some(Cow::Borrowed(c))
213        } else {
214            get_default_component::<T>(self.kind).map(|c| Cow::Owned(c))
215        }
216    }
217}
218
219impl AzaleaRead for ItemStack {
220    fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
221        let count = i32::azalea_read_var(buf)?;
222        if count <= 0 {
223            Ok(ItemStack::Empty)
224        } else {
225            let kind = azalea_registry::Item::azalea_read(buf)?;
226            let component_patch = DataComponentPatch::azalea_read(buf)?;
227            Ok(ItemStack::Present(ItemStackData {
228                count,
229                kind,
230                component_patch,
231            }))
232        }
233    }
234}
235
236impl AzaleaWrite for ItemStack {
237    fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
238        match self {
239            ItemStack::Empty => 0_i32.azalea_write_var(buf)?,
240            ItemStack::Present(i) => {
241                i.count.azalea_write_var(buf)?;
242                i.kind.azalea_write(buf)?;
243                i.component_patch.azalea_write(buf)?;
244            }
245        };
246        Ok(())
247    }
248}
249
250impl From<ItemStackData> for ItemStack {
251    fn from(item: ItemStackData) -> Self {
252        if item.is_empty() {
253            ItemStack::Empty
254        } else {
255            ItemStack::Present(item)
256        }
257    }
258}
259impl From<Item> for ItemStack {
260    fn from(item: Item) -> Self {
261        ItemStack::new(item, 1)
262    }
263}
264impl From<(Item, i32)> for ItemStack {
265    fn from(item: (Item, i32)) -> Self {
266        ItemStack::new(item.0, item.1)
267    }
268}
269impl From<Item> for ItemStackData {
270    fn from(item: Item) -> Self {
271        ItemStackData::new(item, 1)
272    }
273}
274impl From<(Item, i32)> for ItemStackData {
275    fn from(item: (Item, i32)) -> Self {
276        ItemStackData::new(item.0, item.1)
277    }
278}
279
280/// An update to an item's data components.
281///
282/// Note that in vanilla items come with their own set of default components,
283/// and Azalea does not implement that yet.
284#[derive(Default)]
285pub struct DataComponentPatch {
286    components: IndexMap<DataComponentKind, Option<DataComponentUnion>>,
287}
288
289impl DataComponentPatch {
290    /// Returns the value of the component in the generic argument for this
291    /// item.
292    ///
293    /// ```
294    /// # use azalea_inventory::{ItemStackData, DataComponentPatch, components};
295    /// # use azalea_registry::Item;
296    /// # fn example(item: &ItemStackData) -> Option<()> {
297    /// let item_nutrition = item.component_patch.get::<components::Food>()?.nutrition;
298    /// # Some(())
299    /// # }
300    /// ```
301    pub fn get<T: components::DataComponentTrait>(&self) -> Option<&T> {
302        let component = self.get_kind(T::KIND)?;
303        let component_any = component as &dyn Any;
304        component_any.downcast_ref::<T>()
305    }
306
307    pub fn get_kind(
308        &self,
309        kind: DataComponentKind,
310    ) -> Option<&dyn components::EncodableDataComponent> {
311        self.components.get(&kind).and_then(|c| {
312            c.as_ref().map(|c| {
313                // SAFETY: we just got the component from the map, so it must be the correct
314                // kind
315                unsafe { c.as_kind(kind) }
316            })
317        })
318    }
319
320    /// Returns whether the component in the generic argument is present for
321    /// this item.
322    ///
323    /// ```
324    /// # use azalea_inventory::{ItemStackData, DataComponentPatch, components};
325    /// # use azalea_registry::Item;
326    /// # let item = ItemStackData::from(Item::Stone);
327    /// let is_edible = item.component_patch.has::<components::Food>();
328    /// # assert!(!is_edible);
329    /// ```
330    pub fn has<T: components::DataComponentTrait>(&self) -> bool {
331        self.has_kind(T::KIND)
332    }
333
334    pub fn has_kind(&self, kind: DataComponentKind) -> bool {
335        self.get_kind(kind).is_some()
336    }
337
338    pub fn iter<'a>(
339        &'a self,
340    ) -> impl Iterator<
341        Item = (
342            DataComponentKind,
343            Option<&'a dyn components::EncodableDataComponent>,
344        ),
345    > + 'a {
346        self.components.iter().map(|(&kind, component)| {
347            component.as_ref().map_or_else(
348                || (kind, None),
349                |c| (kind, unsafe { Some(c.as_kind(kind)) }),
350            )
351        })
352    }
353    /// Insert a new component into this patch, or mark a component as removed.
354    ///
355    /// # Safety
356    /// The [`DataComponentUnion`] must be of the correct kind.
357    pub unsafe fn unchecked_insert_component(
358        &mut self,
359        kind: DataComponentKind,
360        value: Option<DataComponentUnion>,
361    ) {
362        self.components.insert(kind, value);
363    }
364}
365
366impl Drop for DataComponentPatch {
367    fn drop(&mut self) {
368        // the component values are ManuallyDrop since they're in a union
369        for (kind, component) in &mut self.components {
370            if let Some(component) = component {
371                // SAFETY: we got the kind and component from the map
372                unsafe { component.drop_as(*kind) };
373            }
374        }
375    }
376}
377
378impl AzaleaRead for DataComponentPatch {
379    fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
380        let components_with_data_count = u32::azalea_read_var(buf)?;
381        let components_without_data_count = u32::azalea_read_var(buf)?;
382
383        if components_without_data_count == 0 && components_with_data_count == 0 {
384            return Ok(DataComponentPatch::default());
385        }
386
387        let mut components = IndexMap::new();
388        for _ in 0..components_with_data_count {
389            let component_kind = DataComponentKind::azalea_read(buf)?;
390            let component_data = DataComponentUnion::azalea_read_as(component_kind, buf)?;
391            components.insert(component_kind, Some(component_data));
392        }
393
394        for _ in 0..components_without_data_count {
395            let component_kind = DataComponentKind::azalea_read(buf)?;
396            components.insert(component_kind, None);
397        }
398
399        Ok(DataComponentPatch { components })
400    }
401}
402
403impl AzaleaWrite for DataComponentPatch {
404    fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
405        let mut components_with_data_count: u32 = 0;
406        let mut components_without_data_count: u32 = 0;
407        for component in self.components.values() {
408            if component.is_some() {
409                components_with_data_count += 1;
410            } else {
411                components_without_data_count += 1;
412            }
413        }
414
415        components_with_data_count.azalea_write_var(buf)?;
416        components_without_data_count.azalea_write_var(buf)?;
417
418        let mut component_buf = Vec::new();
419        for (kind, component) in &self.components {
420            if let Some(component) = component {
421                kind.azalea_write(buf)?;
422
423                component_buf.clear();
424                // SAFETY: we got the component from the map and are passing in the same kind
425                unsafe { component.azalea_write_as(*kind, &mut component_buf) }?;
426                buf.write_all(&component_buf)?;
427            }
428        }
429
430        for (kind, component) in &self.components {
431            if component.is_none() {
432                kind.azalea_write(buf)?;
433            }
434        }
435
436        Ok(())
437    }
438}
439
440impl Clone for DataComponentPatch {
441    fn clone(&self) -> Self {
442        let mut components = IndexMap::with_capacity(self.components.len());
443        for (kind, component) in &self.components {
444            components.insert(
445                *kind,
446                component.as_ref().map(|c| unsafe { c.clone_as(*kind) }),
447            );
448        }
449        DataComponentPatch { components }
450    }
451}
452impl fmt::Debug for DataComponentPatch {
453    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
454        f.debug_set().entries(self.components.keys()).finish()
455    }
456}
457impl PartialEq for DataComponentPatch {
458    fn eq(&self, other: &Self) -> bool {
459        if self.components.len() != other.components.len() {
460            return false;
461        }
462        for (kind, component) in &self.components {
463            let Some(other_component) = other.components.get(kind) else {
464                return false;
465            };
466            // we can't use PartialEq, but we can use our own eq method
467            if let Some(component) = component {
468                let Some(other_component) = other_component else {
469                    return false;
470                };
471                // SAFETY: we already checked that the kinds are the same, and we got the
472                // components from the map, so they must be the correct kinds
473                if !unsafe { component.eq_as(other_component, *kind) } {
474                    return false;
475                }
476            } else if other_component.is_some() {
477                return false;
478            }
479        }
480        true
481    }
482}
483
484impl Serialize for DataComponentPatch {
485    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
486    where
487        S: serde::Serializer,
488    {
489        let mut s = serializer.serialize_map(Some(self.components.len()))?;
490        for (kind, component) in &self.components {
491            if let Some(component) = component {
492                unsafe { component.serialize_entry_as(&mut s, *kind) }?;
493            } else {
494                #[derive(Serialize)]
495                struct EmptyComponent;
496                s.serialize_entry(&format!("!{kind}"), &EmptyComponent)?;
497            }
498        }
499        s.end()
500    }
501}
502
503#[cfg(test)]
504mod tests {
505    use super::*;
506    use crate::components::MapId;
507
508    #[test]
509    fn test_get_component() {
510        let item = ItemStack::from(Item::Map).with_component(MapId { id: 1 });
511        let map_id = item.get_component::<MapId>().unwrap();
512        assert_eq!(map_id.id, 1);
513    }
514}