valence_registry/
biome.rs

1//! Contains biomes and the biome registry. Minecraft's default biomes are added
2//! to the registry by default.
3//!
4//! ### **NOTE:**
5//! - Modifying the biome registry after the server has started can break
6//!   invariants within instances and clients! Make sure there are no instances
7//!   or clients spawned before mutating.
8//! - A biome named "minecraft:plains" must exist. Otherwise, vanilla clients
9//!   will be disconnected.
10
11use std::ops::{Deref, DerefMut};
12
13use bevy_app::prelude::*;
14use bevy_ecs::prelude::*;
15use serde::{Deserialize, Serialize};
16use tracing::error;
17use valence_ident::{ident, Ident};
18use valence_nbt::serde::CompoundSerializer;
19
20use crate::codec::{RegistryCodec, RegistryValue};
21use crate::{Registry, RegistryIdx, RegistrySet};
22
23pub struct BiomePlugin;
24
25impl Plugin for BiomePlugin {
26    fn build(&self, app: &mut App) {
27        app.init_resource::<BiomeRegistry>()
28            .add_systems(PreStartup, load_default_biomes)
29            .add_systems(PostUpdate, update_biome_registry.before(RegistrySet));
30    }
31}
32
33fn load_default_biomes(mut reg: ResMut<BiomeRegistry>, codec: Res<RegistryCodec>) {
34    let mut helper = move || -> anyhow::Result<()> {
35        for value in codec.registry(BiomeRegistry::KEY) {
36            let biome = Biome::deserialize(value.element.clone())?;
37
38            reg.insert(value.name.clone(), biome);
39        }
40
41        // Move "plains" to the front so that `BiomeId::default()` is the ID of plains.
42        reg.swap_to_front(ident!("plains"));
43
44        Ok(())
45    };
46
47    if let Err(e) = helper() {
48        error!("failed to load default biomes from registry codec: {e:#}");
49    }
50}
51
52fn update_biome_registry(reg: Res<BiomeRegistry>, mut codec: ResMut<RegistryCodec>) {
53    if reg.is_changed() {
54        let biomes = codec.registry_mut(BiomeRegistry::KEY);
55
56        biomes.clear();
57
58        biomes.extend(reg.iter().map(|(_, name, biome)| {
59            RegistryValue {
60                name: name.into(),
61                element: biome
62                    .serialize(CompoundSerializer)
63                    .expect("failed to serialize biome"),
64            }
65        }));
66    }
67}
68
69#[derive(Resource, Default, Debug)]
70pub struct BiomeRegistry {
71    reg: Registry<BiomeId, Biome>,
72}
73
74impl BiomeRegistry {
75    pub const KEY: Ident<&'static str> = ident!("worldgen/biome");
76}
77
78impl Deref for BiomeRegistry {
79    type Target = Registry<BiomeId, Biome>;
80
81    fn deref(&self) -> &Self::Target {
82        &self.reg
83    }
84}
85
86impl DerefMut for BiomeRegistry {
87    fn deref_mut(&mut self) -> &mut Self::Target {
88        &mut self.reg
89    }
90}
91
92#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
93pub struct BiomeId(u32);
94
95impl BiomeId {
96    pub const DEFAULT: Self = BiomeId(0);
97}
98
99impl RegistryIdx for BiomeId {
100    const MAX: usize = u32::MAX as usize;
101
102    #[inline]
103    fn to_index(self) -> usize {
104        self.0 as usize
105    }
106
107    #[inline]
108    fn from_index(idx: usize) -> Self {
109        Self(idx as u32)
110    }
111}
112
113#[derive(Serialize, Deserialize, Clone, Debug)]
114pub struct Biome {
115    pub downfall: f32,
116    pub effects: BiomeEffects,
117    pub has_precipitation: bool,
118    pub temperature: f32,
119    // TODO: more stuff.
120}
121
122#[derive(Serialize, Deserialize, Clone, Debug)]
123pub struct BiomeEffects {
124    pub fog_color: u32,
125    pub sky_color: u32,
126    pub water_color: u32,
127    pub water_fog_color: u32,
128    #[serde(skip_serializing_if = "Option::is_none")]
129    pub grass_color: Option<u32>,
130    // TODO: more stuff.
131}
132
133impl Default for Biome {
134    fn default() -> Self {
135        Self {
136            downfall: 0.4,
137            effects: BiomeEffects::default(),
138            has_precipitation: true,
139            temperature: 0.8,
140        }
141    }
142}
143
144impl Default for BiomeEffects {
145    fn default() -> Self {
146        Self {
147            fog_color: 12638463,
148            sky_color: 7907327,
149            water_color: 4159204,
150            water_fog_color: 329011,
151            grass_color: None,
152        }
153    }
154}