From a10151a4f37775d76150f03cc94dd32bd4c4705c Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Tue, 11 Feb 2025 22:39:11 +0100 Subject: [PATCH] resource, world: implement fallback to plains for unknown biomes Closes #63 --- CHANGELOG.md | 6 ++++++ crates/resource/src/lib.rs | 11 +++++++++++ src/core/common.rs | 2 +- src/world/layer.rs | 16 ++++++++-------- src/world/section.rs | 20 +++++++++++--------- 5 files changed, 37 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e75a75b..7745ced 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/crates/resource/src/lib.rs b/crates/resource/src/lib.rs index fed9514..a633d06 100644 --- a/crates/resource/src/lib.rs +++ b/crates/resource/src/lib.rs @@ -247,6 +247,8 @@ pub struct BiomeTypes { biome_map: HashMap, /// 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 + } } diff --git a/src/core/common.rs b/src/core/common.rs index b933dcd..edefcb3 100644 --- a/src/core/common.rs +++ b/src/core/common.rs @@ -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 /// diff --git a/src/world/layer.rs b/src/world/layer.rs index 0764711..e59593c 100644 --- a/src/world/layer.rs +++ b/src/world/layer.rs @@ -97,14 +97,14 @@ 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_index, _) = biome_list.insert_full(*biome); - *self.biome = NonZeroU16::new( - (biome_index + 1) - .try_into() - .expect("biome index not in range"), - ); - } + + let biome = section.biomes.biome_at(section.y, coords)?; + let (biome_index, _) = biome_list.insert_full(*biome); + *self.biome = NonZeroU16::new( + (biome_index + 1) + .try_into() + .expect("biome index not in range"), + ); } if block_type.block_color.is(BlockFlag::Water) { diff --git a/src/world/section.rs b/src/world/section.rs index 7988fd5..845ddae 100644 --- a/src/world/section.rs +++ b/src/world/section.rs @@ -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>; + 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>, + 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> { + 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> { + 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())) } }