From e8165aa47d4c3523ff4c95d8664ba6642ac0284e Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Wed, 10 Jan 2024 12:48:34 +0100 Subject: [PATCH] world: hide unknown block/biome type warnings by default While using MinedMap with modded Minecraft version is not officially supported, it should still work reasonably well if you're okay with custom block types being invisible and custom biomes using default grass/color/foliage colors. Avoid spamming the log with messages for each section in this case without --verbose, and instead just print a single warning at the end of processing. --- CHANGELOG.md | 6 +++ src/core/region_processor.rs | 43 ++++++++++++++-- src/world/chunk.rs | 95 ++++++++++++++++++------------------ src/world/section.rs | 44 +++++++++++------ 4 files changed, 120 insertions(+), 68 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2546290..fccf759 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ## [Unreleased] - ReleaseDate +### Changed + +- Without `--verbose`, only a single warning is printed at the end of + processing for unknown block/biome types, rather than once for every + section where such a block/biome is encountered. + ## [2.0.2] - 2024-01-07 ### Added diff --git a/src/core/region_processor.rs b/src/core/region_processor.rs index 9e1bb65..0a8ad0e 100644 --- a/src/core/region_processor.rs +++ b/src/core/region_processor.rs @@ -1,6 +1,14 @@ //! The [RegionProcessor] and related functions -use std::{ffi::OsStr, path::PathBuf, sync::mpsc, time::SystemTime}; +use std::{ + ffi::OsStr, + path::PathBuf, + sync::{ + atomic::{AtomicBool, Ordering}, + mpsc, + }, + time::SystemTime, +}; use anyhow::{Context, Result}; use rayon::prelude::*; @@ -32,6 +40,8 @@ fn parse_region_filename(file_name: &OsStr) -> Option { enum RegionProcessorStatus { /// Region was processed Ok, + /// Region was processed, unknown blocks or biomes were encountered + OkWithUnknown, /// Region was unchanged and skipped Skipped, /// Reading the region failed, previous processed data is reused @@ -68,6 +78,8 @@ struct SingleRegionProcessor<'a> { processed_region: ProcessedRegion, /// Lightmap intermediate data lightmap: image::GrayAlphaImage, + /// True if any unknown block or biome types were encountered during processing + has_unknown: bool, } impl<'a> SingleRegionProcessor<'a> { @@ -104,6 +116,7 @@ impl<'a> SingleRegionProcessor<'a> { lightmap_needed, processed_region, lightmap, + has_unknown: false, }) } @@ -162,8 +175,10 @@ impl<'a> SingleRegionProcessor<'a> { /// Processes a single chunk fn process_chunk(&mut self, chunk_coords: ChunkCoords, data: world::de::Chunk) -> Result<()> { - let chunk = world::chunk::Chunk::new(&data, self.block_types, self.biome_types) - .with_context(|| format!("Failed to decode chunk {:?}", chunk_coords))?; + let (chunk, has_unknown) = + world::chunk::Chunk::new(&data, self.block_types, self.biome_types) + .with_context(|| format!("Failed to decode chunk {:?}", chunk_coords))?; + self.has_unknown |= has_unknown; let Some(layer::LayerData { blocks, biomes, @@ -231,7 +246,11 @@ impl<'a> SingleRegionProcessor<'a> { self.save_region()?; self.save_lightmap()?; - Ok(RegionProcessorStatus::Ok) + Ok(if self.has_unknown { + RegionProcessorStatus::OkWithUnknown + } else { + RegionProcessorStatus::Ok + }) } } @@ -300,6 +319,8 @@ impl<'a> RegionProcessor<'a> { let (processed_send, processed_recv) = mpsc::channel(); let (error_send, error_recv) = mpsc::channel(); + let has_unknown = AtomicBool::new(false); + self.collect_regions()?.par_iter().try_for_each(|&coords| { let ret = self .process_region(coords) @@ -311,6 +332,10 @@ impl<'a> RegionProcessor<'a> { match ret { RegionProcessorStatus::Ok => processed_send.send(()).unwrap(), + RegionProcessorStatus::OkWithUnknown => { + has_unknown.store(true, Ordering::Relaxed); + processed_send.send(()).unwrap(); + } RegionProcessorStatus::Skipped => {} RegionProcessorStatus::ErrorOk | RegionProcessorStatus::ErrorMissing => { error_send.send(()).unwrap() @@ -335,6 +360,16 @@ impl<'a> RegionProcessor<'a> { errors, ); + if has_unknown.into_inner() { + warn!("Unknown block or biome types found during processing"); + eprint!(concat!( + "\n", + " If you're encountering this issue with an unmodified Minecraft version supported by MinedMap,\n", + " please file a bug report including the output with the --verbose flag.\n", + "\n", + )); + } + // Sort regions in a zig-zag pattern to optimize cache usage regions.sort_unstable_by_key(|&TileCoords { x, z }| (x, if x % 2 == 0 { z } else { -z })); diff --git a/src/world/chunk.rs b/src/world/chunk.rs index e52c27b..993a79e 100644 --- a/src/world/chunk.rs +++ b/src/world/chunk.rs @@ -56,7 +56,7 @@ impl<'a> Chunk<'a> { data: &'a de::Chunk, block_types: &'a BlockTypes, biome_types: &'a BiomeTypes, - ) -> Result { + ) -> Result<(Self, bool)> { let data_version = data.data_version.unwrap_or_default(); match &data.chunk { @@ -75,8 +75,9 @@ impl<'a> Chunk<'a> { sections: &'a Vec, block_types: &'a BlockTypes, biome_types: &'a BiomeTypes, - ) -> Result { + ) -> Result<(Self, bool)> { let mut section_map = BTreeMap::new(); + let mut has_unknown = false; for section in sections { match §ion.section { @@ -85,22 +86,27 @@ impl<'a> Chunk<'a> { biomes, block_light, } => { + let (loaded_section, unknown_blocks) = SectionV1_13::new( + data_version, + block_states.data.as_deref(), + &block_states.palette, + block_types, + ) + .with_context(|| format!("Failed to load section at Y={}", section.y))?; + has_unknown |= unknown_blocks; + + let (loaded_biomes, unknown_biomes) = + BiomesV1_18::new(biomes.data.as_deref(), &biomes.palette, biome_types) + .with_context(|| { + format!("Failed to load section biomes at Y={}", section.y) + })?; + has_unknown |= unknown_biomes; + section_map.insert( SectionY(section.y), ( - SectionV1_13::new( - data_version, - block_states.data.as_deref(), - &block_states.palette, - block_types, - ) - .with_context(|| { - format!("Failed to load section at Y={}", section.y) - })?, - BiomesV1_18::new(biomes.data.as_deref(), &biomes.palette, biome_types) - .with_context(|| { - format!("Failed to load section biomes at Y={}", section.y) - })?, + loaded_section, + loaded_biomes, BlockLight::new(block_light.as_deref()).with_context(|| { format!("Failed to load section block light at Y={}", section.y) })?, @@ -111,7 +117,8 @@ impl<'a> Chunk<'a> { }; } - Ok(Chunk::V1_18 { section_map }) + let chunk = Chunk::V1_18 { section_map }; + Ok((chunk, has_unknown)) } /// [Chunk::new] implementation for all pre-1.18 chunk variants @@ -120,9 +127,10 @@ impl<'a> Chunk<'a> { level: &'a de::LevelV0, block_types: &'a BlockTypes, biome_types: &'a BiomeTypes, - ) -> Result { + ) -> Result<(Self, bool)> { let mut section_map_v1_13 = BTreeMap::new(); let mut section_map_v0 = BTreeMap::new(); + let mut has_unknown = false; for section in &level.sections { let block_light = @@ -134,21 +142,13 @@ impl<'a> Chunk<'a> { block_states, palette, } => { - section_map_v1_13.insert( - SectionY(section.y.into()), - ( - SectionV1_13::new( - data_version, - Some(block_states), - palette, - block_types, - ) - .with_context(|| { - format!("Failed to load section at Y={}", section.y) - })?, - block_light, - ), - ); + let (loaded_section, unknown_blocks) = + SectionV1_13::new(data_version, Some(block_states), palette, block_types) + .with_context(|| format!("Failed to load section at Y={}", section.y))?; + has_unknown |= unknown_blocks; + + section_map_v1_13 + .insert(SectionY(section.y.into()), (loaded_section, block_light)); } de::SectionV0Variant::V0 { blocks, data } => { section_map_v0.insert( @@ -166,23 +166,22 @@ impl<'a> Chunk<'a> { } let biomes = BiomesV0::new(level.biomes.as_ref(), biome_types); - - Ok( - match (section_map_v1_13.is_empty(), section_map_v0.is_empty()) { - (true, true) => Chunk::Empty, - (false, true) => Chunk::V1_13 { - section_map: section_map_v1_13, - biomes: biomes?, - }, - (true, false) => Chunk::V0 { - section_map: section_map_v0, - biomes: biomes?, - }, - (false, false) => { - bail!("Mixed section versions"); - } + let chunk = match (section_map_v1_13.is_empty(), section_map_v0.is_empty()) { + (true, true) => Chunk::Empty, + (false, true) => Chunk::V1_13 { + section_map: section_map_v1_13, + biomes: biomes?, }, - ) + (true, false) => Chunk::V0 { + section_map: section_map_v0, + biomes: biomes?, + }, + (false, false) => { + bail!("Mixed section versions"); + } + }; + + Ok((chunk, has_unknown)) } /// Returns true if the chunk does not contain any sections diff --git a/src/world/section.rs b/src/world/section.rs index 998d2f7..97e0061 100644 --- a/src/world/section.rs +++ b/src/world/section.rs @@ -7,7 +7,7 @@ use std::fmt::Debug; use anyhow::{bail, Context, Result}; use num_integer::div_rem; -use tracing::warn; +use tracing::debug; use super::de; use crate::{ @@ -73,7 +73,7 @@ impl<'a> SectionV1_13<'a> { block_states: Option<&'a [i64]>, palette: &'a [de::BlockStatePaletteEntry], block_types: &'a BlockTypes, - ) -> Result { + ) -> Result<(Self, bool)> { let aligned_blocks = data_version >= 2529; let bits = palette_bits(palette.len(), 4, 12).context("Unsupported block palette size")?; @@ -90,23 +90,29 @@ impl<'a> SectionV1_13<'a> { } } + let mut has_unknown = false; + let palette_types = palette .iter() .map(|entry| { let block_type = block_types.get(&entry.name); if block_type.is_none() { - warn!("Unknown block type: {}", entry.name); + debug!("Unknown block type: {}", entry.name); + has_unknown = true; } block_type }) .collect(); - Ok(Self { - block_states, - palette: palette_types, - bits, - aligned_blocks, - }) + Ok(( + Self { + block_states, + palette: palette_types, + bits, + aligned_blocks, + }, + has_unknown, + )) } /// Looks up the block type palette index at the given coordinates @@ -231,7 +237,7 @@ impl<'a> BiomesV1_18<'a> { biomes: Option<&'a [i64]>, palette: &'a [String], biome_types: &'a BiomeTypes, - ) -> Result { + ) -> Result<(Self, bool)> { let bits = palette_bits(palette.len(), 1, 6).context("Unsupported block palette size")?; if let Some(biomes) = biomes { @@ -242,22 +248,28 @@ impl<'a> BiomesV1_18<'a> { } } + let mut has_unknown = false; + let palette_types = palette .iter() .map(|entry| { let biome_type = biome_types.get(entry); if biome_type.is_none() { - warn!("Unknown biome type: {}", entry); + debug!("Unknown biome type: {}", entry); + has_unknown = true; } biome_type }) .collect(); - Ok(BiomesV1_18 { - biomes, - palette: palette_types, - bits, - }) + Ok(( + BiomesV1_18 { + biomes, + palette: palette_types, + bits, + }, + has_unknown, + )) } /// Looks up the block type palette index at the given coordinates