Skip to main content

azalea_inventory/
slot.rs

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