Merge pull request #53 from neocturne/empty-region-fixes

Empty region fixes
This commit is contained in:
Matthias Schiffer 2024-06-14 16:30:34 +02:00 committed by GitHub
commit 4eb963f147
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 65 additions and 39 deletions

View file

@ -2,6 +2,17 @@
## [Unreleased] - ReleaseDate ## [Unreleased] - ReleaseDate
### Fixed
- Fix crash due to incorrect counting in info message
The calculation of the number of skipped regions could underflow when more invalid than valid
regions were encountered.
- Ignore empty region files instead of treating them as invalid
Minecraft generates empty region files in some cases. Just ignore them instead of printing an
error message every time.
## [2.1.0] - 2024-01-27 ## [2.1.0] - 2024-01-27
### Added ### Added

21
Cargo.lock generated
View file

@ -276,6 +276,26 @@ version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
[[package]]
name = "enum-map"
version = "2.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6866f3bfdf8207509a033af1a75a7b08abda06bbaaeae6669323fd5a097df2e9"
dependencies = [
"enum-map-derive",
]
[[package]]
name = "enum-map-derive"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "enumflags2" name = "enumflags2"
version = "0.7.9" version = "0.7.9"
@ -550,6 +570,7 @@ dependencies = [
"anyhow", "anyhow",
"bincode", "bincode",
"clap", "clap",
"enum-map",
"fastnbt", "fastnbt",
"futures-util", "futures-util",
"git-version", "git-version",

View file

@ -40,6 +40,7 @@ pre-release-replacements = [
anyhow = "1.0.68" anyhow = "1.0.68"
bincode = "1.3.3" bincode = "1.3.3"
clap = { version = "4.1.4", features = ["derive", "wrap_help"] } clap = { version = "4.1.4", features = ["derive", "wrap_help"] }
enum-map = "2.7.3"
fastnbt = "2.3.2" fastnbt = "2.3.2"
futures-util = "0.3.28" futures-util = "0.3.28"
git-version = "0.3.5" git-version = "0.3.5"

View file

@ -1,16 +1,9 @@
//! The [RegionProcessor] and related functions //! The [RegionProcessor] and related functions
use std::{ use std::{ffi::OsStr, path::PathBuf, sync::mpsc, time::SystemTime};
ffi::OsStr,
path::PathBuf,
sync::{
atomic::{AtomicBool, Ordering},
mpsc,
},
time::SystemTime,
};
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use enum_map::{Enum, EnumMap};
use rayon::prelude::*; use rayon::prelude::*;
use tracing::{debug, info, warn}; use tracing::{debug, info, warn};
@ -36,7 +29,7 @@ fn parse_region_filename(file_name: &OsStr) -> Option<TileCoords> {
} }
/// [RegionProcessor::process_region] return values /// [RegionProcessor::process_region] return values
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Enum)]
enum RegionProcessorStatus { enum RegionProcessorStatus {
/// Region was processed /// Region was processed
Ok, Ok,
@ -344,11 +337,20 @@ impl<'a> RegionProcessor<'a> {
})? })?
.filter_map(|entry| entry.ok()) .filter_map(|entry| entry.ok())
.filter(|entry| { .filter(|entry| {
// We are only interested in regular files (|| {
matches!( // We are only interested in regular files
entry.file_type().map(|file_type| file_type.is_file()), let file_type = entry.file_type().ok()?;
Ok(true) if !file_type.is_file() {
) return None;
}
let metadata = entry.metadata().ok()?;
if metadata.len() == 0 {
return None;
}
Some(())
})()
.is_some()
}) })
.filter_map(|entry| parse_region_filename(&entry.file_name())) .filter_map(|entry| parse_region_filename(&entry.file_name()))
.collect()) .collect())
@ -363,6 +365,8 @@ impl<'a> RegionProcessor<'a> {
/// ///
/// Returns a list of the coordinates of all processed regions /// Returns a list of the coordinates of all processed regions
pub fn run(self) -> Result<Vec<TileCoords>> { pub fn run(self) -> Result<Vec<TileCoords>> {
use RegionProcessorStatus as Status;
fs::create_dir_all(&self.config.processed_dir)?; fs::create_dir_all(&self.config.processed_dir)?;
fs::create_dir_all(&self.config.tile_dir(TileKind::Lightmap, 0))?; fs::create_dir_all(&self.config.tile_dir(TileKind::Lightmap, 0))?;
fs::create_dir_all(&self.config.entities_dir(0))?; fs::create_dir_all(&self.config.entities_dir(0))?;
@ -370,31 +374,18 @@ impl<'a> RegionProcessor<'a> {
info!("Processing region files..."); info!("Processing region files...");
let (region_send, region_recv) = mpsc::channel(); let (region_send, region_recv) = mpsc::channel();
let (processed_send, processed_recv) = mpsc::channel(); let (status_send, status_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| { self.collect_regions()?.par_iter().try_for_each(|&coords| {
let ret = self let ret = self
.process_region(coords) .process_region(coords)
.with_context(|| format!("Failed to process region {:?}", coords))?; .with_context(|| format!("Failed to process region {:?}", coords))?;
if ret != RegionProcessorStatus::ErrorMissing { if ret != Status::ErrorMissing {
region_send.send(coords).unwrap(); region_send.send(coords).unwrap();
} }
match ret { status_send.send(ret).unwrap();
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()
}
}
anyhow::Ok(()) anyhow::Ok(())
})?; })?;
@ -402,19 +393,21 @@ impl<'a> RegionProcessor<'a> {
drop(region_send); drop(region_send);
let mut regions: Vec<_> = region_recv.into_iter().collect(); let mut regions: Vec<_> = region_recv.into_iter().collect();
drop(processed_send); drop(status_send);
let processed = processed_recv.into_iter().count();
drop(error_send); let mut status = EnumMap::<_, usize>::default();
let errors = error_recv.into_iter().count(); for ret in status_recv {
status[ret] += 1;
}
info!( info!(
"Processed region files ({} processed, {} unchanged, {} errors)", "Processed region files ({} processed, {} unchanged, {} errors)",
processed, status[Status::Ok] + status[Status::OkWithUnknown],
regions.len() - processed - errors, status[Status::Skipped],
errors, status[Status::ErrorOk] + status[Status::ErrorMissing],
); );
if has_unknown.into_inner() { if status[Status::OkWithUnknown] > 0 {
warn!("Unknown block or biome types found during processing"); warn!("Unknown block or biome types found during processing");
eprint!(concat!( eprint!(concat!(
"\n", "\n",