Egress hold: back-burner fix plan (Step0 diagnose / Step1 one-const patch / Step3 device fallback)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-09 13:45:23 -05:00
co-authored by Claude Fable 5
parent c93b2ed786
commit 54101957fd
+92
View File
@@ -0,0 +1,92 @@
# Back-burner plan: honor the ~30s customer-egress hold
Status: BACK BURNER (not started). Context in NET-NOTES.md
("EGRESS DELAY = COMPILED IN, TWO-PHASE").
## Problem restated
The pod's StopMission close is a two-stage, re-entrant handler (byte-identical
in BTLIVE May-96 / BTRAVINE Sep-96 / BTDAVE / Rel410, so not a build regression):
- OUTER handler (Rel410 @0x47c2c4, dispatched): first StopMission sets a flag
@[this+0xd4], schedules itself to re-fire in `3.0*timebase + 0.5` (3.0f
literal @0x47c350), returns. The +3s RE-ENTRY runs destructive cleanup
(0x44e13c, 0x44fa6c, base shutdown via 0x44eeb4) then calls the inner handler.
- INNER handler (Rel410 @0x47b864): base shutdown, egress lamps ON
(0x47bba8 flag=1; lamps 0x16/0x17/0x1e), schedules a +30s LightsOut/exit
(30.0f literal @0x47b8e4).
Observed: the process is recycled by GO.BAT in ~3.4s (the 3.0f stage). The
30s LightsOut is scheduled AFTER teardown has begun, so the exe dies before
it fires -- the 30s is effectively dead. Goal: a real ~30s (ideally
configurable) floor-lamps-on customer-egress hold before the pod exits.
Precondition already true: the floor lamp is lit at mission end and through
the wait (tap: FLOOR bright at mission end, off at the ~3.4s teardown), so a
longer PRE-teardown wait = a longer visible lamps-on egress window.
## Step 0 -- diagnose the pre-emption (cheapest, do first)
Confirm WHICH teardown races the 30s and whether it's config-specific before
touching the binary.
- Run a mission and grep nn.log at close for the "Sending EndMission..."
string and any LightsOut trace; timestamp against the RIO-tap lamp events
(scratchpad lamp_read.py) and the netnub relaunch.
- Test a NON-looping boot (single `netnub -p -f btl4opt` with a DOS `pause`
after, NOT loop.bat) so nothing recycles the exe -- if the 30s LightsOut
then fires (lamps stay on ~30s), the pre-emption is the GO.BAT loop timing,
not an in-handler race, and the "fix" may be purely a loop-conf change.
- If lamps still drop at ~3s even without the loop, the destructive cleanup
in the outer handler's re-entry is the culprit -> go to Step 1/2.
## Step 1 -- PREFERRED: one-constant binary patch (extend stage-1 to 30s)
Rationale: the outer handler's FIRST stage schedules +3s and returns WITHOUT
running the destructive cleanup (that's the re-entry). Extending stage-1 from
3s to 30s keeps the floor lamps on (already lit) for ~30s before the
destructive re-entry -- an authentic egress hold, with a single 4-byte edit.
- Target (Rel410): the 3.0f literal at VA 0x47c350 -> patch bytes
`00 00 40 40` (3.0f) to `00 00 F0 41` (30.0f). Backup first
(BTL4OPT.EXE.pre_egress), assert the original bytes before writing, like the
RIO/idle patches. Per-build literal addresses: find via stopmission_cmp3.py
(it prints the outer handler VA per build: BTRAVINE @0x47c2b0,
BTLIVE/BTDAVE @0x48a3c0; the 3.0f `fld` is a few insns in -- confirm with
btdis2.py `dis <handler>` then read the `fld dword ptr [X]` operand).
- Make it CONFIGURABLE without re-patching: better than a hardcoded 30, tie
the value to an env/INI the device already reads, OR expose it via a small
device hook (see Step 3). Simplest first cut = hardcode 30.0f and verify.
- Verify: RIO tap shows FLOOR lamp on for ~30s post-StopMission before the
sweep+exit; console still recovers; net_loop still recycles after.
- Risk: LOW. One literal, reversible. Watch that the scheduler is wall/real
time not frozen sim-time (Step 0 settles this); if sim-time is frozen at
mission end the timer won't advance and this won't help -> Step 2.
## Step 2 -- ALTERNATIVE: reorder so the inner 30s survives
If Step 1's stage-1 timer can't be used (e.g. frozen sim-time), instead make
the inner handler's existing 30s LightsOut fire before teardown: NOP or move
the destructive cleanup calls (0x44e13c/0x44fa6c/base 0x44eeb4) in the outer
re-entry so lamps-on + the 30s schedule run first and the process only exits
when LightsOut fires. Riskier (control-flow surgery, must not leak resources
or hang); needs the disasm of 0x44e13c/0x44fa6c to know what's safe to defer.
## Step 3 -- FALLBACK: device/GO.BAT egress window (no binary patch)
If binary patching is undesirable, synthesize the hold outside the game:
- net_loop.conf: after the game exits and before netnub relaunch, insert a
~30s DOS delay (e.g. a small TSR/CHOICE-style timeout) so the pod idles in
the egress window; the vpxlog/RIO device drives the floor lamp ON for that
window (device already owns the RIO serial path -- add a "hold floor lamp N
seconds on game-exit" hook keyed by an env var, e.g. VWE_EGRESS_MS).
- Pro: fully configurable, no game binary change, works across all builds.
- Con: the lamp is device-driven, not game-driven -- cosmetic only, and the
timing is approximate. Good enough for venue theater.
## Recommendation
Do Step 0 first (may reveal it's just a loop-conf issue = free fix). If a real
delay is needed, Step 1 (one-constant patch, made configurable) is the clean
path; Step 3 is the no-patch fallback. Tools: scratchpad btdis2.py,
stopmission_cmp3.py, lamp_read.py. Existing patch-toolchain precedent + backup
discipline in tesla-cockpit-emulator-state (RIO EXE patches).