mirror of
https://github.com/neocturne/MinedMap.git
synced 2025-03-04 17:23:33 +01:00
core/region_processor: store processed block entities for each region
This commit is contained in:
parent
c44f6ab859
commit
7297c03567
2 changed files with 101 additions and 20 deletions
|
@ -9,7 +9,12 @@ use std::{
|
|||
use indexmap::IndexSet;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{io::fs::FileMetaVersion, resource::Biome, types::*, world::layer};
|
||||
use crate::{
|
||||
io::fs::FileMetaVersion,
|
||||
resource::Biome,
|
||||
types::*,
|
||||
world::{block_entity::BlockEntity, layer},
|
||||
};
|
||||
|
||||
/// Increase to force regeneration of all output files
|
||||
|
||||
|
@ -36,6 +41,11 @@ pub const LIGHTMAP_FILE_META_VERSION: FileMetaVersion = FileMetaVersion(0);
|
|||
/// Increase when the mipmap generation changes (this should not happen)
|
||||
pub const MIPMAP_FILE_META_VERSION: FileMetaVersion = FileMetaVersion(0);
|
||||
|
||||
/// MinedMap processed entity data version number
|
||||
///
|
||||
/// Increase when entity collection changes bacause of code changes.
|
||||
pub const ENTITIES_FILE_META_VERSION: FileMetaVersion = FileMetaVersion(0);
|
||||
|
||||
/// Coordinate pair of a generated tile
|
||||
///
|
||||
/// Each tile corresponds to one Minecraft region file
|
||||
|
@ -94,6 +104,13 @@ pub struct ProcessedRegion {
|
|||
pub chunks: ChunkArray<Option<Box<ProcessedChunk>>>,
|
||||
}
|
||||
|
||||
/// Data structure for storing entity data between processing and collection steps
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
pub struct ProcessedEntities {
|
||||
/// List of block entities
|
||||
pub block_entities: Vec<BlockEntity>,
|
||||
}
|
||||
|
||||
/// Derives a filename from region coordinates and a file extension
|
||||
///
|
||||
/// Can be used for input regions, processed data or rendered tiles
|
||||
|
@ -122,6 +139,8 @@ pub struct Config {
|
|||
pub output_dir: PathBuf,
|
||||
/// Path for storage of intermediate processed data files
|
||||
pub processed_dir: PathBuf,
|
||||
/// Path for storage of processed entity data files
|
||||
pub entities_dir: PathBuf,
|
||||
/// Path of viewer metadata file
|
||||
pub metadata_path: PathBuf,
|
||||
}
|
||||
|
@ -137,7 +156,8 @@ impl Config {
|
|||
|
||||
let region_dir = [&args.input_dir, Path::new("region")].iter().collect();
|
||||
let level_dat_path = [&args.input_dir, Path::new("level.dat")].iter().collect();
|
||||
let processed_dir = [&args.output_dir, Path::new("processed")].iter().collect();
|
||||
let processed_dir: PathBuf = [&args.output_dir, Path::new("processed")].iter().collect();
|
||||
let entities_dir = [&processed_dir, Path::new("entities")].iter().collect();
|
||||
let metadata_path = [&args.output_dir, Path::new("info.json")].iter().collect();
|
||||
|
||||
Config {
|
||||
|
@ -146,6 +166,7 @@ impl Config {
|
|||
level_dat_path,
|
||||
output_dir: args.output_dir.clone(),
|
||||
processed_dir,
|
||||
entities_dir,
|
||||
metadata_path,
|
||||
}
|
||||
}
|
||||
|
@ -162,6 +183,20 @@ impl Config {
|
|||
[&self.processed_dir, Path::new(&filename)].iter().collect()
|
||||
}
|
||||
|
||||
/// Constructs the base output path for processed entity data
|
||||
pub fn entities_dir(&self, level: usize) -> PathBuf {
|
||||
[&self.entities_dir, Path::new(&level.to_string())]
|
||||
.iter()
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Constructs the path of a processed entity data file
|
||||
pub fn entities_path(&self, level: usize, coords: TileCoords) -> PathBuf {
|
||||
let filename = coord_filename(coords, "bin");
|
||||
let dir = self.entities_dir(level);
|
||||
[Path::new(&dir), Path::new(&filename)].iter().collect()
|
||||
}
|
||||
|
||||
/// Constructs the base output path for a [TileKind] and mipmap level
|
||||
pub fn tile_dir(&self, kind: TileKind, level: usize) -> PathBuf {
|
||||
let prefix = match kind {
|
||||
|
|
|
@ -64,20 +64,28 @@ struct SingleRegionProcessor<'a> {
|
|||
output_path: PathBuf,
|
||||
/// Lightmap output filename
|
||||
lightmap_path: PathBuf,
|
||||
/// Processed entity output filename
|
||||
entities_path: PathBuf,
|
||||
/// Timestamp of last modification of input file
|
||||
input_timestamp: SystemTime,
|
||||
/// Timestamp of last modification of processed region output file (if valid)
|
||||
output_timestamp: Option<SystemTime>,
|
||||
/// Timestamp of last modification of lightmap output file (if valid)
|
||||
lightmap_timestamp: Option<SystemTime>,
|
||||
/// Timestamp of last modification of entity list output file (if valid)
|
||||
entities_timestamp: Option<SystemTime>,
|
||||
/// True if processed region output file needs to be updated
|
||||
output_needed: bool,
|
||||
/// True if lightmap output file needs to be updated
|
||||
lightmap_needed: bool,
|
||||
/// True if entity output file needs to be updated
|
||||
entities_needed: bool,
|
||||
/// Processed region intermediate data
|
||||
processed_region: ProcessedRegion,
|
||||
/// 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,
|
||||
}
|
||||
|
@ -93,14 +101,20 @@ impl<'a> SingleRegionProcessor<'a> {
|
|||
|
||||
let output_path = processor.config.processed_path(coords);
|
||||
let output_timestamp = fs::read_timestamp(&output_path, REGION_FILE_META_VERSION);
|
||||
|
||||
let lightmap_path = processor.config.tile_path(TileKind::Lightmap, 0, coords);
|
||||
let lightmap_timestamp = fs::read_timestamp(&lightmap_path, LIGHTMAP_FILE_META_VERSION);
|
||||
|
||||
let entities_path = processor.config.entities_path(0, coords);
|
||||
let entities_timestamp = fs::read_timestamp(&entities_path, ENTITIES_FILE_META_VERSION);
|
||||
|
||||
let output_needed = Some(input_timestamp) > output_timestamp;
|
||||
let lightmap_needed = Some(input_timestamp) > lightmap_timestamp;
|
||||
let entities_needed = Some(input_timestamp) > entities_timestamp;
|
||||
|
||||
let processed_region = ProcessedRegion::default();
|
||||
let lightmap = image::GrayAlphaImage::new(N, N);
|
||||
let entities = ProcessedEntities::default();
|
||||
|
||||
Ok(SingleRegionProcessor {
|
||||
block_types: &processor.block_types,
|
||||
|
@ -109,13 +123,17 @@ impl<'a> SingleRegionProcessor<'a> {
|
|||
input_path,
|
||||
output_path,
|
||||
lightmap_path,
|
||||
entities_path,
|
||||
input_timestamp,
|
||||
output_timestamp,
|
||||
lightmap_timestamp,
|
||||
entities_timestamp,
|
||||
output_needed,
|
||||
lightmap_needed,
|
||||
entities_needed,
|
||||
processed_region,
|
||||
lightmap,
|
||||
entities,
|
||||
has_unknown: false,
|
||||
})
|
||||
}
|
||||
|
@ -174,34 +192,57 @@ 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(&self) -> Result<()> {
|
||||
if !self.entities_needed {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
storage::write_file(
|
||||
&self.entities_path,
|
||||
&self.entities,
|
||||
storage::Format::Json,
|
||||
ENTITIES_FILE_META_VERSION,
|
||||
self.input_timestamp,
|
||||
)
|
||||
}
|
||||
|
||||
/// Processes a single chunk
|
||||
fn process_chunk(&mut self, chunk_coords: ChunkCoords, data: world::de::Chunk) -> Result<()> {
|
||||
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,
|
||||
block_light,
|
||||
depths,
|
||||
}) = world::layer::top_layer(&mut self.processed_region.biome_list, &chunk)
|
||||
.with_context(|| format!("Failed to process chunk {:?}", chunk_coords))?
|
||||
else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
if self.output_needed {
|
||||
self.processed_region.chunks[chunk_coords] = Some(Box::new(ProcessedChunk {
|
||||
if self.output_needed || self.lightmap_needed {
|
||||
if let Some(layer::LayerData {
|
||||
blocks,
|
||||
biomes,
|
||||
block_light,
|
||||
depths,
|
||||
}));
|
||||
}) = world::layer::top_layer(&mut self.processed_region.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 {
|
||||
blocks,
|
||||
biomes,
|
||||
depths,
|
||||
}));
|
||||
}
|
||||
|
||||
if self.lightmap_needed {
|
||||
let chunk_lightmap = Self::render_chunk_lightmap(block_light);
|
||||
overlay_chunk(&mut self.lightmap, &chunk_lightmap, chunk_coords);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.lightmap_needed {
|
||||
let chunk_lightmap = Self::render_chunk_lightmap(block_light);
|
||||
overlay_chunk(&mut self.lightmap, &chunk_lightmap, chunk_coords);
|
||||
if self.entities_needed {
|
||||
let mut block_entities = chunk.block_entities();
|
||||
self.entities.block_entities.append(&mut block_entities);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -215,7 +256,7 @@ impl<'a> SingleRegionProcessor<'a> {
|
|||
|
||||
/// Processes the region
|
||||
fn run(mut self) -> Result<RegionProcessorStatus> {
|
||||
if !self.output_needed && !self.lightmap_needed {
|
||||
if !self.output_needed && !self.lightmap_needed && !self.entities_needed {
|
||||
debug!(
|
||||
"Skipping unchanged region r.{}.{}.mca",
|
||||
self.coords.x, self.coords.z
|
||||
|
@ -229,7 +270,10 @@ impl<'a> SingleRegionProcessor<'a> {
|
|||
);
|
||||
|
||||
if let Err(err) = self.process_chunks() {
|
||||
if self.output_timestamp.is_some() && self.lightmap_timestamp.is_some() {
|
||||
if self.output_timestamp.is_some()
|
||||
&& self.lightmap_timestamp.is_some()
|
||||
&& self.entities_timestamp.is_some()
|
||||
{
|
||||
warn!(
|
||||
"Failed to process region {:?}, using old data: {:?}",
|
||||
self.coords, err
|
||||
|
@ -246,6 +290,7 @@ impl<'a> SingleRegionProcessor<'a> {
|
|||
|
||||
self.save_region()?;
|
||||
self.save_lightmap()?;
|
||||
self.save_entities()?;
|
||||
|
||||
Ok(if self.has_unknown {
|
||||
RegionProcessorStatus::OkWithUnknown
|
||||
|
@ -313,6 +358,7 @@ impl<'a> RegionProcessor<'a> {
|
|||
pub fn run(self) -> Result<Vec<TileCoords>> {
|
||||
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.entities_dir(0))?;
|
||||
|
||||
info!("Processing region files...");
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue