Files
EscapistsReintroduced/docs/ordinary-input-observer.md
T

44 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Optional ordinary-game input observation
`ERR_OrdinaryInputObserver.c` is an ordinary-game-only, read-only diagnostic. It does nothing unless the existing `errOrdinaryScenario=core` or `chat` option and `errOrdinaryInputObserve=<nonce>` are present. The nonce must contain 1–32 ASCII letters/digits; path punctuation, whitespace and other characters are refused. The observer does not generate input, open/close inventory, move a prisoner, adjust a camera, grant protection or change gameplay. Native compilation and one bounded sampler lifecycle are now evidenced by the [160522 ordinary Chat run](evidence/game-2026-09-19-160522-observer-display-unverified/runner.json.txt). No input was sent; neither that run nor the source-policy tests establishes actual input delivery.
The request starts only after genuine finalized local admission. It pins the exact current mode, native world, local controller, connection ID, controlled body, native character controller, canonical roster life and existing account UUID. Every sample rechecks those bindings, active prison phase and the original conscious living body. The account and identity references remain private and are never logged. Loss of ownership, life, world or phase cancels the request permanently. Native movement-control disablement alone does not invalidate it: inventory UI may legitimately disable ordinary movement while the same body remains conscious.
One owned sampling callback is queued at a time, at 100 ms intervals. Optional screenshots use one separate owned pending callback; a later transition cancels and supersedes the earlier pending capture. At each callback the process tick clock enforces a strict 45-second window from queueing, independently of world time; 450 samples is a second hard bound. Clock reversal cancels/finishes observation. As with any game call queue, a paused or stalled engine cannot execute the callback exactly at its deadline: on resumption it records no further input sample past that real-time bound. Mission stop and world cleanup remove both owned callbacks and the stop subscription. The process-wide one-attempt latch survives cleanup and prevents another round or re-admission silently starting a new experiment.
`READY` requires two continuous real seconds with inventory absent, input and CharacterForward near zero, velocity at most 0.05 m/s and no more than 0.05 m positional drift. Gaps over 500 ms reset that baseline. These tolerances qualify a stationary baseline only; they never classify later movement as successful. Every recorded `SAMPLE` includes the validated nonce, sample index, process/world clocks, actual origin, native CharacterForward value, character movement-input vector, native velocity and inventory visibility. A later wrapper must correlate those observations with separately authorized input timing. Displacement alone is not evidence of walking, and a blocked movement attempt is not a success.
Inventory observation requires the actual `SCR_InventoryMenuUI`, its character controller matching the pinned body's component, a visible root and native top-menu agreement. Only an open after `READY` counts. The subsequent absence of that menu counts as `INVENTORY_CLOSED`, with the original admission still valid. A replacement menu, mismatched body, hidden/incomplete root or a second opening cancels rather than manufacturing a close/reopen sequence. The native top-menu API excludes dialogs; this diagnostic does not claim that no modal overlay exists.
Optional `errOrdinaryInputScreenshots=1` schedules at most three captures, once each for `READY`, first observed open and first observed close, first checked 300 ms after the transition. An open capture also requires `LoadingOverlay` to be absent from that exact inventory root on two revalidated observations at least 100 ms apart: stock `OnQueueProcessed` removes it when native initialization finishes. While the same body/menu/root/top-menu/visible hierarchy remain valid, one callback may retry after 100 ms, always retaining the original request expiry of 3000 ms. The first absent observation only records its time and queues another check; it does not request an image in the same native update that removed the overlay. If the overlay returns, that absence time resets. It never refreshes the original deadline, takes a second open image, or keeps waiting after close, finish or world cleanup. Cancellation clears the absence time too. The delayed callback revalidates the original mode/world/controller/body/roster life and consciousness, observation deadline and world clock. An open capture pins the exact inventory menu and root widget from its transition, then requires those same native objects, the same bound character, a visible widget hierarchy and top-menu agreement. Baseline and closed captures require inventory and top menu to remain absent, plus their original transition state. Baseline capture also rechecks CharacterForward, movement input, velocity and position against the original stationary baseline; any sampled movement before the callback cancels that pending baseline image even if the actor subsequently returns. If a later transition supersedes a pending image, or any binding changes, the old capture is skipped; it is never relabelled with the newer UI state. Finish and world cleanup cancel pending captures. Paths remain fixed-format `$logs:err-input-<nonce>-baseline`, `-open` and `-closed`. Only an actual delayed screenshot request produces `CAPTURE_REQUEST`; queuing alone does not. A later-frame request still does not prove the file was written or inventory pixels were rendered; an external archive must inspect the actual images. No camera changes accompany capture.
## Integrated hooks and runner
The existing ordinary-only `ERR_OrdinaryScenarioLaunch.c` queues this observer after its genuine validated `PlayerSpawned` admission and clears it in `OnBeforeWorldCleanup`, before the native superclass. Both calls remain inside the existing `#ifndef WORKBENCH` guard; no extra spawn override is installed.
Use `headless_game.py --observe-player-input --settle 50` with loose Core or Chat. The runner supplies its fresh run ID as the nonce and enables the bounded captures. Control/packed modes and shorter settling windows are refused before launch setup. After cleanup it validates the exact nonce, ordered finite samples and clocks, QUEUED/READY/FINISHED lifecycle and any inventory/capture transitions. Delayed captures must reference the current last native sample, occur at least 300 ms after their unique transition and remain within the same transition interval and observation window. Baseline may not cross OPEN or include sampled movement; open may not cross CLOSED; sampled inventory state must remain consistent. Skipped captures are allowed, since superseded or invalid UI states cannot safely be photographed under the earlier label. Missing or cancelled observation fails the opt-in diagnostic, while prior native/display/source failures remain failures. This qualifies a sampler lifecycle only; it cannot by itself prove input delivery or inventory usability. Actual image files require separate inspection.
## Installed API evidence and tests
The implementation reads installed generated `CharacterControllerComponent.GetMovementInput`, `GetVelocity` and `IsUnconscious`; the vanilla character controller already reads local `InputManager.GetActionValue`. Installed `SCR_InventoryMenuUI.GetInventoryMenu` returns the actual inventory menu and `GetCharacterController` returns its bound native character controller. `SCR_StringHelper.Filter` demonstrates native string indexing and `Contains` for the nonce whitelist. The existing successor ordinary screenshot request establishes `System.MakeScreenshot` and native call-queue lifecycle usage. Installed shipping `Core/generated/UI/Widget.c:56` declares `IsVisibleInHierarchy`; `GameLib/generated/Menu/MenuBase.c:36` declares `GetRootWidget`. These read-only native APIs support exact root revalidation for the delayed capture. The separate 160522 ordinary run supplies native compilation and sampler-lifecycle evidence; these API references alone do not.
`tests/test_ordinary_input_observer.py` executes the actual nonce/time/baseline policies, admission checks, queue/sample/menu/capture/cleanup methods with explicit native read-boundary adapters. It covers changed controllers, bodies, roster lives and accounts, mode/world changes, unconsciousness, disabled/invalid flags, menu replacement and root ownership, closed/open/closed sequencing, repeated callbacks, both time bounds, clock reversal, screenshot limits, the 300 ms minimum delay, eventual loading-overlay removal, two separated absent observations, returning-overlay reset, insufficient remaining settling time, perpetual-loading timeout, exact context loss during retries, close supersession, early/expired callbacks, exact menu/root replacement and weak-reference loss, hidden root hierarchies, changed body/account, superseded transitions, cancellation and the retained one-attempt latch. Static checks reject input/menu/actor/camera mutators and identity fields in logging. None of these tests claims native user input, human playtesting, multiplayer, FPS improvement or a completed escape route.
## Native sampler result and separate display failure
The [160522 console](evidence/game-2026-09-19-160522-observer-display-unverified/console.log.txt) contains the exact nonce's QUEUED, READY and FINISHED lifecycle without CANCELLED. READY followed the stationary baseline, and 391 samples were recorded before `FINISHED observation-bound` at 45.014 seconds after queueing. The runner parser reports `sampler_complete: true`, with one baseline capture request and no observed inventory open or close. No movement or inventory input was generated, so this proves the bounded read-only observer ran, not that player controls or inventory interactions succeeded. A logged screenshot request remains distinct from verified image content.
The overall run result remains **`display-probe-incomplete`** because private-display game-window ownership was not verified. Its ordinary startup report has a genuine local participant, the Chat header, normal game modules and zero script/runtime or engine errors; cleanup was confirmed and sources were unchanged. Successful sampler evidence does not erase the independent display-verification failure, authorize later input, or qualify a manual playtest.
The [subsequent ordinary run](evidence/game-2026-09-19-163745-observer-display/report.json) passes overall: 398 samples over 45.096 seconds, a canonical prisoner, verified private game-window ownership and mapped existing focus, zero captured engine errors, and verified idle cleanup. No external input is sent and neither inventory transition is observed. The native baseline screenshot shows the authored prison and readable starting objective. This supersedes the display-verification failure only for the new run; earlier evidence remains unchanged.
The first [real keyboard diagnostics](headless-input-proof.md) observe movement and exact inventory transitions but fail on two stock-inventory slot errors. Later adapter runs described below are separate results. The helper sends inputs; this observer remains read-only. The third diagnostic's sampler ends early under failure grace, so its observed transitions are not promoted to complete sampler or clean runtime acceptance. Its open-labelled screenshot shows the prior world frame and cannot establish inventory visibility.
The [175333 adapter run](evidence/game-2026-09-19-175333-inventory-adapter/runner.json.txt) is clean, but its 300 ms capture shows a loading spinner. The [175938 run](evidence/game-2026-09-19-175938-inventory-loading-bound/runner.json.txt) is clean with no open image; its missing capture does not establish that loading or the overlay was stuck, and its cause remains unproven.
The [180423 inventory-ready run](evidence/game-2026-09-19-180423-inventory-ready/runner.json.txt) supplies native compilation and runtime evidence for the settled capture and telemetry: 401 samples, all six paired keyboard events verified separately, native movement and open/close transitions, no captured errors, verified private-window ownership, confirmed cleanup and unchanged 193-file addon sources. Telemetry progresses from stage 6/five hit-zone points with the overlay present to stage 7/seven points with initialization complete and the overlay absent on two observations. `CAPTURE_REQUEST open` follows 619 ms after observed opening. The archived [open image](evidence/game-2026-09-19-180423-inventory-ready/err-input-a75280fda4494a0ca7b14a693cf310e9-open.bmp) shows character, vicinity and clothing slots with no loading overlay. It does not establish readability of the whole interface or item-transfer behavior. The baseline capture is correctly absent once the player begins moving.
For the loading-overlay investigation, the same opt-in open-capture callback can emit at most 30 separate `ERR INVENTORY OBSERVE:` records. These contain only the run nonce, real tick time, retry/timeout reason, native `m_bProcessInitQueue`, `m_eInitStage`, hit-zone point count (−1 if unavailable), overlay presence and overlay hierarchy visibility. `SCR_InventoryMenuUI.ERR_InputObserverInventoryState()` is a read-only accessor, not a lifecycle override; the caller emits only after revalidating the exact original actor/menu/root/top/visibility. Lost ownership, disabled screenshots or cancelled capture cannot emit these records. The distinct prefix leaves the ordinary input parser contract unchanged. The original three-second capture cap remains in force; telemetry does not advance initialization, hide the overlay, extend the deadline or qualify visibility. Installed stock field declarations are at InventoryMenuUI412/424–425, initialization stages at42, hit-zone initialization at506 and overlay removal at824. The 180423 run demonstrates this telemetry natively; the earlier missing-image run remains unexplained.