1#![doc = env!("CARGO_PKG_DESCRIPTION")]
2#![warn(missing_docs)]
3#![warn(clippy::missing_docs_in_private_items)]
4
5mod biomes;
6mod block_color;
7mod block_types;
8mod legacy_biomes;
9mod legacy_block_types;
10
11use std::collections::HashMap;
12
13use enumflags2::{BitFlags, bitflags};
14use serde::{Deserialize, Serialize};
15
16#[bitflags]
18#[repr(u8)]
19#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
20pub enum BlockFlag {
21 Opaque,
23 Grass,
25 Foliage,
27 Birch,
29 Spruce,
31 Water,
33 WallSign,
38}
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
42pub struct Color(pub [u8; 3]);
43
44pub type Colorf = glam::Vec3;
46
47#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
49pub struct BlockColor {
50 pub flags: BitFlags<BlockFlag>,
52 pub color: Color,
54}
55
56impl BlockColor {
57 #[inline]
59 pub fn is(&self, flag: BlockFlag) -> bool {
60 self.flags.contains(flag)
61 }
62}
63
64#[derive(Debug, Clone)]
66struct ConstBlockType {
67 pub block_color: BlockColor,
69 pub sign_material: Option<&'static str>,
71}
72
73#[derive(Debug, Clone)]
75pub struct BlockType {
76 pub block_color: BlockColor,
78 pub sign_material: Option<String>,
80}
81
82impl From<&ConstBlockType> for BlockType {
83 fn from(value: &ConstBlockType) -> Self {
84 BlockType {
85 block_color: value.block_color,
86 sign_material: value.sign_material.map(String::from),
87 }
88 }
89}
90
91#[derive(Debug)]
93pub struct BlockTypes {
94 block_type_map: HashMap<String, BlockType>,
96 legacy_block_types: Box<[[BlockType; 16]; 256]>,
98}
99
100impl Default for BlockTypes {
101 fn default() -> Self {
102 let block_type_map: HashMap<_, _> = block_types::BLOCK_TYPES
103 .iter()
104 .map(|(k, v)| (String::from(*k), BlockType::from(v)))
105 .collect();
106 let legacy_block_types = Box::new(legacy_block_types::LEGACY_BLOCK_TYPES.map(|inner| {
107 inner.map(|id| {
108 block_type_map
109 .get(id)
110 .expect("Unknown legacy block type")
111 .clone()
112 })
113 }));
114
115 BlockTypes {
116 block_type_map,
117 legacy_block_types,
118 }
119 }
120}
121
122impl BlockTypes {
123 #[inline]
125 pub fn get(&self, id: &str) -> Option<&BlockType> {
126 let suffix = id.strip_prefix("minecraft:").unwrap_or(id);
127 self.block_type_map.get(suffix)
128 }
129
130 #[inline]
132 pub fn get_legacy(&self, id: u8, data: u8) -> Option<&BlockType> {
133 Some(&self.legacy_block_types[id as usize][data as usize])
134 }
135}
136
137pub use block_color::{block_color, needs_biome};
138
139#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
141pub enum BiomeGrassColorModifier {
142 DarkForest,
144 Swamp,
146}
147
148#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
153pub struct Biome {
154 pub temp: i8,
159 pub downfall: i8,
164 pub water_color: Option<Color>,
166 pub foliage_color: Option<Color>,
168 pub grass_color: Option<Color>,
170 pub grass_color_modifier: Option<BiomeGrassColorModifier>,
172}
173
174impl Biome {
175 const fn new(temp: i16, downfall: i16) -> Biome {
177 const fn encode(v: i16) -> i8 {
182 (v / 5) as i8
183 }
184 Biome {
185 temp: encode(temp),
186 downfall: encode(downfall),
187 grass_color_modifier: None,
188 water_color: None,
189 foliage_color: None,
190 grass_color: None,
191 }
192 }
193
194 const fn water(self, water_color: [u8; 3]) -> Biome {
196 Biome {
197 water_color: Some(Color(water_color)),
198 ..self
199 }
200 }
201
202 const fn foliage(self, foliage_color: [u8; 3]) -> Biome {
204 Biome {
205 foliage_color: Some(Color(foliage_color)),
206 ..self
207 }
208 }
209
210 const fn grass(self, grass_color: [u8; 3]) -> Biome {
212 Biome {
213 grass_color: Some(Color(grass_color)),
214 ..self
215 }
216 }
217
218 const fn modify(self, grass_color_modifier: BiomeGrassColorModifier) -> Biome {
220 Biome {
221 grass_color_modifier: Some(grass_color_modifier),
222 ..self
223 }
224 }
225
226 fn decode(val: i8) -> f32 {
229 f32::from(val) / 20.0
230 }
231
232 pub fn temp(&self) -> f32 {
234 Self::decode(self.temp)
235 }
236
237 pub fn downfall(&self) -> f32 {
239 Self::decode(self.downfall)
240 }
241}
242
243#[derive(Debug)]
245pub struct BiomeTypes {
246 biome_map: HashMap<String, &'static Biome>,
248 legacy_biomes: Box<[&'static Biome; 256]>,
250 fallback_biome: &'static Biome,
252}
253
254impl Default for BiomeTypes {
255 fn default() -> Self {
256 let mut biome_map: HashMap<_, _> = biomes::BIOMES
257 .iter()
258 .map(|(k, v)| (String::from(*k), v))
259 .collect();
260
261 for &(old, new) in legacy_biomes::BIOME_ALIASES.iter().rev() {
262 let biome = biome_map
263 .get(new)
264 .copied()
265 .expect("Biome alias for unknown biome");
266 assert!(biome_map.insert(String::from(old), biome).is_none());
267 }
268
269 let legacy_biomes = (0..=255)
270 .map(|index| {
271 let id = legacy_biomes::legacy_biome(index);
272 *biome_map.get(id).expect("Unknown legacy biome")
273 })
274 .collect::<Box<[_]>>()
275 .try_into()
276 .unwrap();
277
278 let fallback_biome = *biome_map.get("plains").expect("Plains biome undefined");
279
280 Self {
281 biome_map,
282 legacy_biomes,
283 fallback_biome,
284 }
285 }
286}
287
288impl BiomeTypes {
289 #[inline]
291 pub fn get(&self, id: &str) -> Option<&Biome> {
292 let suffix = id.strip_prefix("minecraft:").unwrap_or(id);
293 self.biome_map.get(suffix).copied()
294 }
295
296 #[inline]
298 pub fn get_legacy(&self, id: u8) -> Option<&Biome> {
299 Some(self.legacy_biomes[id as usize])
300 }
301
302 #[inline]
304 pub fn get_fallback(&self) -> &Biome {
305 self.fallback_biome
306 }
307}