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
### 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
### Added

View file

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

View file

@ -97,7 +97,8 @@ impl LayerEntry<'_> {
if self.is_empty() {
*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);
*self.biome = NonZeroU16::new(
(biome_index + 1)
@ -105,7 +106,6 @@ impl LayerEntry<'_> {
.expect("biome index not in range"),
);
}
}
if block_type.block_color.is(BlockFlag::Water) {
return Ok(false);

View file

@ -208,7 +208,7 @@ impl Section for SectionV0<'_> {
/// Trait for common functions of [BiomesV1_18] and [BiomesV0]
pub trait Biomes: Debug {
/// 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
@ -226,7 +226,7 @@ pub struct BiomesV1_18<'a> {
/// to whole i64 values.
biomes: Option<&'a [i64]>,
/// 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*
bits: u8,
}
@ -253,12 +253,11 @@ impl<'a> BiomesV1_18<'a> {
let palette_types = palette
.iter()
.map(|entry| {
let biome_type = biome_types.get(entry);
if biome_type.is_none() {
biome_types.get(entry).unwrap_or_else(|| {
debug!("Unknown biome type: {}", entry);
has_unknown = true;
}
biome_type
biome_types.get_fallback()
})
})
.collect();
@ -295,7 +294,7 @@ impl<'a> BiomesV1_18<'a> {
}
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);
Ok(*self
.palette
@ -350,7 +349,7 @@ impl<'a> BiomesV0<'a> {
}
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 {
BiomesV0Data::IntArrayV15(data) => {
let LayerBlockCoords { x, z } = coords.xz;
@ -370,7 +369,10 @@ impl Biomes for BiomesV0<'_> {
}
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()))
}
}