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.
This commit is contained in:
Matthias Schiffer 2024-01-10 12:48:34 +01:00
parent 1e41e1bc6a
commit e8165aa47d
Signed by: neocturne
GPG key ID: 16EF3F64CB201D9C
4 changed files with 120 additions and 68 deletions

View file

@ -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

View file

@ -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<TileCoords> {
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)
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 }));

View file

@ -56,7 +56,7 @@ impl<'a> Chunk<'a> {
data: &'a de::Chunk,
block_types: &'a BlockTypes,
biome_types: &'a BiomeTypes,
) -> Result<Self> {
) -> 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<de::SectionV1_18>,
block_types: &'a BlockTypes,
biome_types: &'a BiomeTypes,
) -> Result<Self> {
) -> Result<(Self, bool)> {
let mut section_map = BTreeMap::new();
let mut has_unknown = false;
for section in sections {
match &section.section {
@ -85,22 +86,27 @@ impl<'a> Chunk<'a> {
biomes,
block_light,
} => {
section_map.insert(
SectionY(section.y),
(
SectionV1_13::new(
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)
})?,
.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),
(
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<Self> {
) -> 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,9 +166,7 @@ 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()) {
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,
@ -181,8 +179,9 @@ impl<'a> Chunk<'a> {
(false, false) => {
bail!("Mixed section versions");
}
},
)
};
Ok((chunk, has_unknown))
}
/// Returns true if the chunk does not contain any sections

View file

@ -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<Self> {
) -> 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 {
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<Self> {
) -> 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 {
Ok((
BiomesV1_18 {
biomes,
palette: palette_types,
bits,
})
},
has_unknown,
))
}
/// Looks up the block type palette index at the given coordinates