azalea_protocol/packets/game/
s_container_click.rs

1use azalea_buf::AzBuf;
2use azalea_core::{checksum::Checksum, registry_holder::RegistryHolder};
3use azalea_inventory::{ItemStack, operations::ClickType};
4use azalea_protocol_macros::ServerboundGamePacket;
5use azalea_registry::builtin::{DataComponentKind, ItemKind};
6use indexmap::IndexMap;
7
8#[derive(Clone, Debug, AzBuf, PartialEq, ServerboundGamePacket)]
9pub struct ServerboundContainerClick {
10    #[var]
11    pub container_id: i32,
12    #[var]
13    pub state_id: u32,
14    pub slot_num: i16,
15    pub button_num: u8,
16    pub click_type: ClickType,
17    pub changed_slots: IndexMap<u16, HashedStack>,
18    pub carried_item: HashedStack,
19}
20
21/// Similar to an [`ItemStack`] but only carrying a CRC32 hash of the value of
22/// added data components instead of their entire contents.
23#[derive(Clone, Debug, AzBuf, PartialEq)]
24pub struct HashedStack(pub Option<HashedActualItem>);
25
26#[derive(Clone, Debug, AzBuf, PartialEq)]
27pub struct HashedActualItem {
28    pub kind: ItemKind,
29    #[var]
30    pub count: i32,
31    pub components: HashedPatchMap,
32}
33
34#[derive(Clone, Debug, AzBuf, PartialEq)]
35pub struct HashedPatchMap {
36    #[limit(256)]
37    pub added_components: Vec<(DataComponentKind, Checksum)>,
38    #[limit(256)]
39    pub removed_components: Vec<DataComponentKind>,
40}
41
42impl HashedStack {
43    /// Convert your [`ItemStack`] into a [`HashedStack`] by hashing the data
44    /// components.
45    ///
46    /// Minecraft uses this whenever the client sends data components to the
47    /// server.
48    ///
49    /// The [`RegistryHolder`] is required as some components will hash
50    /// differently based on the server's registries.
51    pub fn from_item_stack(item: &ItemStack, registries: &RegistryHolder) -> Self {
52        let ItemStack::Present(item) = item else {
53            return HashedStack(None);
54        };
55
56        let mut added_components = Vec::new();
57        let mut removed_components = Vec::new();
58
59        for (kind, data) in item.component_patch.iter() {
60            if let Some(data) = data {
61                added_components.push((kind, data.crc_hash(registries)));
62            } else {
63                removed_components.push(kind);
64            }
65        }
66
67        let components = HashedPatchMap {
68            added_components,
69            removed_components,
70        };
71        let item = HashedActualItem {
72            kind: item.kind,
73            count: item.count,
74            components,
75        };
76        Self(Some(item))
77    }
78}