scopedvr-map-core/editor/test_run.gd
2026-11-01 19:31:33 +01:00

110 lines
4.5 KiB
GDScript

class_name TestRun
extends RefCounted
## ALT (Karte t_1cdc1a72): dieser Client-Start ist seit dem Lauf-Modus im
## Map-Builder selbst NICHT mehr der Standard-Weg — „▶ Karte testen" startet
## jetzt IN DIESER APP (core/editor/playtest/, kein Client-Ordner, kein
## zweiter Prozess, kein VR). Der Rückbau dieser Datei kommt in einer
## späteren Karte; bis dahin bleibt die Export-/Such-Logik als Fallback
## erhalten und getestet (tests/test_testrun.gd).
##
## Ursprünglicher Zweck (Karte t_b84af40f): Export der AKTUELLEN Map nach Temp
## + Start des scopedvr-client als EIGENES Fenster im Testlauf-Modus
## (--testmap <pfad>). Der Editor bleibt offen — der Client ist ein eigener
## Prozess; das Spiel beendet sich selbst sauber (ESC = „Zurück zum Editor").
##
## Client-Suche (erste Treffer-Zeile gewinnt):
## 1. Konfigurationsdatei user://scopedvr_editor.cfg, Abschnitt [test]:
## client_dir="/pfad/zu/scopedvr-client"
## 2. Klon neben dem Editor: <Editor-Projektordner>/../scopedvr-client
## (erkennbar an project.godot im Ordner).
##
## Godot-Binary für den Start: das Binary, unter dem der Editor selbst läuft
## (OS.get_executable_path — Sly startet den Editor über den Godot-Editor,
## also kann DAS auch den Client starten). Läuft der Editor irgendwann als
## exportierte App, muss [test] godot_path gesetzt werden.
##
## Alles static + testbar ohne UI (tests/test_testrun.gd).
const CFG_PATH := "user://scopedvr_editor.cfg"
const TESTRUN_DIR := "user://testrun"
static func load_cfg() -> ConfigFile:
var cfg := ConfigFile.new()
if FileAccess.file_exists(CFG_PATH):
cfg.load(CFG_PATH) # Fehler → Default-Logik greift weiter unten.
return cfg
## Liefert den Client-Projektordner (mit project.godot) oder "".
static func find_client(project_dir: String, cfg: ConfigFile = null) -> String:
var candidates: Array = []
if cfg != null:
var configured := str(cfg.get_value("test", "client_dir", ""))
# Explizit gesetzter Pfad gewinnt, sobald der ORDNER existiert
# (bewusst ohne project.godot-Check — wer ihn konfiguriert, weiß es besser).
if not configured.is_empty() and DirAccess.dir_exists_absolute(configured):
return configured
var parent := project_dir.simplify_path().get_base_dir()
candidates.append(parent.path_join("scopedvr-client"))
for candidate in candidates:
var dir := str(candidate).simplify_path()
if FileAccess.file_exists(dir.path_join("project.godot")):
return dir
return ""
## Godot-Binary zum Starten des Clients (Editor-Binary oder Konfiguration).
static func find_godot(cfg: ConfigFile = null) -> String:
var exe := OS.get_executable_path()
if exe.get_file().to_lower().contains("godot"):
return exe
if cfg != null:
return str(cfg.get_value("test", "godot_path", ""))
return ""
## Deterministischer Temp-Pfad aus dem Map-Namen (erneuter Testlauf
## überschreibt die alte Export-Datei — kein Müll-Berg).
static func temp_map_path(doc: Dictionary) -> String:
var raw := str(doc.get("info", {}).get("name", "")).strip_edges()
if raw.is_empty():
raw = "karte"
var safe := ""
for ch in raw:
var c := ch.to_lower()
if (c >= "a" and c <= "z") or (c >= "0" and c <= "9") or ch == "_" or ch == "-" or ch == " ":
safe += ch
else:
safe += "_"
safe = safe.strip_edges().replace(" ", "_").rstrip("_")
if safe.is_empty():
safe = "karte"
return TESTRUN_DIR.path_join(safe + ".svmap")
## Exportiert das AKTUELLE Dokument (auch ungespeicherte Maps!) nach Temp.
## Rückgabe: {ok, errors, path} — path ist globalisiert (für den Client-Prozess).
static func export_temp_map(doc: Dictionary) -> Dictionary:
var rel := temp_map_path(doc)
DirAccess.make_dir_recursive_absolute(ProjectSettings.globalize_path(TESTRUN_DIR))
var result := SvmapIO.save_to_file(rel, doc)
result["path"] = ProjectSettings.globalize_path(rel)
return result
## Hand-Haltungs-Tool: liegt im Client-Projekt, weil es dessen Rig und Waffen
## direkt benutzt (kein VR, eigenes Fenster).
static func hand_pose_args(client_dir: String) -> PackedStringArray:
return PackedStringArray(["--xr-mode", "off", "--path", client_dir,
"res://tools/hand_pose_tool/hand_pose_tool.tscn"])
## Import-Lauf für den Client, nötig nach neuem Stand (neue Modelle/Skripte).
static func import_args(client_dir: String) -> PackedStringArray:
return PackedStringArray(["--headless", "--path", client_dir, "--import"])
## Argumentliste für den Client-Start (getestet: exakt diese Form).
static func launch_args(client_dir: String, map_path: String) -> PackedStringArray:
return PackedStringArray(["--path", client_dir, "--", "--testmap", map_path, "--mock-master"])