128 lines
19 KiB
Markdown
128 lines
19 KiB
Markdown
# Ordinary game startup validation
|
||
|
||
Ordinary rounds use progressive mission generation: startup waits for the prison
|
||
(or crash scene), its initial population/supplies, and the extraction vehicle.
|
||
The remaining camps, scouting posts, roadblocks and support are prepared after
|
||
player admission. The server logs separate `[generation]: spawn-ready` and
|
||
`[generation]: background complete` elapsed times. They measure scenario
|
||
preparation, not engine boot or client loading. Explicit historical Workbench
|
||
smoke fixtures retain their full-world preparation contract.
|
||
|
||
`tools/headless_game.py` prepares a bounded **ordinary game** run using the installed stable `ArmaReforgerSteam.exe`, rather than Workbench. It loads either Core or the full Chat dependency chain and the authored successor scenario header. It does not activate any WORKBENCH smoke plugin or add a server flag.
|
||
|
||
Example, only when other runners have stopped:
|
||
|
||
```sh
|
||
python3 tools/headless_game.py --compat /tmp/erri-headless-compat --profile /tmp/erri-game-profile --project core --timeout 180
|
||
```
|
||
|
||
Use `--project chat` for the companion dependency chain. The program constructs `-addonsDir <successor>/addons -addons <selected addon GUID> -errOrdinaryScenario <core|chat> -profile <private profile> -noBackend -noSound -noSplash -noThrow -disableCrashReporter -logconsole`. Engine arguments follow the official [Arma Reforger startup parameters](https://community.bistudio.com/wiki/Arma_Reforger:Startup_Parameters). `-errOrdinaryScenario` is our explicit diagnostic argument, handled by the successor launcher described below. The server-only `-server` parameter is deliberately absent. The official startup-parameter page was checked again on 2026-09-19: it documents `-noSplash` for skipping splash screens and `-noThrow` for disabling error dialogs. Neither option promises to omit layout resource loading or to ignore logged errors.
|
||
|
||
## Isolation
|
||
|
||
Automated tests must stay hidden. Use the existing headless game, Workbench,
|
||
and packaging runners; never place test windows on a physical monitor or
|
||
change the user's Steam launch options. The obsolete right-monitor KWin
|
||
helper has been removed. Normal Steam play is independent of these runners.
|
||
|
||
- Read-only `/proc` enumeration refuses **any** existing ArmaReforgerSteam/SteamDiag game instance before launch, including a Wine/Proton launcher command or truncated process name. Unreadable process state fails closed. It does not stop the existing game.
|
||
- The disposable cloned prefix must already exist outside Steam. The same `.erri-workbench.lock` used by the Workbench runner prevents concurrent use. Existing Wine, Proton, Gamescope and Windows process candidates carrying that prefix also cause refusal. Readable native process metadata excludes unrelated services; ambiguous candidates fail closed. The read-only `--inspect-processes` mode reports all process blockers without launching, locking, configuring audio or creating profiles. Do not use the actual Steam compatdata prefix or user game profile.
|
||
- The game profile is separate from source, Steam and the prefix tree. An existing directory must have this runner's exact private-profile marker. Unexpected symlinks/addons are rejected; only the installed vanilla data link is created. This does not import the user's game profile or workshop subscription folder.
|
||
- Gamescope uses its headless backend; no main/right-monitor window is requested. `-noSound`, a unique muted null sink and routing only the run's identified audio streams prevent test output. No default sink or unrelated application stream is changed.
|
||
- Cleanup targets only the launched process group and the locked disposable prefix. Only the null sink module created for this run is unloaded. Cleanup errors are retained as failures; source/log sealing requires confirmed prefix cleanup.
|
||
|
||
This program never calls Steam account, friend, workshop, login or profile APIs. It does not modify Steam configuration. `-noBackend` disables the game backend; it is not a claim that the proprietary Steam-enabled executable performs no platform initialization internally.
|
||
|
||
## What constitutes success
|
||
|
||
A successful run requires all of:
|
||
|
||
1. A native `Module: Game` log with neither `WORKBENCH` nor `ENF_WB` in its defines.
|
||
2. The selected native mission header and actual `GetSystemsConfig()` both match `MissionSystems.conf`, followed by the normal successor preparation transition `phase 0 -> 1`, proving the ordinary game loaded and initialized the mission.
|
||
3. Another 15 seconds (configurable 5–120) without native errors, startup failure or leaked `ERR SMOKE` fixture output.
|
||
4. The launched processes stopped, with unchanged addon bytes and exact final raw-log provenance.
|
||
|
||
Timeout, process exit before readiness, unexpected multiple log sessions, any native engine/script error or unverified module defines fails. It does not infer success from an executable starting or a blank window.
|
||
|
||
The ordinary game log may require a disclaimer/menu interaction or may reject offline startup in a particular installed build. This runner does not click past dialogs, change account state, weaken checks, or silently switch to Workbench. Such a run remains incomplete and its captured log should determine the next bounded action.
|
||
|
||
The source snapshot is captured before process launch and sealed after cleanup, using the existing provenance helpers. `runner.json`, `game-startup-report.json` and `headless-launch.log` accompany the raw game session. The result is `game-started`, **not** `smoke-completed`; existing release qualification therefore cannot count it as a completed escape/restart fixture. Existing archive tooling can retain it as additional evidence, subject to its ordinary error checks and provenance validation.
|
||
|
||
## Current evidence
|
||
|
||
Thirty source-local tests exercise process refusal (including inaccessible `/proc` data), prefix/profile separation, exact game arguments and readiness/error gates. They import the runner without launching processes, audio, Steam or the engine. The first host launch is preserved in [game-2026-09-19-072609](evidence/game-2026-09-19-072609/report.json). It failed on a vanilla intro-splash unknown-class error before Game compilation. GameLib defines included ENF_WB, so the executable choice and startup resource path remain under investigation. No ordinary-game startup pass is claimed.
|
||
|
||
The intended gate is production script compilation/startup validation with WORKBENCH undefined. It is not ordinary player input, a completed escape, manual vehicle use, multiplayer/dedicated-server behavior, an FPS benchmark.
|
||
|
||
## Installed executable selection and first failed attempt
|
||
|
||
The first Diag attempt, `logs_2026-09-19_07-26-09`, initialized GameLib with `ENF_WB` and `ENABLE_DIAG`. Its vanilla IntroSplashScreen then reported `Unknown class SCR_WidgetExportRuleRoot` before Game-module compilation completed. The runner retained failure; it did not mask the resource error or count startup.
|
||
|
||
Read-only installed binary inspection distinguishes three executables:
|
||
|
||
| Executable | Bytes | SHA-256 |
|
||
|---|---:|---|
|
||
| ArmaReforgerSteam.exe | 37,272,632 | `1d221a990fa16c78fe02f685a7ba1330be115ca93cd3b641e67e5d3f1b40d9c9` |
|
||
| ArmaReforgerSteamDiag.exe | 40,060,472 | `51f941dee28bda7865e2428d7fc92fcbe7c5c7ff666caf642c0ff23c2aced457` |
|
||
| ArmaReforger_BE.exe | 1,489,648 | `f1d9117d6c503857fc28ad8be2c828e1ae6124d3055b1fae13ad7e99e3f01d82` |
|
||
|
||
The regular and Diag programs are x86-64 PE executables; the separate BE launcher is a much smaller x86 PE. The runner selects the installed regular game directly. It does not invoke the BE launcher or silently fall back to another program.
|
||
|
||
Diag contains `ENF_WB` and `ENABLE_DIAG` strings in the script-initialization constant block; regular contains neither. Both contain the same module/defines log format, so the existing explicit module-evidence gate remains appropriate. Combined with the actual Diag GameLib log, this supports a built-in Diag configuration explanation, not an addon-defined WORKBENCH macro. The regular attempt below confirms its GameLib runtime defines lack WORKBENCH/ENF_WB; a completed Game-module compile is still required. Binary strings alone are not a compilation result.
|
||
|
||
The installed splash layout contains SCR_WidgetExportRuleRoot at lines15–18, while that class is authored in `scripts/Game/UI/Components/SCR_WidgetExportRule.c:82`. The log loads the layout after GameLib compilation but before Game completes. This establishes the observed module-order mismatch; it does not prove that regular execution avoids it. No layout override, class stub, error filter, or native acceptance relaxation is included.
|
||
|
||
The regular executable was attempted in `logs_2026-09-19_07-32-31`. GameLib compiled without WORKBENCH/ENF_WB. The splash layout still reported the same unknown class at 07:32:32.696 despite `-noSplash` being present in the logged command. `Compiling Game scripts` followed at 07:32:32.770, but no completed `Module: Game` or mission readiness was captured before fail-fast cleanup. This is failed startup evidence, not an ordinary-game compilation pass.
|
||
|
||
Installed `scripts/Game/UI/Components/SCR_WidgetExportRule.c:82` declares `SCR_WidgetExportRuleRoot` outside any WORKBENCH guard; only its helper methods are guarded later. Thus ordinary-build exclusion of that class is not supported by the source. The observed layout load precedes Game compilation, which is consistent with a module-order issue but does not establish its engine-internal cause. There is no supported alternative splash-bypass flag established by this audit; the existing documented `-noSplash` remains. No asset stub, installed-resource edit, or log filter is proposed.
|
||
|
||
Read-only evidence SHA-256: extracted vanilla `SCR_WidgetExportRule.c` = `3a4b5dc3a8669bf20d903277d249d41ee3ae7e1e74d2c329ed31d435624fa4b5`; raw regular-run `console.log` from `logs_2026-09-19_07-32-31` = `20684f6328c7e955d1f08edf16e470e19efc71284856701b43ed31811693536f`.
|
||
|
||
|
||
## Optional failure diagnostic grace
|
||
|
||
`--failure-grace 30` keeps a failed ordinary run alive for at most 30 seconds after the first observed native/startup error so downstream Game compilation or mission-startup diagnostics can reach the raw log. The default is `0`, preserving immediate failure cleanup. Only integer values 0–30 are accepted before any launch or audio mutation.
|
||
|
||
The first failure stays latched. Repeated errors never reset its timestamp, and later Game compilation/readiness cannot turn the run into a success. The grace expires at the earlier of first failure plus the configured grace or the original absolute run timeout, measured with a monotonic clock. Process exit ends observation immediately. Cleanup, source snapshots, post-stop sealing and raw-log hashing use the same path as ordinary failure.
|
||
|
||
`runner.json` records `failure_grace_seconds`, `failure_observed_during_polling` and `first_failure_elapsed_seconds` (relative to the run's monotonic start, or null). `game-startup-report.json` preserves the final raw error counts and marks an earlier observed failure even if the process later truncated/replaced its log; it never invents error lines. Any later readiness remains failed and does not qualify a release. Safety/preflight failures and cleanup errors do not receive this logging grace.
|
||
|
||
The twelve additional source-local tests exercise default fail-fast behavior, repeated-error deadlines, readiness before/after error, absolute-timeout truncation, early process exit, log-replacement failure retention, continuous clean settle behavior, exit at the settle boundary, CLI defaults and bound rejection. They use synthetic observations and launch no native process. The extended capture in [074505](evidence/game-2026-09-19-074505/report.json) confirms completed Game compilation and preserves the subsequent guard-spawn crash.
|
||
|
||
|
||
## External crash reporter
|
||
|
||
The runner adds documented `-disableCrashReporter` to disable automatic external crash reporting. `-noBackend` controls backend HTTP communication, and did not prevent the separate reporter in ordinary run `logs_2026-09-19_07-45-05`: its raw log records `Crash reporter started`, `Sending analytical data` and `Crash Analytics: OK`. The official [Crash Report documentation](https://community.bistudio.com/wiki/Arma_Reforger:Crash_Report) names `-disableCrashReporter` for stopping automatic sending; the installed regular executable contains that exact parameter. This changes reporting behavior only: local errors, crash logs and failed results remain available. The runner does not use `-silentCrashReport` or its old name `-noCrashDialog`, which automatically send reports without a dialog according to the official [startup parameters](https://community.bistudio.com/wiki/Arma_Reforger:Startup_Parameters). No test has intentionally induced another crash to validate the option.
|
||
|
||
|
||
## Header-based ordinary launch candidate
|
||
|
||
Direct `-world Worlds/ERR_Everon.ent` did compile the ordinary Game module (5,728 files without WORKBENCH/ENF_WB) in `logs_2026-09-19_07-45-05`, created a real local controller and reached the strict identity gate. It then crashed on the first vanilla USSR squad leader spawn. Its world-systems log named no config; the comparable Workbench run explicitly loaded BaseGameModeSystems. `MissionSystems.conf` inherits BaseGameModeSystems, which inherits ChimeraSystemsConfig including PawnEntitySystem, character animation/damage/control systems and others. Missing configured systems is therefore a grounded cause to investigate, not a proven native crash diagnosis. No identity workaround is included.
|
||
|
||
The ordinary runner now starts the normal main menu and explicitly requests `-errOrdinaryScenario core` or `chat`. Core owns the opt-in `ERR_OrdinaryScenarioLaunch.c`; Chat adds only its own header/addon selection override, preserving the dependency direction. Both are absent from WORKBENCH compilation and inert without the explicit argument. The launcher queues one attempt per game instance, validates the selected installed successor header/addon pair and only calls the native transition from an idle main menu. Active worlds and pending native transitions are never replaced. World cleanup cancels an outstanding callback. No click, user identity, audit callback or backend state is synthesized.
|
||
|
||
The exact installed API is `GameStateTransitions.RequestScenarioChangeTransition(ResourceName missionOrWorld, ResourceName worldSystemsConfig, string addonList)`: its documentation accepts a mission header or world. Vanilla `game.c:1246` uses it in `PlayGameConfig`. Both current fixture selectors pass `{C1E20667DCE324ED}Missions/ERR_Everon.conf` after retirement of the duplicate Base header; both specify `{C50579A2EC48E8EF}Configs/Systems/MissionSystems.conf`. Once a successor world starts, the diagnostic only inspects its active header and actual `GetSystemsConfig()`; the runner requires that verification plus ordinary module/phase/settle evidence. Existing splash errors still permanently fail the run, even if downstream header verification succeeds.
|
||
|
||
This uses the documented [native transition API](https://community.bistudio.com/wikidata/external-data/arma-reforger/ArmaReforgerScriptAPIPublic/interfaceGameStateTransitions.html), not an invented `-scenario` engine parameter. The official `-worldSystemsConfig` startup parameter is marked Diag-only, so it is not assumed to configure the regular executable. Native compilation and actual header transition are pending; failure to reach an idle main menu is retained as a failed diagnostic, without retries that could replace gameplay.
|
||
|
||
The runner now additionally requires a matching local-participant marker after normal finalized admission: the actual local controller must own the living body, the canonical roster must accept it, and the mission must be in PRISON. Header/world readiness alone can no longer pass while player admission is absent. This new observation is gated by the ordinary diagnostic flag and never creates a body or identity. Its ordinary native compilation is pending.
|
||
|
||
## Optional ordinary prisoner view
|
||
|
||
The explicit `-errOrdinaryScenario core|chat -errOrdinaryScreenshot 1` combination requests one screenshot three seconds after genuine finalized local prisoner admission. The runner exposes this as `--capture-prison-view` for loose successor startup; packed and vanilla-control runs refuse it. The request is absent from Workbench compilation and inert without both diagnostic arguments. It does not select a camera, inject input, move an entity or change presentation, so a captured menu, loading overlay or unexpected viewpoint is a finding to investigate rather than something the helper hides.
|
||
|
||
Before requesting `System.MakeScreenshot("$logs:err-ordinary-prison")`, the helper revalidates the same current world/mode, active prison phase, living actor, canonical roster-life/account, local controller and player-manager binding. It requires the exact opt-in value `1` and a non-console authoritative game. The owned delayed callback is removed on mission stop and by the existing native `OnBeforeWorldCleanup` hook; the per-world attempt latch prevents a second admission notification from requesting another capture. Invalidated admission cancels without retry. A new world resets the latch.
|
||
|
||
`tests/test_ordinary_screenshot.py` executes the helper's actual gate, queue, validation, capture and cleanup methods through adapters, including replaced controllers/bodies/lives, death, phase/world changes and late callbacks. This verifies source behavior only. The native capture call and actual image must still be checked in the ordinary game; a screenshot alone does not qualify manual escape, multiplayer or performance. Preserve the image with that run's raw logs and source provenance.
|
||
|
||
[121730](evidence/game-2026-09-19-121730-prison-view/report.json) passes ordinary Chat startup and the optional capture. The [unaltered player view](evidence/game-2026-09-19-121730-prison-view/ordinary-prison-view.png) shows the prisoner inside the authored walls with the correct escape objective and instructions. The exact native BMP is retained beside a documented lossless PNG conversion. The image also exposed tall wheat inside the yard; the first bounded decal candidate in [122353](evidence/game-2026-09-19-122353-yard-candidate/report.json) clears only a small central patch and is not yet full-yard treatment. Neither capture supplies player-input or full-route acceptance.
|
||
|
||
[125906 integrated startup](evidence/game-2026-09-19-125906-integrated-prison/report.json) passes after the admission-budget, interaction-feedback, optional aircraft-target and stock-scale yard changes. The native player view remains correct. The scale-ten vanilla decal clears a larger irregular patch while wheat remains near and within parts of the enclosure; complete yard clearance is not claimed. This is ordinary-game startup and initial-view evidence only.
|
||
|
||
Roadblock props remain replicated independently of player distance so an admin
|
||
viewing the island through Game Master can see the same barriers as nearby
|
||
players. This is limited to the 18 props per post (at most 540); guard and
|
||
vehicle streaming is unchanged. A post is counted only after all 18 props have
|
||
valid replication nodes. Local Workbench validation does not by itself prove
|
||
remote-client visibility.
|