mc_core/biome/
mod.rs

1use std::collections::HashMap;
2
3use crate::util::OpaquePtr;
4
5
6/// A basic biome structure. This structure is made for static definition.
7#[derive(Debug)]
8pub struct Biome {
9    name: &'static str,
10    id: i32
11}
12
13
14/// The type of hashable value that can represent a biome as a map key.
15/// See `Biome::get_key`, its only usable for statically defined biomes.
16pub type BiomeKey = OpaquePtr<Biome>;
17
18
19impl Biome {
20
21    pub const fn new(name: &'static str, id: i32) -> Self {
22        Self { name, id }
23    }
24
25    #[inline]
26    pub fn get_name(&self) -> &'static str {
27        self.name
28    }
29
30    #[inline]
31    pub fn get_key(&'static self) -> BiomeKey {
32        OpaquePtr::new(self)
33    }
34
35    #[inline]
36    pub fn get_id(&self) -> i32 {
37        self.id
38    }
39
40}
41
42impl PartialEq for &'static Biome {
43    fn eq(&self, other: &Self) -> bool {
44        std::ptr::eq(*self, *other)
45    }
46}
47
48impl Eq for &'static Biome {}
49
50
51/// This is a global biomes palette, it is used in chunk storage to store biomes.
52/// It allows you to register individual biomes in it as well as static biomes
53/// arrays defined using the macro `biomes!`.
54pub struct GlobalBiomes {
55    next_sid: u16,
56    biome_to_sid: HashMap<BiomeKey, u16>,
57    sid_to_biome: Vec<&'static Biome>,
58    name_to_biome: HashMap<&'static str, &'static Biome>,
59    id_to_biome: HashMap<i32, &'static Biome>
60}
61
62impl GlobalBiomes {
63
64    pub fn new() -> Self {
65        Self {
66            next_sid: 0,
67            biome_to_sid: HashMap::new(),
68            sid_to_biome: Vec::new(),
69            name_to_biome: HashMap::new(),
70            id_to_biome: HashMap::new()
71        }
72    }
73
74    /// A simple constructor to directly call `register_all` with given biomes slice.
75    pub fn with_all(slice: &[&'static Biome]) -> Result<Self, ()> {
76        let mut biomes = Self::new();
77        biomes.register_all(slice)?;
78        Ok(biomes)
79    }
80
81    /// Register a single biome to this palette, returns `Err` if no more save ID (SID) is
82    /// available, `Ok` is returned if successful, if a biome was already in the palette
83    /// it also returns `Ok`.
84    pub fn register(&mut self, biome: &'static Biome) -> Result<(), ()> {
85
86        let sid = self.next_sid;
87        let next_sid = sid.checked_add(1).ok_or(())?;
88
89        if let None = self.biome_to_sid.insert(biome.get_key(), sid) {
90            self.next_sid = next_sid;
91            self.sid_to_biome.push(biome);
92            self.name_to_biome.insert(biome.name, biome);
93            self.id_to_biome.insert(biome.id, biome);
94        }
95
96        Ok(())
97
98    }
99
100    /// An optimized way to call `register` multiple times for each given biome,
101    /// the returned follow the same rules as `register`, if an error happens, it
102    /// return without and previous added biomes are kept.
103    pub fn register_all(&mut self, slice: &[&'static Biome]) -> Result<(), ()> {
104        let count = slice.len();
105        self.biome_to_sid.reserve(count);
106        self.sid_to_biome.reserve(count);
107        self.name_to_biome.reserve(count);
108        self.id_to_biome.reserve(count);
109        for &biome in slice {
110            self.register(biome)?;
111        }
112        Ok(())
113    }
114
115    pub fn get_sid_from(&self, biome: &'static Biome) -> Option<u16> {
116        Some(*self.biome_to_sid.get(&biome.get_key())?)
117    }
118
119    pub fn get_biome_from(&self, sid: u16) -> Option<&'static Biome> {
120        Some(*self.sid_to_biome.get(sid as usize)?)
121    }
122
123    pub fn get_biome_from_name(&self, name: &str) -> Option<&'static Biome> {
124        self.name_to_biome.get(name).cloned()
125    }
126
127    pub fn get_biome_from_id(&self, id: i32) -> Option<&'static Biome> {
128        self.id_to_biome.get(&id).cloned()
129    }
130
131    pub fn has_biome(&self, biome: &'static Biome) -> bool {
132        self.biome_to_sid.contains_key(&biome.get_key())
133    }
134
135    pub fn check_biome<E>(&self, biome: &'static Biome, err: impl FnOnce() -> E) -> Result<&'static Biome, E> {
136        if self.has_biome(biome) { Ok(biome) } else { Err(err()) }
137    }
138
139    pub fn biomes_count(&self) -> usize {
140        self.sid_to_biome.len()
141    }
142
143}
144
145
146#[macro_export]
147macro_rules! biomes {
148    ($global_vis:vis $static_id:ident $namespace:literal [
149        $($biome_id:ident $biome_name:literal $biome_numeric_id:literal),*
150        $(,)?
151    ]) => {
152
153        $($global_vis static $biome_id: $crate::biome::Biome = $crate::biome::Biome::new(
154            concat!($namespace, ':', $biome_name),
155            $biome_numeric_id
156        );)*
157
158        $global_vis static $static_id: [&'static $crate::biome::Biome; $crate::count!($($biome_id)*)] = [
159            $(&$biome_id),*
160        ];
161
162    };
163}