mirror of
https://github.com/neocturne/MinedMap.git
synced 2025-04-18 18:45:09 +02:00
core/region_processor: refactor
Separate configuration and mutable state, also allowing to avoid a few mem::take().
This commit is contained in:
parent
53a0f24600
commit
708fb9645d
1 changed files with 67 additions and 48 deletions
|
@ -1,6 +1,6 @@
|
||||||
//! The [RegionProcessor] and related functions
|
//! 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 anyhow::{Context, Result};
|
||||||
use enum_map::{Enum, EnumMap};
|
use enum_map::{Enum, EnumMap};
|
||||||
|
@ -45,6 +45,37 @@ enum RegionProcessorStatus {
|
||||||
ErrorMissing,
|
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<Biome>,
|
||||||
|
/// Processed region chunk intermediate data
|
||||||
|
chunks: ChunkArray<Option<Box<ProcessedChunk>>>,
|
||||||
|
/// 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
|
/// Handles processing for a single region
|
||||||
struct SingleRegionProcessor<'a> {
|
struct SingleRegionProcessor<'a> {
|
||||||
/// Registry of known block types
|
/// Registry of known block types
|
||||||
|
@ -75,26 +106,13 @@ struct SingleRegionProcessor<'a> {
|
||||||
lightmap_needed: bool,
|
lightmap_needed: bool,
|
||||||
/// True if entity output file needs to be updated
|
/// True if entity output file needs to be updated
|
||||||
entities_needed: bool,
|
entities_needed: bool,
|
||||||
/// [IndexSet] of biomes used by the processed region
|
|
||||||
biome_list: IndexSet<Biome>,
|
|
||||||
/// Processed region intermediate data
|
|
||||||
processed_region: ProcessedRegion,
|
|
||||||
/// Lightmap intermediate data
|
|
||||||
lightmap: image::GrayAlphaImage,
|
|
||||||
/// Processed entity intermediate data
|
|
||||||
entities: ProcessedEntities,
|
|
||||||
/// Format of generated map tiles
|
/// Format of generated map tiles
|
||||||
image_format: image::ImageFormat,
|
image_format: image::ImageFormat,
|
||||||
/// True if any unknown block or biome types were encountered during processing
|
|
||||||
has_unknown: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> SingleRegionProcessor<'a> {
|
impl<'a> SingleRegionProcessor<'a> {
|
||||||
/// Initializes a [SingleRegionProcessor]
|
/// Initializes a [SingleRegionProcessor]
|
||||||
fn new(processor: &'a RegionProcessor<'a>, coords: TileCoords) -> Result<Self> {
|
fn new(processor: &'a RegionProcessor<'a>, coords: TileCoords) -> Result<Self> {
|
||||||
/// 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_path = processor.config.region_path(coords);
|
||||||
let input_timestamp = fs::modified_timestamp(&input_path)?;
|
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 lightmap_needed = Some(input_timestamp) > lightmap_timestamp;
|
||||||
let entities_needed = Some(input_timestamp) > entities_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 {
|
Ok(SingleRegionProcessor {
|
||||||
block_types: &processor.block_types,
|
block_types: &processor.block_types,
|
||||||
biome_types: &processor.biome_types,
|
biome_types: &processor.biome_types,
|
||||||
|
@ -131,12 +144,7 @@ impl<'a> SingleRegionProcessor<'a> {
|
||||||
output_needed,
|
output_needed,
|
||||||
lightmap_needed,
|
lightmap_needed,
|
||||||
entities_needed,
|
entities_needed,
|
||||||
processed_region,
|
|
||||||
biome_list,
|
|
||||||
lightmap,
|
|
||||||
entities,
|
|
||||||
image_format: processor.config.tile_image_format(),
|
image_format: processor.config.tile_image_format(),
|
||||||
has_unknown: false,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,14 +168,14 @@ impl<'a> SingleRegionProcessor<'a> {
|
||||||
/// Saves processed region data
|
/// Saves processed region data
|
||||||
///
|
///
|
||||||
/// The timestamp is the time of the last modification of the input 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 {
|
if !self.output_needed {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
storage::write_file(
|
storage::write_file(
|
||||||
&self.output_path,
|
&self.output_path,
|
||||||
&self.processed_region,
|
processed_region,
|
||||||
REGION_FILE_META_VERSION,
|
REGION_FILE_META_VERSION,
|
||||||
self.input_timestamp,
|
self.input_timestamp,
|
||||||
)
|
)
|
||||||
|
@ -176,7 +184,7 @@ impl<'a> SingleRegionProcessor<'a> {
|
||||||
/// Saves a lightmap tile
|
/// Saves a lightmap tile
|
||||||
///
|
///
|
||||||
/// The timestamp is the time of the last modification of the input region data.
|
/// 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 {
|
if !self.lightmap_needed {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
@ -186,7 +194,7 @@ impl<'a> SingleRegionProcessor<'a> {
|
||||||
LIGHTMAP_FILE_META_VERSION,
|
LIGHTMAP_FILE_META_VERSION,
|
||||||
self.input_timestamp,
|
self.input_timestamp,
|
||||||
|file| {
|
|file| {
|
||||||
self.lightmap
|
lightmap
|
||||||
.write_to(file, self.image_format)
|
.write_to(file, self.image_format)
|
||||||
.context("Failed to save image")
|
.context("Failed to save image")
|
||||||
},
|
},
|
||||||
|
@ -196,27 +204,32 @@ impl<'a> SingleRegionProcessor<'a> {
|
||||||
/// Saves processed entity data
|
/// Saves processed entity data
|
||||||
///
|
///
|
||||||
/// The timestamp is the time of the last modification of the input region 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 {
|
if !self.entities_needed {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.entities.block_entities.sort_unstable();
|
entities.block_entities.sort_unstable();
|
||||||
|
|
||||||
storage::write_file(
|
storage::write_file(
|
||||||
&self.entities_path,
|
&self.entities_path,
|
||||||
&self.entities,
|
entities,
|
||||||
ENTITIES_FILE_META_VERSION,
|
ENTITIES_FILE_META_VERSION,
|
||||||
self.input_timestamp,
|
self.input_timestamp,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Processes a single chunk
|
/// 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) =
|
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))?;
|
.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 self.output_needed || self.lightmap_needed {
|
||||||
if let Some(layer::LayerData {
|
if let Some(layer::LayerData {
|
||||||
|
@ -224,11 +237,11 @@ impl<'a> SingleRegionProcessor<'a> {
|
||||||
biomes,
|
biomes,
|
||||||
block_light,
|
block_light,
|
||||||
depths,
|
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))?
|
.with_context(|| format!("Failed to process chunk {:?}", chunk_coords))?
|
||||||
{
|
{
|
||||||
if self.output_needed {
|
if self.output_needed {
|
||||||
self.processed_region.chunks[chunk_coords] = Some(Box::new(ProcessedChunk {
|
data.chunks[chunk_coords] = Some(Box::new(ProcessedChunk {
|
||||||
blocks,
|
blocks,
|
||||||
biomes,
|
biomes,
|
||||||
depths,
|
depths,
|
||||||
|
@ -237,7 +250,7 @@ impl<'a> SingleRegionProcessor<'a> {
|
||||||
|
|
||||||
if self.lightmap_needed {
|
if self.lightmap_needed {
|
||||||
let chunk_lightmap = Self::render_chunk_lightmap(block_light);
|
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,
|
chunk_coords,
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
self.entities.block_entities.append(&mut block_entities);
|
data.entities.block_entities.append(&mut block_entities);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Processes the chunks of the region
|
/// Processes the chunks of the region
|
||||||
fn process_chunks(&mut self) -> Result<()> {
|
fn process_chunks(&self, data: &mut SingleRegionData) -> Result<()> {
|
||||||
crate::nbt::region::from_file(&self.input_path)?
|
crate::nbt::region::from_file(&self.input_path)?.foreach_chunk(
|
||||||
.foreach_chunk(|chunk_coords, data| self.process_chunk(chunk_coords, data))
|
|chunk_coords, chunk_data| self.process_chunk(data, chunk_coords, chunk_data),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Processes the region
|
/// Processes the region
|
||||||
fn run(mut self) -> Result<RegionProcessorStatus> {
|
fn run(&self) -> Result<RegionProcessorStatus> {
|
||||||
if !self.output_needed && !self.lightmap_needed && !self.entities_needed {
|
if !self.output_needed && !self.lightmap_needed && !self.entities_needed {
|
||||||
debug!(
|
debug!(
|
||||||
"Skipping unchanged region r.{}.{}.mca",
|
"Skipping unchanged region r.{}.{}.mca",
|
||||||
|
@ -276,7 +290,9 @@ impl<'a> SingleRegionProcessor<'a> {
|
||||||
self.coords.x, self.coords.z
|
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()
|
if self.output_timestamp.is_some()
|
||||||
&& self.lightmap_timestamp.is_some()
|
&& self.lightmap_timestamp.is_some()
|
||||||
&& self.entities_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_region(&processed_region)?;
|
||||||
self.save_lightmap()?;
|
self.save_lightmap(&data.lightmap)?;
|
||||||
self.save_entities()?;
|
self.save_entities(&mut data.entities)?;
|
||||||
|
|
||||||
Ok(if self.has_unknown {
|
Ok(if data.has_unknown {
|
||||||
RegionProcessorStatus::OkWithUnknown
|
RegionProcessorStatus::OkWithUnknown
|
||||||
} else {
|
} else {
|
||||||
RegionProcessorStatus::Ok
|
RegionProcessorStatus::Ok
|
||||||
|
|
Loading…
Add table
Reference in a new issue