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