valence_registry/
codec.rs

1use std::collections::BTreeMap;
2
3use bevy_app::prelude::*;
4use bevy_ecs::prelude::*;
5use tracing::error;
6use valence_ident::Ident;
7use valence_nbt::{compound, Compound, List, Value};
8
9use crate::RegistrySet;
10
11pub(super) fn build(app: &mut App) {
12    app.init_resource::<RegistryCodec>()
13        .add_systems(PostUpdate, cache_registry_codec.in_set(RegistrySet));
14}
15
16/// Contains the registry codec sent to all players while joining. This contains
17/// information for biomes and dimensions among other things.
18///
19/// Generally, end users should not manipulate the registry codec directly. Use
20/// one of the other registry resources instead.
21#[derive(Resource, Debug)]
22pub struct RegistryCodec {
23    pub registries: BTreeMap<Ident<String>, Vec<RegistryValue>>,
24    // TODO: store this in binary form?
25    cached_codec: Compound,
26}
27
28#[derive(Clone, Debug)]
29pub struct RegistryValue {
30    pub name: Ident<String>,
31    pub element: Compound,
32}
33
34impl RegistryCodec {
35    pub fn cached_codec(&self) -> &Compound {
36        &self.cached_codec
37    }
38
39    pub fn registry(&self, registry_key: Ident<&str>) -> &Vec<RegistryValue> {
40        self.registries
41            .get(registry_key.as_str())
42            .unwrap_or_else(|| panic!("missing registry for {registry_key}"))
43    }
44
45    pub fn registry_mut(&mut self, registry_key: Ident<&str>) -> &mut Vec<RegistryValue> {
46        self.registries
47            .get_mut(registry_key.as_str())
48            .unwrap_or_else(|| panic!("missing registry for {registry_key}"))
49    }
50}
51
52impl Default for RegistryCodec {
53    fn default() -> Self {
54        let codec = include_bytes!("../extracted/registry_codec.dat");
55        let compound = valence_nbt::from_binary(&mut codec.as_slice())
56            .expect("failed to decode vanilla registry codec")
57            .0;
58
59        let mut registries = BTreeMap::new();
60
61        for (k, v) in compound {
62            let reg_name: Ident<String> = Ident::new(k).expect("invalid registry name").into();
63            let mut reg_values = vec![];
64
65            let Value::Compound(mut outer) = v else {
66                error!("registry {reg_name} is not a compound");
67                continue;
68            };
69
70            let values = match outer.remove("value") {
71                Some(Value::List(List::Compound(values))) => values,
72                Some(Value::List(List::End)) => continue,
73                _ => {
74                    error!("missing \"value\" compound in {reg_name}");
75                    continue;
76                }
77            };
78
79            for mut value in values {
80                let Some(Value::String(name)) = value.remove("name") else {
81                    error!("missing \"name\" string in value for {reg_name}");
82                    continue;
83                };
84
85                let name = match Ident::new(name) {
86                    Ok(n) => n.into(),
87                    Err(e) => {
88                        error!("invalid registry value name \"{}\"", e.0);
89                        continue;
90                    }
91                };
92
93                let Some(Value::Compound(element)) = value.remove("element") else {
94                    error!("missing \"element\" compound in value for {reg_name}");
95                    continue;
96                };
97
98                reg_values.push(RegistryValue { name, element });
99            }
100
101            registries.insert(reg_name, reg_values);
102        }
103
104        Self {
105            registries,
106            // Cache will be created later.
107            cached_codec: Compound::new(),
108        }
109    }
110}
111
112fn cache_registry_codec(codec: ResMut<RegistryCodec>) {
113    if codec.is_changed() {
114        let codec = codec.into_inner();
115
116        codec.cached_codec.clear();
117
118        for (reg_name, reg) in &codec.registries {
119            let mut value = vec![];
120
121            for (id, v) in reg.iter().enumerate() {
122                value.push(compound! {
123                    "id" => id as i32,
124                    "name" => v.name.as_str(),
125                    "element" => v.element.clone(),
126                });
127            }
128
129            let registry = compound! {
130                "type" => reg_name.as_str(),
131                "value" => List::Compound(value),
132            };
133
134            codec.cached_codec.insert(reg_name.as_str(), registry);
135        }
136    }
137}