chunkedge_registry/
codec.rs

1use std::collections::BTreeMap;
2
3use bevy_app::prelude::*;
4use bevy_ecs::prelude::*;
5use chunkedge_ident::Ident;
6use chunkedge_nbt::{compound, Compound, List, Value};
7use tracing::error;
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        // TODO: we might want to reorder the biome registry so that the
55        // biome ID match with the actual biomes see https://minecraft.fandom.com/wiki/Biome/ID
56        // maybe this could be handled in the extractor or we just ignore it, since
57        // biomes can be added/removed at runtime.
58        let codec = include_bytes!("../extracted/registry_codec.json");
59        let compound = serde_json::from_slice::<Compound>(codec)
60            .expect("failed to decode vanilla registry codec");
61
62        let mut registries = BTreeMap::new();
63
64        for (k, v) in compound {
65            let reg_name: Ident<String> = Ident::new(k).expect("invalid registry name").into();
66            let mut reg_values = vec![];
67
68            let Value::Compound(inner) = v else {
69                error!("registry {reg_name} is not a compound");
70                continue;
71            };
72
73            for (k, v) in inner {
74                let name = match Ident::new(k) {
75                    Ok(n) => n.into(),
76                    Err(e) => {
77                        error!("invalid registry value name \"{}\"", e.0);
78                        continue;
79                    }
80                };
81
82                let Value::Compound(value) = v else {
83                    error!("registry value {name} is not a compound");
84                    continue;
85                };
86
87                reg_values.push(RegistryValue {
88                    name,
89                    element: value,
90                });
91            }
92
93            registries.insert(reg_name, reg_values);
94        }
95
96        Self {
97            registries,
98            // Cache will be created later.
99            cached_codec: Compound::new(),
100        }
101    }
102}
103
104fn cache_registry_codec(codec: ResMut<RegistryCodec>) {
105    if codec.is_changed() {
106        let codec = codec.into_inner();
107
108        codec.cached_codec.clear();
109
110        for (reg_name, reg) in &codec.registries {
111            let mut value = vec![];
112
113            for (id, v) in reg.iter().enumerate() {
114                value.push(compound! {
115                    "id" => id as i32,
116                    "name" => v.name.as_str(),
117                    "element" => v.element.clone(),
118                });
119            }
120
121            let registry = compound! {
122                "type" => reg_name.as_str(),
123                "value" => List::Compound(value),
124            };
125
126            codec.cached_codec.insert(reg_name.as_str(), registry);
127        }
128    }
129}