#!/usr/bin/env bash # End-to-end driver for the FTB Chunks -> Xaero's World Map reveal transplant. # # See analysis/FTB-XAERO-MAP-MIGRATION.md for the reasoning. This script does the # parts that can be automated; the singleplayer pass in step 3 has to be done by hand # on a machine that can run the pack. # # Usage: # migrate-player.sh [--dimension ] [--work ] # # label used for output paths, e.g. Stirwick # that player's local/ftbchunks/data//, or a bare zip/dir # of region files, or an already-extracted mask .json # the world save containing region/ # # Example: # tools/migrate-player.sh Stirwick ~/Stirwick-ftbchunks ~/server/world \ # --dimension minecraft:overworld --work /var/tmp/xaero-migration set -euo pipefail here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" if [ $# -lt 3 ]; then sed -n '2,20p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//' exit 2 fi player="$1"; ftb="$2"; world="$3"; shift 3 dimension="minecraft:overworld" work="$(pwd)/xaero-migration" while [ $# -gt 0 ]; do case "$1" in --dimension) dimension="$2"; shift 2 ;; --work) work="$2"; shift 2 ;; *) echo "unknown option: $1" >&2; exit 2 ;; esac done out="$work/$player" mask="$out/mask.json" sp="$out/sp-world" echo "player : $player" echo "ftb data : $ftb" echo "world : $world" echo "dimension : $dimension" echo "output : $out" echo if [ ! -d "$world/region" ]; then echo "error: $world/region not found" >&2 exit 1 fi mkdir -p "$out" echo "== 1/2 extracting reveal mask ==" if [ "${ftb##*.}" = "json" ]; then cp -f "$ftb" "$mask" echo " using prebuilt mask: $ftb" python3 - "$mask" <<'PY' import json, sys m = json.load(open(sys.argv[1])) dims = m["dimensions"] print(f" {m['total_chunks']:,} chunks / " f"{sum(d['region_count'] for d in dims.values())} regions / " f"dimension(s) {list(dims)}") PY else python3 "$here/ftbchunks-reveal.py" "$ftb" --out "$mask" --dimension "$dimension" --verify fi echo echo "== 2/2 pruning the world to those chunks (dry run first) ==" python3 "$here/prune-world-to-reveal.py" "$world" "$mask" "$sp" \ --dimension "$dimension" --minimal --dry-run echo python3 "$here/prune-world-to-reveal.py" "$world" "$mask" "$sp" \ --dimension "$dimension" --minimal cat </saves/Stirwick-reveal/ Open it, open the map (M), let it fill, then quit. Xaero's maps what is in the save, and this save holds only the revealed chunks. Do not walk around -- Minecraft regenerates chunks near the login position and they will show up. Where the result lands: look under /xaero/world-map/ for a folder that is not Multiplayer_*, e.g. World/null//. Sort by modification time if unsure -- those files were just written. SANITY CHECK: you should end up with one zip per region in the mask ($(python3 -c "import json;print(sum(d['region_count'] for d in json.load(open('$mask'))['dimensions'].values()))") for $player). A handful means it only mapped the spawn area and you should use 'Convert All Regions' in the world map settings, or report back. 4. Clean the HARVEST before anything else -- this is the step that removes terrain Minecraft regenerated around the login point. Run it on the harvested folder, NOT on the player's own map folder: python3 $here/filter-xaero-regions.py \\ "/xaero/world-map/World/null//" \\ $mask --dimension $dimension Check the list, then re-run with --delete. 5. Merge the surviving _.zip files into the player's existing map folder, WITHOUT overwriting anything already there (-n), so any terrain they have explored since the swap is kept: cp -n /*.zip \\ "/xaero/world-map/Multiplayer_advilplug.top/null//" The right instance is the mw\$... folder there. If the player has not joined the server since the swap, have them join once first so Xaero's creates it. 6. Delete $out -- scratch, usually a few hundred MB. Per-dimension: repeat with --dimension and the matching map folder (null = overworld, DIM-1 = nether, DIM1 = end). Other players: each needs their own local/ftbchunks/data//; there is no server-side copy of this data. EOF