azalea_protocol/packets/game/
s_container_click.rs

1use std::collections::HashMap;
2
3use azalea_buf::{AzBuf, AzaleaWrite};
4use azalea_inventory::{ItemStack, operations::ClickType};
5use azalea_protocol_macros::ServerboundGamePacket;
6
7#[derive(Clone, Debug, AzBuf, 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: HashMap<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)]
23pub struct HashedStack(pub Option<HashedActualItem>);
24
25#[derive(Clone, Debug, AzBuf)]
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)]
34pub struct HashedPatchMap {
35    /// The value is a CRC32 hash of the data component's network serialization.
36    /// (kind + data)
37    #[limit(256)]
38    pub added_components: Vec<(azalea_registry::DataComponentKind, u32)>,
39    #[limit(256)]
40    pub removed_components: Vec<azalea_registry::DataComponentKind>,
41}
42
43/// Convert your [`ItemStack`] into a [`HashedStack`] by hashing the data
44/// components.
45///
46/// This will be necessary if you're writing a client or server, but if you're
47/// just making a proxy then you can remove the `crc32` dependency by disabling
48/// the `crc32` feature on `azalea-protocol`.
49#[cfg(feature = "crc32")]
50impl From<&ItemStack> for HashedStack {
51    fn from(item: &ItemStack) -> Self {
52        let ItemStack::Present(item) = item else {
53            return Self(None);
54        };
55
56        let mut added_components = Vec::new();
57        let mut removed_components = Vec::new();
58
59        for (&kind, data) in &item.components.components {
60            if let Some(data) = data {
61                // encodeCap in TypedDataComponent.java
62                let mut buf = Vec::new();
63                kind.azalea_write(&mut buf).unwrap();
64                data.encode(&mut buf).unwrap();
65                added_components.push((kind, crc32fast::hash(&buf)));
66            } else {
67                removed_components.push(kind);
68            }
69        }
70
71        let components = HashedPatchMap {
72            added_components,
73            removed_components,
74        };
75        let item = HashedActualItem {
76            kind: item.kind,
77            count: item.count,
78            components,
79        };
80        Self(Some(item))
81    }
82}