From 708fb9645dc89f6e454f3564b81f8e9a265457d7 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Thu, 13 Mar 2025 20:27:51 +0100 Subject: [PATCH] core/region_processor: refactor Separate configuration and mutable state, also allowing to avoid a few mem::take(). --- src/core/region_processor.rs | 115 ++++++++++++++++++++--------------- 1 file changed, 67 insertions(+), 48 deletions(-) diff --git a/src/core/region_processor.rs b/src/core/region_processor.rs index 56f54ed..e638342 100644 --- a/src/core/region_processor.rs +++ b/src/core/region_processor.rs @@ -1,6 +1,6 @@ //! The [RegionProcessor] and related functions -use std::{ffi::OsStr, mem, path::PathBuf, sync::mpsc, time::SystemTime}; +use std::{ffi::OsStr, path::PathBuf, sync::mpsc, time::SystemTime}; use anyhow::{Context, Result}; use enum_map::{Enum, EnumMap}; @@ -45,6 +45,37 @@ enum RegionProcessorStatus { ErrorMissing, } +/// Data of a region being processed by a [SingleRegionProcessor] +#[derive(Debug)] +struct SingleRegionData { + /// [IndexSet] of biomes used by the processed region + biome_list: IndexSet, + /// Processed region chunk intermediate data + chunks: ChunkArray>>, + /// Lightmap intermediate data + lightmap: image::GrayAlphaImage, + /// Processed entity intermediate data + entities: ProcessedEntities, + /// True if any unknown block or biome types were encountered during processing + has_unknown: bool, +} + +impl Default for SingleRegionData { + fn default() -> Self { + /// Width/height of the region data + const N: u32 = (BLOCKS_PER_CHUNK * CHUNKS_PER_REGION) as u32; + + let lightmap = image::GrayAlphaImage::new(N, N); + Self { + biome_list: Default::default(), + chunks: Default::default(), + lightmap, + entities: Default::default(), + has_unknown: false, + } + } +} + /// Handles processing for a single region struct SingleRegionProcessor<'a> { /// Registry of known block types @@ -75,26 +106,13 @@ struct SingleRegionProcessor<'a> { lightmap_needed: bool, /// True if entity output file needs to be updated entities_needed: bool, - /// [IndexSet] of biomes used by the processed region - biome_list: IndexSet, - /// Processed region intermediate data - processed_region: ProcessedRegion, - /// Lightmap intermediate data - lightmap: image::GrayAlphaImage, - /// Processed entity intermediate data - entities: ProcessedEntities, /// Format of generated map tiles image_format: image::ImageFormat, - /// True if any unknown block or biome types were encountered during processing - has_unknown: bool, } impl<'a> SingleRegionProcessor<'a> { /// Initializes a [SingleRegionProcessor] fn new(processor: &'a RegionProcessor<'a>, coords: TileCoords) -> Result { - /// Width/height of the region data - const N: u32 = (BLOCKS_PER_CHUNK * CHUNKS_PER_REGION) as u32; - let input_path = processor.config.region_path(coords); let input_timestamp = fs::modified_timestamp(&input_path)?; @@ -111,11 +129,6 @@ impl<'a> SingleRegionProcessor<'a> { let lightmap_needed = Some(input_timestamp) > lightmap_timestamp; let entities_needed = Some(input_timestamp) > entities_timestamp; - let processed_region = ProcessedRegion::default(); - let biome_list = IndexSet::default(); - let lightmap = image::GrayAlphaImage::new(N, N); - let entities = ProcessedEntities::default(); - Ok(SingleRegionProcessor { block_types: &processor.block_types, biome_types: &processor.biome_types, @@ -131,12 +144,7 @@ impl<'a> SingleRegionProcessor<'a> { output_needed, lightmap_needed, entities_needed, - processed_region, - biome_list, - lightmap, - entities, image_format: processor.config.tile_image_format(), - has_unknown: false, }) } @@ -160,14 +168,14 @@ impl<'a> SingleRegionProcessor<'a> { /// Saves processed region data /// /// The timestamp is the time of the last modification of the input region data. - fn save_region(&self) -> Result<()> { + fn save_region(&self, processed_region: &ProcessedRegion) -> Result<()> { if !self.output_needed { return Ok(()); } storage::write_file( &self.output_path, - &self.processed_region, + processed_region, REGION_FILE_META_VERSION, self.input_timestamp, ) @@ -176,7 +184,7 @@ impl<'a> SingleRegionProcessor<'a> { /// Saves a lightmap tile /// /// The timestamp is the time of the last modification of the input region data. - fn save_lightmap(&self) -> Result<()> { + fn save_lightmap(&self, lightmap: &image::GrayAlphaImage) -> Result<()> { if !self.lightmap_needed { return Ok(()); } @@ -186,7 +194,7 @@ impl<'a> SingleRegionProcessor<'a> { LIGHTMAP_FILE_META_VERSION, self.input_timestamp, |file| { - self.lightmap + lightmap .write_to(file, self.image_format) .context("Failed to save image") }, @@ -196,27 +204,32 @@ impl<'a> SingleRegionProcessor<'a> { /// Saves processed entity data /// /// The timestamp is the time of the last modification of the input region data. - fn save_entities(&mut self) -> Result<()> { + fn save_entities(&self, entities: &mut ProcessedEntities) -> Result<()> { if !self.entities_needed { return Ok(()); } - self.entities.block_entities.sort_unstable(); + entities.block_entities.sort_unstable(); storage::write_file( &self.entities_path, - &self.entities, + entities, ENTITIES_FILE_META_VERSION, self.input_timestamp, ) } /// Processes a single chunk - fn process_chunk(&mut self, chunk_coords: ChunkCoords, data: world::de::Chunk) -> Result<()> { + fn process_chunk( + &self, + data: &mut SingleRegionData, + chunk_coords: ChunkCoords, + chunk_data: world::de::Chunk, + ) -> Result<()> { let (chunk, has_unknown) = - world::chunk::Chunk::new(&data, self.block_types, self.biome_types) + world::chunk::Chunk::new(&chunk_data, self.block_types, self.biome_types) .with_context(|| format!("Failed to decode chunk {:?}", chunk_coords))?; - self.has_unknown |= has_unknown; + data.has_unknown |= has_unknown; if self.output_needed || self.lightmap_needed { if let Some(layer::LayerData { @@ -224,11 +237,11 @@ impl<'a> SingleRegionProcessor<'a> { biomes, block_light, depths, - }) = world::layer::top_layer(&mut self.biome_list, &chunk) + }) = world::layer::top_layer(&mut data.biome_list, &chunk) .with_context(|| format!("Failed to process chunk {:?}", chunk_coords))? { if self.output_needed { - self.processed_region.chunks[chunk_coords] = Some(Box::new(ProcessedChunk { + data.chunks[chunk_coords] = Some(Box::new(ProcessedChunk { blocks, biomes, depths, @@ -237,7 +250,7 @@ impl<'a> SingleRegionProcessor<'a> { if self.lightmap_needed { let chunk_lightmap = Self::render_chunk_lightmap(block_light); - overlay_chunk(&mut self.lightmap, &chunk_lightmap, chunk_coords); + overlay_chunk(&mut data.lightmap, &chunk_lightmap, chunk_coords); } } } @@ -249,20 +262,21 @@ impl<'a> SingleRegionProcessor<'a> { chunk_coords, ) })?; - self.entities.block_entities.append(&mut block_entities); + data.entities.block_entities.append(&mut block_entities); } Ok(()) } /// Processes the chunks of the region - fn process_chunks(&mut self) -> Result<()> { - crate::nbt::region::from_file(&self.input_path)? - .foreach_chunk(|chunk_coords, data| self.process_chunk(chunk_coords, data)) + fn process_chunks(&self, data: &mut SingleRegionData) -> Result<()> { + crate::nbt::region::from_file(&self.input_path)?.foreach_chunk( + |chunk_coords, chunk_data| self.process_chunk(data, chunk_coords, chunk_data), + ) } /// Processes the region - fn run(mut self) -> Result { + fn run(&self) -> Result { if !self.output_needed && !self.lightmap_needed && !self.entities_needed { debug!( "Skipping unchanged region r.{}.{}.mca", @@ -276,7 +290,9 @@ impl<'a> SingleRegionProcessor<'a> { self.coords.x, self.coords.z ); - if let Err(err) = self.process_chunks() { + let mut data = SingleRegionData::default(); + + if let Err(err) = self.process_chunks(&mut data) { if self.output_timestamp.is_some() && self.lightmap_timestamp.is_some() && self.entities_timestamp.is_some() @@ -295,13 +311,16 @@ impl<'a> SingleRegionProcessor<'a> { } } - self.processed_region.biome_list = mem::take(&mut self.biome_list).into_iter().collect(); + let processed_region = ProcessedRegion { + biome_list: data.biome_list.into_iter().collect(), + chunks: data.chunks, + }; - self.save_region()?; - self.save_lightmap()?; - self.save_entities()?; + self.save_region(&processed_region)?; + self.save_lightmap(&data.lightmap)?; + self.save_entities(&mut data.entities)?; - Ok(if self.has_unknown { + Ok(if data.has_unknown { RegionProcessorStatus::OkWithUnknown } else { RegionProcessorStatus::Ok