1
2
Fork 0
mirror of https://github.com/mat-1/azalea.git synced 2025-08-02 06:16:04 +00:00

update ServerboundContainerClick to use HashedStack from 1.21.5

This commit is contained in:
mat 2025-03-29 23:10:55 +00:00
parent d0b459e827
commit 43ebbee94a
7 changed files with 85 additions and 12 deletions

1
Cargo.lock generated
View file

@ -492,6 +492,7 @@ dependencies = [
"azalea-registry", "azalea-registry",
"azalea-world", "azalea-world",
"bevy_ecs", "bevy_ecs",
"crc32fast",
"flate2", "flate2",
"futures", "futures",
"futures-lite", "futures-lite",

View file

@ -80,6 +80,7 @@ num-format = "0.4.4"
indexmap = "2.7.1" indexmap = "2.7.1"
paste = "1.0.15" paste = "1.0.15"
compact_str = "0.8.1" compact_str = "0.8.1"
crc32fast = "1.4.2"
# --- Profile Settings --- # --- Profile Settings ---

View file

@ -10,7 +10,8 @@ use azalea_inventory::{
}, },
}; };
use azalea_protocol::packets::game::{ use azalea_protocol::packets::game::{
s_container_click::ServerboundContainerClick, s_container_close::ServerboundContainerClose, s_container_click::{HashedStack, ServerboundContainerClick},
s_container_close::ServerboundContainerClose,
s_set_carried_item::ServerboundSetCarriedItem, s_set_carried_item::ServerboundSetCarriedItem,
}; };
use azalea_registry::MenuKind; use azalea_registry::MenuKind;
@ -23,7 +24,7 @@ use bevy_ecs::{
schedule::{IntoSystemConfigs, SystemSet}, schedule::{IntoSystemConfigs, SystemSet},
system::{Commands, Query}, system::{Commands, Query},
}; };
use tracing::warn; use tracing::{error, warn};
use super::packet::game::handle_outgoing_packets; use super::packet::game::handle_outgoing_packets;
use crate::{ use crate::{
@ -667,8 +668,8 @@ pub fn handle_container_click_event(
for event in events.read() { for event in events.read() {
let (entity, mut inventory) = query.get_mut(event.entity).unwrap(); let (entity, mut inventory) = query.get_mut(event.entity).unwrap();
if inventory.id != event.window_id { if inventory.id != event.window_id {
warn!( error!(
"Tried to click container with ID {}, but the current container ID is {}", "Tried to click container with ID {}, but the current container ID is {}. Click packet won't be sent.",
event.window_id, inventory.id event.window_id, inventory.id
); );
continue; continue;
@ -681,11 +682,11 @@ pub fn handle_container_click_event(
// see which slots changed after clicking and put them in the hashmap // see which slots changed after clicking and put them in the hashmap
// the server uses this to check if we desynced // the server uses this to check if we desynced
let mut changed_slots: HashMap<u16, ItemStack> = HashMap::new(); let mut changed_slots: HashMap<u16, HashedStack> = HashMap::new();
for (slot_index, old_slot) in old_slots.iter().enumerate() { for (slot_index, old_slot) in old_slots.iter().enumerate() {
let new_slot = &menu.slots()[slot_index]; let new_slot = &menu.slots()[slot_index];
if old_slot != new_slot { if old_slot != new_slot {
changed_slots.insert(slot_index as u16, new_slot.clone()); changed_slots.insert(slot_index as u16, HashedStack::from(new_slot));
} }
} }
@ -721,7 +722,7 @@ fn handle_set_container_content_event(
if event.container_id != inventory.id { if event.container_id != inventory.id {
warn!( warn!(
"Tried to set container content with ID {}, but the current container ID is {}", "Got SetContainerContentEvent for container with ID {}, but the current container ID is {}",
event.container_id, inventory.id event.container_id, inventory.id
); );
continue; continue;

View file

@ -178,7 +178,8 @@ impl AzaleaWrite for ItemStack {
/// and Azalea does not implement that yet. /// and Azalea does not implement that yet.
#[derive(Default)] #[derive(Default)]
pub struct DataComponentPatch { pub struct DataComponentPatch {
components: IndexMap<DataComponentKind, Option<Box<dyn components::EncodableDataComponent>>>, pub components:
IndexMap<DataComponentKind, Option<Box<dyn components::EncodableDataComponent>>>,
} }
impl DataComponentPatch { impl DataComponentPatch {

View file

@ -48,8 +48,10 @@ tokio-util = { workspace = true, features = ["codec"] }
tracing.workspace = true tracing.workspace = true
hickory-resolver = { workspace = true, features = ["tokio-runtime"] } hickory-resolver = { workspace = true, features = ["tokio-runtime"] }
uuid.workspace = true uuid.workspace = true
crc32fast = { workspace = true, optional = true }
[features] [features]
connecting = [] connecting = []
default = ["packets"] default = ["packets"]
packets = ["connecting", "dep:azalea-core"] packets = ["connecting", "dep:azalea-core", "crc32"]
crc32 = ["crc32fast"]

View file

@ -9,13 +9,16 @@ use syn::{
fn as_packet_derive(input: TokenStream, state: proc_macro2::TokenStream) -> TokenStream { fn as_packet_derive(input: TokenStream, state: proc_macro2::TokenStream) -> TokenStream {
let DeriveInput { ident, data, .. } = parse_macro_input!(input); let DeriveInput { ident, data, .. } = parse_macro_input!(input);
// technically it would still work with enums and non-named structs but for
// consistency in the api it's nicer if they are all just structs, which is why
// we enforce this here
let syn::Data::Struct(syn::DataStruct { fields, .. }) = &data else { let syn::Data::Struct(syn::DataStruct { fields, .. }) = &data else {
panic!("#[derive(*Packet)] can only be used on structs") panic!("#[derive(*Packet)] can only be used on structs")
}; };
let (syn::Fields::Named(_) | syn::Fields::Unit) = fields else { let (syn::Fields::Named(_) | syn::Fields::Unit) = fields else {
panic!("#[derive(*Packet)] can only be used on structs with named fields") panic!("#[derive(*Packet)] can only be used on structs with named fields")
}; };
let variant_name = variant_name_from(&ident); let variant_name = variant_name_from(&ident);
let contents = quote! { let contents = quote! {

View file

@ -1,6 +1,6 @@
use std::collections::HashMap; use std::collections::HashMap;
use azalea_buf::AzBuf; use azalea_buf::{AzBuf, AzaleaWrite};
use azalea_inventory::{ItemStack, operations::ClickType}; use azalea_inventory::{ItemStack, operations::ClickType};
use azalea_protocol_macros::ServerboundGamePacket; use azalea_protocol_macros::ServerboundGamePacket;
@ -13,6 +13,70 @@ pub struct ServerboundContainerClick {
pub slot_num: i16, pub slot_num: i16,
pub button_num: u8, pub button_num: u8,
pub click_type: ClickType, pub click_type: ClickType,
pub changed_slots: HashMap<u16, ItemStack>, pub changed_slots: HashMap<u16, HashedStack>,
pub carried_item: ItemStack, pub carried_item: ItemStack,
} }
/// 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))
}
}