#!/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/.glb (flache GLBs) # models/military_desert//.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/.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 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/.png — im Client liegen die PNGs # aber verschachtelt (assets/textures//…). 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."