azalea_inventory/
slot.rs

1use std::{
2    any::Any,
3    borrow::Cow,
4    fmt::{self, Debug},
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::builtin::{DataComponentKind, ItemKind};
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(Clone, Debug, 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 [`ItemKind`]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: ItemKind, 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 [`ItemKind::Air`]
83    pub fn kind(&self) -> ItemKind {
84        match self {
85            ItemStack::Empty => ItemKind::Air,
86            ItemStack::Present(i) => i.kind,
87        }
88    }
89
90    /// Update whether this slot is empty, based on the count.
91    pub fn update_empty(&mut self) {
92        if let ItemStack::Present(i) = self
93            && i.is_empty()
94        {
95            *self = ItemStack::Empty;
96        }
97    }
98
99    /// Convert this slot into an [`ItemStackData`], if it's present.
100    pub fn as_present(&self) -> Option<&ItemStackData> {
101        match self {
102            ItemStack::Empty => None,
103            ItemStack::Present(i) => Some(i),
104        }
105    }
106
107    pub fn as_present_mut(&mut self) -> Option<&mut ItemStackData> {
108        match self {
109            ItemStack::Empty => None,
110            ItemStack::Present(i) => Some(i),
111        }
112    }
113
114    /// Get the value of a data component for this item.
115    ///
116    /// This is used for things like getting the damage of an item, or seeing
117    /// how much food it replenishes.
118    pub fn get_component<'a, T: components::DataComponentTrait>(&'a self) -> Option<Cow<'a, T>> {
119        self.as_present().and_then(|i| i.get_component::<T>())
120    }
121
122    pub fn with_component<
123        T: components::EncodableDataComponent + components::DataComponentTrait,
124    >(
125        mut self,
126        component: impl Into<Option<T>>,
127    ) -> Self {
128        if let ItemStack::Present(i) = &mut self {
129            let component: Option<T> = component.into();
130            let component: Option<DataComponentUnion> = component.map(|c| c.into());
131            i.component_patch.components.insert(T::KIND, component);
132        }
133        self
134    }
135}
136impl Serialize for ItemStack {
137    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
138    where
139        S: serde::Serializer,
140    {
141        match self {
142            ItemStack::Empty => serializer.serialize_unit(),
143            ItemStack::Present(i) => i.serialize(serializer),
144        }
145    }
146}
147
148/// An item in an inventory, with a count and a set of data components.
149///
150/// Usually you want [`ItemStack`] or
151/// [`ItemKind`](azalea_registry::builtin::ItemKind) instead.
152#[derive(Clone, Debug, PartialEq, Serialize)]
153pub struct ItemStackData {
154    #[serde(rename = "id")]
155    pub kind: ItemKind,
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 [`ItemKind`]s.
168    pub fn new(item: ItemKind, count: i32) -> Self {
169        ItemStackData {
170            count,
171            kind: item,
172            component_patch: Default::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 == ItemKind::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::builtin::ItemKind;
195    /// let mut a = ItemStackData::from(ItemKind::Stone);
196    /// let mut b = ItemStackData::new(ItemKind::Stone, 2);
197    /// assert!(a.is_same_item_and_components(&b));
198    ///
199    /// b.kind = ItemKind::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 = ItemKind::azalea_read(buf)?;
226            let component_patch = DataComponentPatch::azalea_read(buf)?;
227            Ok(ItemStack::from(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<ItemKind> for ItemStack {
260    fn from(item: ItemKind) -> Self {
261        ItemStack::new(item, 1)
262    }
263}
264impl From<(ItemKind, i32)> for ItemStack {
265    fn from(item: (ItemKind, i32)) -> Self {
266        ItemStack::new(item.0, item.1)
267    }
268}
269impl From<ItemKind> for ItemStackData {
270    fn from(item: ItemKind) -> Self {
271        ItemStackData::new(item, 1)
272    }
273}
274impl From<(ItemKind, i32)> for ItemStackData {
275    fn from(item: (ItemKind, 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: Box<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::builtin::ItemKind;
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::builtin::ItemKind;
326    /// # let item = ItemStackData::from(ItemKind::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 self.components.iter_mut() {
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 {
400            components: Box::new(components),
401        })
402    }
403}
404
405impl AzaleaWrite for DataComponentPatch {
406    fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
407        let mut components_with_data_count: u32 = 0;
408        let mut components_without_data_count: u32 = 0;
409        for component in self.components.values() {
410            if component.is_some() {
411                components_with_data_count += 1;
412            } else {
413                components_without_data_count += 1;
414            }
415        }
416
417        components_with_data_count.azalea_write_var(buf)?;
418        components_without_data_count.azalea_write_var(buf)?;
419
420        let mut component_buf = Vec::new();
421        for (kind, component) in self.components.iter() {
422            if let Some(component) = component {
423                kind.azalea_write(buf)?;
424
425                component_buf.clear();
426                // SAFETY: we got the component from the map and are passing in the same kind
427                unsafe { component.azalea_write_as(*kind, &mut component_buf) }?;
428                buf.write_all(&component_buf)?;
429            }
430        }
431
432        for (kind, component) in self.components.iter() {
433            if component.is_none() {
434                kind.azalea_write(buf)?;
435            }
436        }
437
438        Ok(())
439    }
440}
441
442impl Clone for DataComponentPatch {
443    fn clone(&self) -> Self {
444        let mut components = IndexMap::with_capacity(self.components.len());
445        for (kind, component) in self.components.iter() {
446            components.insert(
447                *kind,
448                component.as_ref().map(|c| unsafe { c.clone_as(*kind) }),
449            );
450        }
451        DataComponentPatch {
452            components: Box::new(components),
453        }
454    }
455}
456impl Debug for DataComponentPatch {
457    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
458        f.debug_set().entries(self.components.keys()).finish()
459    }
460}
461impl PartialEq for DataComponentPatch {
462    fn eq(&self, other: &Self) -> bool {
463        if self.components.len() != other.components.len() {
464            return false;
465        }
466        for (kind, component) in self.components.iter() {
467            let Some(other_component) = other.components.get(kind) else {
468                return false;
469            };
470            // we can't use PartialEq, but we can use our own eq method
471            if let Some(component) = component {
472                let Some(other_component) = other_component else {
473                    return false;
474                };
475                // SAFETY: we already checked that the kinds are the same, and we got the
476                // components from the map, so they must be the correct kinds
477                if !unsafe { component.eq_as(other_component, *kind) } {
478                    return false;
479                }
480            } else if other_component.is_some() {
481                return false;
482            }
483        }
484        true
485    }
486}
487
488impl Serialize for DataComponentPatch {
489    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
490    where
491        S: serde::Serializer,
492    {
493        let mut s = serializer.serialize_map(Some(self.components.len()))?;
494        for (kind, component) in self.components.iter() {
495            if let Some(component) = component {
496                unsafe { component.serialize_entry_as(&mut s, *kind) }?;
497            } else {
498                #[derive(Serialize)]
499                struct EmptyComponent;
500                s.serialize_entry(&format!("!{kind}"), &EmptyComponent)?;
501            }
502        }
503        s.end()
504    }
505}
506
507#[cfg(test)]
508mod tests {
509    use super::*;
510    use crate::components::MapId;
511
512    #[test]
513    fn test_get_component() {
514        let item = ItemStack::from(ItemKind::Map).with_component(MapId { id: 1 });
515        let map_id = item.get_component::<MapId>().unwrap();
516        assert_eq!(map_id.id, 1);
517    }
518}