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 NBT. Usually you want
150/// [`ItemStack`] or [`azalea_registry::Item`] instead.
151#[derive(Debug, Clone, PartialEq, Serialize)]
152pub struct ItemStackData {
153    #[serde(rename = "id")]
154    pub kind: azalea_registry::Item,
155    /// The amount of the item in this slot.
156    ///
157    /// The count can be zero or negative, but this is rare.
158    pub count: i32,
159    /// The item's components that the server set to be different from the
160    /// defaults.
161    #[serde(rename = "components", skip_serializing_if = "is_default")]
162    pub component_patch: DataComponentPatch,
163}
164
165impl ItemStackData {
166    /// Create a new [`ItemStackData`] with the given number of [`Item`]s.
167    pub fn new(item: Item, count: i32) -> Self {
168        ItemStackData {
169            count,
170            kind: item,
171            component_patch: DataComponentPatch::default(),
172        }
173    }
174
175    /// Remove `count` items from this slot, returning the removed items.
176    pub fn split(&mut self, count: u32) -> ItemStackData {
177        let returning_count = i32::min(count as i32, self.count);
178        let mut returning = self.clone();
179        returning.count = returning_count;
180        self.count -= returning_count;
181        returning
182    }
183
184    /// Check if the count of the item is <= 0 or if the item is air.
185    pub fn is_empty(&self) -> bool {
186        self.count <= 0 || self.kind == azalea_registry::Item::Air
187    }
188
189    /// Whether this item is the same as another item, ignoring the count.
190    ///
191    /// ```
192    /// # use azalea_inventory::ItemStackData;
193    /// # use azalea_registry::Item;
194    /// let mut a = ItemStackData::from(Item::Stone);
195    /// let mut b = ItemStackData::new(Item::Stone, 2);
196    /// assert!(a.is_same_item_and_components(&b));
197    ///
198    /// b.kind = Item::Dirt;
199    /// assert!(!a.is_same_item_and_components(&b));
200    /// ```
201    pub fn is_same_item_and_components(&self, other: &ItemStackData) -> bool {
202        self.kind == other.kind && self.component_patch == other.component_patch
203    }
204
205    /// Get the value of a data component for this item.
206    ///
207    /// This is used for things like getting the damage of an item, or seeing
208    /// how much food it replenishes.
209    pub fn get_component<'a, T: components::DataComponentTrait>(&'a self) -> Option<Cow<'a, T>> {
210        if let Some(c) = self.component_patch.get::<T>() {
211            Some(Cow::Borrowed(c))
212        } else {
213            get_default_component::<T>(self.kind).map(|c| Cow::Owned(c))
214        }
215    }
216}
217
218impl AzaleaRead for ItemStack {
219    fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
220        let count = i32::azalea_read_var(buf)?;
221        if count <= 0 {
222            Ok(ItemStack::Empty)
223        } else {
224            let kind = azalea_registry::Item::azalea_read(buf)?;
225            let component_patch = DataComponentPatch::azalea_read(buf)?;
226            Ok(ItemStack::Present(ItemStackData {
227                count,
228                kind,
229                component_patch,
230            }))
231        }
232    }
233}
234
235impl AzaleaWrite for ItemStack {
236    fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
237        match self {
238            ItemStack::Empty => 0_i32.azalea_write_var(buf)?,
239            ItemStack::Present(i) => {
240                i.count.azalea_write_var(buf)?;
241                i.kind.azalea_write(buf)?;
242                i.component_patch.azalea_write(buf)?;
243            }
244        };
245        Ok(())
246    }
247}
248
249impl From<ItemStackData> for ItemStack {
250    fn from(item: ItemStackData) -> Self {
251        if item.is_empty() {
252            ItemStack::Empty
253        } else {
254            ItemStack::Present(item)
255        }
256    }
257}
258impl From<Item> for ItemStack {
259    fn from(item: Item) -> Self {
260        ItemStack::new(item, 1)
261    }
262}
263impl From<(Item, i32)> for ItemStack {
264    fn from(item: (Item, i32)) -> Self {
265        ItemStack::new(item.0, item.1)
266    }
267}
268impl From<Item> for ItemStackData {
269    fn from(item: Item) -> Self {
270        ItemStackData::new(item, 1)
271    }
272}
273impl From<(Item, i32)> for ItemStackData {
274    fn from(item: (Item, i32)) -> Self {
275        ItemStackData::new(item.0, item.1)
276    }
277}
278
279/// An update to an item's data components.
280///
281/// Note that in vanilla items come with their own set of default components,
282/// and Azalea does not implement that yet.
283#[derive(Default)]
284pub struct DataComponentPatch {
285    components: IndexMap<DataComponentKind, Option<DataComponentUnion>>,
286}
287
288impl DataComponentPatch {
289    /// Returns the value of the component in the generic argument for this
290    /// item.
291    ///
292    /// ```
293    /// # use azalea_inventory::{ItemStackData, DataComponentPatch, components};
294    /// # use azalea_registry::Item;
295    /// # fn example(item: &ItemStackData) -> Option<()> {
296    /// let item_nutrition = item.component_patch.get::<components::Food>()?.nutrition;
297    /// # Some(())
298    /// # }
299    /// ```
300    pub fn get<T: components::DataComponentTrait>(&self) -> Option<&T> {
301        let component = self.get_kind(T::KIND)?;
302        let component_any = component as &dyn Any;
303        component_any.downcast_ref::<T>()
304    }
305
306    pub fn get_kind(
307        &self,
308        kind: DataComponentKind,
309    ) -> Option<&dyn components::EncodableDataComponent> {
310        self.components.get(&kind).and_then(|c| {
311            c.as_ref().map(|c| {
312                // SAFETY: we just got the component from the map, so it must be the correct
313                // kind
314                unsafe { c.as_kind(kind) }
315            })
316        })
317    }
318
319    /// Returns whether the component in the generic argument is present for
320    /// this item.
321    ///
322    /// ```
323    /// # use azalea_inventory::{ItemStackData, DataComponentPatch, components};
324    /// # use azalea_registry::Item;
325    /// # let item = ItemStackData::from(Item::Stone);
326    /// let is_edible = item.component_patch.has::<components::Food>();
327    /// # assert!(!is_edible);
328    /// ```
329    pub fn has<T: components::DataComponentTrait>(&self) -> bool {
330        self.has_kind(T::KIND)
331    }
332
333    pub fn has_kind(&self, kind: DataComponentKind) -> bool {
334        self.get_kind(kind).is_some()
335    }
336
337    pub fn iter<'a>(
338        &'a self,
339    ) -> impl Iterator<
340        Item = (
341            DataComponentKind,
342            Option<&'a dyn components::EncodableDataComponent>,
343        ),
344    > + 'a {
345        self.components.iter().map(|(&kind, component)| {
346            component.as_ref().map_or_else(
347                || (kind, None),
348                |c| (kind, unsafe { Some(c.as_kind(kind)) }),
349            )
350        })
351    }
352    /// Insert a new component into this patch, or mark a component as removed.
353    ///
354    /// # Safety
355    /// The [`DataComponentUnion`] must be of the correct kind.
356    pub unsafe fn unchecked_insert_component(
357        &mut self,
358        kind: DataComponentKind,
359        value: Option<DataComponentUnion>,
360    ) {
361        self.components.insert(kind, value);
362    }
363}
364
365impl Drop for DataComponentPatch {
366    fn drop(&mut self) {
367        // the component values are ManuallyDrop since they're in a union
368        for (kind, component) in &mut self.components {
369            if let Some(component) = component {
370                // SAFETY: we got the kind and component from the map
371                unsafe { component.drop_as(*kind) };
372            }
373        }
374    }
375}
376
377impl AzaleaRead for DataComponentPatch {
378    fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
379        let components_with_data_count = u32::azalea_read_var(buf)?;
380        let components_without_data_count = u32::azalea_read_var(buf)?;
381
382        if components_without_data_count == 0 && components_with_data_count == 0 {
383            return Ok(DataComponentPatch::default());
384        }
385
386        let mut components = IndexMap::new();
387        for _ in 0..components_with_data_count {
388            let component_kind = DataComponentKind::azalea_read(buf)?;
389            let component_data = DataComponentUnion::azalea_read_as(component_kind, buf)?;
390            components.insert(component_kind, Some(component_data));
391        }
392
393        for _ in 0..components_without_data_count {
394            let component_kind = DataComponentKind::azalea_read(buf)?;
395            components.insert(component_kind, None);
396        }
397
398        Ok(DataComponentPatch { components })
399    }
400}
401
402impl AzaleaWrite for DataComponentPatch {
403    fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
404        let mut components_with_data_count: u32 = 0;
405        let mut components_without_data_count: u32 = 0;
406        for component in self.components.values() {
407            if component.is_some() {
408                components_with_data_count += 1;
409            } else {
410                components_without_data_count += 1;
411            }
412        }
413
414        components_with_data_count.azalea_write_var(buf)?;
415        components_without_data_count.azalea_write_var(buf)?;
416
417        let mut component_buf = Vec::new();
418        for (kind, component) in &self.components {
419            if let Some(component) = component {
420                kind.azalea_write(buf)?;
421
422                component_buf.clear();
423                // SAFETY: we got the component from the map and are passing in the same kind
424                unsafe { component.azalea_write_as(*kind, &mut component_buf) }?;
425                buf.write_all(&component_buf)?;
426            }
427        }
428
429        for (kind, component) in &self.components {
430            if component.is_none() {
431                kind.azalea_write(buf)?;
432            }
433        }
434
435        Ok(())
436    }
437}
438
439impl Clone for DataComponentPatch {
440    fn clone(&self) -> Self {
441        let mut components = IndexMap::with_capacity(self.components.len());
442        for (kind, component) in &self.components {
443            components.insert(
444                *kind,
445                component.as_ref().map(|c| unsafe { c.clone_as(*kind) }),
446            );
447        }
448        DataComponentPatch { components }
449    }
450}
451impl fmt::Debug for DataComponentPatch {
452    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
453        f.debug_set().entries(self.components.keys()).finish()
454    }
455}
456impl PartialEq for DataComponentPatch {
457    fn eq(&self, other: &Self) -> bool {
458        if self.components.len() != other.components.len() {
459            return false;
460        }
461        for (kind, component) in &self.components {
462            let Some(other_component) = other.components.get(kind) else {
463                return false;
464            };
465            // we can't use PartialEq, but we can use our own eq method
466            if let Some(component) = component {
467                let Some(other_component) = other_component else {
468                    return false;
469                };
470                // SAFETY: we already checked that the kinds are the same, and we got the
471                // components from the map, so they must be the correct kinds
472                if !unsafe { component.eq_as(other_component, *kind) } {
473                    return false;
474                }
475            } else if other_component.is_some() {
476                return false;
477            }
478        }
479        true
480    }
481}
482
483impl Serialize for DataComponentPatch {
484    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
485    where
486        S: serde::Serializer,
487    {
488        let mut s = serializer.serialize_map(Some(self.components.len()))?;
489        for (kind, component) in &self.components {
490            if let Some(component) = component {
491                unsafe { component.serialize_entry_as(&mut s, *kind) }?;
492            } else {
493                #[derive(Serialize)]
494                struct EmptyComponent;
495                s.serialize_entry(&format!("!{kind}"), &EmptyComponent)?;
496            }
497        }
498        s.end()
499    }
500}
501
502#[cfg(test)]
503mod tests {
504    use super::*;
505    use crate::components::MapId;
506
507    #[test]
508    fn test_get_component() {
509        let item = ItemStack::from(Item::Map).with_component(MapId { id: 1 });
510        let map_id = item.get_component::<MapId>().unwrap();
511        assert_eq!(map_id.id, 1);
512    }
513}