core: merge entity data for all regions into one file

Introduce the EntityCollector, using the TileCollector and TileMerger
traits.
This commit is contained in:
Matthias Schiffer 2023-11-26 12:39:51 +01:00
parent 1143396068
commit cde6a4b6e6
Signed by: neocturne
GPG key ID: 16EF3F64CB201D9C
3 changed files with 132 additions and 1 deletions

View file

@ -141,6 +141,8 @@ pub struct Config {
pub processed_dir: PathBuf, pub processed_dir: PathBuf,
/// Path for storage of processed entity data files /// Path for storage of processed entity data files
pub entities_dir: PathBuf, pub entities_dir: PathBuf,
/// Path for storage of the final merged processed entity data file
pub entities_path_final: PathBuf,
/// Path of viewer metadata file /// Path of viewer metadata file
pub metadata_path: PathBuf, pub metadata_path: PathBuf,
} }
@ -157,7 +159,8 @@ impl Config {
let region_dir = [&args.input_dir, Path::new("region")].iter().collect(); 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 level_dat_path = [&args.input_dir, Path::new("level.dat")].iter().collect();
let processed_dir: PathBuf = [&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 entities_dir: PathBuf = [&processed_dir, Path::new("entities")].iter().collect();
let entities_path_final = [&entities_dir, Path::new("entities.bin")].iter().collect();
let metadata_path = [&args.output_dir, Path::new("info.json")].iter().collect(); let metadata_path = [&args.output_dir, Path::new("info.json")].iter().collect();
Config { Config {
@ -167,6 +170,7 @@ impl Config {
output_dir: args.output_dir.clone(), output_dir: args.output_dir.clone(),
processed_dir, processed_dir,
entities_dir, entities_dir,
entities_path_final,
metadata_path, metadata_path,
} }
} }

View file

@ -0,0 +1,123 @@
//! The [EntityCollector]
use std::path::Path;
use anyhow::{Context, Result};
use tracing::{info, warn};
use super::{common::*, tile_collector::TileCollector, tile_merger::TileMerger};
use crate::io::{fs, storage};
/// Generates mipmap tiles from full-resolution tile images
pub struct EntityCollector<'a> {
/// Common MinedMap configuration from command line
config: &'a Config,
/// List of populated tiles for base mipmap level (level 0)
regions: &'a [TileCoords],
}
impl<'a> TileMerger for EntityCollector<'a> {
fn file_meta_version(&self) -> fs::FileMetaVersion {
ENTITIES_FILE_META_VERSION
}
fn tile_path(&self, level: usize, coords: TileCoords) -> std::path::PathBuf {
self.config.entities_path(level, coords)
}
fn write_tile(
&self,
file: &mut std::io::BufWriter<std::fs::File>,
sources: &[super::tile_merger::Source],
) -> Result<()> {
Self::merge_entity_lists(file, sources.iter().map(|source| &source.1))
}
}
impl<'a> TileCollector for EntityCollector<'a> {
type CollectOutput = ();
fn tiles(&self) -> &[TileCoords] {
self.regions
}
fn prepare(&self, level: usize) -> Result<()> {
fs::create_dir_all(&self.config.entities_dir(level))
}
fn finish(
&self,
_level: usize,
_outputs: impl Iterator<Item = Self::CollectOutput>,
) -> Result<()> {
Ok(())
}
fn collect_one(
&self,
level: usize,
coords: TileCoords,
prev: &TileCoordMap,
) -> Result<Self::CollectOutput> {
self.merge_tiles(level, coords, prev)?;
Ok(())
}
}
impl<'a> EntityCollector<'a> {
/// Constructs a new EntityCollector
pub fn new(config: &'a Config, regions: &'a [TileCoords]) -> Self {
EntityCollector { config, regions }
}
/// Merges multiple entity lists into one
fn merge_entity_lists<P: AsRef<Path>>(
file: &mut std::io::BufWriter<std::fs::File>,
sources: impl Iterator<Item = P>,
) -> Result<()> {
let mut output = ProcessedEntities::default();
for source_path in sources {
let mut source: ProcessedEntities =
match storage::read_file(source_path.as_ref(), storage::Format::Json) {
Ok(source) => source,
Err(err) => {
warn!(
"Failed to read entity data file {}: {:?}",
source_path.as_ref().display(),
err,
);
continue;
}
};
output.block_entities.append(&mut source.block_entities);
}
storage::write(file, &output, storage::Format::Json).context("Failed to write entity data")
}
/// Runs the mipmap generation
pub fn run(self) -> Result<()> {
info!("Collecting entity data...");
let tile_stack = self.collect_tiles()?;
// Final merge
let level = tile_stack.len() - 1;
let tile_map = &tile_stack[level];
let sources: Vec<_> = [(-1, -1), (-1, 0), (0, -1), (0, 0)]
.into_iter()
.map(|(x, z)| TileCoords { x, z })
.filter(|&coords| tile_map.contains(coords))
.map(|coords| self.tile_path(level, coords))
.collect();
fs::create_with_tmpfile(&self.config.entities_path_final, |file| {
Self::merge_entity_lists(file, sources.iter())
})?;
info!("Collected entity data.");
Ok(())
}
}

View file

@ -1,6 +1,7 @@
//! Core functions of the MinedMap CLI //! Core functions of the MinedMap CLI
mod common; mod common;
mod entity_collector;
mod metadata_writer; mod metadata_writer;
mod region_group; mod region_group;
mod region_processor; mod region_processor;
@ -21,6 +22,8 @@ use region_processor::RegionProcessor;
use tile_mipmapper::TileMipmapper; use tile_mipmapper::TileMipmapper;
use tile_renderer::TileRenderer; use tile_renderer::TileRenderer;
use self::entity_collector::EntityCollector;
/// MinedMap version number /// MinedMap version number
const VERSION: &str = git_version!( const VERSION: &str = git_version!(
args = ["--abbrev=7", "--match=v*", "--dirty=-modified"], args = ["--abbrev=7", "--match=v*", "--dirty=-modified"],
@ -77,6 +80,7 @@ pub fn cli() -> Result<()> {
let regions = RegionProcessor::new(&config).run()?; let regions = RegionProcessor::new(&config).run()?;
TileRenderer::new(&config, &rt, &regions).run()?; TileRenderer::new(&config, &rt, &regions).run()?;
let tiles = TileMipmapper::new(&config, &regions).run()?; let tiles = TileMipmapper::new(&config, &regions).run()?;
EntityCollector::new(&config, &regions).run()?;
MetadataWriter::new(&config, &tiles).run()?; MetadataWriter::new(&config, &tiles).run()?;
Ok(()) Ok(())