diff --git a/mapserver/src/config.rs b/mapserver/src/config.rs index 245a7b2..ee8b12a 100644 --- a/mapserver/src/config.rs +++ b/mapserver/src/config.rs @@ -25,7 +25,6 @@ impl Default for Config { mapper_threads: 4, minetestmapper_args: vec![ String::from("--drawalpha"), - String::from("--noemptyimage"), String::from("--max-y"), String::from("2000"), String::from("--colors"), diff --git a/mapserver/src/main.rs b/mapserver/src/main.rs index 193be2a..0aa6160 100644 --- a/mapserver/src/main.rs +++ b/mapserver/src/main.rs @@ -10,6 +10,7 @@ use tide::Request; #[derive(Debug)] enum Error { + Image(image::ImageError), Io(std::io::Error), Minetestmapper, Todo, @@ -114,10 +115,56 @@ fn generate_tile( drop(tasks_guard); let res: Result<(), Error> = try { - if z == config.zoom_default { - run_minetestmapper(z, x, y, tile_dir, tile_path, config)?; - } else { - Err(Error::Todo)?; + match z.cmp(&config.zoom_default) { + std::cmp::Ordering::Equal => { + run_minetestmapper(z, x, y, tile_dir, tile_path, config)? + } + std::cmp::Ordering::Less => { + let tile1_path = + get_dep_tile(z + 1, x * 2, y * 2, tasks.clone(), config.clone())?; + let tile2_path = get_dep_tile( + z + 1, + x * 2, + y * 2 + 1, + tasks.clone(), + config.clone(), + )?; + let tile3_path = get_dep_tile( + z + 1, + x * 2 + 1, + y * 2, + tasks.clone(), + config.clone(), + )?; + let tile4_path = get_dep_tile( + z + 1, + x * 2 + 1, + y * 2 + 1, + tasks.clone(), + config.clone(), + )?; + + let tile1 = image::open(tile1_path).map_err(Error::Image)?.into_rgb8(); + let tile2 = image::open(tile2_path).map_err(Error::Image)?.into_rgb8(); + let tile3 = image::open(tile3_path).map_err(Error::Image)?.into_rgb8(); + let tile4 = image::open(tile4_path).map_err(Error::Image)?.into_rgb8(); + + let mut tile = image::ImageBuffer::, Vec>::new( + config.tile_size as u32 * 2, + config.tile_size as u32 * 2, + ); + image::imageops::replace(&mut tile, &tile1, 0, 0); + image::imageops::replace(&mut tile, &tile2, 0, config.tile_size as i64); + image::imageops::replace(&mut tile, &tile3, config.tile_size as i64, 0); + image::imageops::replace( + &mut tile, + &tile4, + config.tile_size as i64, + config.tile_size as i64, + ); + tile.save(tile_path).map_err(Error::Image)?; + } + std::cmp::Ordering::Greater => Err(Error::Todo)?, } }; @@ -132,6 +179,44 @@ fn generate_tile( } } +fn get_dep_tile( + z: i32, + x: i32, + y: i32, + tasks: Arc>>>, + config: Arc, +) -> Result { + let tile_dir = format!("{}{}/{}", config.output_path, z, x); + let tile_path = format!("{}/{}.png", tile_dir, y); + + match std::fs::metadata(&tile_path) { + Ok(metadata) => match metadata.modified() { + Ok(modified_time) => { + if SystemTime::now() + .duration_since(modified_time) + .unwrap() + .as_secs() > config.cache_age + { + generate_tile(z, x, y, &tile_dir, &tile_path, tasks, config)?; + } + } + Err(e) => { + warn!("Cannot get modified time of `{}`: {}", tile_path, e); + generate_tile(z, x, y, &tile_dir, &tile_path, tasks, config)?; + } + }, + Err(e) => { + if e.kind() == std::io::ErrorKind::NotFound { + debug!("Tile not found `{}`", tile_path); + } else { + warn!("Cannot get metadata of `{}`: {}", tile_path, e); + } + generate_tile(z, x, y, &tile_dir, &tile_path, tasks, config)?; + } + } + Ok(tile_path) +} + fn resp_tile( z: i32, x: i32, @@ -151,6 +236,11 @@ async fn req_tile( config: Arc, ) -> tide::Result { let z: i32 = req.param("z")?.parse()?; + + if z > config.zoom_max || z < config.zoom_min { + return Ok(tide::StatusCode::Forbidden.into()); + } + let x: i32 = req.param("x")?.parse()?; let y = req.param("y")?; let y = y.strip_suffix(".png").unwrap_or(y).parse()?;