#!/usr/bin/env bash # sync-generators-data.sh — mirror every remote data source the generators app # reads, so generators.${BASE_DOMAIN} works with ZERO internet at party time. # # The generators app (a pinned fork of misode.github.io) is 100% CLIENT-SIDE: # every data fetch comes from the player's browser, not from this host. So # "cochi has no WAN" protects nothing on its own — the URLs themselves have to # point at our own origin. The fork rewrites two constants: # # DataFetcher.ts mcmetaUrl = '/mcmeta' # vanillaMcdocUrl = '/vanilla-mcdoc' # # and it builds paths as `${mcmetaUrl}/${ref}-${type}/...`, so the directory # layout below is not arbitrary — it mirrors upstream's URL shape exactly, # which is what keeps the fork diff to two lines instead of a rewrite. # # Serves at: # /mcmeta/-{summary,data,atlas,assets}/... per-version snapshots # /mcmeta/summary/versions/data.min.json dynamic version list # /vanilla-mcdoc/generated/symbols.json THE schema source # # mcmeta publishes per-version snapshots as TAGS, not branches (~4000 of them), # hence `--branch --depth 1` below. # # Needs WAN. Run deliberately, before the party — never at the venue. # Output dir is gitignored; only this script is tracked. # # Cautious by design: halts on any error, verifies each expected file exists # rather than trusting a clean exit, and refuses to leave a half-synced tree in # place. Idempotent — re-runs replace each source atomically. set -euo pipefail cd "$(dirname "$0")/.." # repo root log() { printf '\n\033[1;32m==>\033[0m %s\n' "$*"; } warn() { printf '\033[1;33m!!\033[0m %s\n' "$*" >&2; } die() { printf '\033[1;31mxx\033[0m %s\n' "$*" >&2; exit 1; } command -v git >/dev/null || die "git required" # The MC version the server runs. mcmeta tags are -; the generators # UI is locked to the config.json entry {id: '1.21', ref: '1.21.1'}, so this # MUST stay in step with that ref and with NEOFORGE/VERSION in docker-compose. MC_REF="${MC_REF:-1.21.1}" OUT="${GENERATORS_DATA_DIR:-generators-data}" MCMETA_REPO="https://github.com/misode/mcmeta.git" MCDOC_REPO="https://github.com/SpyglassMC/vanilla-mcdoc.git" # Each entry: :. # # assets-tiny is assets MINUS the ~hundreds of MB of .ogg sound files. We serve # it AT the `-assets` path because that is what the app requests (lang files, # textures, models all live there). Consequence: sound PLAYBACK 404s, which is # why the fork deletes the sounds generator from the UI. If you ever re-add it, # swap this to the full `-assets` tag and re-measure the disk cost first. SOURCES=( "${MC_REF}-summary:${MC_REF}-summary" "${MC_REF}-data:${MC_REF}-data" "${MC_REF}-atlas:${MC_REF}-atlas" "${MC_REF}-assets-tiny:${MC_REF}-assets" ) # One file per source that MUST exist afterwards. A clean `git clone` exit does # not prove the tag held what the app needs — a renamed path upstream would # sync "successfully" and then die in the browser, offline, at the party. declare -A SENTINEL=( ["${MC_REF}-summary"]="registries/data.min.json" ["${MC_REF}-data"]="data/minecraft/worldgen/biome/plains.json" ["${MC_REF}-atlas"]="all/atlas.png" ["${MC_REF}-assets"]="assets/minecraft/lang/en_us.json" ) # Shallow-clone one ref into a temp dir, verify its sentinel, then swap it in. # Never deletes the live copy until the new one has been verified. sync_ref() { local repo="$1" ref="$2" dest="$3" sentinel="$4" local tmp="${dest}.tmp.$$" rm -rf "$tmp" git clone -q --depth 1 --branch "$ref" "$repo" "$tmp" \ || die "clone failed: $repo @ $ref" rm -rf "$tmp/.git" [ -f "$tmp/$sentinel" ] || { rm -rf "$tmp" die "$ref synced but '$sentinel' is missing — upstream layout changed. Refusing to install a tree the app cannot read. Investigate before retrying." } rm -rf "$dest" mv "$tmp" "$dest" printf ' %s -> %s (%s)\n' "$ref" "$dest" "$(du -sh "$dest" | cut -f1)" } log "mirroring generators data for MC ${MC_REF} -> ${OUT}/" mkdir -p "$OUT/mcmeta" for entry in "${SOURCES[@]}"; do tag="${entry%%:*}" dir="${entry##*:}" log "mcmeta tag ${tag}" sync_ref "$MCMETA_REPO" "$tag" "$OUT/mcmeta/$dir" "${SENTINEL[$dir]}" done # The dynamic version list. NOT a version tag — DataFetcher.ts:205 fetches it # from the live `summary` BRANCH head, and it runs during Spyglass init. Miss # this one and the app dead-ends offline while looking perfectly healthy online. # It is a single file, so fetch it directly rather than cloning the whole # branch (which is another ~31M of the LATEST version's summary, all unused). log "mcmeta branch summary (versions/data.min.json only)" command -v curl >/dev/null || die "curl required" mkdir -p "$OUT/mcmeta/summary/versions" curl -fsSL "https://raw.githubusercontent.com/misode/mcmeta/summary/versions/data.min.json" \ -o "$OUT/mcmeta/summary/versions/data.min.json.tmp" \ || die "failed to fetch summary/versions/data.min.json" # Verify it parses as JSON before installing it — a captive-portal HTML error # page would download with a clean exit code and break Spyglass init offline. python3 -c "import json,sys; json.load(open(sys.argv[1]))" \ "$OUT/mcmeta/summary/versions/data.min.json.tmp" \ || die "summary/versions/data.min.json is not valid JSON — refusing to install it" mv "$OUT/mcmeta/summary/versions/data.min.json.tmp" \ "$OUT/mcmeta/summary/versions/data.min.json" printf ' summary/versions/data.min.json (%s)\n' \ "$(du -h "$OUT/mcmeta/summary/versions/data.min.json" | cut -f1)" # The schema source. Without it there are no schemas, no validation, no # generator — this is the single most load-bearing file in the mirror. # # The app requests /vanilla-mcdoc/generated/symbols.json. In the upstream URL # that decomposes as // = vanilla-mcdoc / branch `generated` / # `symbols.json` — `generated` is a BRANCH NAME, not a directory, and # symbols.json sits at the branch root. So the clone lands in a directory named # after the branch to reproduce the same URL shape locally. log "vanilla-mcdoc branch generated" sync_ref "$MCDOC_REPO" "generated" "$OUT/vanilla-mcdoc/generated" "symbols.json" log "done — $(du -sh "$OUT" | cut -f1) in ${OUT}/" echo " mount it read-only into the generators container (docker-compose.yml)" echo " verify offline BEFORE the party: block all non-origin requests in" echo " DevTools and walk the generators you actually intend to use."