Launch MVP tile delivery server
This commit is contained in:
@@ -336,11 +336,12 @@ redownloaded when a player returns to a region.
|
||||
- [ ] Define tile adjacency and stitching rules.
|
||||
- [ ] Define tile versioning rules so terrain can improve without corrupting player state.
|
||||
- [ ] Define server-side tile delivery protocol.
|
||||
- [ ] Launch a near-term MVP tile-serving cloud server on Ubuntu or another Linux distro.
|
||||
- [x] Launch a near-term MVP tile-serving VM on Ubuntu or another Linux distro.
|
||||
- [x] Add repeatable static Ground Zero tile-delivery package and Ubuntu nginx bootstrap scripts.
|
||||
- [ ] Publish a tiny Ground Zero tile manifest and package from the tile server.
|
||||
- [ ] Prove client/server tile lookup, download, local cache, and redownload flow with the Ground Zero tile and immediate-neighbor metadata.
|
||||
- [ ] Add tile-serving server cost controls and shutdown/runbook notes so MVP testing stays free or near-free.
|
||||
- [x] Publish a tiny Ground Zero tile manifest and package from the tile server.
|
||||
- [x] Prove client/server tile lookup, download, local cache, and redownload flow with the Ground Zero tile and immediate-neighbor metadata.
|
||||
- [x] Add tile-serving server cost controls and shutdown/runbook notes so MVP testing stays free or near-free.
|
||||
- [ ] Move the MVP tile server from shared Ubuntu-Codex hosting to a dedicated `Agrarian-TileServer` VM or external cloud host before public testing.
|
||||
- [ ] Define client local tile cache layout.
|
||||
- [ ] Define local cache retention policy for old or unused tiles.
|
||||
- [ ] Define local cache redownload/revalidation behavior.
|
||||
@@ -1430,7 +1431,7 @@ Earliest incomplete foundation items:
|
||||
- [ ] Create the multiplayer/networking design document.
|
||||
- [ ] Create the persistence design document.
|
||||
- [ ] Create the Earth-scale terrain/tile streaming design document.
|
||||
- [ ] Launch near-term MVP map-tile serving cloud VM and prove Ground Zero tile lookup/download/cache flow.
|
||||
- [x] Launch near-term MVP map-tile serving cloud VM and prove Ground Zero tile lookup/download/cache flow.
|
||||
- [ ] Create economy and AGR design document.
|
||||
- [ ] Create art direction, UX/HUD direction, coding standards, Blueprint standards, and asset/folder naming standards.
|
||||
- [x] Organize `Content/Agrarian/` root folder and remove unused starter variant content.
|
||||
@@ -1439,4 +1440,4 @@ Earliest incomplete foundation items:
|
||||
|
||||
Immediate next item:
|
||||
|
||||
- [ ] Launch near-term MVP map-tile serving cloud VM.
|
||||
- [ ] Define MVP day/night length, survival pressure target, success loop, failure conditions, and closed-test readiness criteria.
|
||||
|
||||
@@ -49,6 +49,48 @@ and publishes:
|
||||
- `http://SERVER_IP/ground_zero_tiles.json`
|
||||
- `http://SERVER_IP/tiles/gz_us_ca_pacifica_utm10n_e544_n4160/v0/...`
|
||||
|
||||
## Current MVP VM
|
||||
|
||||
The first Unraid-hosted MVP tile server is running on the existing
|
||||
`Ubuntu-Codex` VM while we keep costs at zero and avoid creating a new paid
|
||||
cloud host too early.
|
||||
|
||||
Current endpoint:
|
||||
|
||||
```text
|
||||
http://192.168.5.10:18080
|
||||
```
|
||||
|
||||
The uncommon port is intentional for this local MVP proof. The server publishes:
|
||||
|
||||
- `http://192.168.5.10:18080/health`
|
||||
- `http://192.168.5.10:18080/manifest.json`
|
||||
- `http://192.168.5.10:18080/ground_zero_tiles.json`
|
||||
- `http://192.168.5.10:18080/tiles/gz_us_ca_pacifica_utm10n_e544_n4160/v0/...`
|
||||
|
||||
The attempted dedicated `Agrarian-TileServer` VM bootstrap was deferred because
|
||||
the Ubuntu cloud image download from Unraid was too slow to be practical during
|
||||
this pass. A dedicated VM remains the next hardening step before exposing the
|
||||
tile server outside the LAN.
|
||||
|
||||
## Verify Client Lookup And Cache
|
||||
|
||||
From a representative Linux client with the repo mounted:
|
||||
|
||||
```bash
|
||||
cd /mnt/projects/AgrarianGameBulid
|
||||
Scripts/verify_tile_delivery_client.sh http://192.168.5.10:18080
|
||||
```
|
||||
|
||||
The verification script:
|
||||
|
||||
- downloads `manifest.json`;
|
||||
- resolves the tile registry and package paths;
|
||||
- downloads the Ground Zero terrain package files into a local cache;
|
||||
- verifies `SHA256SUMS`;
|
||||
- checks that immediate-neighbor metadata exists for the Ground Zero tile;
|
||||
- deletes and redownloads the heightmap to prove cache recovery.
|
||||
|
||||
## Cost Control
|
||||
|
||||
Keep the MVP server small:
|
||||
@@ -63,7 +105,8 @@ Keep the MVP server small:
|
||||
|
||||
## Security Baseline
|
||||
|
||||
- Allow inbound `80/tcp` for the public MVP endpoint.
|
||||
- Allow inbound `18080/tcp` for the local MVP endpoint. Use `80/tcp` or
|
||||
`443/tcp` only when a real DNS name and HTTPS path are assigned.
|
||||
- Allow SSH only from trusted admin IPs.
|
||||
- Add HTTPS with certbot when a real DNS name is assigned.
|
||||
- Treat tile packages as immutable by version. Publish fixes as a new package
|
||||
@@ -71,6 +114,7 @@ Keep the MVP server small:
|
||||
|
||||
## Next Proof
|
||||
|
||||
The next implementation step after this runbook is to launch the MVP cloud VM,
|
||||
publish this static package, and prove lookup/download/cache/redownload behavior
|
||||
from a representative client.
|
||||
The current implementation proves static lookup/download/cache/redownload on
|
||||
the LAN. The next operational hardening step is to move this from shared
|
||||
`Ubuntu-Codex` hosting to a dedicated `Agrarian-TileServer` VM or external cloud
|
||||
host when we need public testing.
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
ENDPOINT="${1:-${AGRARIAN_TILE_SERVER_URL:-http://192.168.5.10:18080}}"
|
||||
ENDPOINT="${ENDPOINT%/}"
|
||||
CACHE_ROOT="${AGRARIAN_TILE_CLIENT_CACHE:-/tmp/agrarian-tile-client-cache-${USER:-user}}"
|
||||
URL_LIST="${CACHE_ROOT}/download-list.txt"
|
||||
REDOWNLOAD_REL=""
|
||||
|
||||
log() {
|
||||
printf '[agrarian-tile-client] %s\n' "$*"
|
||||
}
|
||||
|
||||
download_path() {
|
||||
local rel="$1"
|
||||
local target="${CACHE_ROOT}/${rel#./}"
|
||||
mkdir -p "$(dirname "$target")"
|
||||
curl -fsS "${ENDPOINT}/${rel#./}" -o "$target"
|
||||
}
|
||||
|
||||
rm -rf "${CACHE_ROOT}"
|
||||
mkdir -p "${CACHE_ROOT}"
|
||||
|
||||
log "Endpoint: ${ENDPOINT}"
|
||||
|
||||
download_path "manifest.json"
|
||||
|
||||
python3 - "${CACHE_ROOT}/manifest.json" "${URL_LIST}" <<'PY'
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
manifest_path = Path(sys.argv[1])
|
||||
url_list_path = Path(sys.argv[2])
|
||||
|
||||
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
|
||||
if manifest.get("service") != "agrarian-tile-delivery":
|
||||
raise SystemExit("unexpected service in manifest")
|
||||
|
||||
packages = manifest.get("tile_packages") or []
|
||||
if len(packages) != 1:
|
||||
raise SystemExit(f"expected exactly one tile package, found {len(packages)}")
|
||||
|
||||
package = packages[0]
|
||||
base_url = package["base_url"].strip("/")
|
||||
terrain = package.get("terrain") or {}
|
||||
if "heightmap_r16" not in terrain:
|
||||
raise SystemExit("manifest is missing terrain.heightmap_r16")
|
||||
|
||||
paths = [
|
||||
"manifest.json",
|
||||
manifest["tiles_registry"].strip("/"),
|
||||
"SHA256SUMS",
|
||||
]
|
||||
paths.extend(f"{base_url}/{value}" for value in terrain.values())
|
||||
|
||||
url_list_path.write_text("\n".join(paths) + "\n", encoding="utf-8")
|
||||
print(f"{base_url}/{terrain['heightmap_r16']}")
|
||||
PY
|
||||
|
||||
while IFS= read -r rel; do
|
||||
[[ -n "$rel" ]] || continue
|
||||
log "Downloading ${rel}"
|
||||
download_path "$rel"
|
||||
done < "${URL_LIST}"
|
||||
|
||||
while read -r _checksum rel; do
|
||||
[[ -n "${rel:-}" ]] || continue
|
||||
rel="${rel#./}"
|
||||
if [[ ! -f "${CACHE_ROOT}/${rel}" ]]; then
|
||||
log "Downloading checksum-listed file ${rel}"
|
||||
download_path "$rel"
|
||||
fi
|
||||
done < "${CACHE_ROOT}/SHA256SUMS"
|
||||
|
||||
(
|
||||
cd "${CACHE_ROOT}"
|
||||
sha256sum -c SHA256SUMS >/dev/null
|
||||
)
|
||||
|
||||
python3 - "${CACHE_ROOT}/manifest.json" "${CACHE_ROOT}/ground_zero_tiles.json" <<'PY'
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
manifest = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
|
||||
registry = json.loads(Path(sys.argv[2]).read_text(encoding="utf-8"))
|
||||
default_tile = manifest["default_tile_id"]
|
||||
|
||||
tiles = registry.get("tiles") or []
|
||||
matches = [tile for tile in tiles if tile.get("tile_id") == default_tile]
|
||||
if not matches:
|
||||
raise SystemExit(f"default tile not found in registry: {default_tile}")
|
||||
|
||||
neighbors = matches[0].get("neighbors") or {}
|
||||
if not neighbors:
|
||||
raise SystemExit("default tile has no neighbor metadata")
|
||||
|
||||
print(f"registry_tile={default_tile}")
|
||||
print(f"neighbor_count={len(neighbors)}")
|
||||
PY
|
||||
|
||||
REDOWNLOAD_REL="$(head -1 "${URL_LIST}")"
|
||||
REDOWNLOAD_REL="$(python3 - "${CACHE_ROOT}/manifest.json" <<'PY'
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
manifest = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
|
||||
package = manifest["tile_packages"][0]
|
||||
base_url = package["base_url"].strip("/")
|
||||
print(f"{base_url}/{package['terrain']['heightmap_r16']}")
|
||||
PY
|
||||
)"
|
||||
|
||||
rm -f "${CACHE_ROOT}/${REDOWNLOAD_REL}"
|
||||
log "Redownloading ${REDOWNLOAD_REL}"
|
||||
download_path "${REDOWNLOAD_REL}"
|
||||
|
||||
(
|
||||
cd "${CACHE_ROOT}"
|
||||
sha256sum -c SHA256SUMS >/dev/null
|
||||
)
|
||||
|
||||
log "Tile lookup, download, cache checksum, and redownload verification passed."
|
||||
log "Cache: ${CACHE_ROOT}"
|
||||
Reference in New Issue
Block a user