aboutsummaryrefslogtreecommitdiff
path: root/azalea-core/src/codec_utils.rs
diff options
context:
space:
mode:
authormat <27899617+mat-1@users.noreply.github.com>2025-08-10 18:55:23 -0500
committerGitHub <noreply@github.com>2025-08-10 18:55:23 -0500
commit7120842f9d2c659a2f12d8922299c2a761bc5582 (patch)
tree0d7976ceec82d914e4c75f23adcdd5839f9960a4 /azalea-core/src/codec_utils.rs
parent3b659833c1ad4cca89b4cd553193edcb6d223163 (diff)
downloadazalea-drasl-7120842f9d2c659a2f12d8922299c2a761bc5582.tar.xz
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
Diffstat (limited to 'azalea-core/src/codec_utils.rs')
-rw-r--r--azalea-core/src/codec_utils.rs83
1 files changed, 83 insertions, 0 deletions
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: Default + PartialEq>(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<S: Serializer, T: Serialize>(x: &Vec<T>, s: S) -> Result<S::Ok, S::Error> {
+ 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<Uuid>>,
+ serializer: S,
+) -> Result<S::Ok, S::Error> {
+ 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<const N: usize>(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<const N: usize>(pub [i64; N]);
+
+impl<const N: usize> Serialize for IntArray<N> {
+ fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+ // 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<const N: usize> Serialize for LongArray<N> {
+ fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+ // 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()
+ }
+}