From 628a702fd74cae06aa53aa480a6bae5e4a716360 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sun, 30 Jul 2023 21:19:24 +0200 Subject: [PATCH] Store source modified timestamp with processed, lightmap and map tiles Allows to check whether the source is newer than the last update of the output files. --- src/bin/minedmap/region_processor.rs | 27 ++++++++++++----- src/bin/minedmap/tile_renderer.rs | 13 +++++--- src/io/fs.rs | 45 ++++++++++++++++++++++++++-- src/io/storage.rs | 5 ++-- 4 files changed, 75 insertions(+), 15 deletions(-) diff --git a/src/bin/minedmap/region_processor.rs b/src/bin/minedmap/region_processor.rs index 854c506..2081785 100644 --- a/src/bin/minedmap/region_processor.rs +++ b/src/bin/minedmap/region_processor.rs @@ -1,4 +1,4 @@ -use std::path::Path; +use std::{path::Path, time::SystemTime}; use anyhow::{Context, Result}; @@ -65,14 +65,25 @@ impl<'a> RegionProcessor<'a> { }) } - fn save_region(&self, coords: TileCoords, processed_region: &ProcessedRegion) -> Result<()> { + fn save_region( + &self, + coords: TileCoords, + processed_region: &ProcessedRegion, + timestamp: SystemTime, + ) -> Result<()> { let output_path = self.config.processed_path(coords); - storage::write(&output_path, processed_region) + storage::write(&output_path, processed_region, timestamp) } - fn save_lightmap(&self, coords: TileCoords, lightmap: &image::GrayAlphaImage) -> Result<()> { - fs::create_with_tmpfile( + fn save_lightmap( + &self, + coords: TileCoords, + lightmap: &image::GrayAlphaImage, + timestamp: SystemTime, + ) -> Result<()> { + fs::create_with_timestamp( &self.config.tile_path(TileKind::Lightmap, 0, coords), + timestamp, |file| { lightmap .write_to(file, image::ImageFormat::Png) @@ -90,6 +101,8 @@ impl<'a> RegionProcessor<'a> { let mut processed_region = ProcessedRegion::default(); let mut lightmap = image::GrayAlphaImage::new(N, N); + let timestamp = fs::modified_timestamp(path)?; + minedmap::io::region::from_file(path)?.foreach_chunk( |chunk_coords, data: world::de::Chunk| { let Some(layer::LayerData{ blocks, biomes, block_light, depths }) = self @@ -111,8 +124,8 @@ impl<'a> RegionProcessor<'a> { }, )?; - self.save_region(coords, &processed_region)?; - self.save_lightmap(coords, &lightmap)?; + self.save_region(coords, &processed_region, timestamp)?; + self.save_lightmap(coords, &lightmap, timestamp)?; Ok(()) } diff --git a/src/bin/minedmap/tile_renderer.rs b/src/bin/minedmap/tile_renderer.rs index 2956081..0b804c8 100644 --- a/src/bin/minedmap/tile_renderer.rs +++ b/src/bin/minedmap/tile_renderer.rs @@ -1,3 +1,5 @@ +use std::time::SystemTime; + use anyhow::{Context, Result}; use minedmap::{ @@ -17,9 +19,12 @@ impl<'a> TileRenderer<'a> { TileRenderer { config } } - fn load_region(&self, coords: TileCoords) -> Result { + fn load_region(&self, coords: TileCoords) -> Result<(ProcessedRegion, SystemTime)> { let processed_path = self.config.processed_path(coords); - storage::read(&processed_path).context("Failed to load processed region data") + let timestamp = fs::modified_timestamp(&processed_path)?; + let region = + storage::read(&processed_path).context("Failed to load processed region data")?; + Ok((region, timestamp)) } fn render_chunk(image: &mut image::RgbaImage, coords: ChunkCoords, chunk: &ProcessedChunk) { @@ -69,11 +74,11 @@ impl<'a> TileRenderer<'a> { .display(), ); - let region = self.load_region(coords)?; + let (region, timestamp) = self.load_region(coords)?; let mut image = image::RgbaImage::new(N, N); Self::render_region(&mut image, ®ion); - fs::create_with_tmpfile(&output_path, |file| { + fs::create_with_timestamp(&output_path, timestamp, |file| { image .write_to(file, image::ImageFormat::Png) .context("Failed to save image") diff --git a/src/io/fs.rs b/src/io/fs.rs index 2109961..0cb2a4d 100644 --- a/src/io/fs.rs +++ b/src/io/fs.rs @@ -2,19 +2,34 @@ use std::{ fs::{self, File}, io::{BufReader, BufWriter, Read, Write}, path::{Path, PathBuf}, + time::SystemTime, }; use anyhow::{Context, Ok, Result}; +use serde::Serialize; -fn tmpfile_name(path: &Path) -> PathBuf { +#[derive(Debug, Serialize)] +struct FileMeta { + timestamp: SystemTime, +} + +fn suffix_name(path: &Path, suffix: &str) -> PathBuf { let mut file_name = path.file_name().unwrap_or_default().to_os_string(); - file_name.push(".tmp"); + file_name.push(suffix); let mut ret = path.to_path_buf(); ret.set_file_name(file_name); ret } +fn tmpfile_name(path: &Path) -> PathBuf { + suffix_name(path, ".tmp") +} + +fn metafile_name(path: &Path) -> PathBuf { + suffix_name(path, ".meta") +} + pub fn create_dir_all(path: &Path) -> Result<()> { fs::create_dir_all(path) .with_context(|| format!("Failed to create directory {}", path.display(),)) @@ -84,3 +99,29 @@ where ret } + +pub fn modified_timestamp(path: &Path) -> Result { + fs::metadata(path) + .and_then(|meta| meta.modified()) + .with_context(|| { + format!( + "Failed to get modified timestamp of file {}", + path.display() + ) + }) +} + +pub fn create_with_timestamp(path: &Path, timestamp: SystemTime, f: F) -> Result +where + F: FnOnce(&mut BufWriter) -> Result, +{ + let ret = create_with_tmpfile(path, f)?; + + let meta_path = metafile_name(path); + create(&meta_path, |file| { + serde_json::to_writer(file, &FileMeta { timestamp })?; + Ok(()) + })?; + + Ok(ret) +} diff --git a/src/io/storage.rs b/src/io/storage.rs index c1a9e5e..b889231 100644 --- a/src/io/storage.rs +++ b/src/io/storage.rs @@ -2,6 +2,7 @@ use std::{ fs::File, io::{Read, Write}, path::Path, + time::SystemTime, }; use anyhow::{Context, Result}; @@ -9,8 +10,8 @@ use serde::{de::DeserializeOwned, Serialize}; use super::fs; -pub fn write(path: &Path, value: &T) -> Result<()> { - fs::create_with_tmpfile(path, |file| { +pub fn write(path: &Path, value: &T, timestamp: SystemTime) -> Result<()> { + fs::create_with_timestamp(path, timestamp, |file| { let data = bincode::serialize(value)?; let len = u32::try_from(data.len())?; let compressed = zstd::bulk::compress(&data, 1)?;