resource, world: implement fallback to plains for unknown biomes

Closes #63
This commit is contained in:
Matthias Schiffer 2025-02-11 22:39:11 +01:00
parent d7fc95c950
commit a10151a4f3
Signed by: neocturne
GPG key ID: 16EF3F64CB201D9C
5 changed files with 37 additions and 18 deletions

View file

@ -2,6 +2,12 @@
## [Unreleased] - ReleaseDate ## [Unreleased] - ReleaseDate
### Changed
- Unknown biome types (from not yet supported or modded versions of Minecraft)
will now use plains biome colors as a fallback instead of resulting in water,
grass and foliage blocks to be rendered as transparent pixels
## [2.4.0] - 2025-01-11 ## [2.4.0] - 2025-01-11
### Added ### Added

View file

@ -247,6 +247,8 @@ pub struct BiomeTypes {
biome_map: HashMap<String, &'static Biome>, biome_map: HashMap<String, &'static Biome>,
/// Array used to look up old numeric biome IDs /// Array used to look up old numeric biome IDs
legacy_biomes: Box<[&'static Biome; 256]>, legacy_biomes: Box<[&'static Biome; 256]>,
/// Fallback for unknown (new/modded) biomes
fallback_biome: &'static Biome,
} }
impl Default for BiomeTypes { impl Default for BiomeTypes {
@ -273,9 +275,12 @@ impl Default for BiomeTypes {
.try_into() .try_into()
.unwrap(); .unwrap();
let fallback_biome = *biome_map.get("plains").expect("Plains biome undefined");
Self { Self {
biome_map, biome_map,
legacy_biomes, legacy_biomes,
fallback_biome,
} }
} }
} }
@ -293,4 +298,10 @@ impl BiomeTypes {
pub fn get_legacy(&self, id: u8) -> Option<&Biome> { pub fn get_legacy(&self, id: u8) -> Option<&Biome> {
Some(self.legacy_biomes[id as usize]) Some(self.legacy_biomes[id as usize])
} }
/// Returns the fallback for unknown (new/modded) biomes
#[inline]
pub fn get_fallback(&self) -> &Biome {
self.fallback_biome
}
} }

View file

@ -25,7 +25,7 @@ use crate::{
/// ///
/// Increase when the generation of processed regions from region data changes /// Increase when the generation of processed regions from region data changes
/// (usually because of updated resource data) /// (usually because of updated resource data)
pub const REGION_FILE_META_VERSION: FileMetaVersion = FileMetaVersion(3); pub const REGION_FILE_META_VERSION: FileMetaVersion = FileMetaVersion(4);
/// MinedMap map tile data version number /// MinedMap map tile data version number
/// ///

View file

@ -97,7 +97,8 @@ impl LayerEntry<'_> {
if self.is_empty() { if self.is_empty() {
*self.block = Some(block_type.block_color); *self.block = Some(block_type.block_color);
if let Some(biome) = section.biomes.biome_at(section.y, coords)? {
let biome = section.biomes.biome_at(section.y, coords)?;
let (biome_index, _) = biome_list.insert_full(*biome); let (biome_index, _) = biome_list.insert_full(*biome);
*self.biome = NonZeroU16::new( *self.biome = NonZeroU16::new(
(biome_index + 1) (biome_index + 1)
@ -105,7 +106,6 @@ impl LayerEntry<'_> {
.expect("biome index not in range"), .expect("biome index not in range"),
); );
} }
}
if block_type.block_color.is(BlockFlag::Water) { if block_type.block_color.is(BlockFlag::Water) {
return Ok(false); return Ok(false);

View file

@ -208,7 +208,7 @@ impl Section for SectionV0<'_> {
/// Trait for common functions of [BiomesV1_18] and [BiomesV0] /// Trait for common functions of [BiomesV1_18] and [BiomesV0]
pub trait Biomes: Debug { pub trait Biomes: Debug {
/// Returns the [Biome] at a coordinate tuple inside the chunk /// Returns the [Biome] at a coordinate tuple inside the chunk
fn biome_at(&self, section: SectionY, coords: SectionBlockCoords) -> Result<Option<&Biome>>; fn biome_at(&self, section: SectionY, coords: SectionBlockCoords) -> Result<&Biome>;
} }
/// Minecraft v1.18+ section biome data /// Minecraft v1.18+ section biome data
@ -226,7 +226,7 @@ pub struct BiomesV1_18<'a> {
/// to whole i64 values. /// to whole i64 values.
biomes: Option<&'a [i64]>, biomes: Option<&'a [i64]>,
/// Biome palette indexed by entries encoded in *biomes* /// Biome palette indexed by entries encoded in *biomes*
palette: Vec<Option<&'a Biome>>, palette: Vec<&'a Biome>,
/// Number of bits used for each entry in *biomes* /// Number of bits used for each entry in *biomes*
bits: u8, bits: u8,
} }
@ -253,12 +253,11 @@ impl<'a> BiomesV1_18<'a> {
let palette_types = palette let palette_types = palette
.iter() .iter()
.map(|entry| { .map(|entry| {
let biome_type = biome_types.get(entry); biome_types.get(entry).unwrap_or_else(|| {
if biome_type.is_none() {
debug!("Unknown biome type: {}", entry); debug!("Unknown biome type: {}", entry);
has_unknown = true; has_unknown = true;
} biome_types.get_fallback()
biome_type })
}) })
.collect(); .collect();
@ -295,7 +294,7 @@ impl<'a> BiomesV1_18<'a> {
} }
impl Biomes for BiomesV1_18<'_> { impl Biomes for BiomesV1_18<'_> {
fn biome_at(&self, _section: SectionY, coords: SectionBlockCoords) -> Result<Option<&Biome>> { fn biome_at(&self, _section: SectionY, coords: SectionBlockCoords) -> Result<&Biome> {
let index = self.palette_index_at(coords); let index = self.palette_index_at(coords);
Ok(*self Ok(*self
.palette .palette
@ -350,7 +349,7 @@ impl<'a> BiomesV0<'a> {
} }
impl Biomes for BiomesV0<'_> { impl Biomes for BiomesV0<'_> {
fn biome_at(&self, section: SectionY, coords: SectionBlockCoords) -> Result<Option<&Biome>> { fn biome_at(&self, section: SectionY, coords: SectionBlockCoords) -> Result<&Biome> {
let id = match self.data { let id = match self.data {
BiomesV0Data::IntArrayV15(data) => { BiomesV0Data::IntArrayV15(data) => {
let LayerBlockCoords { x, z } = coords.xz; let LayerBlockCoords { x, z } = coords.xz;
@ -370,7 +369,10 @@ impl Biomes for BiomesV0<'_> {
} }
BiomesV0Data::ByteArray(data) => data[coords.xz.offset()] as u8, BiomesV0Data::ByteArray(data) => data[coords.xz.offset()] as u8,
}; };
Ok(self.biome_types.get_legacy(id)) Ok(self
.biome_types
.get_legacy(id)
.unwrap_or(self.biome_types.get_fallback()))
} }
} }