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:
parent
d0b459e827
commit
43ebbee94a
7 changed files with 85 additions and 12 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -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",
|
||||||
|
|
|
@ -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 ---
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -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! {
|
||||||
|
|
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue