mapserver: config & readme

This commit is contained in:
Pascal Engélibert 2022-02-22 11:27:30 +01:00
parent e21c3f825b
commit bc7e9629c1
Signed by: tuxmain
GPG Key ID: 3504BC6D362F7DCA
6 changed files with 226 additions and 56 deletions

View File

@ -8,6 +8,12 @@ The output can be used with [Leaflet](https://leafletjs.com/).
## Use
Choose the strategy you prefer between complete periodic generation and cached on-demand generation:
### Python generator
Runs minetestmapper once for a given part of the map. You can run it with cron. You also need an external webserver (e.g. Apache, Nginx) to serve the generated tiles.
Dependencies:
* [Python3](https://www.python.org/)
@ -17,12 +23,34 @@ Dependencies:
Edit the settings in `mtmapper.py` according to your configuration.
```
```bash
python3 mtmapper.py
```
Depending on the world and the settings (number of zoom levels, number of blocks), it may take a while.
### Rust mapserver
A program that runs minetestserver automatically and serves cached tiles through an included webserver. You can run it as a daemon.
Dependencies:
* [Rust](https://rustup.rs/) (minimal toolchain is enough) (for building only)
* [minetestmapper](https://github.com/minetest/minetestmapper)
* [Colors.txt](https://github.com/MilesBDyson/Colors.txt) (optional -- more complete colorset)
```bash
cd mapserver
cargo build --release
./target/release/mapserver --help
# Example with arguments:
./target/release/mapserver -w /var/games/minetest-server/.minetest/worlds/juneland-survival/ -p /home/tuxmain/minetestmapper/minetestmapper -- --drawalpha --max-y 2000 --colors /home/tuxmain/Colors.txt/colors.txt
```
### Web interface
Both generation methods need a webpage(can be served by a webserver like Apache, Nginx):
Edit `index.html` (change the tiles URL, title, attribution and points of interest).
You can keep my server as a CDN for Leaflet, but it's preferable to host your own copy of Leaflet.

View File

@ -58,7 +58,7 @@ function onMapClick(e) {
map.on('click', onMapClick);
L.control.layers({
"Juneland Survival": L.tileLayer(MAP_TILES, {attribution: "CC BY-SA Juneland Players"}).addTo(map)
"Juneland Survival": L.tileLayer(MAP_TILES, {attribution: '<a href="https://git.p2p.legal/tuxmain/minetest-tiler">minetest-tiler</a> | CC BY-SA <a href="https://juneland.fr">Juneland</a> Players'}).addTo(map)
}, {
"Points d'intérêt": L.layerGroup(points).addTo(map)
}).addTo(map);

120
mapserver/Cargo.lock generated
View File

@ -447,6 +447,33 @@ dependencies = [
]
[[package]]
name = "clap"
version = "3.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d76c22c9b9b215eeb8d016ad3a90417bd13cb24cf8142756e6472445876cab7"
dependencies = [
"bitflags",
"clap_derive",
"indexmap",
"lazy_static",
"os_str_bytes",
"textwrap",
]
[[package]]
name = "clap_derive"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fd1122e63869df2cb309f449da1ad54a7c6dfeb7c7e6ccd8e0825d9eb93bb72"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "color_quant"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@ -602,6 +629,26 @@ dependencies = [
]
[[package]]
name = "directories"
version = "4.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780"
dependencies = [
"libc",
"redox_users",
"winapi",
]
[[package]]
name = "discard"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@ -770,6 +817,18 @@ dependencies = [
]
[[package]]
name = "hashbrown"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
[[package]]
name = "heck"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
@ -876,6 +935,16 @@ dependencies = [
]
[[package]]
name = "indexmap"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "infer"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@ -950,7 +1019,9 @@ name = "mapserver"
version = "0.1.0"
dependencies = [
"async-std",
"clap",
"crossbeam-channel",
"directories",
"http-types",
"image",
"log",
@ -1045,6 +1116,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "os_str_bytes"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
dependencies = [
"memchr",
]
[[package]]
name = "parking"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@ -1160,6 +1240,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro-hack"
version = "0.5.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
@ -1264,6 +1368,16 @@ dependencies = [
]
[[package]]
name = "redox_users"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
dependencies = [
"getrandom 0.2.4",
"redox_syscall",
]
[[package]]
name = "route-recognizer"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@ -1539,6 +1653,12 @@ dependencies = [
]
[[package]]
name = "textwrap"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
[[package]]
name = "thiserror"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"

View File

@ -5,7 +5,9 @@ edition = "2021"
[dependencies]
async-std = { version = "1.6.5", features = ["attributes"] }
clap = { version = "3.1.1", default-features = false, features = ["derive", "std"] }
crossbeam-channel = "0.5.2"
directories = "4.0.0"
http-types = "2.12.0"
image = { version = "0.24.1", default-features = false, features = ["png"] }
log = "0.4.14"

View File

@ -1,44 +0,0 @@
// TODO x,y bounds
pub struct Config {
/// Must start and end with `/`
pub base_url: String,
pub cache_age: u64,
pub listen: String,
pub mapper_threads: usize,
pub minetestmapper_args: Vec<String>,
pub minetestmapper_path: String,
/// Should end with `/`
pub output_path: String,
pub tile_size: usize,
pub world_path: String,
pub zoom_default: i32,
pub zoom_max: i32,
pub zoom_min: i32,
}
impl Default for Config {
fn default() -> Self {
Self {
base_url: String::from("/"),
cache_age: 86400,
listen: String::from("127.0.0.1:8080"),
mapper_threads: 4,
minetestmapper_args: vec![
String::from("--drawalpha"),
String::from("--max-y"),
String::from("2000"),
String::from("--colors"),
String::from("/home/tuxmain/Colors.txt/colors.txt"),
],
minetestmapper_path: String::from("/home/tuxmain/minetestmapper/minetestmapper"),
output_path: String::from("/var/www/html/minetest-map/survival/"),
tile_size: 256,
world_path: String::from(
"/var/games/minetest-server/.minetest/worlds/juneland-survival/",
),
zoom_default: 7,
zoom_max: 10,
zoom_min: 4,
}
}
}

View File

@ -1,13 +1,70 @@
#![feature(try_blocks)]
mod config;
//mod config;
use clap::Parser;
use crossbeam_channel::Sender;
use log::{debug, error, warn};
use log::{debug, error, info, warn};
use parking_lot::RwLock;
use std::{collections::HashMap, io::Read, sync::Arc, time::SystemTime};
use tide::Request;
fn get_cache_dir() -> String {
let dir = directories::ProjectDirs::from("tk", "txmn", "minetest-tiler").map_or_else(
String::new,
|o| {
o.cache_dir()
.to_str()
.map_or_else(String::new, String::from)
},
);
if dir.ends_with('/') {
dir
} else {
format!("{}/", dir)
}
}
#[derive(Parser, Debug)]
#[clap(
author = "tuxmain <t@txmn.tk> (GNU AGPLv3)",
about = "Map generator & server for MineTest"
)]
struct Args {
/// Must start and end with `/`
#[clap(short = 'b', long, default_value = "/")]
base_url: String,
/// Cache duration in seconds
#[clap(short = 'c', long, default_value_t = 86400)]
cache_age: u64,
/// Address to listen to
#[clap(short = 'l', long, default_value = "0.0.0.0:30800")]
listen: String,
/// Directory that will contain the map (should end with `/`)
#[clap(short='o', long, default_value_t=get_cache_dir())]
output_path: String,
/// World directory
#[clap(
short = 'w',
long,
default_value = "/var/games/minetest-server/.minetest/world/world/"
)]
world_path: String,
#[clap(short = 'z', long, default_value_t = 7)]
zoom_default: i32,
#[clap(short = 'm', long, default_value_t = 4)]
zoom_min: i32,
#[clap(short = 'M', long, default_value_t = 10)]
zoom_max: i32,
#[clap(short = 's', long, default_value_t = 256)]
tile_size: usize,
/// Path to minetestmapper executable
#[clap(short = 'p', long, default_value = "minetestmapper")]
minetestmapper_path: String,
#[clap(last = true)]
minetestmapper_args: Vec<String>,
}
#[derive(Debug)]
enum Error {
Image(image::ImageError),
@ -17,6 +74,8 @@ enum Error {
#[async_std::main]
async fn main() -> tide::Result<()> {
let config = Args::parse();
simplelog::TermLogger::init(
simplelog::LevelFilter::Info,
simplelog::Config::default(),
@ -25,7 +84,7 @@ async fn main() -> tide::Result<()> {
)
.unwrap();
let config = config::Config::default();
info!("Output dir: {}", config.output_path);
assert!(
config.base_url.starts_with('/') && config.base_url.ends_with('/'),
"`base_url` must start and end with `/`"
@ -50,7 +109,7 @@ fn run_minetestmapper(
y: i32,
tile_dir: &str,
tile_path: &str,
config: Arc<config::Config>,
config: Arc<Args>,
) -> Result<(), Error> {
debug!("Generating tile ({},{},{})", z, x, y);
std::fs::create_dir_all(tile_dir).map_err(|e| {
@ -97,7 +156,7 @@ fn generate_tile(
tile_dir: &str,
tile_path: &str,
tasks: Arc<RwLock<HashMap<(i32, i32, i32), Sender<()>>>>,
config: Arc<config::Config>,
config: Arc<Args>,
) -> Result<(), Error> {
let mut generate = true;
loop {
@ -195,8 +254,13 @@ fn generate_tile(
})?;
}
std::cmp::Ordering::Greater => {
let ntile_path =
get_dep_tile(z - 1, lock_coord.1 / 2, lock_coord.2 / 2, tasks.clone(), config.clone())?;
let ntile_path = get_dep_tile(
z - 1,
lock_coord.1 / 2,
lock_coord.2 / 2,
tasks.clone(),
config.clone(),
)?;
let ntile = image::open(ntile_path).map_err(Error::Image)?;
let ntile = image::imageops::resize(
&ntile,
@ -310,7 +374,7 @@ fn get_dep_tile(
x: i32,
y: i32,
tasks: Arc<RwLock<HashMap<(i32, i32, i32), Sender<()>>>>,
config: Arc<config::Config>,
config: Arc<Args>,
) -> Result<String, Error> {
let tile_dir = format!("{}{}/{}", config.output_path, z, x);
let tile_path = format!("{}/{}.png", tile_dir, y);
@ -350,7 +414,7 @@ fn resp_tile(
tile_dir: &str,
tile_path: &str,
tasks: Arc<RwLock<HashMap<(i32, i32, i32), Sender<()>>>>,
config: Arc<config::Config>,
config: Arc<Args>,
) -> Result<Vec<u8>, Error> {
generate_tile(z, x, y, tile_dir, tile_path, tasks, config)?;
read_tile_file(tile_path)
@ -359,7 +423,7 @@ fn resp_tile(
async fn req_tile(
req: Request<()>,
tasks: Arc<RwLock<HashMap<(i32, i32, i32), Sender<()>>>>,
config: Arc<config::Config>,
config: Arc<Args>,
) -> tide::Result {
let z: i32 = req.param("z")?.parse()?;