azalea_core/registry_holder/
mod.rs

1//! The data sent to the client in the `ClientboundRegistryDataPacket`.
2//!
3//! This module contains the structures used to represent the registry
4//! sent to the client upon login. This contains a lot of information about
5//! the game, including the types of chat messages, dimensions, and
6//! biomes.
7
8pub mod block_predicate;
9pub mod block_state_provider;
10pub mod components;
11pub mod dimension_type;
12pub mod enchantment;
13pub mod entity_effect;
14pub mod float_provider;
15pub mod value;
16
17use std::{collections::HashMap, io::Cursor};
18
19use azalea_registry::identifier::Identifier;
20use indexmap::IndexMap;
21use simdnbt::{DeserializeError, FromNbtTag, borrow, owned::NbtCompound};
22use thiserror::Error;
23use tracing::error;
24
25/// The base of the registry.
26///
27/// This is the registry that is sent to the client upon login.
28///
29/// Note that `azalea-client` stores registries in `Instance` rather than
30/// per-client like you might expect. This is an optimization for swarms to
31/// reduce memory usage, since registries are expected to be the same for every
32/// client in a world.
33#[derive(Default, Debug, Clone)]
34pub struct RegistryHolder {
35    // if you add new fields here, don't forget to also update `RegistryHolder::append`,
36    // `protocol_id_to_identifier`, and `define_default_deserializes_to!` in
37    // `data_registry.rs`.
38    #[rustfmt::skip] // allow empty line
39
40    /// Attributes about the dimension.
41    pub dimension_type: RegistryType<dimension_type::DimensionKindElement>,
42
43    pub enchantment: RegistryType<enchantment::EnchantmentData>,
44
45    /// Registries that we haven't implemented deserializable types for.
46    ///
47    /// You can still access these just fine, but they'll be NBT instead of
48    /// nicer structs.
49    pub extra: HashMap<Identifier, RegistryType<NbtCompound>>,
50}
51
52macro_rules! registry_holder {
53    ($($registry:ident),* $(,)?) => {
54        impl RegistryHolder {
55            pub fn append(
56                &mut self,
57                id: Identifier,
58                entries: Vec<(Identifier, Option<NbtCompound>)>,
59            ) {
60
61                if id.namespace() == "minecraft" {
62                    match id.path() {
63                        $(
64                            stringify!($registry) => {
65                                return self.$registry.append_nbt(id, entries);
66                            }
67                        )*
68                        _ => {}
69                    }
70                }
71
72                self.extra
73                    .entry(id.clone())
74                    .or_default()
75                    .append_nbt(id, entries);
76            }
77
78            pub fn extend(&mut self, other: RegistryHolder) {
79                $(
80                    self.$registry = other.$registry;
81                )*
82                self.extra.extend(other.extra);
83            }
84
85            /// Convert a protocol ID for a registry key (like the protocol_id for
86            /// something that implements `DataRegistry`) and convert it to its string
87            /// name.
88            pub fn protocol_id_to_identifier(
89                &self,
90                registry: Identifier,
91                protocol_id: u32,
92            ) -> Option<&Identifier> {
93                let index = protocol_id as usize;
94
95                if registry.namespace() == "minecraft" {
96                    match registry.path() {
97                        $(
98                            stringify!($registry) => {
99                                return self.$registry.map.get_index(index).map(|(k, _)| k);
100                            }
101                        )*
102                        _ => {}
103                    }
104                }
105
106                self.extra
107                    .get(&registry)
108                    .and_then(|r| r.map.get_index(index))
109                    .map(|(k, _)| k)
110            }
111        }
112    };
113}
114
115registry_holder!(dimension_type, enchantment);
116
117fn nbt_to_serializable_type<T: simdnbt::Deserialize>(
118    value: &NbtCompound,
119) -> Result<T, NbtToSerializableTypeError> {
120    // convert the value to T
121    let mut nbt_bytes = Vec::new();
122    value.write(&mut nbt_bytes);
123    let nbt_borrow_compound = simdnbt::borrow::read_compound(&mut Cursor::new(&nbt_bytes))?;
124    T::from_compound((&nbt_borrow_compound).into()).map_err(Into::into)
125}
126
127#[derive(Error, Debug)]
128enum NbtToSerializableTypeError {
129    #[error(transparent)]
130    NbtError(#[from] simdnbt::Error),
131    #[error(transparent)]
132    DeserializeError(#[from] simdnbt::DeserializeError),
133}
134
135/// A collection of values for a certain type of registry data.
136#[derive(Debug, Clone)]
137pub struct RegistryType<T: simdnbt::Deserialize> {
138    pub map: IndexMap<Identifier, T>,
139}
140
141impl<T: simdnbt::Deserialize> Default for RegistryType<T> {
142    fn default() -> Self {
143        Self {
144            map: IndexMap::new(),
145        }
146    }
147}
148
149impl<T: simdnbt::Deserialize> RegistryType<T> {
150    fn append_nbt(&mut self, id: Identifier, entries: Vec<(Identifier, Option<NbtCompound>)>) {
151        let map = &mut self.map;
152        for (key, value) in entries {
153            if let Some(value) = value {
154                match nbt_to_serializable_type(&value) {
155                    Ok(value) => {
156                        map.insert(key, value);
157                    }
158                    Err(err) => {
159                        error!("Error deserializing {id} entry {key}: {err:?}\n{value:?}");
160                    }
161                }
162            } else {
163                map.shift_remove(&key);
164            }
165        }
166    }
167}
168
169pub trait RegistryDeserializesTo: simdnbt::Deserialize {
170    fn get_for_registry<'a>(
171        registries: &'a RegistryHolder,
172        registry_name: &'static str,
173        protocol_id: u32,
174    ) -> Option<(&'a Identifier, &'a Self)>;
175}
176
177impl RegistryDeserializesTo for NbtCompound {
178    fn get_for_registry<'a>(
179        registries: &'a RegistryHolder,
180        registry_name: &'static str,
181        protocol_id: u32,
182    ) -> Option<(&'a Identifier, &'a Self)> {
183        registries
184            .extra
185            .get(&Identifier::new(registry_name))?
186            .map
187            .get_index(protocol_id as usize)
188    }
189}
190impl RegistryDeserializesTo for dimension_type::DimensionKindElement {
191    fn get_for_registry<'a>(
192        registries: &'a RegistryHolder,
193        registry_name: &'static str,
194        protocol_id: u32,
195    ) -> Option<(&'a Identifier, &'a Self)> {
196        if registry_name != "dimension_type" {
197            error!(
198                "called RegistryDeserializesTo::get_for_registry with the wrong registry: {registry_name}"
199            );
200        }
201        registries
202            .dimension_type
203            .map
204            .get_index(protocol_id as usize)
205    }
206}
207impl RegistryDeserializesTo for enchantment::EnchantmentData {
208    fn get_for_registry<'a>(
209        registries: &'a RegistryHolder,
210        registry_name: &'static str,
211        protocol_id: u32,
212    ) -> Option<(&'a Identifier, &'a Self)> {
213        if registry_name != "enchantment" {
214            error!(
215                "called RegistryDeserializesTo::get_for_registry with the wrong registry: {registry_name}"
216            );
217        }
218        registries.enchantment.map.get_index(protocol_id as usize)
219    }
220}
221
222pub fn get_in_compound<T: FromNbtTag>(
223    compound: &borrow::NbtCompound,
224    key: &str,
225) -> Result<T, DeserializeError> {
226    let value = compound.get(key).ok_or(DeserializeError::MissingField)?;
227    T::from_nbt_tag(value).ok_or(DeserializeError::MissingField)
228}