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

7.5 KiB
Raw Blame History

Opt-in ordinary gate action observation

This is a read-only diagnostic for a future private ordinary-input prison test. It is not enabled or natively qualified by this source change. It sends no input, selects no action, invokes no action completion and changes no actor, camera, gate, phase or denial text.

ERR_OrdinaryGateObserver.c is excluded from Workbench. Its static Sample(mode, world, controller, actor, nonce, started, index, now) is designed for the existing ordinary input observer immediately after a validated sample, with the same original process-millisecond start and consecutive sample index. Activation requires -errOrdinaryGateObserve NONCE, matching -errOrdinaryInputObserve NONCE, -errOrdinaryScenario core|chat, local server RplMode.None, and a non-console process. No timer or new observation budget is created: at most 450 consecutive samples within the original 45 seconds; one attempted binding per process.

The caller currently stops outside PRISON. That remains unchanged. The new utility accepts PRISON and RADIO_SEARCH, but recording a successful native unlock transition would require a separately reviewed change to the caller’s lifetime/phase checks. An approach-only test can remain entirely in PRISON. Later inventory/menu opening permanently cancels this observer; it does not cancel the main input observer.

Native API basis

The installed generated UserAction/BaseInteractionHandlerComponent.c exposes GetSelectedAction; ExtBaseInteractionHandlerComponent.c exposes GetCurrentContext, GetControlledEntity and GetFilteredActions. BaseActionsManagerComponent.c exposes GetContext and FindAction. The observer pins the original registered, intact mode.PrisonEscape().Gate(), its manager, SCR_DoorUserAction context, native door component and exactly one context-owned ERR_UnlockPrisonAction. Its native duration must remain four seconds, matching the authored Prefabs/ERR/PrisonDoor.et.

Installed Interactions/SCR_InteractionHandlerComponent.c stores actual accumulated duration in protected m_fCurrentProgress, along with m_bIsPerforming, m_pLastUserAction and m_pLastContext. The new file adds only a read accessor through a modded class; it overrides no callbacks. Progress is returned only when the native selection and context match the pinned action/context, the last action matches, and an ongoing action also has the original last context. Unrelated/stale progress becomes progressBound=0 progress=0 performing=0. Calling BaseUserAction.GetActionProgress(0, 0) would not retrieve accumulated progress and is deliberately avoided.

cachedPerformable reports the existing native filtered-list boolean, not a new permission decision. Vanilla explicitly warns this cache may be stale. Calling successor CanBePerformed would update its denial reason, so the observer never does so. A future input helper must treat cached permission as one necessary observation, not authorization by itself.

Generated UserActionContext.c explicitly documents GetOrigin() as world space. GetRadius, BaseDoorComponent.GetNormalizedDoorState and GetControlValue are native getters. Prison center comes from mode.PrisonPosition(), gate origin from the pinned entity, and gateForward is normalized native GetTransform()[2]; no historical coordinates are substituted. These measurements do not prove obstacle clearance or safe walking.

Record contract

All records carry run=NONCE sample=INDEX real=PROCESS_MS. The first valid sample emits:

ERR GATE OBSERVE: run=N sample=I real=T BASIS registered-prison-lock-native-interaction
ERR GATE OBSERVE: run=N sample=I real=T SAMPLE phase=P selected=B context=B cachedPerformable=B progressBound=B performing=B progress=F duration=F pressed=F
ERR GATE OBSERVE: run=N sample=I real=T GEOMETRY body=<x,y,z> context=<x,y,z> radius=F doorState=F doorControl=F prison=<x,y,z> gate=<x,y,z> gateForward=<x,y,z>

SAMPLE and GEOMETRY repeat together. pressed is native PerformAction input value. selected identifies the exact registered lock action; context identifies its exact native context. The BASIS records source-enforced identity pinning, not a synthetic entity identifier. A consumer must join records with the same nonce/index/time and independently constrain geometry and input. Selection, progress or phase alone does not demonstrate physical escape.

Loss of the original world, mode, controller, roster life/account, conscious on-foot actor, gate/action/context/component identity, menu-free state, explicit opt-in, sample/clock continuity or finite native geometry emits one terminal record:

ERR GATE OBSERVE: run=N sample=I real=T CANCELLED reason

The observer never adopts a replacement and never revives. Destroying the gate cancels this intact-lock diagnostic; the separate destroyed-gate progression fixture covers that path.

Verification

python tests/test_ordinary_gate_observer.py executes the actual Enforce method bodies as C++ against fake native boundaries. It covers real progress-field attribution, stale selection/context rejection, cached-array mismatch, absent/ambiguous original registration, account/body/controller/registry replacement, menu/vehicle/phase loss, changes during sampling, explicit scope, original time/index limits, finite geometry and permanent cancellation. It is policy coverage, not native compilation, ordinary input, multiplayer or a completed escape proof. The next native check must confirm this installed handler’s selection/progress and geometry at runtime before any gate input is enabled.

First native observation

Ordinary Chat184437 compiles and emits59 gate samples before inventory opening cancels the gate observer. Independent validation passes41 joined READY-through-last observations with zero static-geometry drift. Native context is (6092.77,213.226,7154), radius2, gate (6090,211.344,7154), prison center (6090,211.444,7160), duration4. The lock is not selected at the distant spawn; no hold was sent. Same-API gate/input position agrees within1cm; the camera observer's transform translation trails GetOrigin by1–3cm while walking, bounded to5cm in the join. No broader route/action proof is inferred.

Optional native action-choice diagnosis

--observe-prison-choices requires the existing gate/input observers and forwards the same nonce as errOrdinaryGateChoices. With input testing it additionally requires the explicit approach mode. The diagnostic calls only native identity/selected-action/filtered-cache getters; it logs at most eight ordered classes/IDs and cached permissions, under a separate ERR GATE CHOICE prefix. Normal SAMPLE/GEOMETRY and default observers remain unchanged. ordinary_gate_choices.py joins optional records to raw input/camera/gate samples by nonce, index and native clock.

The 194928 diagnosis parses353 native records. Samples304–353 show exactly disabled ERR_PrisonDoorAction ID1 first, followed by permitted ERR_UnlockPrisonAction ID0; the selected action is the disabled door. The exact gate context and lock permission are present. The original alignment runner remains failed because it never selected the lock; no wheel/F input was sent. Future selection must pin the original same-run IDs rather than treat these per-instance native numbers as universal constants. Changing action visibility, duration or interaction geometry to force this test is unnecessary.