aboutsummaryrefslogtreecommitdiff
path: root/azalea-protocol/src/packets/game/s_container_click.rs
blob: 190853637b4407e9c079e386d390819319c1a0c2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
use std::collections::HashMap;

use azalea_buf::{AzBuf, AzaleaWrite};
use azalea_inventory::{ItemStack, operations::ClickType};
use azalea_protocol_macros::ServerboundGamePacket;

#[derive(Clone, Debug, AzBuf, ServerboundGamePacket)]
pub struct ServerboundContainerClick {
    #[var]
    pub container_id: i32,
    #[var]
    pub state_id: u32,
    pub slot_num: i16,
    pub button_num: u8,
    pub click_type: ClickType,
    pub changed_slots: HashMap<u16, HashedStack>,
    pub carried_item: HashedStack,
}

/// Similar to an [`ItemStack`] but only carrying a CRC32 hash of the value of
/// added data components instead of their entire contents.
#[derive(Clone, Debug, AzBuf)]
pub struct HashedStack(pub Option<HashedActualItem>);

#[derive(Clone, Debug, AzBuf)]
pub struct HashedActualItem {
    pub kind: azalea_registry::Item,
    #[var]
    pub count: i32,
    pub components: HashedPatchMap,
}

#[derive(Clone, Debug, AzBuf)]
pub struct HashedPatchMap {
    /// The value is a CRC32 hash of the data component's network serialization.
    /// (kind + data)
    #[limit(256)]
    pub added_components: Vec<(azalea_registry::DataComponentKind, u32)>,
    #[limit(256)]
    pub removed_components: Vec<azalea_registry::DataComponentKind>,
}

/// Convert your [`ItemStack`] into a [`HashedStack`] by hashing the data
/// components.
///
/// This will be necessary if you're writing a client or server, but if you're
/// just making a proxy then you can remove the `crc32` dependency by disabling
/// the `crc32` feature on `azalea-protocol`.
#[cfg(feature = "crc32")]
impl From<&ItemStack> for HashedStack {
    fn from(item: &ItemStack) -> Self {
        let ItemStack::Present(item) = item else {
            return Self(None);
        };

        let mut added_components = Vec::new();
        let mut removed_components = Vec::new();

        for (&kind, data) in &item.components.components {
            if let Some(data) = data {
                // encodeCap in TypedDataComponent.java
                let mut buf = Vec::new();
                kind.azalea_write(&mut buf).unwrap();
                data.encode(&mut buf).unwrap();
                added_components.push((kind, crc32fast::hash(&buf)));
            } else {
                removed_components.push(kind);
            }
        }

        let components = HashedPatchMap {
            added_components,
            removed_components,
        };
        let item = HashedActualItem {
            kind: item.kind,
            count: item.count,
            components,
        };
        Self(Some(item))
    }
}