scopedvr-map-editor/src/editor/test_run.gd

91 lines
3.6 KiB
GDScript

class_name TestRun
extends RefCounted
## „▶ Karte testen" (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
## 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"])