Add portable tracked Godot launcher

This commit is contained in:
gamer147
2026-08-02 15:09:41 -04:00
parent 498db10a52
commit 83b8004941
4 changed files with 200 additions and 4 deletions

View File

@@ -28,6 +28,9 @@ S:\Game Hacking\Eushully\Himegari\ ← workspace root (three siblings)
└── age-reimpl/ ← OUR WORK (everything we made lives here) └── age-reimpl/ ← OUR WORK (everything we made lives here)
├── run-godot.ps1 / run-godot.cmd tracked development launcher + Windows wrapper;
│ resolves Godot/game-root from parameters, environment,
│ PATH, and the conventional sibling install
├── tools/ Python tooling (parser/disassembler + extractors + VM) ├── tools/ Python tooling (parser/disassembler + extractors + VM)
│ ├── paths.py ★ central path anchor — the ONLY place that knows │ ├── paths.py ★ central path anchor — the ONLY place that knows
│ │ where the game / extracted / build dirs are. All │ │ where the game / extracted / build dirs are. All

View File

@@ -311,7 +311,7 @@ at 2.5M lines). All observe-only → parity preserved; all on `run`/`play`/`swee
steps / 493k sleeps). Dumped when the scene ends or the window closes. e.g. steps / 493k sleeps). Dumped when the scene ends or the window closes. e.g.
`godot --path godot -- --scene SC0000 --boot --shot out/p1.png --trace-histogram out/hist.txt`. `godot --path godot -- --scene SC0000 --boot --shot out/p1.png --trace-histogram out/hist.txt`.
**Godot frontend** (`S:/Godot/Godot_v4.7…`; project = `godot/`). Toolchain: `godot --headless --path godot **Godot frontend** (Godot 4.7 .NET; project = `godot/`). Toolchain: `godot --headless --path godot
--import``dotnet build godot/Himegari.csproj``godot [--headless] --path godot [-- <userargs>]`. --import``dotnet build godot/Himegari.csproj``godot [--headless] --path godot [-- <userargs>]`.
Plays the real bytecode with call-script execution on (subroutines run live). The game root is selected Plays the real bytecode with call-script execution on (subroutines run live). The game root is selected
before catalog loading: `--game-root <directory>` wins, otherwise the directory containing the running before catalog loading: `--game-root <directory>` wins, otherwise the directory containing the running
@@ -350,9 +350,13 @@ input, or audible output; run those gates on a real Linux desktop.
With the exported executable placed in an AGE install, a no-argument launch therefore uses that install With the exported executable placed in an AGE install, a no-argument launch therefore uses that install
and starts the persistent `SYSTEM4.BIN` root naturally. Direct development runs are hosted by the Godot and starts the persistent `SYSTEM4.BIN` root naturally. Direct development runs are hosted by the Godot
editor executable, so pass `--game-root <install>` after Godot's `--` separator; the local editor executable, so pass `--game-root <install>` after Godot's `--` separator. The tracked
`run-godot.ps1`/`.cmd` launchers pass the local script's directly configured `$gameRoot` value. Edit that `run-godot.ps1`/`.cmd` launchers resolve Godot and the game root without machine-specific tracked paths.
one assignment when testing another installed game. Those launchers make the natural route explicit with Explicit `-GodotConsole`/`-GameRoot` values win, followed by `AGE_GODOT_CONSOLE`/`AGE_GAME_ROOT`; Godot
then falls back to `godot4`, `godot`, or `godot-mono` on `PATH`, while the game root falls back to the
conventional `../Himegari_Game` sibling only when it contains `SYS4INI.BIN`. `run-godot.ps1 -Doctor`
prints the resolved repository, Godot, game-root, .NET, and Python prerequisites without building or
launching. The launchers make the natural route explicit with
`--scene SYSTEM4` and pass neither `--boot` nor SC0000 seeds. Examples below focus on their `--scene SYSTEM4` and pass neither `--boot` nor SC0000 seeds. Examples below focus on their
feature-specific arguments and assume the game root is already selected this way. `--headless` can't feature-specific arguments and assume the game root is already selected this way. `--headless` can't
render texture ops (no GPU context) — run windowed for real scenes. render texture ops (no GPU context) — run windowed for real scenes.

5
run-godot.cmd Normal file
View File

@@ -0,0 +1,5 @@
@echo off
REM Default windowed launch follows SYSTEM4, exposes TITLE's shipped developer menu,
REM and uses the accepted GPU retained renderer. Performance logging is opt-in again.
REM -StartupDiagnostics overrides the developer-menu flag for a native-faithful capture.
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0run-godot.ps1" -NativeDebugMenu %*

184
run-godot.ps1 Normal file
View File

@@ -0,0 +1,184 @@
# run-godot.ps1 -- build the C# and launch the Himegari Godot project (how we've been running it).
#
# .\run-godot.ps1 build, then run the persistent SYSTEM4 root windowed
# .\run-godot.ps1 -SelfTest build, then headless self-test (asserts the dialogue trace vs vm0)
# .\run-godot.ps1 -Import also (re)import the project first (needed after adding assets)
# .\run-godot.ps1 -NoBuild skip the dotnet build, just launch
# .\run-godot.ps1 -NativeDebugMenu expose TITLE's unreachable shipped developer menu
# .\run-godot.ps1 -StartupDiagnostics capture the natural TITLE -> Game Start -> SC0000 route
# .\run-godot.ps1 -PerfLog write a timestamped frame/compositor CSV under build/perf
# .\run-godot.ps1 -SoftwareRenderer use the retained software correctness oracle
# .\run-godot.ps1 -Doctor resolve and print prerequisites without building or launching
#
# Uses the *console* Godot build so GD.Print / DRAW logs / the selftest result show in the terminal.
# Resolution order is explicit parameter, AGE_* environment variable, then a portable fallback.
param(
[string]$GodotConsole,
[string]$GameRoot,
[switch]$SelfTest,
[switch]$Import,
[switch]$NoBuild,
[switch]$NativeDebugMenu,
[switch]$StartupDiagnostics,
[switch]$PerfLog,
[switch]$SoftwareRenderer,
[switch]$Doctor
)
$ErrorActionPreference = 'Stop'
$repo = $PSScriptRoot # age-reimpl/
$project = Join-Path $repo 'godot'
$csproj = Join-Path $project 'Himegari.csproj'
function Resolve-ConfiguredFile {
param(
[string]$ExplicitValue,
[string]$EnvironmentValue,
[string[]]$PathCommands,
[string]$Description
)
$configured = if ($ExplicitValue) { $ExplicitValue } elseif ($EnvironmentValue) { $EnvironmentValue } else { $null }
if ($configured) {
if (-not (Test-Path -LiteralPath $configured -PathType Leaf)) {
throw "$Description not found: $configured"
}
return (Resolve-Path -LiteralPath $configured).Path
}
foreach ($commandName in $PathCommands) {
$command = Get-Command $commandName -CommandType Application -ErrorAction SilentlyContinue |
Select-Object -First 1
if ($command) { return $command.Source }
}
throw "$Description not found. Pass -GodotConsole, set AGE_GODOT_CONSOLE, or add Godot to PATH."
}
function Resolve-ConfiguredGameRoot {
param(
[string]$ExplicitValue,
[string]$EnvironmentValue,
[string]$ConventionalValue
)
$configured = if ($ExplicitValue) {
$ExplicitValue
} elseif ($EnvironmentValue) {
$EnvironmentValue
} else {
$ConventionalValue
}
if (-not (Test-Path -LiteralPath $configured -PathType Container)) {
throw "Game root not found: $configured. Pass -GameRoot or set AGE_GAME_ROOT."
}
$resolved = (Resolve-Path -LiteralPath $configured).Path
if (-not (Test-Path -LiteralPath (Join-Path $resolved 'SYS4INI.BIN') -PathType Leaf)) {
throw "Game root does not contain SYS4INI.BIN: $resolved"
}
return $resolved
}
$godotArguments = @{
ExplicitValue = $GodotConsole
EnvironmentValue = $env:AGE_GODOT_CONSOLE
PathCommands = @('godot4', 'godot', 'godot-mono')
Description = 'Godot .NET console executable'
}
$godot = Resolve-ConfiguredFile @godotArguments
$gameRootArguments = @{
ExplicitValue = $GameRoot
EnvironmentValue = $env:AGE_GAME_ROOT
ConventionalValue = Join-Path (Split-Path $repo -Parent) 'Himegari_Game'
}
$resolvedGameRoot = Resolve-ConfiguredGameRoot @gameRootArguments
if ($Doctor) {
$dotnet = Get-Command dotnet -CommandType Application -ErrorAction SilentlyContinue |
Select-Object -First 1
$python = Get-Command py -CommandType Application -ErrorAction SilentlyContinue |
Select-Object -First 1
if (-not $dotnet) { throw '.NET SDK not found on PATH.' }
if (-not $python) { throw 'Python launcher py.exe not found on PATH.' }
Write-Host "Repository : $repo"
Write-Host "Godot : $godot"
Write-Host "Game root : $resolvedGameRoot"
Write-Host "dotnet : $($dotnet.Source)"
Write-Host "Python : $($python.Source)"
exit 0
}
if ($SelfTest -and $StartupDiagnostics) {
throw '-SelfTest and -StartupDiagnostics are mutually exclusive.'
}
$diagnosticFiles = @()
if ($StartupDiagnostics) {
$diagnosticDir = Join-Path $repo 'build\validation\title-newgame'
New-Item -ItemType Directory -Force -Path $diagnosticDir | Out-Null
$diagnosticFiles = @(
(Join-Path $diagnosticDir 'godot.log'),
(Join-Path $diagnosticDir 'timeline.jsonl'),
(Join-Path $diagnosticDir 'histogram.txt'),
(Join-Path $diagnosticDir 'page-map.jsonl')
)
}
$perfLogFile = $null
if ($PerfLog -and -not $SelfTest) {
$perfLogDir = Join-Path $repo 'build\perf'
New-Item -ItemType Directory -Force -Path $perfLogDir | Out-Null
$perfLogFile = Join-Path $perfLogDir ("run-{0}.csv" -f (Get-Date -Format 'yyyyMMdd-HHmmss-fff'))
}
if (-not $NoBuild) {
Write-Host "==> dotnet build $csproj" -ForegroundColor Cyan
dotnet build $csproj -c Debug --nologo -v q
if ($LASTEXITCODE -ne 0) { throw "build failed" }
}
if ($Import) {
Write-Host "==> importing project" -ForegroundColor Cyan
& $godot --headless --path $project --import
}
if ($SelfTest) {
Write-Host "==> headless self-test" -ForegroundColor Cyan
& $godot --headless --path $project -- --selftest --game-root $resolvedGameRoot
} else {
# SYSTEM4 owns the real initializer/title/New Game chain. Do not add --boot or SC0000
# seeds here: those belong only to explicit direct-scene diagnostics.
Write-Host "==> launching windowed from SYSTEM4" -ForegroundColor Cyan
$userArgs = @('--scene', 'SYSTEM4', '--game-root', $resolvedGameRoot)
if ($perfLogFile) {
Write-Host "==> performance log: $perfLogFile" -ForegroundColor Cyan
$userArgs += @('--perf-log', $perfLogFile)
}
if ($SoftwareRenderer) { $userArgs += @('--render-backend', 'software') }
if ($NativeDebugMenu -and -not $StartupDiagnostics) { $userArgs += '--native-debug-menu' }
if ($StartupDiagnostics) {
Write-Host "==> startup diagnostics enabled (native exit semantics)" -ForegroundColor Cyan
$userArgs += @(
'--timeline-log', $diagnosticFiles[1],
'--trace-histogram', $diagnosticFiles[2],
'--page-map', $diagnosticFiles[3]
)
& $godot --log-file $diagnosticFiles[0] --path $project -- @userArgs
} else {
& $godot --path $project -- @userArgs
}
}
$godotExitCode = $LASTEXITCODE
if ($StartupDiagnostics) {
Write-Host "==> startup diagnostic artifacts" -ForegroundColor Cyan
foreach ($file in $diagnosticFiles) {
if (Test-Path -LiteralPath $file) {
$length = (Get-Item -LiteralPath $file).Length
Write-Host (" {0} ({1:N0} bytes)" -f $file, $length)
} else {
Write-Warning "diagnostic artifact was not written: $file"
}
}
}
if ($godotExitCode -ne 0) { exit $godotExitCode }