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};
7
8use azalea_buf::{AzBuf, AzBufVar, 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 AzBuf 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    fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
235        match self {
236            ItemStack::Empty => 0_i32.azalea_write_var(buf)?,
237            ItemStack::Present(i) => {
238                i.count.azalea_write_var(buf)?;
239                i.kind.azalea_write(buf)?;
240                i.component_patch.azalea_write(buf)?;
241            }
242        };
243        Ok(())
244    }
245}
246
247impl From<ItemStackData> for ItemStack {
248    fn from(item: ItemStackData) -> Self {
249        if item.is_empty() {
250            ItemStack::Empty
251        } else {
252            ItemStack::Present(item)
253        }
254    }
255}
256impl From<ItemKind> for ItemStack {
257    fn from(item: ItemKind) -> Self {
258        ItemStack::new(item, 1)
259    }
260}
261impl From<(ItemKind, i32)> for ItemStack {
262    fn from(item: (ItemKind, i32)) -> Self {
263        ItemStack::new(item.0, item.1)
264    }
265}
266impl From<ItemKind> for ItemStackData {
267    fn from(item: ItemKind) -> Self {
268        ItemStackData::new(item, 1)
269    }
270}
271impl From<(ItemKind, i32)> for ItemStackData {
272    fn from(item: (ItemKind, i32)) -> Self {
273        ItemStackData::new(item.0, item.1)
274    }
275}
276
277/// An update to an item's data components.
278///
279/// Note that in vanilla items come with their own set of default components,
280/// and Azalea does not implement that yet.
281#[derive(Default)]
282pub struct DataComponentPatch {
283    components: Box<IndexMap<DataComponentKind, Option<DataComponentUnion>>>,
284}
285
286impl DataComponentPatch {
287    /// Returns the value of the component in the generic argument for this
288    /// item.
289    ///
290    /// ```
291    /// # use azalea_inventory::{ItemStackData, DataComponentPatch, components};
292    /// # use azalea_registry::builtin::ItemKind;
293    /// # fn example(item: &ItemStackData) -> Option<()> {
294    /// let item_nutrition = item.component_patch.get::<components::Food>()?.nutrition;
295    /// # Some(())
296    /// # }
297    /// ```
298    pub fn get<T: components::DataComponentTrait>(&self) -> Option<&T> {
299        let component = self.get_kind(T::KIND)?;
300        let component_any = component as &dyn Any;
301        component_any.downcast_ref::<T>()
302    }
303
304    pub fn get_kind(
305        &self,
306        kind: DataComponentKind,
307    ) -> Option<&dyn components::EncodableDataComponent> {
308        self.components.get(&kind).and_then(|c| {
309            c.as_ref().map(|c| {
310                // SAFETY: we just got the component from the map, so it must be the correct
311                // kind
312                unsafe { c.as_kind(kind) }
313            })
314        })
315    }
316
317    /// Returns whether the component in the generic argument is present for
318    /// this item.
319    ///
320    /// ```
321    /// # use azalea_inventory::{ItemStackData, DataComponentPatch, components};
322    /// # use azalea_registry::builtin::ItemKind;
323    /// # let item = ItemStackData::from(ItemKind::Stone);
324    /// let is_edible = item.component_patch.has::<components::Food>();
325    /// # assert!(!is_edible);
326    /// ```
327    pub fn has<T: components::DataComponentTrait>(&self) -> bool {
328        self.has_kind(T::KIND)
329    }
330
331    pub fn has_kind(&self, kind: DataComponentKind) -> bool {
332        self.get_kind(kind).is_some()
333    }
334
335    pub fn iter<'a>(
336        &'a self,
337    ) -> impl Iterator<
338        Item = (
339            DataComponentKind,
340            Option<&'a dyn components::EncodableDataComponent>,
341        ),
342    > + 'a {
343        self.components.iter().map(|(&kind, component)| {
344            component.as_ref().map_or_else(
345                || (kind, None),
346                |c| (kind, unsafe { Some(c.as_kind(kind)) }),
347            )
348        })
349    }
350    /// Insert a new component into this patch, or mark a component as removed.
351    ///
352    /// # Safety
353    /// The [`DataComponentUnion`] must be of the correct kind.
354    pub unsafe fn unchecked_insert_component(
355        &mut self,
356        kind: DataComponentKind,
357        value: Option<DataComponentUnion>,
358    ) {
359        let existing = self.components.insert(kind, value);
360        if let Some(Some(mut existing)) = existing {
361            // SAFETY: we just got it from self.components, so it must already be the
362            // correct type
363            unsafe { existing.drop_as(kind) };
364        }
365    }
366}
367
368impl Drop for DataComponentPatch {
369    fn drop(&mut self) {
370        // the component values are ManuallyDrop since they're in a union
371        for (kind, component) in self.components.iter_mut() {
372            if let Some(component) = component {
373                // SAFETY: we got the kind and component from the map
374                unsafe { component.drop_as(*kind) };
375            }
376        }
377    }
378}
379
380impl AzBuf for DataComponentPatch {
381    fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
382        let components_with_data_count = u32::azalea_read_var(buf)?;
383        let components_without_data_count = u32::azalea_read_var(buf)?;
384
385        if components_without_data_count == 0 && components_with_data_count == 0 {
386            return Ok(DataComponentPatch::default());
387        }
388
389        let mut components = DataComponentPatch::default();
390
391        for _ in 0..components_with_data_count {
392            let component_kind = DataComponentKind::azalea_read(buf)?;
393            let component_data = DataComponentUnion::azalea_read_as(component_kind, buf)?;
394            // SAFETY: it must be of the correct type because we just read using
395            // azalea_read_as
396            unsafe { components.unchecked_insert_component(component_kind, Some(component_data)) };
397        }
398
399        for _ in 0..components_without_data_count {
400            let component_kind = DataComponentKind::azalea_read(buf)?;
401            // SAFETY: the value is None so the kind doesn't matter anyways
402            unsafe { components.unchecked_insert_component(component_kind, None) };
403        }
404
405        Ok(components)
406    }
407    fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
408        let mut components_with_data_count: u32 = 0;
409        let mut components_without_data_count: u32 = 0;
410        for component in self.components.values() {
411            if component.is_some() {
412                components_with_data_count += 1;
413            } else {
414                components_without_data_count += 1;
415            }
416        }
417
418        components_with_data_count.azalea_write_var(buf)?;
419        components_without_data_count.azalea_write_var(buf)?;
420
421        let mut component_buf = Vec::new();
422        for (kind, component) in self.components.iter() {
423            if let Some(component) = component {
424                kind.azalea_write(buf)?;
425
426                component_buf.clear();
427                // SAFETY: we got the component from the map and are passing in the same kind
428                unsafe { component.azalea_write_as(*kind, &mut component_buf) }?;
429                buf.write_all(&component_buf)?;
430            }
431        }
432
433        for (kind, component) in self.components.iter() {
434            if component.is_none() {
435                kind.azalea_write(buf)?;
436            }
437        }
438
439        Ok(())
440    }
441}
442
443impl Clone for DataComponentPatch {
444    fn clone(&self) -> Self {
445        let mut components = IndexMap::with_capacity(self.components.len());
446        for (kind, component) in self.components.iter() {
447            components.insert(
448                *kind,
449                component.as_ref().map(|c| unsafe { c.clone_as(*kind) }),
450            );
451        }
452        DataComponentPatch {
453            components: Box::new(components),
454        }
455    }
456}
457impl Debug for DataComponentPatch {
458    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
459        f.debug_set().entries(self.components.keys()).finish()
460    }
461}
462impl PartialEq for DataComponentPatch {
463    fn eq(&self, other: &Self) -> bool {
464        if self.components.len() != other.components.len() {
465            return false;
466        }
467        for (kind, component) in self.components.iter() {
468            let Some(other_component) = other.components.get(kind) else {
469                return false;
470            };
471            // we can't use PartialEq, but we can use our own eq method
472            if let Some(component) = component {
473                let Some(other_component) = other_component else {
474                    return false;
475                };
476                // SAFETY: we already checked that the kinds are the same, and we got the
477                // components from the map, so they must be the correct kinds
478                if !unsafe { component.eq_as(other_component, *kind) } {
479                    return false;
480                }
481            } else if other_component.is_some() {
482                return false;
483            }
484        }
485        true
486    }
487}
488
489impl Serialize for DataComponentPatch {
490    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
491    where
492        S: serde::Serializer,
493    {
494        let mut s = serializer.serialize_map(Some(self.components.len()))?;
495        for (kind, component) in self.components.iter() {
496            if let Some(component) = component {
497                unsafe { component.serialize_entry_as(&mut s, *kind) }?;
498            } else {
499                #[derive(Serialize)]
500                struct EmptyComponent;
501                s.serialize_entry(&format!("!{kind}"), &EmptyComponent)?;
502            }
503        }
504        s.end()
505    }
506}
507
508#[cfg(test)]
509mod tests {
510    use super::*;
511    use crate::components::MapId;
512
513    #[test]
514    fn test_get_component() {
515        let item = ItemStack::from(ItemKind::Map).with_component(MapId { id: 1 });
516        let map_id = item.get_component::<MapId>().unwrap();
517        assert_eq!(map_id.id, 1);
518    }
519}