t_1e82f747: Test-Umgebung aufgeraeumt — test_updater Setup robust, test_glb_view auf Pack-Palette (beide Suiten statt immer rot jetzt gruen/SKIP). test_updater: Verzeichnisname war Time.get_ticks_msec (kleine Zahl, kollidierte mit nicht loeschbaren Alt-Laeufen aus read-only git-Packs) → jetzt Unix-Zeit+ticks_usec; Cleanup chmod -R u+w (blocking via OS.execute mit output-Array, 2-Arg-Variante ist async) + include_hidden (.git ist versteckt) + rm -rf-Fallback; git-Fehler geben echten Fehlertext statt roter Zahl. Echte Updater-Pruefungen laufen wieder (75/75, vorher 15/75 FAIL an Setup). test_glb_view: Nachbar-Repos suchen wie tools/sync_objects.sh (Env SCOPEDVR_CLIENT_DIR/SCOPEDVR_MATCH_SERVER_DIR, dann ../+../../-Siblings), Quellen zur Laufzeit in ProjectSettings gesetzt (basis-relativ gerechnet, path_join ketten Absolutpfade); ohne Nachbar klare SKIP-Zeilen+Exit 0 statt 15 FAILs. Camo-Erwartungen entfernt (ueberholt seit d0d4fbd): jetzt Pack-Palette — alle Flaehen albedo_texture != null, Kante 1024², pack_datei_fuer-Zuordnung (military_base→Demonstration_Textures1.png, picture_1/2→Demonstration_picture_N.png 2048²) + Pack-Dateien im Nachbar-Repo (48/48, vorher 4/38 FAIL). Notiz tools/TEST_UMGEBUNG.md (Abhaengigkeiten + test_gui_smoke-F5 vorbestehend rot am 0adb161 via git-stash verifiziert, nicht Teil dieser Karte).

This commit is contained in:
Hermes (scopedvr-dev) 2026-10-20 07:06:46 +00:00
parent 2c10863759
commit dca0947a71
3 changed files with 267 additions and 52 deletions

View file

@ -12,12 +12,49 @@ extends SceneTree
## über den Texturen.
## 4. Fehlende GLB → Platzhalter + Eintrag in fehlende_glbs (Statuszeile).
## 5. Cache pro OID: zweiter Abruf liefert DIESELBE Mesh-Instanz.
## 6. Pack-Texturen (Karte t_1e82f747, Stand d0d4fbd): die GLB-Flächen
## tragen ihre ORIGINAL-Texturen (GLTF löst die URIs selbst auf),
## Kante = Pack-Palette 1024²; Zuordnung pack_datei_fuer wie
## tools/PACK_TEXTURE_MAPPING.md (Client-Repo, verbindlich):
## military_base(+_00x) → Demonstration_Textures1.png (1024²),
## picture_1/2/3 → Demonstration_picture_N.png (2048² Multicam).
## Die alten Camo-Erwartungen (desert_multicam_1/2.png + geteilte
## Cache-Instanz) sind ENTFERNT — die Welt hat sich gedreht.
##
## Quelle-Fund (Karte t_1e82f747): die GLBs liegen in den NACHBAR-Repos.
## Gesucht wird wie in tools/sync_objects.sh: Env SCOPEDVR_MATCH_SERVER_DIR /
## SCOPEDVR_CLIENT_DIR zuerst, dann ../ und ../../ Siblings. Ohne Fund endet
## die Suite mit klaren SKIP-Zeilen (Exit 0) statt einer roten Zahl — die
## GLB-Prüfungen laufen, sobald ein Nachbar-Repo klonbar/abgelegt ist.
var failures := 0
var checks := 0
## Absolute Repo-Wurzel der gefundenen Nachbar-Repos (Ladereihenfolge).
var repos: Array = []
func _init() -> void:
repos = _finde_repos()
if repos.is_empty():
# Karte t_1e82f747: ohne Nachbar-Repo sind die GLB-Prüfungen hier
# nicht ausführbar — klare SKIP-Zeilen statt irreführender roter
# Zahlen. Die echten Checks laufen, sobald ein Nachbar da ist.
print("SKIP: test_glb_view — kein Nachbar-Repo mit assets/objekte gefunden.")
print("SKIP GRUND: gesucht: ../scopedvr-match-server, ../scopedvr-client, ../../… (relativ zum Editor-Repo)")
print("SKIP GRUND: sowie Env SCOPEDVR_MATCH_SERVER_DIR / SCOPEDVR_CLIENT_DIR (wie tools/sync_objects.sh).")
print("SKIP GRUND: die GLBs + Pack-Texturen liegen in den Sibling-Repos — erst klonen, dann Suite fahren.")
quit(0)
return
# Quellen konfigurierbar machen: die gefundenen Repos landen in
# ProjectSettings (GlbMeshStore.QUELLEN_SETTING) — glb_quellen sind
# Basis-relativ, deshalb ../-Pfade statt Absolutpfade.
var quellen: Array = []
for root: String in repos:
quellen.append(_relativ_zu(ProjectSettings.globalize_path("res://"),
root.path_join("assets/objekte")))
print("GLB-Quellen: %s" % ", ".join(PackedStringArray(quellen)))
ProjectSettings.set_setting(GlbMeshStore.QUELLEN_SETTING, quellen)
GlbMeshStore.cache_leeren()
_run_tests()
if failures == 0:
print("ALL OK: %d checks" % checks)
@ -27,6 +64,51 @@ func _init() -> void:
quit(1)
## Nachbar-Repos finden wie tools/sync_objects.sh: Env-Variablen gewinnen,
## dann ../- und ../../-Siblings relativ zum Editor-Projekt. Ein Repo zählt
## nur, wenn assets/objekte existiert (Mesh-Lager).
## (Achtung, Probe 20.10.: der Repo-Name beginnt schon mit "scopedvr-" —
## die Env-Keys sind deshalb als feste Map notiert, nicht abgeleitet.)
func _finde_repos() -> Array:
var basis := ProjectSettings.globalize_path("res://")
var env_keys := {
"scopedvr-match-server": "SCOPEDVR_MATCH_SERVER_DIR",
"scopedvr-client": "SCOPEDVR_CLIENT_DIR",
}
var out: Array = []
for name: String in env_keys:
var env_pfad := OS.get_environment(env_keys[name]).simplify_path()
if not env_pfad.is_empty() and DirAccess.dir_exists_absolute(
env_pfad.path_join("assets/objekte")):
out.append(env_pfad)
for rel: String in ["../", "../../"]:
var kandidat := basis.path_join(rel + name).simplify_path()
if DirAccess.dir_exists_absolute(kandidat.path_join("assets/objekte")) \
and not out.has(kandidat):
out.append(kandidat)
return out
## glb_quellen sind Basis-RELATIVE Pfade (GlbMeshStore._kandidaten joinkt sie
## auf res:// — String.path_join KETTET auch bei absoluten Argumenten, ein
## Absolutpfad würde also kaputt gehen). Aus einem absoluten Env-Pfad wird
## deshalb ein ../-Pfad gerechnet.
func _relativ_zu(von: String, nach: String) -> String:
var v: PackedStringArray = von.simplify_path().split("/", false)
var n: PackedStringArray = nach.simplify_path().split("/", false)
var i := 0
while i < v.size() and i < n.size() and v[i] == n[i]:
i += 1
var teile := PackedStringArray()
for _j: int in range(i, v.size()):
teile.append("..")
for k: int in range(i, n.size()):
teile.append(n[k])
if teile.is_empty():
return "."
return "/".join(teile)
func _check(cond: bool, label: String) -> void:
checks += 1
if cond:
@ -41,7 +123,7 @@ func _run_tests() -> void:
_test_store_ladet_moosfelsen()
_test_store_normalisierung()
_test_store_cache()
_test_camo_tex()
_test_pack_tex()
_test_viewport_rebuild_echt()
_test_viewport_rebuild_fehlend()
@ -115,74 +197,95 @@ func _test_store_cache() -> void:
_check(GlbMeshStore.mesh_fuer(PROBE_OID) != null, "cache_leeren → wird neu geladen")
# ------------------------------------------------------------ Camo-Textur ----
# --------------------------------------------------------- Pack-Texturen ----
## Beweis (t_50a07d4b): das Live-GLB-Mesh trägt ECHTE Multicam-Texturen
## (albedo_texture != null je Fläche), Zuordnung je Kategorie wie
## tools/CAMO_TEXTURE_MAPPING.md (Client-Repo, verbindlich).
## Die ImageTexture wird zur Laufzeit von der Platte geladen → resource_path
## ist leer; der Pfad steht im Store-Cache (_camo_tex_cache) und wird hier
## ausgegeben (Zahlen/Pfade ins Handoff).
func _test_camo_tex() -> void:
print("Testgruppe: Camo-Textur im Live-Pfad — WYSIWYG Editor = Spiel (t_50a07d4b)")
## Karte t_1e82f747 (Stand d0d4fbd): die Camo-Erwartungen sind überholt —
## die Pack-GLBs tragen ihre ORIGINAL-Texturen (GLTFDocument löst die externen
## URIs inzwischen selbst auf, je Parse eine eigene ImageTexture-Instanz;
## der _camo_tex_cache bleibt deshalb leer). Diese Gruppe beschreibt, was IST:
## Flächen getextured, Textur-Kante = Pack-Palette 1024², Zuordnung
## pack_datei_fuer wie tools/PACK_TEXTURE_MAPPING.md (Client-Repo).
func _test_pack_tex() -> void:
print("Testgruppe: Pack-Texturen im Live-Pfad — WYSIWYG Editor = Spiel (d0d4fbd)")
var mesh_w := GlbMeshStore.mesh_fuer("wall_001")
_check(mesh_w != null, "wall_001 (walls → _1) geladen")
_check(mesh_w != null, "wall_001 (walls, military_base) geladen")
if mesh_w != null:
_camo_flachen_pruefen(mesh_w, "wall_001", "desert_multicam_1.png")
_pack_flachen_pruefen(mesh_w, "wall_001", 1024)
var mesh_g := GlbMeshStore.mesh_fuer("ground_001")
_check(mesh_g != null, "ground_001 (nature/ground → _2) geladen")
_check(mesh_g != null, "ground_001 (nature/ground, military_base) geladen")
if mesh_g != null:
_camo_flachen_pruefen(mesh_g, "ground_001", "desert_multicam_2.png")
# Eine-Quelle-Cache: wall_002 teilt DIESELBE ImageTexture-Instanz wie
# wall_001 (291 GLBs teilen sich 2 Texturen statt je eine Kopie).
var mesh_w2 := GlbMeshStore.mesh_fuer("wall_002")
if mesh_w2 != null and mesh_w != null:
var t1 := _camo_mat_tex(mesh_w, 0)
var t2 := _camo_mat_tex(mesh_w2, 0)
_check(t1 != null and t1 == t2,
"wall_001 und wall_002 teilen DIESELBE Textur-Instanz (VRAM-Cache)")
_check(GlbMeshStore._camo_tex_cache.size() == 2,
"Camo-Textur-Cache: genau 2 Texturen (Objekt _1 + Boden _2), ist %d" % GlbMeshStore._camo_tex_cache.size())
_pack_flachen_pruefen(mesh_g, "ground_001", 1024)
# Pack-Zuordnung (pure Funktion — Quelle: tools/PACK_TEXTURE_MAPPING.md):
_check(GlbMeshStore.pack_datei_fuer("military_base") == GlbMeshStore.PACK_TEX_PALETTE,
"military_base → 1024er Palette (Demonstration_Textures1.png)")
_check(GlbMeshStore.pack_datei_fuer("military_base_003") == GlbMeshStore.PACK_TEX_PALETTE,
"military_base_NNN → Palette (Präfix-Fallback)")
_check(GlbMeshStore.pack_datei_fuer("picture_1") == GlbMeshStore.PACK_TEX_PICTURE_1,
"picture_1 → 2048er Multicam (Demonstration_picture_1.png)")
_check(GlbMeshStore.pack_datei_fuer("picture_2") == GlbMeshStore.PACK_TEX_PICTURE_2,
"picture_2 → 2048er Multicam (Demonstration_picture_2.png)")
_check(GlbMeshStore.pack_datei_fuer("gibts_nicht_material") == GlbMeshStore.PACK_TEX_PALETTE,
"unbekanntes Material → Palette (Default-Fallback)")
_pack_dateien_pruefen()
func _camo_flachen_pruefen(mesh: Mesh, oid: String, erwartet_name: String) -> void:
## Jede Fläche trägt eine Textur und die Kante entspricht der Pack-Palette
## (1024² für military_base-Objekte) — bewiesener Live-Stand (Probe 20.10.).
func _pack_flachen_pruefen(mesh: Mesh, oid: String, erwartete_kante: int) -> void:
var count := mesh.get_surface_count()
_check(count > 0, "%s hat Flächen (%d)" % [oid, count])
var textured := 0
var kante_ok := true
for s in count:
if _camo_mat_tex(mesh, s) != null:
var tex := _mat_tex(mesh, s)
if tex == null:
continue
textured += 1
var img := tex.get_image()
if img != null and (img.get_width() != erwartete_kante
or img.get_height() != erwartete_kante):
kante_ok = false
_check(textured == count,
"%s: ALLE %d Flächen haben albedo_texture != null (getextured: %d)" % [oid, count, textured])
# Pfad + Zuordnung: die Textur am Mesh ist DIESELBE Instanz wie der
# Cache-Eintrag mit dem erwarteten Multicam-Pfad.
var erwartet: ImageTexture = null
var pfad_text := ""
for pfad in GlbMeshStore._camo_tex_cache.keys():
var p := str(pfad)
if p.ends_with(erwartet_name):
var v: Variant = GlbMeshStore._camo_tex_cache[pfad]
if v is ImageTexture:
erwartet = v
pfad_text = p
_check(erwartet != null, "%s: Multicam-Textur %s im Cache geladen" % [oid, erwartet_name])
if erwartet != null:
print(" info: %s → Textur-Pfad: %s" % [oid, pfad_text])
var instanz_ok := false
for s in count:
if _camo_mat_tex(mesh, s) == erwartet:
instanz_ok = true
_check(instanz_ok,
"%s: Flächen tragen DIESELBE Textur-Instanz wie der Cache (%s)" % [oid, erwartet_name])
_check(kante_ok, "%s: Textur-Kante %d² (Pack-Palette)" % [oid, erwartete_kante])
func _camo_mat_tex(mesh: Mesh, s: int) -> Texture2D:
func _mat_tex(mesh: Mesh, s: int) -> Texture2D:
var mat := mesh.surface_get_material(s)
if mat is BaseMaterial3D:
return (mat as BaseMaterial3D).albedo_texture
return null
## Die Pack-Dateien liegen im gefundenen Nachbar-Repo: Palette 1024²,
## Multicam 2048². Fehlen sie dort, ist das ein echter Bruch (die GLBs
## kommen ja aus ebendiesem Repo) und wird laut gemeldet — kein SKIP-Fall.
func _pack_dateien_pruefen() -> void:
print("Testgruppe: Pack-Dateien im Nachbar-Repo (Palette 1024² / Multicam 2048²)")
var treffer := ""
for root: String in repos:
var dir := root.path_join("assets/textures/military_desert")
if FileAccess.file_exists(dir.path_join(GlbMeshStore.PACK_TEX_PALETTE)):
treffer = dir
break
_check(not treffer.is_empty(),
"textures/military_desert im Nachbar-Repo gefunden (%s)" % treffer)
if treffer.is_empty():
return
_kante_pruefen(treffer.path_join(GlbMeshStore.PACK_TEX_PALETTE), 1024, "Palette")
_kante_pruefen(treffer.path_join(GlbMeshStore.PACK_TEX_PICTURE_1), 2048, "picture_1")
_kante_pruefen(treffer.path_join(GlbMeshStore.PACK_TEX_PICTURE_2), 2048, "picture_2")
func _kante_pruefen(pfad: String, kante: int, label: String) -> void:
var img := Image.load_from_file(pfad)
_check(img != null, "%s ladbar (%s)" % [label, pfad.get_file()])
if img == null:
return
_check(img.get_width() == kante and img.get_height() == kante,
"%s ist %d² (ist %d×%d)" % [label, kante, img.get_width(), img.get_height()])
# --------------------------------------------------------- Viewport-Rebuild --
func _proben_doc(oid: String, scale: float) -> Dictionary:

View file

@ -22,11 +22,18 @@ var work := ""
func _init() -> void:
_cleanup_old()
base = ProjectSettings.globalize_path(
TEST_DIR_PREFIX + "_" + str(Time.get_ticks_msec()))
# Karte t_1e82f747: Time.get_ticks_msec() ist nur die Zeit seit Godot-Start
# (kleine Zahl, z.B. 115) — bei jedem CLI-Run derselbe Wert! Kollidierte
# Namen mit nicht löschbaren Alt-Läufen → git init lief in einer ALTEN
# Sitzung (remote existiert schon, alte Dateien im Baum → rote Setup-Zeilen).
# Deswegen: Unix-Zeit + ticks_usec = garantiert einzigartig pro Prozess/Run.
base = ProjectSettings.globalize_path(TEST_DIR_PREFIX + "_" +
str(int(Time.get_unix_time_from_system())) + "_" +
str(Time.get_ticks_usec()))
DirAccess.make_dir_recursive_absolute(base)
_run_tests()
_test_ls_remote_warnzeile()
_force_writable(base)
_cleanup_dir(base) # best effort — git-Pack-Dateien können read-only sein
if failures == 0:
print("ALL OK: %d checks" % checks)
@ -54,7 +61,27 @@ func _cleanup_old() -> void:
return
for name in root.get_directories():
if name.begins_with("test_updater"):
_cleanup_dir(ProjectSettings.globalize_path("user://").path_join(name))
var alt := ProjectSettings.globalize_path("user://").path_join(name)
_force_writable(alt)
_cleanup_dir(alt)
## Karte t_1e82f747: git macht .git-Packs read-only, dann schlägt
## DirAccess.remove_absolute fehlt → der Ordner bleibt liegen und KOLLIDIERT
## mit künftigen Läufen (derselbe ticks_msec-Name!). chmod -R u+w macht die
## Bäume löschbar. Linux/OSX-Fallback per /usr/bin/chmod (Tests sind CLI,
## Windows-spezifische ACLs sind hier nicht der Fall).
func _force_writable(path: String) -> void:
if not DirAccess.dir_exists_absolute(path):
return
var chmod := "/usr/bin/chmod"
if not FileAccess.file_exists(chmod):
chmod = "chmod"
# WICHTIG: das 2-Argument-OS.execute ist in Godot 4 ASYNCHRON (gibt nur
# eine PID zurück) — ohne output-Array wartet der Lauf nicht, und die
# Löschung passiert vor dem chmod. Mit output-Array blockiert er.
var out: PackedStringArray = []
OS.execute(chmod, PackedStringArray(["-R", "u+w", path]), out)
func _cleanup_dir(base_dir: String) -> void:
@ -63,18 +90,33 @@ func _cleanup_dir(base_dir: String) -> void:
var d := DirAccess.open(base_dir)
if d == null:
return
# Karte t_1e82f747: get_files()/get_directories() überspringen defaultmäßig
# VERSTECKTE Ordner — und .git ist versteckt. Ohne dieses Flag blieb der
# Repo-Kern liegen, der Basisordner war nie leer und löschbar.
d.include_hidden = true
for file in d.get_files():
DirAccess.remove_absolute(base_dir.path_join(file))
for sub in d.get_directories():
_cleanup_dir(base_dir.path_join(sub))
DirAccess.remove_absolute(base_dir)
# Rückfallebene: git-Pack-Bäume haben teils noch andere Sonderfälle — wenn
# der Ordner trotzdem liegt, räumt rm -rf (CLI-Umgebung) garantiert ab.
if DirAccess.dir_exists_absolute(base_dir):
var out: PackedStringArray = []
OS.execute("/bin/rm", PackedStringArray(["-rf", base_dir]), out)
func _git(dir: String, args: Array) -> Dictionary:
var packed := PackedStringArray()
for a in args:
packed.append(str(a))
return EditorUpdater.git_run("git", dir, packed)
var r := EditorUpdater.git_run("git", dir, packed)
if not bool(r["ok"]):
# Karte t_1e82f747: rote Setup-Nummern ohne Grund sind wertlos —
# der echte git-Fehlertext gehört in die Ausgabe.
print(" git [%s] %s → code=%s: %s" % [dir, " ".join(args),
r["code"], str(r["output"]).strip_edges()])
return r
func _write_file(path: String, text: String) -> void:

70
tools/TEST_UMGEBUNG.md Normal file
View file

@ -0,0 +1,70 @@
# Test-Umgebung: woran die Editor-Suiten hängen (Karte t_1e82f747, 20.10.2026)
Kurzreferenz für die nächste Person: was die zwei zuvor „immer rot"-Suiten
brauchen, warum sie in einem nackten Klon rot/skip waren, und wie man sie
grün fährt. Für die Pack-Textur-Zuordnung selbst siehe
`PACK_TEXTURE_MAPPING.md` im **Client-Repo** (verbindlich).
## tests/test_updater.gd (75 Checks)
Braucht: nur das `git`-Binary (keine Identität — die Suite setzt
`user.name`/`user.email` lokal in ihren Temp-Repos; kein Netz, alles läuft
gegen ein Bare-Repo unter `user://test_updater_…`).
**Früherer Dauer-Fehler (behoben):** der Lauf nutzte
`user://test_updater_<Time.get_ticks_msec()>` als Verzeichnis —
`ticks_msec` zählt nur seit Godot-Start und ist bei CLI-Läufen immer eine
kleine Zahl (z. B. 115). Die Namen kollidierten mit Alt-Läufen, die sich
nicht löschen ließen (git macht `.git`-Pack-Dateien read-only), und
`git init` lief dann in einer ALTEN Sitzung: remote „already exists",
alter Arbeitsbaum, rote Setup-Zeilen ohne echten Bug.
Jetzt:
- Verzeichnisname = Unix-Zeit + `ticks_usec` (pro Run einzigartig).
- Aufräumen erzwingt Schreibrechte (`chmod -R u+w`, **blocking** via
`OS.execute` mit output-Array — die 2-Argument-Variante ist in Godot 4
asynchron!) und löscht inkl. versteckter Ordner
(`DirAccess.include_hidden = true`, `.git` ist versteckt) mit
`rm -rf`-Rückfallebene.
- Fehlerhafte git-Aufrufe geben den echten git-Fehlertext aus
(`git [dir] args → code=…`), nicht nur eine rote Zahl.
## tests/test_glb_view.gd (48 Checks)
Braucht: ein **Nachbar-Repo** mit `assets/objekte` (Mesh-Lager) —
`scopedvr-client` oder `scopedvr-match-server`. Gesucht wird wie in
`tools/sync_objects.sh`:
1. Env `SCOPEDVR_CLIENT_DIR` / `SCOPEDVR_MATCH_SERVER_DIR` (absolute Pfade
werden testseitig in `../`-Pfade umgerechnet — `glb_quellen` müssen
basis-relativ sein, `String.path_join` ketten Absolutpfade nur an),
2. dann `../`- und `../../`-Siblings relativ zum Editor-Repo.
Ohne Fund: **klare SKIP-Zeilen + Exit 0** (kein rotes Krachen). Die
GLB-Quellen werden zur Laufzeit in `ProjectSettings` gesetzt
(`scopedvr/editor/glb_quellen`) — kein Eingriff in `src/`.
Textur-Erwartungen (Stand `d0d4fbd`): die Pack-GLBs tragen ihre
ORIGINAL-Texturen (GLTFDocument löst die externen URIs selbst auf, je Parse
eine eigene ImageTexture-Instanz — der `_camo_tex_cache` bleibt leer). Die
alten Camo-Erwartungen (`desert_multicam_1/2.png`, geteilte Cache-Instanz)
sind entfernt. Geprüft wird jetzt: alle Flächen `albedo_texture != null`,
Kante = Palette-Größe (1024²), reine Zuordnung `pack_datei_fuer`
(military_base(+_00x) → `Demonstration_Textures1.png`, picture_1/2 →
`Demonstration_picture_N.png` 2048²) und die Pack-Dateien im gefundenen
Nachbar-Repo.
Schnelltest in diesem Container:
cd <klon>/scopedvr-map-editor
/opt/data/home/bin/godot4 --headless --path . --import # Pflicht vor jeder -s-Suite
/opt/data/home/bin/godot4 --headless --path . --script res://tests/test_glb_view.gd
# ohne Nachbar-Repo: SKIP-Zeilen; mit: ALL OK: 48 checks
## test_gui_smoke (F5) — vorbestehend rot, NICHT Teil von t_1e82f747
`F5: Karte-testen-Flow angesprungen` + `F5: Fehler-Dialog im
nicht-gefunden-Zweig geöffnet` (2/89) schlagen auch am jungfräulichen
`0adb161` fehl (per `git stash`-Verfahren verifiziert, 20.10.). Ursache ist
nicht die Test-Pflege dieser Karte; Grenze der Karte war ausdrücklich nur
`test_updater.gd` + `test_glb_view.gd` + Notizen.