Harden participant and map ownership and validate bounded physical flight
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
# Native shared-map replacement fixture
|
||||
|
||||
`addons/map/Scripts/Game/ERRMap/ERRM_NativeMapOwnershipProbe.c` is entirely
|
||||
`WORKBENCH` guarded and inert unless `-errSmoke 1 -errSmokeOutcome map-ownership`
|
||||
selects it. It does not change production map code, inventory, player state or
|
||||
settings. The first native execution compiled and passed both replacement/callback
|
||||
sequences but failed its immediate deletion assertion. It remains a failed run;
|
||||
the revised bounded deletion observation passed in native run 12:06:33. Offline regressions are
|
||||
not native rendering or multiplayer evidence.
|
||||
|
||||
Chat smoke integration: after the real local player and map entity initialize,
|
||||
retain `ref ERRM_NativeMapOwnershipProbe`, create it without constructor arguments,
|
||||
and call `Tick(GetWorld().GetWorldTime())` on the existing smoke cadence. Return
|
||||
`0` means pending, `-1` failed, `1` complete with owned cleanup finished. Call
|
||||
`Stop()` during smoke teardown and release the reference. The caller owns the
|
||||
final smoke completion marker. Add the outcome to the Chat runner and the test
|
||||
`tests/test_native_shared_map_ownership.py` to the repository check list. The
|
||||
fixture's own deadline is 15 seconds after first tick; normal stages take about
|
||||
9 seconds with a 500 ms caller cadence, with at most three seconds allowed for
|
||||
native deletion before the final 1.5 second callback window.
|
||||
|
||||
The fixture refuses an already-open map or a current camera other than the
|
||||
actual local player's camera. It spawns two real successor map prefabs next to
|
||||
that player, without inserting them into anyone's inventory. Their real
|
||||
components initialize their own render roots. A third hidden root is used for
|
||||
the replacement view. All native map opens/closes target these verified roots;
|
||||
no menu is opened and no synthetic map component replaces the actual prefab.
|
||||
|
||||
First, the production capture method opens the owned root. The fixture closes
|
||||
only that verified view and opens a distinct native `MapConfiguration` pointing
|
||||
to the hidden replacement root. The distinct config copies the native plain
|
||||
config's fields, retaining the same settings/module references; it does not edit
|
||||
the resource or invoke fullscreen-specific UI modules. The second pass calls
|
||||
native `SetupMapConfig(PLAIN, ..., replacementRoot)` and requires that vanilla
|
||||
reuse the **same config object** while changing its root. This is the ownership
|
||||
case that a config-pointer-only check misses.
|
||||
|
||||
For each pass the fixture waits for the actual native map-open-complete event.
|
||||
It checks native current pan and target zoom around the real stale centering
|
||||
method, then schedules the actual production center and finish callbacks through
|
||||
`ScriptCallQueue`. WORKBENCH adapters count their execution and always call
|
||||
`super`; the fixture fails if either callback never ran or ran twice. Native
|
||||
map-close events, active config/root, pan, zoom and successor completion calls
|
||||
are checked after the callbacks. The ordinary production stop and real vanilla
|
||||
`ModeClear(IN_HAND)` chain then run. Both actual map entities are deleted,
|
||||
including the never-capturing spare, while the replacement remains open. Each
|
||||
delete request is issued exactly once. The probe waits at most three seconds
|
||||
for **all four** native entity/component references to become null, plus exactly
|
||||
one observed `OnDelete` completion per tagged component **after** the complete
|
||||
successor/vanilla `super.OnDelete(owner)` chain returns. The observer also checks
|
||||
that production released its root, target, capture references and in-hand state;
|
||||
it never clears those fields itself. Missing/duplicate callbacks, remaining
|
||||
references or cleanup fields fail. A final 1.5 second observation period begins
|
||||
only after this proof, rejecting late close/restore/completion callbacks.
|
||||
Stop removes only this probe's subscriptions, entities and widgets, and closes
|
||||
the native map only if its exact current config/root is still the replacement.
|
||||
It does not restore or close an unrelated view that took over during a failure.
|
||||
|
||||
The original 100/500 ms capture callbacks are deliberately removed only on the
|
||||
fixture-owned component and rescheduled at 100/200 ms **after** the replacement's
|
||||
native initialization. This ensures a stale pan would affect a live map, rather
|
||||
than passing because vanilla ignored a pan before initialization. Production
|
||||
cadence outside these two owned prefabs is unchanged. The fixture invokes the
|
||||
same stale camera-restore method explicitly, verifying that its authorization
|
||||
rejects the replacement; it never intercepts or suppresses a native operation.
|
||||
|
||||
Camera evidence has a precise limit. Installed `PlayerController` exposes
|
||||
`bool SetCharacterCameraRenderActive(bool active)` but no render-enabled getter;
|
||||
`PlayerCamera`/`CameraBase` also provide no such getter. This fixture therefore
|
||||
records successor restore-method calls and their exact ownership authorization,
|
||||
and actual native map-close events. It does **not** claim a measured camera render
|
||||
flag or visual screenshot. Native `OpenMap` and `CloseMap` retain their normal
|
||||
camera behavior, and no guessed API or global camera override is introduced.
|
||||
The ordinary actor's camera identity is checked, not its undocumented render
|
||||
state. This remains a private diagnostic UI exercise, not human interaction or
|
||||
remote/JIP qualification.
|
||||
|
||||
Installed-source audit (SHA-256 of the extracted read-only files):
|
||||
|
||||
| Source | Relevant behavior | SHA-256 |
|
||||
| --- | --- | --- |
|
||||
| `scripts/Game/Map/SCR_MapEntity.c` | `SetupMapConfig` reuses equal-mode config/root; `OpenMap`/`CloseMap` perform native camera changes; map-open-complete, pan/zoom and current-root APIs | `09ca0631ce2f5a196b7074e12a67c08255b7119b9600da635894097b35e4ad92` |
|
||||
| `scripts/Game/Components/Gadgets/SCR_MapGadgetComponent.c` | no deletion override; `ModeClear` only unfocuses an in-hand map belonging to the local actor when actually focused | `3bfc0b6715a173654a3f9ecc5a4538be508905eb9d50aef82cb80262950fec9e` |
|
||||
| `scripts/Game/Components/Gadgets/SCR_GadgetComponent.c` | `OnDelete` disconnects from gadget system then delegates; ordinary `ModeClear` only removes its own loadout subscription | `015c18b4c94245e72c923e718b74cf933cd516c0a9dc24946735482c2e4e191b` |
|
||||
| `scripts/Game/generated/Player/PlayerController.c` | native camera-render setter without getter | `832c5925edbd41e05c469803abdea3b202c1de58d58251d77a8a7e136bdb7edc` |
|
||||
| `scripts/GameCode/Map/MapConfiguration.c` | exact fields used by the distinct plain-config copy | `4ceb904d36c7e466ed5e02e7d72fe24944fb6dd4f7cae07809a2428fdf4d234e` |
|
||||
|
||||
An unused/spare map has no vanilla character owner or focused state, so its real
|
||||
superclass cleanup cannot schedule native `ToggleMapGadget(false)`. A previously
|
||||
focused stock map has a separate native delayed focus/menu path and is outside
|
||||
this fixture's claim. The tests execute the actual fixture cleanup and evidence
|
||||
decisions against explicit API doubles, including foreign config/root, changed
|
||||
singleton, duplicate cleanup, unrelated observer ownership, missing callbacks,
|
||||
changed pan and close/reopen rejection.
|
||||
|
||||
## First deletion observation and bounded follow-up
|
||||
|
||||
[Native run 2026-09-19 11:56:46](evidence/native-2026-09-19-115646-map-deletion-refusal/console.log.txt)
|
||||
passed both actual map replacement sequences, native open-complete, delayed
|
||||
callback execution, unchanged pan/zoom and actual stop/mode-clear checks. At
|
||||
11:57:47.732 it failed immediately after requesting the two deletions, before
|
||||
waiting a native frame. That record does not identify which reference remained
|
||||
or whether either `OnDelete` had run, so deferred deletion is a hypothesis, not
|
||||
a proven explanation or a reason to relabel the failed run.
|
||||
|
||||
Installed `scripts/Game/Helpers/SCR_EntityHelper.c:177` is only a wrapper for
|
||||
`RplComponent.DeleteRplEntity(entity, false)`. The generated native contract
|
||||
describes authority/proxy physical deletion, but promises no synchronous
|
||||
reference invalidation before the method returns. Source SHA-256 values:
|
||||
`31a7f7651237cd859400774fb6be62a0079f204e593831db55093ab59c69812c`
|
||||
(helper) and
|
||||
`0bdf89c3c65e23fa3cf1cab27f8899fd89f3c5fbb6e6c3431f5fdb007590f34c`
|
||||
(`scripts/Game/generated/Components/RplComponent.c`). The revised probe logs
|
||||
non-sensitive reference-presence booleans and callback counts at request return,
|
||||
at actual post-super cleanup, and at later completion or timeout. These
|
||||
observations distinguish delayed invalidation from incomplete cleanup without
|
||||
manual reference clearing, repeated delete requests, weakened acceptance or
|
||||
changing production deletion behavior. The offline regression now exercises
|
||||
request-versus-completion separation, exact once-only requests, missing/duplicate
|
||||
post-super evidence and retained component references.
|
||||
|
||||
## Native deletion diagnosis and scoped pass
|
||||
|
||||
[Native run 2026-09-19 12:06:33](evidence/native-2026-09-19-120633-map-ownership/console.log.txt)
|
||||
passed the complete fixture. At 12:07:34.567 both tagged components reported
|
||||
`OnDelete` **after super** with `released=1`, one callback each; both entity
|
||||
references were already null, but both component references still existed. At
|
||||
12:07:35.069 the next probe tick found all four references null, with no duplicate
|
||||
callbacks or leftover production state. The unchanged 1.5 second observation
|
||||
window passed at 12:07:36.582. This establishes delayed component-reference
|
||||
invalidation as the cause of the original same-stack assertion failure; actual
|
||||
superclass cleanup was already synchronous in this observed run.
|
||||
|
||||
This evidence covers the two real replacement/config-root cases, real queued
|
||||
callbacks and native prefab deletion in the isolated local Workbench runtime.
|
||||
It does not establish a measured camera render flag, remote/JIP behavior, a
|
||||
previously focused native map's unrelated menu callbacks, or performance. The
|
||||
11:56:46 attempt remains recorded as failed.
|
||||
Reference in New Issue
Block a user