From 3443c1c65e9f1753e1e656b35d08416c1fa298f2 Mon Sep 17 00:00:00 2001 From: godosa Date: Wed, 7 Oct 2026 00:14:38 +0200 Subject: worldmap-viewer: initial public history --- deploy/.containerignore | 13 ++++++ deploy/Containerfile | 27 +++++++++++++ deploy/README.md | 101 +++++++++++++++++++++++++++++++++++++++++++++++ deploy/requirements.lock | 6 +++ 4 files changed, 147 insertions(+) create mode 100644 deploy/.containerignore create mode 100644 deploy/Containerfile create mode 100644 deploy/README.md create mode 100644 deploy/requirements.lock (limited to 'deploy') diff --git a/deploy/.containerignore b/deploy/.containerignore new file mode 100644 index 0000000..62a308d --- /dev/null +++ b/deploy/.containerignore @@ -0,0 +1,13 @@ +# build context = the folder holding worldgen/ and worldmap-viewer/: send only the code +* +!worldgen/mapgen +!worldmap-viewer/*.py +!worldmap-viewer/LICENSE +!worldmap-viewer/CREDITS.md +!worldmap-viewer/licenses.md +!worldmap-viewer/deploy/requirements.lock +!worldmap-viewer/viewer/index.html +!worldmap-viewer/viewer/style.css +!worldmap-viewer/viewer/js +!worldmap-viewer/viewer/vendor +**/__pycache__ diff --git a/deploy/Containerfile b/deploy/Containerfile new file mode 100644 index 0000000..4e82eea --- /dev/null +++ b/deploy/Containerfile @@ -0,0 +1,27 @@ +# The map server for a public, read-only map. Build from the folder that holds worldgen/ and worldmap-viewer/: +# podman build -f worldmap-viewer/deploy/Containerfile -t worldmap-viewer . +# Code only: worlds (config/, places/, built out/) are mounted at run time — see deploy/README.md. +FROM docker.io/library/python:3.14-slim + +ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1 \ + WORLDGEN=/app/worldgen NUMBA_CACHE_DIR=/var/cache/worldmap-viewer/numba \ + OPENBLAS_THREAD_TIMEOUT=4 \ + WORLDMAP_PUBLIC=1 WORLDMAP_BIND=0.0.0.0 WORLDMAP_PORT=8765 + +COPY worldmap-viewer/deploy/requirements.lock /app/requirements.lock +RUN pip install --no-cache-dir --root-user-action=ignore -r /app/requirements.lock + +COPY worldgen/mapgen /app/worldgen/mapgen +COPY worldmap-viewer/*.py worldmap-viewer/LICENSE worldmap-viewer/CREDITS.md worldmap-viewer/licenses.md /app/worldmap-viewer/ +COPY worldmap-viewer/viewer/index.html worldmap-viewer/viewer/style.css /app/worldmap-viewer/viewer/ +COPY worldmap-viewer/viewer/js /app/worldmap-viewer/viewer/js +COPY worldmap-viewer/viewer/vendor /app/worldmap-viewer/viewer/vendor + +RUN useradd --uid 8765 --no-create-home --home-dir /nonexistent worldmap-viewer \ + && mkdir -p /var/cache/worldmap-viewer/numba && chown -R worldmap-viewer /var/cache/worldmap-viewer +USER worldmap-viewer +WORKDIR /app/worldmap-viewer +EXPOSE 8765 +# health check (Quadlet: HealthCmd=; OCI images drop HEALTHCHECK): +# python -c "import os, urllib.request as u; u.urlopen('http://127.0.0.1:' + os.environ['WORLDMAP_PORT'] + '/api/worlds', timeout=8)" +ENTRYPOINT ["python", "serve.py"] diff --git a/deploy/README.md b/deploy/README.md new file mode 100644 index 0000000..4ce6a0a --- /dev/null +++ b/deploy/README.md @@ -0,0 +1,101 @@ +# Public map server (container) + +A read-only map for anyone, on a small shared machine. Worlds are **built elsewhere** (a build needs far more memory +and time than serving) and copied over; the container holds only code. + +## 1. Build the image + +From the folder that holds `worldgen/` and `worldmap-viewer/`: + +```sh +podman build -f worldmap-viewer/deploy/Containerfile --ignorefile worldmap-viewer/deploy/.containerignore \ + -t worldmap-viewer . +``` + +`requirements.lock` pins the libraries the worlds were built and checked with (same numbers → the same tiles as on +the build machine). + +## 2. Copy a world + +```sh +python deploy.py bundle WORLD_DIR server:/srv/data/map/WORLD_NAME [--res N] # --dry-run lists it first +``` + +Copies `config/`, `places/regions.json` and `out/r/`, leaving out: + +- pins, exports, lore notes and build inputs, which a public server never serves; +- refined areas and tiles of earlier builds. + +It uses `rsync -a`, which keeps modification times. The build fingerprint depends on them; without them, the +server would rebuild every refined area. Nothing is deleted on the server: it prunes what it no longer uses at +start, and keeps the tiles it rendered. + +Then, on the server, let the eras' serve caches share their identical blocks again (a copy loses that; ≈ half +their size on btrfs or reflink XFS): + +```sh +podman exec worldmap-viewer python deploy.py dedupe /data/WORLD_NAME +``` + +Data disk: btrfs with `compress=zstd:1`, normal copy-on-write (no `chattr +C`/`nodatacow`: that turns off both +compression and block sharing). Back up nothing under `out/`: it can all be copied again. + +## 3. Run it + +Settings come from `WORLDMAP_