azalea_protocol/packets/game/
s_container_click.rs

1use std::collections::HashMap;
2
3use azalea_buf::AzBuf;
4use azalea_core::{checksum::Checksum, registry_holder::RegistryHolder};
5use azalea_inventory::{ItemStack, operations::ClickType};
6use azalea_protocol_macros::ServerboundGamePacket;
7
8#[derive(Clone, Debug, AzBuf, 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: HashMap<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)]
24pub struct HashedStack(pub Option<HashedActualItem>);
25
26#[derive(Clone, Debug, AzBuf)]
27pub struct HashedActualItem {
28    pub kind: azalea_registry::Item,
29    #[var]
30    pub count: i32,
31    pub components: HashedPatchMap,
32}
33
34#[derive(Clone, Debug, AzBuf)]
35pub struct HashedPatchMap {
36    /// The value is a CRC32 hash of the data component's network serialization.
37    /// (kind + data)
38    #[limit(256)]
39    pub added_components: Vec<(azalea_registry::DataComponentKind, Checksum)>,
40    #[limit(256)]
41    pub removed_components: Vec<azalea_registry::DataComponentKind>,
42}
43
44impl HashedStack {
45    /// Convert your [`ItemStack`] into a [`HashedStack`] by hashing the data
46    /// components.
47    ///
48    /// Minecraft uses this whenever the client sends data components to the
49    /// server.
50    ///
51    /// The [`RegistryHolder`] is required as some components will hash
52    /// differently based on the server's registries.
53    pub fn from_item_stack(item: &ItemStack, registries: &RegistryHolder) -> Self {
54        let ItemStack::Present(item) = item else {
55            return HashedStack(None);
56        };
57
58        let mut added_components = Vec::new();
59        let mut removed_components = Vec::new();
60
61        for (kind, data) in item.component_patch.iter() {
62            if let Some(data) = data {
63                added_components.push((kind, data.crc_hash(registries)));
64            } else {
65                removed_components.push(kind);
66            }
67        }
68
69        let components = HashedPatchMap {
70            added_components,
71            removed_components,
72        };
73        let item = HashedActualItem {
74            kind: item.kind,
75            count: item.count,
76            components,
77        };
78        Self(Some(item))
79    }
80}