//! The data sent to the client in the `ClientboundRegistryDataPacket`. //! //! This module contains the structures used to represent the registry //! sent to the client upon login. This contains a lot of information about //! the game, including the types of chat messages, dimensions, and //! biomes. use azalea_buf::{BufReadError, McBufReadable, McBufWritable}; 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)] pub struct RegistryHolder { pub map: HashMap, } impl RegistryHolder { 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> { 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 { 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> { 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", 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", 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", simdnbt(deny_unknown_fields))] pub struct TrimMaterialElement { pub asset_name: String, pub ingredient: ResourceLocation, pub item_model_index: f32, pub override_armor_materials: HashMap, pub description: Option, } /// Data about a kind of chat message #[derive(Debug, Clone, Serialize, Deserialize)] #[cfg_attr(feature = "strict_registry", simdnbt(deny_unknown_fields))] pub struct ChatTypeElement { pub chat: ChatTypeData, pub narration: ChatTypeData, } /// Data about a chat message. #[derive(Debug, Clone, Serialize, Deserialize)] #[cfg_attr(feature = "strict_registry", simdnbt(deny_unknown_fields))] pub struct ChatTypeData { pub translation_key: String, pub parameters: Vec, pub style: Option, } /// The style of a chat message. #[derive(Debug, Clone, Serialize, Deserialize)] #[cfg_attr(feature = "strict_registry", simdnbt(deny_unknown_fields))] pub struct ChatTypeStyle { pub color: Option, pub bold: Option, pub italic: Option, pub underlined: Option, pub strikethrough: Option, pub obfuscated: Option, } /// Dimension attributes. #[cfg(feature = "strict_registry")] #[derive(Debug, Clone, Serialize, Deserialize)] #[simdnbt(deny_unknown_fields)] pub struct DimensionTypeElement { pub ambient_light: f32, #[serde(with = "Convert")] pub bed_works: bool, pub coordinate_scale: f32, pub effects: ResourceLocation, #[serde(default)] #[serde(skip_serializing_if = "Option::is_none")] pub fixed_time: Option, #[serde(with = "Convert")] pub has_ceiling: bool, #[serde(with = "Convert")] pub has_raids: bool, #[serde(with = "Convert")] pub has_skylight: bool, pub height: u32, pub infiniburn: ResourceLocation, pub logical_height: u32, pub min_y: i32, pub monster_spawn_block_light_limit: u32, pub monster_spawn_light_level: MonsterSpawnLightLevel, #[serde(with = "Convert")] pub natural: bool, #[serde(with = "Convert")] pub piglin_safe: bool, #[serde(with = "Convert")] pub respawn_anchor_works: bool, #[serde(with = "Convert")] pub ultrawarm: bool, } /// Dimension attributes. #[cfg(not(feature = "strict_registry"))] #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DimensionTypeElement { pub height: u32, pub min_y: i32, #[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)] // #[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 { 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", simdnbt(deny_unknown_fields))] pub struct MonsterSpawnLightLevelValues { #[simdnbt(rename = "min_inclusive")] pub min: u32, #[simdnbt(rename = "max_inclusive")] pub max: u32, } /// Biome attributes. #[derive(Debug, Clone, Serialize, Deserialize)] #[cfg_attr(feature = "strict_registry", simdnbt(deny_unknown_fields))] pub struct WorldTypeElement { pub has_precipitation: bool, pub temperature: f32, pub temperature_modifier: Option, pub downfall: f32, pub effects: BiomeEffects, } /// The precipitation of a biome. #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum BiomePrecipitation { None, Rain, 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", simdnbt(deny_unknown_fields))] pub struct BiomeEffects { pub sky_color: u32, pub fog_color: u32, pub water_color: u32, pub water_fog_color: u32, pub foliage_color: Option, pub grass_color: Option, pub grass_color_modifier: Option, pub music: Option, pub mood_sound: BiomeMoodSound, pub additions_sound: Option, pub ambient_sound: Option, pub particle: Option, } /// The music of the biome. /// /// Some biomes have unique music that only play when inside them. #[derive(Debug, Clone, Serialize, Deserialize)] #[cfg_attr(feature = "strict_registry", simdnbt(deny_unknown_fields))] pub struct BiomeMusic { pub replace_current_music: bool, pub max_delay: u32, pub min_delay: u32, pub sound: azalea_registry::SoundEvent, } #[derive(Debug, Clone, Serialize, Deserialize)] #[cfg_attr(feature = "strict_registry", simdnbt(deny_unknown_fields))] pub struct BiomeMoodSound { pub tick_delay: u32, pub block_search_extent: u32, pub offset: f32, pub sound: azalea_registry::SoundEvent, } #[derive(Debug, Clone, Serialize, Deserialize)] #[cfg_attr(feature = "strict_registry", simdnbt(deny_unknown_fields))] pub struct AdditionsSound { pub tick_chance: f32, pub sound: azalea_registry::SoundEvent, } /// Biome particles. /// /// Some biomes have particles that spawn in the air. #[derive(Debug, Clone, Serialize, Deserialize)] #[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", simdnbt(deny_unknown_fields))] pub struct TrimPatternElement { #[simdnbt(flatten)] pub pattern: HashMap, } #[derive(Debug, Clone, Serialize, Deserialize)] #[cfg_attr(feature = "strict_registry", simdnbt(deny_unknown_fields))] pub struct DamageTypeElement { pub message_id: String, pub scaling: String, pub exhaustion: f32, pub effects: Option, pub death_message_type: Option, }