scopedvr-map-editor/tools/sync_models.sh
scopedvr-operator e317706df0 tools/sync_models.sh: Pack-Referenzordner als Textur-Quelle
Operator-Befund auf dem PC (20.10.): die Original-Texturen liegen NICHT im
Client-Klon, sondern in einem eigenen Pack-Ordner:
  ~/godot-projects/ithappy_pack_ref/ithappy/military_desert/textures/
Deshalb sucht das Skript jetzt zusaetzlich dort (<clone>/../ithappy_pack_ref/...)
und als letzte Instanz per find -maxdepth 6 unter <clone>/.. und <client>/..
(also neben dem Projekt). Belegt mit einem nachgebauten PC-Aufbau: Palette wird
gefunden, landet FLACH in models/textures/, --check danach Exit 0.
2026-10-20 09:58:47 +00:00

236 lines
9.6 KiB
Bash
Executable file

#!/usr/bin/env bash
# Modelle-Sync für den eigenständigen Map Builder (Karte t_12cf94fe).
#
# Was das Script macht:
# Füllt den Ordner models/ DIREKT NEBEN der App (im Editor-Repo = Projekt-
# ordner, in der exportierten App = neben der EXE) mit den echten 3D-Modellen
# (.glb) und den Pack-Texturen — kopiert aus einem Client-Klon. Danach läuft
# der Map Builder auf jedem beliebigen PC OHNE Client/Godot-Installation.
#
# Spiegelstruktur (wichtig: die App erwartet die GLBs DIREKT im models-Ordner):
# models/<oid>.glb (flache GLBs)
# models/military_desert/<kategorie>/<oid>.glb (verschachtelte GLBs)
# models/textures/*.png (Pack-Palette)
#
# Aufruf (aus dem Map-Editor-Repo oder aus der exportierten App):
# tools/sync_models.sh # models/ füllen (idempotent — gefahrlos wiederholbar)
# tools/sync_models.sh --check # nur prüfen: Exit 0 = models/ aktuell,
# # Exit 1 = es fehlt/wich ab (ohne Änderung)
#
# Client-Klon finden: Umgebungsvariable SCOPEDVR_CLIENT_DIR, sonst die üblichen
# Nachbar-Ordner ../scopedvr-client und ../../scopedvr-client.
#
# Abhängigkeiten: python3 (Standard auf Linux/Mac; auf Windows python.org oder
# Git-Bash). KEINE rsync-Abhängigkeit — das Spiegeln (Kopieren + Überflüssiges
# löschen) macht ein eingebautes Python-Script, damit es auf jedem PC ohne
# Zusatzinstallation läuft (Slys Wunsch: keinerlei Abhängigkeiten).
#
# Lizenz (WICHTIG, siehe README_INSTALL.md): die Modelle/Texturen stammen aus
# dem ITHappy-Pack und dürfen NICHT weitergegeben werden — der models/-Ordner
# bleibt auf Slys eigenen Rechnern. Deshalb steht models/ auch in .gitignore.
#
set -u
MODE="sync"
if [ "${1:-}" = "--check" ]; then
MODE="check"
fi
if [ "${1:-}" != "" ] && [ "${1:-}" != "--check" ]; then
echo "Unbekannter Parameter: $1 (erlaubt: --check oder nichts)"
exit 2
fi
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
TARGET="$DIR/models"
CLIENT="${SCOPEDVR_CLIENT_DIR:-}"
# --- Client-Klon finden (wie tools/sync_objects.sh) -------------------------
if [ -z "$CLIENT" ]; then
for k in "$DIR/../scopedvr-client" "$DIR/../../scopedvr-client"; do
[ -d "$k/assets/objekte" ] && CLIENT="$(cd "$k" && pwd)" && break
done
fi
if [ -z "$CLIENT" ] || [ ! -d "$CLIENT/assets/objekte" ]; then
echo "FEHLER: Client-Klon mit assets/objekte nicht gefunden."
echo " Ordner ../scopedvr-client neben den Map-Editor legen oder"
echo " SCOPEDVR_CLIENT_DIR=/pfad/zum/client setzen."
exit 1
fi
echo "Client: $CLIENT"
echo "models/: $TARGET"
echo "Modus: $MODE"
if ! command -v python3 >/dev/null 2>&1; then
echo "FEHLER: python3 fehlt (Standard auf Linux/Mac; auf Windows python.org installieren)."
exit 1
fi
# --- .gdignore: Godot soll models/ NICHT importieren/packen ------------------
# (FileAccess mit absoluten Pfaden funktioniert trotzdem — genau dafür
# ist der Ordner da. Fehlt die Datei, ist das eine Abweichung.)
GDIGNORE="$TARGET/.gdignore"
mkdir -p "$TARGET"
GDIGNORE_MISSING=0
if [ ! -f "$GDIGNORE" ]; then
GDIGNORE_MISSING=1
if [ "$MODE" = "check" ]; then
echo "ABWEICHUNG: $GDIGNORE fehlt (Godot würde den Ordner sonst importieren)."
else
touch "$GDIGNORE"
echo "angelegt: $GDIGNORE"
fi
fi
# --- Spiegeln: Objekte (GLBs) + Pack-Texturen, ohne .import-Leichen ---------
# WICHTIG (Karte t_12cf94fe): die App erwartet die GLBs DIREKT im models-Ordner
# (models/<oid>.glb) — NICHT verschachtelt in models/assets/objekte.
# Deshalb wird $CLIENT/assets/objekte nach $TARGET (models/) gespiegelt.
SRC_OBJEKTE="$CLIENT/assets/objekte"
DST_OBJEKTE="$TARGET"
DST_TEX="$TARGET/textures"
# Pack-Texturen FINDEN statt fest verdrahten (Operator-Nachbesserung 20.10.):
# Die Palette (Demonstration_Textures*.png) liegt je Client-Klon in einem anderen
# Unterordner und fehlt im Server-Container ganz. Reihenfolge: SCOPEDVR_TEX_DIR →
# assets/textures/military_desert → assets/textures → der Ordner, in dem die
# Palette irgendwo unter assets/ liegt. Erster Treffer gewinnt.
SRC_TEX="${SCOPEDVR_TEX_DIR:-}"
if [ -z "$SRC_TEX" ] || [ ! -d "$SRC_TEX" ]; then
for cand in "$CLIENT/assets/textures/military_desert" "$CLIENT/assets/textures"; do
if [ -d "$cand" ]; then SRC_TEX="$cand"; break; fi
done
fi
if [ -z "$SRC_TEX" ] || [ ! -d "$SRC_TEX" ]; then
PAL="$(find "$CLIENT/assets" -name 'Demonstration_Textures*.png' 2>/dev/null | head -1)"
if [ -n "$PAL" ]; then SRC_TEX="$(dirname "$PAL")"; fi
fi
# Pack-Referenzordner (Operator-Befund 20.10. auf dem PC): die Original-Texturen
# liegen NICHT im Client-Klon, sondern in einem eigenen Pack-Ordner
# (~/godot-projects/ithappy_pack_ref/ithappy/military_desert/textures).
for cand in "$DIR/../ithappy_pack_ref/ithappy/military_desert/textures" \
"$CLIENT/../ithappy_pack_ref/ithappy/military_desert/textures" \
"$DIR/../ithappy_pack_ref"; do
if [ -z "$SRC_TEX" ] || [ ! -d "$SRC_TEX" ]; then
if [ -d "$cand" ]; then SRC_TEX="$cand"; break; fi
fi
done
if [ -z "$SRC_TEX" ] || [ ! -d "$SRC_TEX" ]; then
PAL="$(find "$DIR/.." "$CLIENT/.." -maxdepth 6 -name 'Demonstration_Textures*.png' 2>/dev/null | head -1)"
if [ -n "$PAL" ]; then SRC_TEX="$(dirname "$PAL")"; fi
fi
if [ -z "$SRC_TEX" ] || [ ! -d "$SRC_TEX" ]; then
echo "HINWEIS: Pack-Texturen nicht gefunden — gesucht in SCOPEDVR_TEX_DIR,"
echo " assets/textures/military_desert, assets/textures und nach"
echo " Demonstration_Textures*.png unter assets/."
echo " Die Modelle werden trotzdem gespiegelt; die Optik nutzt dann"
echo " die Standardfarben. Nachreichen: SCOPEDVR_TEX_DIR=/pfad"
SRC_TEX="$CLIENT/assets/textures" # absichtlich nicht vorhanden -> HINWEIS statt stiller Leerlauf
fi
echo "Objekte: $SRC_OBJEKTE"
echo "Texturen: $SRC_TEX"
# Python macht das Spiegeln (copy2 + überflüssige Dateien löschen).
# --check (Dry-Run) zeigt Abweichungen OHNE etwas zu verändern.
python3 - "$SRC_OBJEKTE" "$DST_OBJEKTE" "$SRC_TEX" "$DST_TEX" "$MODE" <<'PY'
import os, sys, shutil, filecmp
pairs = [
("Objekte", sys.argv[1], sys.argv[2]),
("Texturen", sys.argv[3], sys.argv[4]),
]
mode = sys.argv[5]
def scan(root: str, skip_top: set = frozenset()) -> dict:
out = {}
if not os.path.isdir(root):
return out
for r, dirs, files in os.walk(root):
dirs[:] = [d for d in dirs if d != ".godot" and d not in skip_top]
for f in files:
if f.endswith(".import") or f == ".gdignore":
continue
full = os.path.join(r, f)
out[os.path.relpath(full, root)] = full
return out
def run(label: str, src: str, dst: str, flat: bool = False,
skip_top: set = frozenset()) -> int:
if not os.path.isdir(src):
print("ABWEICHUNG: Quelle fehlt: %s" % src)
return 1
s_map = scan(src, skip_top)
d_map = scan(dst, skip_top)
# flat=True (Pack-Texturen, Operator-Nachbesserung 20.10.): Die App sucht die
# Palette FLACH unter models/textures/<name>.png — im Client liegen die PNGs
# aber verschachtelt (assets/textures/<unterordner>/…). Deshalb ohne
# Unterordner spiegeln. Alte verschachtelte Reste erscheinen dadurch als
# "überflüssig" und werden automatisch entfernt (selbstheilend).
if flat:
s_map = {os.path.basename(p): p for p in s_map.values()}
changes = []
for rel, sp in s_map.items():
dp = os.path.join(dst, rel)
if not os.path.exists(dp):
changes.append(("neu", rel))
else:
try:
if not filecmp.cmp(sp, dp, shallow=False):
changes.append(("geändert", rel))
except OSError:
changes.append(("geändert", rel))
for rel in d_map:
if rel not in s_map:
changes.append(("überflüssig", rel))
if not changes:
return 0
if mode == "check":
for action, rel in changes[:20]:
print("ABWEICHUNG (%s): %s: %s" % (label, action, rel))
if len(changes) > 20:
print("ABWEICHUNG (%s): … und %d weitere" % (label, len(changes) - 20))
return 1
for action, rel in changes:
dp = os.path.join(dst, rel)
if action == "überflüssig":
if os.path.exists(dp):
os.remove(dp)
else:
os.makedirs(os.path.dirname(dp), exist_ok=True)
shutil.copy2(s_map[rel], dp)
print("%s: %d Änderungen übernommen." % (label, len(changes)))
return 0
dev = 0
# skip_top: der Modell-Spiegel darf den Textur-Unterordner NICHT als
# "überflüssig" löschen (Operator-Nachbesserung 20.10. — sonst löscht jeder
# Lauf models/textures/, der Textur-Spiegel legt ihn neu an: Endlosschleife,
# --check nie sauber).
dev |= run("Objekte", pairs[0][1], pairs[0][2], skip_top={"textures"})
dev |= run("Texturen", pairs[1][1], pairs[1][2], flat=True)
sys.exit(dev)
PY
DEVIATION=$?
# --- Größe + Zahl am Ende ----------------------------------------------------
GLB_COUNT="$(find "$DST_OBJEKTE" -name '*.glb' 2>/dev/null | wc -l)"
TEX_COUNT="$(find "$DST_TEX" -name '*.png' 2>/dev/null | wc -l)"
SIZE="$(du -sh "$TARGET" 2>/dev/null | cut -f1)"
echo "---"
echo "models/: $GLB_COUNT GLBs, $TEX_COUNT Pack-Texturen, Gesamtgröße: $SIZE"
if [ "$MODE" = "check" ]; then
if [ "$DEVIATION" -ne 0 ] || [ "$GDIGNORE_MISSING" -ne 0 ]; then
echo "--check: models/ weicht vom Client-Klon ab (Exit 1)."
exit 1
fi
echo "--check: models/ ist aktuell (Exit 0)."
exit 0
fi
if [ "$DEVIATION" -ne 0 ]; then
echo "Sync mit Fehlern (siehe oben)."
exit 1
fi
echo "Sync fertig: models/ ist auf dem Stand des Client-Klons."