From 7120842f9d2c659a2f12d8922299c2a761bc5582 Mon Sep 17 00:00:00 2001 From: mat <27899617+mat-1@users.noreply.github.com> Date: Sun, 10 Aug 2025 18:55:23 -0500 Subject: Send correct data component checksums (#234) * start implementing data component crc32 hashes * start doing serde impls for checksums * make more components hashable * make all data components serializable * support recursive components * fix simdnbt dep * update changelog * clippy --- azalea-core/src/codec_utils.rs | 83 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 azalea-core/src/codec_utils.rs (limited to 'azalea-core/src/codec_utils.rs') diff --git a/azalea-core/src/codec_utils.rs b/azalea-core/src/codec_utils.rs new file mode 100644 index 00000000..0014f86d --- /dev/null +++ b/azalea-core/src/codec_utils.rs @@ -0,0 +1,83 @@ +//! Some functions that are useful to have when implementing +//! `Serialize`/`Deserialize`, which Azalea uses to imitate Minecraft codecs. + +use azalea_buf::SerializableUuid; +use serde::{Serialize, Serializer, ser::SerializeTupleStruct}; +use uuid::Uuid; + +/// Intended to be used for skipping serialization if the value is the default. +/// +/// ```no_run +/// #[serde(skip_serializing_if = "is_default")] +/// ``` +pub fn is_default(t: &T) -> bool { + *t == Default::default() +} + +/// Intended to be used for skipping serialization if the value is `true`. +/// +/// ```no_run +/// #[serde(skip_serializing_if = "is_true")] +/// ``` +pub fn is_true(t: &bool) -> bool { + *t +} + +/// If the array has a single item, don't serialize as an array +/// +/// ```no_run +/// #[serde(serialize_with = "flatten_array")] +/// ``` +pub fn flatten_array(x: &Vec, s: S) -> Result { + if x.len() == 1 { + x[0].serialize(s) + } else { + x.serialize(s) + } +} + +/// Minecraft writes UUIDs as an IntArray<4> +pub fn uuid<'a, S: Serializer>( + uuid: impl Into<&'a Option>, + serializer: S, +) -> Result { + if let Some(uuid) = uuid.into() { + let arr: [u32; 4] = uuid.to_int_array(); + let arr: [i32; 4] = [arr[0] as i32, arr[1] as i32, arr[2] as i32, arr[3] as i32]; + IntArray(arr).serialize(serializer) + } else { + serializer.serialize_unit() + } +} + +/// An internal type that makes the i32 array be serialized differently. +/// +/// Azalea currently only uses this when writing checksums, but Minecraft also +/// uses this internally when converting types to NBT. +pub struct IntArray(pub [i32; N]); +/// An internal type that makes the i64 array be serialized differently. +/// +/// Azalea currently only uses this when writing checksums, but Minecraft also +/// uses this internally when converting types to NBT. +pub struct LongArray(pub [i64; N]); + +impl Serialize for IntArray { + fn serialize(&self, serializer: S) -> Result { + // see checksum::serialize_tuple_struct + let mut seq = serializer.serialize_tuple_struct("azalea:int_array", N)?; + for &item in &self.0 { + seq.serialize_field(&item)?; + } + seq.end() + } +} +impl Serialize for LongArray { + fn serialize(&self, serializer: S) -> Result { + // see checksum::serialize_tuple_struct + let mut seq = serializer.serialize_tuple_struct("azalea:long_array", N)?; + for &item in &self.0 { + seq.serialize_field(&item)?; + } + seq.end() + } +} -- cgit v1.2.3