5.9 KiB
Back-burner plan: honor the ~30s customer-egress hold
Status: BACK BURNER. Step 0 DONE 2026-07-10 -> Step 1 is the confirmed fix. Context in NET-NOTES.md ("EGRESS DELAY = COMPILED IN, TWO-PHASE").
Step 0 RESULT (2026-07-10, net_full.conf single-shot, RIO tap)
Booted single-shot (netnub then DOS pause, NO loop.bat). Floor lamp:
mission-end bright @108.8s -> off @112.7s = 3.9s window, then the game
(btl4opt) EXITED on its own to the DOS pause (dosbox left at COMMAND).
So the ~3s egress is the GAME'S OWN behavior, NOT a GO.BAT-loop artifact --
the process exits AT the stage-1 (3.0f) timer; the inner 30s timer is never
reached. Entry lighting this run: 77.4s->93.3s = 15.9s (varies with boot).
=> Not a free conf-fix. But since the lamps stay on for exactly the stage-1
duration and the exit fires when it elapses, Step 1 (extend the 3.0f
constant to 30.0f) directly extends the honored hold to ~30s. Proceed to
Step 1 when picked up.
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 btl4optwith a DOSpauseafter, 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) to00 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.0ffldis a few insns in -- confirm with btdis2.pydis <handler>then read thefld 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).