Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
164 lines
15 KiB
Markdown
164 lines
15 KiB
Markdown
# Spectator controls and recovery
|
||
|
||
While spectating, **Left / Right arrow** selects an eligible teammate or player
|
||
corpse. **Enter** (including numpad Enter) toggles between orbit and the living
|
||
teammate's sampled camera position, orientation and field of view. Orbit retains
|
||
mouse-look and zoom. This is presentation only: no teammate is possessed.
|
||
`/esc spectate next|previous` remains available without administrator permission.
|
||
|
||
If nobody is available to follow, mouse-look and zoom still orbit the casualty's
|
||
position (or the last known observer anchor), with a 2–12 metre zoom range. This includes
|
||
playing alone, an all-unconscious squad, and a selected entity that has not yet
|
||
streamed in. The camera stays anchored; it does not move the body or allow free
|
||
flight. Menus and chat continue to pause camera input.
|
||
|
||
When the teammate uses a compatible native PiP scope, spectator first-person
|
||
view renders the magnified image inside the visible 3D optic, with the player's
|
||
eye view around it. Both the watched player and spectator must have **2D scopes
|
||
disabled** in their game settings. The watched player must actually be using
|
||
PiP; a flat source view cannot supply the second camera.
|
||
|
||
Standard 2D optics, unsupported PiP materials, a spectator's 2D preference or
|
||
failed PiP setup retain the full-screen scope view, including the authored
|
||
reticle, sampled magnification, zeroing and illumination. Iron sights and
|
||
ordinary first-person poses keep the existing camera relay. Unrelated custom
|
||
scope renderers are not automatically reproduced.
|
||
|
||
Both camera poses and lens state travel in one bounded, authorized packet.
|
||
Eye and aiming poses are interpolated on matching timelines; PiP FOV, parallax
|
||
and lens shading use the aiming timeline too. Displayed weapon identity and
|
||
sight index must match the streamed target. No spectator ADS callbacks, weapon
|
||
input registration or remote zoom changes are invoked. Only the full-screen
|
||
fallback hides/restores the local weapon/optic rendering. PiP uses the native
|
||
local lens-rendering lifecycle and leaves the weapon visible. Switching targets,
|
||
leaving player view, death/recovery, a displaced camera or expired samples closes
|
||
the renderer. Failed setup retries are limited to once every two seconds.
|
||
|
||
PiP adds a second scene render on the spectator's computer. This implementation
|
||
passed focused packet/interpolation/lifecycle checks and a hidden Reforger 1.8
|
||
Workbench compile. Multiplayer visual alignment, addon-specific materials and
|
||
FPS impact remain in-game validation items; these have not been established by
|
||
the earlier camera fixture runs described below.
|
||
|
||
Samples expire after 350 ms; stale or missing views fall back to orbit rather
|
||
than showing a stale scope. Changing between scope and ordinary view snaps to
|
||
the new camera sample, avoiding interpolation through the weapon. Same-view
|
||
updates retain the existing short interpolation.
|
||
|
||
The camera activates `ERR_ObserverContext`, inherited from vanilla's manual-camera context. Dedicated input actions bind the arrows and Enter; normal gameplay input remains inherited from `chimeraInputCommon.conf`. Held keys trigger once; opposing arrows cancel. Camera input is disabled while ordinary menus/dialogs are open. These controls replace raw `Debug.KeyState` polling; the old bit-15 mask is documented by the engine, so inspection alone does not establish it as the cause of the reported missed arrows.
|
||
|
||
Chat's explicit open state now takes priority before the observer activates its
|
||
camera context. The observer renews native `ChatContext` for open registered
|
||
panels, and for a still-open panel found through the focused widget's parents if
|
||
the HUD has unregistered it. This keeps submission available even without the
|
||
usual HUD update. Ordinary edit/password fields and native menus still pause the
|
||
camera; only actual open chat renews chat input. Closing chat retains the existing
|
||
release requirement, so holding Enter or an arrow through submission cannot
|
||
immediately switch view or teammate. Installed defaults open chat with Slash and
|
||
send with Enter; no bindings change.
|
||
|
||
This addresses a possible input failure reported during corpse observation.
|
||
Native `#login` uses the controller's chat component, independently of the mod's
|
||
`/esc` commands and body state. Available client logs do not record whether the
|
||
historical login command was submitted, so the cause of that attempt remains
|
||
unconfirmed. This change neither grants administrator rights nor changes login
|
||
handling. [Input handoff evidence](evidence/observer-chat-2026-10-13/README.md).
|
||
|
||
The 250 ms local monitor records an unconscious controlled body independently of whichever camera is active. When that same body becomes conscious, it asks the native editor manager (Game Master/ArmaVision) to close and explicitly selects the controller's player camera. This also handles a separate manual camera taking over from the mission's observer camera. Asynchronous editor closure or unavailable player cameras are retried for up to ten seconds; refusal is logged rather than permanently forcing the camera. Once returned, conscious admins can open their cameras normally. Round end, body loss, or changed possession cancels the pending handoff. No remote player is possessed and no administrative privileges change.
|
||
|
||
The spectator hint explains why the player is observing and how to switch targets/views. Availability uses the replicated selected entity ID, not local streaming completion. The hint refreshes on first display, a reason change, or having/no selected survivor; stable state does not repeatedly show it. Missing/refusing hint managers retain the retry.
|
||
|
||
## Verification
|
||
|
||
`test_observer_orbit.py`, `test_unconscious_controls.py`, `test_spectator_recovery.py`, `test_spectator_guidance.py`, `test_hint_delivery.py`, and `test_participant_selection.py` exercise production orbit placement/input with and without a target, held/opposing/menu keys, Enter toggling, camera/editor recovery retries and cancellation, guidance, and canonical server selection. Native APIs and remote clients are doubled in these tests.
|
||
|
||
`ERR_UnconsciousCameraProbe` tests native incapacitation, unchanged possession, physical camera tracking, loaded input actions including inherited orbit input, toggling to/from head view, a different manual camera taking over before healing, return to the normal camera, and repeated knockout. It uses a physical stand-in for another player and programmatic input-handler calls; it does not prove physical keyboard delivery or dedicated multiplayer target streaming. Native run results are recorded separately under `.local/analysis/spectator-2026-09-27/`.
|
||
|
||
On 13 October, the anchor-only camera fix passed 38 native assertions, including
|
||
seven new checks for yaw, pitch, zoom, disabled input, unchanged possession and
|
||
application of the resulting pose on subsequent native frames. Previously, all
|
||
orbit input and placement were inside the selected-entity branch, so a solo
|
||
casualty or all-unconscious squad without a corpse to follow had a fixed view.
|
||
The behavioral regression fails on that original branch and passes on the fix.
|
||
The native test supplies action values; physical mouse delivery and multiplayer
|
||
remain unverified. The isolated run moves only the legacy test pickup marker to
|
||
flat ground so unrelated rescue-fleet preparation can finish. There were no
|
||
script/runtime failures; 21 inherited engine/resource errors still fail the
|
||
strict native report. The full repository check remains blocked by the existing
|
||
missing root entity ID in `ERRM_AtlasLeaf.et`.
|
||
[Evidence and limitations](evidence/observer-orbit-2026-10-13/README.md).
|
||
|
||
On 27 September, the isolated native fixture completed 25 assertions without script errors or VM exceptions. It verified both camera views, the loaded controls, a separate manual camera takeover, restoration after healing, and repeated knockout. The runner subsequently hit a transient cleanup-classification failure; separate follow-up confirmed the private runtime idle and authored sources unchanged. Existing external resource errors remain. Raw results and failed diagnostic iterations are preserved in the evidence directory above. Physical keyboard delivery, remote client cycling, and native Game Master/ArmaVision menu closure remain manual multiplayer checks.
|
||
|
||
## Sideways-drift investigation, 29 September 2026
|
||
|
||
The reported first-person drift is not yet reproduced. Temporary diagnostics
|
||
compared the normal local first-person camera with `PlayerView` through scripted
|
||
aim changes and a stationary hold. The [aim comparison](evidence/native-2026-09-29-camera-aim-diagnostic/report.json)
|
||
and [position comparison](evidence/native-2026-09-29-camera-position-diagnostic/report.json)
|
||
both completed the existing 25 camera/recovery assertions with no script/runtime
|
||
failures, unchanged launch sources and confirmed cleanup. Existing inherited
|
||
asset errors remain in the reports.
|
||
|
||
Across the second run's 26 samples the forward vectors matched to logged
|
||
precision; viewpoint position differed because spectator view is offset ahead
|
||
of the animated head. This does not establish the cause of the user's drift.
|
||
Attempts to exercise freelook and leaning through one-shot input-context setters
|
||
did not hold those states (the trace remained at zero lean), so those cases and
|
||
remote-player replication remain unverified. The temporary probe is retained as
|
||
`diagnostic-probe.c.txt` in the second archive and removed from the active addon.
|
||
Those initial diagnostic runs did not change production camera behavior. The
|
||
user subsequently clarified that the symptom affects teammate first-person view,
|
||
possibly sliding left and later over the shoulder.
|
||
|
||
Inspection found a concrete fallback bug: a failed head-pose read fell through
|
||
to orbit placement and orbit mouse/zoom input even with player view selected.
|
||
The camera now caches the last valid pose in target-local space, preventing this
|
||
fallback after tracking has begun. This fixes a plausible path to the reported
|
||
shoulder view; it does not establish that a missing pose caused the user's exact
|
||
multiplayer symptom. Animated head sway remains, including genuine leaning.
|
||
|
||
The production cache regression exercises body translation/rotation, 1,000
|
||
missing samples without accumulating movement, fresh-pose recovery, target
|
||
changes and target loss. The native fixture now deliberately withholds head
|
||
samples while moving its physical stand-in, then restores them and checks that
|
||
head tracking resumes before returning to orbit. This is an injected sampling
|
||
gap, not a reproduced network outage.
|
||
|
||
The [final engine run](evidence/native-2026-09-29-camera-gap-fixed/report.json)
|
||
passed all 27 camera/recovery assertions, including the moving-target pose gap
|
||
and resumed head tracking. Repository checks passed. Addon sources remained
|
||
unchanged during the run and cleanup was confirmed; no script/runtime failures
|
||
occurred. The strict report retains 13 inherited asset errors. An earlier
|
||
[test-hook registration failure](evidence/native-2026-09-29-camera-gap-fixture-diagnostic/report.json)
|
||
is retained; fault injection now lives behind the explicit Workbench fixture
|
||
flag without modding the prefab component's registration. A separate
|
||
[pose diagnostic](evidence/native-2026-09-29-camera-pose-diagnostic/report.json)
|
||
also exercised sustained native leaning without reproducing angular drift;
|
||
its freelook input did not activate, so freelook remains unverified.
|
||
No Workshop publication or live-server update was performed.
|
||
|
||
### Observer monitor ownership (2 October 2026)
|
||
|
||
A retained former local controller now stops its observer callback before it can restore its old player camera, close the editor or spawn a spectator camera for the new local session. It clears only its own timer, pending recovery state and owned observer camera. The current controller's timer and view remain intact; the existing 250 ms binder now remains active until stop/end so a replacement controller starts its monitor once. Repeated checks do not create extra timers. Native manual-camera teardown retains its ordinary previous-camera fallback when the deleted camera itself is current.
|
||
|
||
An actual-method regression independently reproduces the old camera/editor change and passes with the guard. All **279 registered regression programs pass**. The [hidden/muted native fixture](evidence/native-2026-10-02-observer-ownership/report.json) completes **27 assertions and one round** for ordinary unconsciousness, target tracking, both views, a head-pose sampling gap, healing and repeated knockout. It has zero script/runtime failures and thirteen retained installed-resource errors, with unchanged source/external inputs and confirmed cleanup. The initial wrong-selector failure is preserved; `unconscious-camera` now requires `--project core` before runtime setup. Native controller replacement, shared input internals and real multiplayer remain unqualified. This correction does not establish the cause of the previously reported first-person drift. [Review and preservation](evidence/observer-ownership-2026-10-02/review.json).
|
||
|
||
## Recovery warnings while unconscious (2 October 2026)
|
||
|
||
The ordinary phase guide intentionally yields to spectator presentation. The observer now appends the existing replicated `RecoveryStatus`, so an unconscious player receives the same treatment/natural-recovery, possible returning rescuer, or uninterrupted thirty-second medical-defeat warning that conscious players can read in objective details. Medical policy and timing are unchanged.
|
||
|
||
The accepted status joins reason and survivor availability in hint deduplication. A missing hint manager or rejected delivery does not consume the message; an unchanged accepted message does not repeat. Waking, end-of-round cleanup and old-controller ownership checks preserve the existing camera behavior and clear only the owning controller's cache.
|
||
|
||
An actual-method regression fails on the earlier missing-warning behavior and passes with the fix, including status changes, delivery retries and round/controller replacement. The [native camera fixture](evidence/native-2026-10-02-recovery-warning/report.json) accepts the real ongoing-treatment status and clears it on awakening while retaining the earlier camera/recovery checks: 29 assertions, zero script failures and thirteen retained engine errors. This proves the accepted hint path, not full-resolution readability or remote multiplayer presentation. [Review and exact source scope](evidence/recovery-launcher-2026-10-02/review.json).
|
||
|
||
## Dead-player selection (6 October 2026)
|
||
|
||
Camera cycling includes canonical player corpses alongside surviving teammates,
|
||
not only after the last survivor dies. It includes the observer's own body and
|
||
bodies whose player has disconnected. Loss of native controller or faction
|
||
association during corpse cleanup does not remove the round-roster identity.
|
||
Deleted bodies and known foreign-faction bodies remain excluded. A selected
|
||
corpse stays selected during ordinary polling until the player cycles or the
|
||
body is removed.
|