From 2c610826fc9f8e16897f52313faa8e0602d1dc3d Mon Sep 17 00:00:00 2001 From: mat <27899617+mat-1@users.noreply.github.com> Date: Sun, 19 Nov 2023 22:07:38 -0600 Subject: Replace azalea-nbt with simdnbt (#111) * delete azalea-nbt and replace with simdnbt * use simdnbt from crates.io * remove serde dependency on azalea-registry --- azalea-core/src/registry_holder.rs | 287 ++++++++++++++--------------------- azalea-core/src/resource_location.rs | 25 ++- azalea-core/src/slot.rs | 40 ----- 3 files changed, 135 insertions(+), 217 deletions(-) delete mode 100755 azalea-core/src/slot.rs (limited to 'azalea-core/src') diff --git a/azalea-core/src/registry_holder.rs b/azalea-core/src/registry_holder.rs index 7f811e23..6d58f77a 100644 --- a/azalea-core/src/registry_holder.rs +++ b/azalea-core/src/registry_holder.rs @@ -6,100 +6,103 @@ //! biomes. use azalea_buf::{BufReadError, McBufReadable, McBufWritable}; -use azalea_nbt::Nbt; -use serde::{ - de::{self, DeserializeOwned}, - Deserialize, Deserializer, Serialize, Serializer, +use simdnbt::{ + owned::{NbtCompound, NbtTag}, + Deserialize, FromNbtTag, Serialize, ToNbtTag, }; use std::{collections::HashMap, io::Cursor}; +use tracing::error; use crate::resource_location::ResourceLocation; /// The base of the registry. /// /// This is the registry that is sent to the client upon login. -#[derive(Default, Debug, Clone, Serialize, Deserialize)] +#[derive(Default, Debug, Clone)] pub struct RegistryHolder { - pub map: HashMap, + pub map: HashMap, } impl RegistryHolder { - fn get(&self, name: &ResourceLocation) -> Option { - let nbt = self.map.get(name)?; - serde_json::from_value(serde_json::to_value(nbt).ok()?).ok() + fn get( + &self, + name: &ResourceLocation, + ) -> Option> { + self.map.get(name).map(|nbt| T::from_compound(nbt.clone())) } /// Get the dimension type registry, or `None` if it doesn't exist. You /// should do some type of error handling if this returns `None`. pub fn dimension_type(&self) -> Option> { - self.get(&ResourceLocation::new("minecraft:dimension_type")) - } -} - -impl TryFrom for RegistryHolder { - type Error = serde_json::Error; - - fn try_from(value: Nbt) -> Result { - Ok(RegistryHolder { - map: serde_json::from_value(serde_json::to_value(value)?)?, - }) - } -} - -impl TryInto for RegistryHolder { - type Error = serde_json::Error; - - fn try_into(self) -> Result { - serde_json::from_value(serde_json::to_value(self.map)?) + let name = ResourceLocation::new("minecraft:dimension_type"); + match self.get(&name) { + Some(Ok(registry)) => Some(registry), + Some(Err(err)) => { + error!( + "Error deserializing dimension type registry: {err:?}\n{:?}", + self.map.get(&name) + ); + None + } + None => None, + } } } impl McBufReadable for RegistryHolder { fn read_from(buf: &mut Cursor<&[u8]>) -> Result { - RegistryHolder::try_from(Nbt::read_from(buf)?) - .map_err(|e| BufReadError::Deserialization { source: e }) + let nbt_compound = NbtCompound::read_from(buf)?; + Ok(RegistryHolder { + map: simdnbt::Deserialize::from_compound(nbt_compound)?, + }) } } impl McBufWritable for RegistryHolder { fn write_into(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> { - TryInto::::try_into(self.clone())?.write_into(buf) + let mut written = Vec::new(); + self.map.clone().to_compound().write_into(&mut written)?; + buf.write_all(&written) } } /// A collection of values for a certain type of registry data. #[derive(Debug, Clone, Serialize, Deserialize)] -#[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] -pub struct RegistryType { - #[serde(rename = "type")] +#[cfg_attr(feature = "strict_registry", simdnbt(deny_unknown_fields))] +pub struct RegistryType +where + T: Serialize + Deserialize, +{ + #[simdnbt(rename = "type")] pub kind: ResourceLocation, pub value: Vec>, } /// A value for a certain type of registry data. #[derive(Debug, Clone, Serialize, Deserialize)] -#[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] -pub struct TypeValue { +#[cfg_attr(feature = "strict_registry", simdnbt(deny_unknown_fields))] +pub struct TypeValue +where + T: Serialize + Deserialize, +{ pub id: u32, pub name: ResourceLocation, pub element: T, } #[derive(Debug, Clone, Serialize, Deserialize)] -#[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] +#[cfg_attr(feature = "strict_registry", simdnbt(deny_unknown_fields))] pub struct TrimMaterialElement { pub asset_name: String, pub ingredient: ResourceLocation, pub item_model_index: f32, pub override_armor_materials: HashMap, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] pub description: Option, } /// Data about a kind of chat message #[derive(Debug, Clone, Serialize, Deserialize)] -#[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] +#[cfg_attr(feature = "strict_registry", simdnbt(deny_unknown_fields))] pub struct ChatTypeElement { pub chat: ChatTypeData, pub narration: ChatTypeData, @@ -107,48 +110,29 @@ pub struct ChatTypeElement { /// Data about a chat message. #[derive(Debug, Clone, Serialize, Deserialize)] -#[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] +#[cfg_attr(feature = "strict_registry", simdnbt(deny_unknown_fields))] pub struct ChatTypeData { pub translation_key: String, pub parameters: Vec, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] pub style: Option, } /// The style of a chat message. #[derive(Debug, Clone, Serialize, Deserialize)] -#[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] +#[cfg_attr(feature = "strict_registry", simdnbt(deny_unknown_fields))] pub struct ChatTypeStyle { - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] pub color: Option, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] - #[serde(with = "Convert")] pub bold: Option, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] - #[serde(with = "Convert")] pub italic: Option, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] - #[serde(with = "Convert")] pub underlined: Option, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] - #[serde(with = "Convert")] pub strikethrough: Option, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] - #[serde(with = "Convert")] pub obfuscated: Option, } /// Dimension attributes. #[cfg(feature = "strict_registry")] #[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] +#[simdnbt(deny_unknown_fields)] pub struct DimensionTypeElement { pub ambient_light: f32, #[serde(with = "Convert")] @@ -186,99 +170,124 @@ pub struct DimensionTypeElement { pub struct DimensionTypeElement { pub height: u32, pub min_y: i32, - #[serde(flatten)] - pub _extra: HashMap, + #[simdnbt(flatten)] + pub _extra: HashMap, } /// The light level at which monsters can spawn. /// /// This can be either a single minimum value, or a formula with a min and /// max. -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(untagged)] -#[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] +#[derive(Debug, Clone)] +// #[serde(untagged)] +#[cfg_attr(feature = "strict_registry", simdnbt(deny_unknown_fields))] pub enum MonsterSpawnLightLevel { /// A simple minimum value. Simple(u32), /// A complex value with a type, minimum, and maximum. /// Vanilla minecraft only uses one type, "minecraft:uniform". Complex { - #[serde(rename = "type")] kind: ResourceLocation, value: MonsterSpawnLightLevelValues, }, } +impl FromNbtTag for MonsterSpawnLightLevel { + fn from_nbt_tag(tag: simdnbt::owned::NbtTag) -> Option { + if let Some(value) = tag.int() { + Some(Self::Simple(value as u32)) + } else if let Some(value) = tag.compound() { + let kind = ResourceLocation::from_nbt_tag(value.get("type")?.clone())?; + let value = MonsterSpawnLightLevelValues::from_nbt_tag(value.get("value")?.clone())?; + Some(Self::Complex { kind, value }) + } else { + None + } + } +} + +impl ToNbtTag for MonsterSpawnLightLevel { + fn to_nbt_tag(self) -> simdnbt::owned::NbtTag { + match self { + Self::Simple(value) => value.to_nbt_tag(), + Self::Complex { kind, value } => { + let mut compound = NbtCompound::new(); + compound.insert("type", kind.to_nbt_tag()); + compound.insert("value", value.to_nbt_tag()); + simdnbt::owned::NbtTag::Compound(compound) + } + } + } +} + /// The min and max light levels at which monsters can spawn. /// /// Values are inclusive. #[derive(Debug, Copy, Clone, Serialize, Deserialize)] -#[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] +#[cfg_attr(feature = "strict_registry", simdnbt(deny_unknown_fields))] pub struct MonsterSpawnLightLevelValues { - #[serde(rename = "min_inclusive")] + #[simdnbt(rename = "min_inclusive")] pub min: u32, - #[serde(rename = "max_inclusive")] + #[simdnbt(rename = "max_inclusive")] pub max: u32, } /// Biome attributes. #[derive(Debug, Clone, Serialize, Deserialize)] -#[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] +#[cfg_attr(feature = "strict_registry", simdnbt(deny_unknown_fields))] pub struct WorldTypeElement { - #[serde(with = "Convert")] pub has_precipitation: bool, pub temperature: f32, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] pub temperature_modifier: Option, pub downfall: f32, pub effects: BiomeEffects, } /// The precipitation of a biome. -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] -#[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] +#[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum BiomePrecipitation { - #[serde(rename = "none")] None, - #[serde(rename = "rain")] Rain, - #[serde(rename = "snow")] Snow, } +impl FromNbtTag for BiomePrecipitation { + fn from_nbt_tag(tag: NbtTag) -> Option { + match tag.string()?.to_str().as_ref() { + "none" => Some(Self::None), + "rain" => Some(Self::Rain), + "snow" => Some(Self::Snow), + _ => None, + } + } +} +impl ToNbtTag for BiomePrecipitation { + fn to_nbt_tag(self) -> NbtTag { + match self { + Self::None => NbtTag::String("none".into()), + Self::Rain => NbtTag::String("rain".into()), + Self::Snow => NbtTag::String("snow".into()), + } + } +} /// The effects of a biome. /// /// This includes the sky, fog, water, and grass color, /// as well as music and other sound effects. #[derive(Debug, Clone, Serialize, Deserialize)] -#[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] +#[cfg_attr(feature = "strict_registry", simdnbt(deny_unknown_fields))] pub struct BiomeEffects { pub sky_color: u32, pub fog_color: u32, pub water_color: u32, pub water_fog_color: u32, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] pub foliage_color: Option, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] pub grass_color: Option, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] pub grass_color_modifier: Option, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] pub music: Option, pub mood_sound: BiomeMoodSound, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] pub additions_sound: Option, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] pub ambient_sound: Option, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] pub particle: Option, } @@ -286,9 +295,8 @@ pub struct BiomeEffects { /// /// Some biomes have unique music that only play when inside them. #[derive(Debug, Clone, Serialize, Deserialize)] -#[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] +#[cfg_attr(feature = "strict_registry", simdnbt(deny_unknown_fields))] pub struct BiomeMusic { - #[serde(with = "Convert")] pub replace_current_music: bool, pub max_delay: u32, pub min_delay: u32, @@ -296,7 +304,7 @@ pub struct BiomeMusic { } #[derive(Debug, Clone, Serialize, Deserialize)] -#[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] +#[cfg_attr(feature = "strict_registry", simdnbt(deny_unknown_fields))] pub struct BiomeMoodSound { pub tick_delay: u32, pub block_search_extent: u32, @@ -305,7 +313,7 @@ pub struct BiomeMoodSound { } #[derive(Debug, Clone, Serialize, Deserialize)] -#[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] +#[cfg_attr(feature = "strict_registry", simdnbt(deny_unknown_fields))] pub struct AdditionsSound { pub tick_chance: f32, pub sound: azalea_registry::SoundEvent, @@ -315,98 +323,25 @@ pub struct AdditionsSound { /// /// Some biomes have particles that spawn in the air. #[derive(Debug, Clone, Serialize, Deserialize)] -#[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] +#[cfg_attr(feature = "strict_registry", simdnbt(deny_unknown_fields))] pub struct BiomeParticle { pub probability: f32, pub options: HashMap, } #[derive(Debug, Clone, Serialize, Deserialize)] -#[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] +#[cfg_attr(feature = "strict_registry", simdnbt(deny_unknown_fields))] pub struct TrimPatternElement { - #[serde(flatten)] + #[simdnbt(flatten)] pub pattern: HashMap, } #[derive(Debug, Clone, Serialize, Deserialize)] -#[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] +#[cfg_attr(feature = "strict_registry", simdnbt(deny_unknown_fields))] pub struct DamageTypeElement { pub message_id: String, pub scaling: String, pub exhaustion: f32, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] pub effects: Option, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] pub death_message_type: Option, } - -// Using a trait because you can't implement methods for -// types you don't own, in this case Option and bool. -trait Convert: Sized { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer; - - fn deserialize<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>; -} - -// Convert between bool and u8 -impl Convert for bool { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_u8(if *self { 1 } else { 0 }) - } - - fn deserialize<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - convert::(u8::deserialize(deserializer)?) - } -} - -// Convert between Option and u8 -impl Convert for Option { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - if let Some(value) = self { - Convert::serialize(value, serializer) - } else { - serializer.serialize_none() - } - } - - fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> - where - D: Deserializer<'de>, - { - if let Some(value) = Option::::deserialize(deserializer)? { - Ok(Some(convert::(value)?)) - } else { - Ok(None) - } - } -} - -// Deserializing logic here to deduplicate code -fn convert<'de, D>(value: u8) -> Result -where - D: Deserializer<'de>, -{ - match value { - 0 => Ok(false), - 1 => Ok(true), - other => Err(de::Error::invalid_value( - de::Unexpected::Unsigned(other as u64), - &"zero or one", - )), - } -} diff --git a/azalea-core/src/resource_location.rs b/azalea-core/src/resource_location.rs index cc669841..e6a70247 100755 --- a/azalea-core/src/resource_location.rs +++ b/azalea-core/src/resource_location.rs @@ -1,7 +1,11 @@ //! A resource, like minecraft:stone use azalea_buf::{BufReadError, McBufReadable, McBufWritable}; -use std::io::{Cursor, Write}; +use simdnbt::{owned::NbtTag, FromNbtTag, ToNbtTag}; +use std::{ + io::{Cursor, Write}, + str::FromStr, +}; #[cfg(feature = "serde")] use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; @@ -47,6 +51,13 @@ impl std::fmt::Debug for ResourceLocation { write!(f, "{}:{}", self.namespace, self.path) } } +impl FromStr for ResourceLocation { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + Ok(ResourceLocation::new(s)) + } +} impl McBufReadable for ResourceLocation { fn read_from(buf: &mut Cursor<&[u8]>) -> Result { @@ -88,6 +99,18 @@ impl<'de> Deserialize<'de> for ResourceLocation { } } +impl FromNbtTag for ResourceLocation { + fn from_nbt_tag(tag: NbtTag) -> Option { + tag.string().and_then(|s| s.to_str().parse().ok()) + } +} + +impl ToNbtTag for ResourceLocation { + fn to_nbt_tag(self) -> NbtTag { + NbtTag::String(self.to_string().into()) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/azalea-core/src/slot.rs b/azalea-core/src/slot.rs deleted file mode 100755 index 22961437..00000000 --- a/azalea-core/src/slot.rs +++ /dev/null @@ -1,40 +0,0 @@ -// TODO: have an azalea-inventory or azalea-container crate and put this there - -use azalea_buf::{BufReadError, McBuf, McBufReadable, McBufWritable}; -use azalea_nbt::Nbt; -use std::io::{Cursor, Write}; - -#[derive(Debug, Clone, Default)] -pub enum Slot { - #[default] - Empty, - Present(SlotData), -} - -#[derive(Debug, Clone, McBuf)] -pub struct SlotData { - #[var] - pub id: u32, - pub count: u8, - pub nbt: Nbt, -} - -impl McBufReadable for Slot { - fn read_from(buf: &mut Cursor<&[u8]>) -> Result { - let slot = Option::::read_from(buf)?; - Ok(slot.map_or(Slot::Empty, Slot::Present)) - } -} - -impl McBufWritable for Slot { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - match self { - Slot::Empty => false.write_into(buf)?, - Slot::Present(i) => { - true.write_into(buf)?; - i.write_into(buf)?; - } - }; - Ok(()) - } -} -- cgit v1.2.3