Files
VoxelForge/Standalone/Research/WorldAssemblerM3/Linux70/RunEngineSpawnTracesL70.py
T
Fr0zkaandClaude Opus 5.5 bd68aee1ca parity: Unreal and headless resolve the same four-strate plan (L70, v214)
- Export commandlet writes the live Core adapter's world_assembly + resolved layout pair;
  the headless owner path imports that pair (manifest/layout equal, layout_chunk_width_q
  validated). owner_world.json re-exported from the owner's staged assets on the Linux host.
- Adapter SW route: Core clearance/radius floors, 0.5 m support search, outward-facing
  tie-break; passage mouth/mid radius floors match Core (36/24 owner voxels).
- FI in Unreal now uses the legacy (compiled analytic) generator unless a plan is imported:
  the auto native-FI plan failed the passage audit on the owner plan (1843 blocked samples,
  945 open-air violations). Owner decides whether native FI returns.
- In-engine spawn traces per strate (3 each): SW GenerateTile 42.7 worker-s vs 20.1 headless
  for equal tile counts; LOD0 p95 45-169 ms in UE.
- Fast tier green; all three passages pass on the shared plan.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
2026-10-27 06:22:48 +01:00

124 lines
5.9 KiB
Python

#!/usr/bin/env python3
"""Run bounded Unreal startup traces at each exported owner-plan arrival."""
from __future__ import annotations
import argparse
import json
import subprocess
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
HOST = Path("/home/jahni/VFHost")
PLUGIN_SAVED = ROOT / "Saved/Linux/l70"
HOST_PLUGIN_SAVED = HOST / "Plugins/VoxelForge/Saved/Linux/l70"
HOST_SAVED = HOST / "Saved/Linux/l70"
EDITOR = Path("/home/jahni/UnrealEngine/Engine/Binaries/Linux/UnrealEditor-Cmd")
TRACES = PLUGIN_SAVED / "traces"
SPAWNS_M = {
0: (8.0, 16.0, -372.2626953125),
1: (25.8369140625, 3.19921875, -737.0),
2: (19.166015625, -14.2001953125, -753.3740234375),
3: (-20.0625, -5.068359375, -900.353515625),
}
def run(strate: int, trial: int) -> dict:
label = f"strate{strate}-trial{trial}"
trace = TRACES / f"{label}.trace.jsonl"
abslog = TRACES / f"{label}.log"
stdout = TRACES / f"{label}.stdout.log"
output = HOST_PLUGIN_SAVED / "output" / label
userdir = HOST_SAVED / label / "User"
zendata = HOST_SAVED / label / "Zen"
ddc = HOST_SAVED / "DDC"
for directory in (output, userdir, zendata, ddc, TRACES):
directory.mkdir(parents=True, exist_ok=True)
spawn_cm = ",".join(f"{value * 100.0:.3f}" for value in SPAWNS_M[strate])
command = [
str(EDITOR), str(HOST / "VFHost.uproject"), "-nullrhi", "-unattended",
"-nop4", "-nosplash", "-NoSound", "-corelimit=12", "-NoZenAutoLaunch",
"-notraceserver", "-nocrashreports",
"-DisablePlugins=AndroidFileServer",
"-ddc=NoZenLocalFallback",
f"-userdir={userdir}", f"-ZenDataPath={zendata}", f"-LocalDataCachePath={ddc}",
"-run=VoxelForgeExplore", "-seed=0", "-archetype=TunnelNetwork", "-slot=0",
"-modes=export", "-gameconfig=1", "-coreworldtest=1", "-ownerfourstrateplan=1",
"-corevoxelsizecm=25", "-coreislandtest=0", "-corefallbacktest=0",
"-coreofftest=0", "-coreplantest=0",
"-settingsref=/Game/VoxelForge/DA_Settings.DA_Settings", "-corehybridmesher=0",
f"-out={output}",
"-voxel.StreamingCoarseFirst=0", "-voxel.StreamingDynamicNearReserve=1",
"-voxel.StreamingLegacyPendingResultCap=0", "-voxel.CoreRegionConcurrentAdmission=1",
"-voxel.StreamingResultBacklogCap=256", "-voxel.StreamingReadyQueueDepth=8",
"-voxel.StreamingBackgroundWorkerCap=1", "-voxel.CoreColumnCacheDirectMapped=0",
"-voxel.CoreMCGradientMemoization=1", "-voxel.StreamingCollisionLookahead=1",
"-coreworldstreamtrace=1", "-voxel.TestMove=0",
"-voxel.TestMoveSpeedCmPerSecond=0", f"-voxel.StartupTraceSpawnCm={spawn_cm}",
f"-voxel.StartupTraceFile={trace}", "-voxel.StartupTraceUntilEndPlay=1",
"-voxel.StartupTraceMaxTiles=16384", "-voxel.CollisionGate=0",
f"-abslog={abslog}",
]
wrapped = ["taskset", "-c", "0-11", "prlimit", "--rss=6442450944", "--",
"timeout", "1740s", *command]
started = time.monotonic()
with stdout.open("w", encoding="utf-8") as stream:
result = subprocess.run(wrapped, cwd=ROOT, stdout=stream,
stderr=subprocess.STDOUT, timeout=1740, check=False)
elapsed = time.monotonic() - started
event_names: set[str] = set()
if trace.is_file():
for line in trace.read_text(encoding="utf-8", errors="replace").splitlines():
try:
event_names.add(json.loads(line).get("type", ""))
except json.JSONDecodeError:
continue
row = {
"label": label, "strate": strate, "trial": trial, "spawn_m": SPAWNS_M[strate],
"spawn_cm": spawn_cm, "elapsed_s": round(elapsed, 3), "exit_code": result.returncode,
"trace": str(trace), "stdout": str(stdout), "events": sorted(event_names),
"has_walkable_gate": "walkable_gate" in event_names,
"has_lod0_ring": "lod0_ring_ready" in event_names,
"has_initial_set": "stream_trace_milestone" in event_names,
"has_summary": "summary" in event_names,
}
print(f"[L70EngineTrace] {json.dumps(row, sort_keys=True)}", flush=True)
return row
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--strates", nargs="+", type=int, choices=range(4), required=True)
parser.add_argument("--first-trial", type=int, default=1, choices=range(1, 4))
parser.add_argument("--trials", type=int, default=3)
args = parser.parse_args()
if not EDITOR.is_file():
raise SystemExit(f"missing UE commandlet executable: {EDITOR}")
HOST_PLUGIN_SAVED.mkdir(parents=True, exist_ok=True)
if args.trials < args.first_trial or args.trials > 3:
parser.error("--trials must be 1..3 and >= --first-trial")
results = [run(strate, trial) for strate in args.strates
for trial in range(args.first_trial, args.trials + 1)]
summary = PLUGIN_SAVED / "traces" / ("engine-summary-" + "-".join(map(str, args.strates)) + ".json")
summary.parent.mkdir(parents=True, exist_ok=True)
if summary.is_file():
try:
previous = json.loads(summary.read_text(encoding="utf-8"))
merged = {row["label"]: row for row in previous}
merged.update({row["label"]: row for row in results})
results = sorted(merged.values(), key=lambda row: (row["strate"], row["trial"]))
except (OSError, json.JSONDecodeError, KeyError, TypeError):
pass
summary.write_text(json.dumps(results, indent=2) + "\n", encoding="utf-8")
complete = all(row["exit_code"] == 0 and row["has_walkable_gate"] and row["has_lod0_ring"]
and row["has_initial_set"] and row["has_summary"] for row in results)
print(f"[L70EngineTraceSet] strata={args.strates} trials_each={args.trials} "
f"total={len(results)} status={'PASS' if complete else 'INCOMPLETE'} summary={summary}",
flush=True)
return 0 if complete else 2
if __name__ == "__main__":
raise SystemExit(main())