From 373889760c203bbc0adf002a7edd5001349b726f Mon Sep 17 00:00:00 2001 From: arcattack Date: Sat, 25 Jul 2026 18:49:49 -0500 Subject: [PATCH] #66: joyconfig.bat no longer hangs the game -- clear BT_JOYCONFIG once the wizard is done Found while sweeping the shipped launchers for the PATH-shadowing bug class. Previously recorded ONLY as row 10 of docs/INPUT_PATH_AUDIT.md and never filed, so it was invisible on the tracker. joyconfig.bat sets BOTH `BT_JOYCONFIG=1` and `BT_FE_SOLO=1`, so after the wizard writes bindings.txt the boot continues into the FRONT END, which hands off by CreateProcessW()ing the next generation with the CURRENT environment. That clear-list (btl4console.cpp) covers only BT_FE_EGG/PODS/SECS/LOOP -- BT_JOYCONFIG is never cleared -- so the handed-off generation re-entered BTJoyConfigWizard() and blocked on _getch() BEHIND the 3D window: an unrecoverable hang, hitting exactly the players most likely to run that bat (flight stick / HOTAS / pedals). Fix: SetEnvironmentVariableA("BT_JOYCONFIG", NULL) immediately after the wizard returns. Cleared in the WIN32 environment (what CreateProcess inherits, matching the console's own pattern) rather than the CRT copy, so exactly one generation ever runs it. Chosen over extending the console's clear-list because it also covers any future relaunch path. NOT YET REBUILT: the exe was locked by a live play session when this landed. Must be rebuilt + verified (run joyconfig.bat, finish the wizard, confirm no second "=== BattleTech joystick setup ===" banner and that the game proceeds into the menu) before the next zip is cut. Co-Authored-By: Claude Opus 5 (1M context) --- docs/INPUT_PATH_AUDIT.md | 2 +- game/btl4main.cpp | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/INPUT_PATH_AUDIT.md b/docs/INPUT_PATH_AUDIT.md index 2e2ef74..9cf143c 100644 --- a/docs/INPUT_PATH_AUDIT.md +++ b/docs/INPUT_PATH_AUDIT.md @@ -78,7 +78,7 @@ Ranked by impact. **Reverse thrust `0x3F` (key LALT / pad B) is FIXED** and conf | 7 | **Torso recenter from the keyboard** (`0x42` is pad/mouse only) | Center a twisted torso — the manual's anti-disorientation control (p9) | `0x42` itself is **LIVE** (streamed `subsys 17 attr 14 -> +0x208` = `Torso::centerCommand`, consumed `torso.cpp:636-640`) but `content/bindings.txt:83,86` bind it only on `pad Y`/`DPAD_UP`. The port's keyboard path (`gBTTorsoRecenter`, `mech4.cpp:2875`) is fed from the zeroed `gBTInput` and its consumer sits inside the stood-down bridge (`mechmppr.cpp:854-863`, gate `:655-660`) | **S** | Twist with `E`, press `X`: `currentTwist` does not return to 0. Add `key X button 0x42` (two rows on one VK both fire — `L4PADRIO.cpp:580-629` iterates all bindings) | | 8 | **Any clickable button latches if the mouse-up is lost** (worst on `0x3F` reverse, `0x40` trigger) | A momentary click always releases | Neither WndProc handles `WM_CAPTURECHANGED` or `WM_KILLFOCUS` — grep over `engine/` + `game/` returns **zero** hits. Alt-tab or a system dialog between press and release leaves `gCkPressed` set and `SetScreenButton(a,0)` never runs | **M** — trap: `btl4main.cpp:137` calls `ReleaseCapture()` itself, which re-enters `WM_CAPTURECHANGED`; guard on `gCkPressed != -1` (it is cleared before `ReleaseCapture`) | Click-hold `0x3F`, alt-tab, release outside the window: `BT_PAD_LOG=1` shows no release line and `rev` stays 1 | | 9 | **Every HOTAS / flight stick / rudder** (whole `joydev/joyaxis/joybutton/joyhat` grammar) | Drive the pod channels from a DirectInput device | `content/bindings.txt` is 89 lines and has **zero** joy rows; the joy poll is gated on `joyAxisBindingCount>0 \|\| …` (`L4PADRIO.cpp:775-777`) so `BTJoyInit/BTJoyPoll` are never called. `PadBindingProfile::Load` writes the default only when the file is **absent** (`L4PADBINDINGS.cpp:708-713`), and the file is gitignored (`.gitignore:23`) — so the zip ships **this machine's stale file** | **S** now (ship without it / refresh it) · **M** later (version marker + additive merge) | `content/jstest.log`: `bindings loaded: 40 keys, 10 pad buttons, 5 pad axes, 0 joy axes…`; no `[joy]` line exists in any log in `content/` | -| 10 | **`joyconfig.bat` re-runs the wizard after it finishes** | Run once, then play | `BT_JOYCONFIG` is never cleared (`game/btl4main.cpp:287-291`), and the front end relaunches the exe inheriting the environment (`btl4console.cpp:138-174` clears only `BT_FE_EGG/PODS/SECS/LOOP`) → the mission process re-enters the wizard and blocks on `_getch()` behind the 3D window | **S** (1 line) | Run `joyconfig.bat`, finish it, watch for a second `=== BattleTech joystick setup ===` banner | +| 10 | **`joyconfig.bat` re-runs the wizard after it finishes** -- ✅ **FIXED + FILED AS #66 (2026-07-25)** | Run once, then play | `BT_JOYCONFIG` is never cleared (`game/btl4main.cpp:287-291`), and the front end relaunches the exe inheriting the environment (`btl4console.cpp:138-174` clears only `BT_FE_EGG/PODS/SECS/LOOP`) → the mission process re-enters the wizard and blocks on `_getch()` behind the 3D window | **S** (1 line) | Run `joyconfig.bat`, finish it, watch for a second `=== BattleTech joystick setup ===` banner | | 11 | **`0x14`** Searchlight (+Searchlight2) | Turn the light on for night maps | ✅ **FIXED 2026-07-25 (#61).** `Searchlight::MessageHandlers` was default-constructed, so the `ToggleLamp` body was unreachable; it now chains `PowerWatcher::GetMessageHandlers()` with id 3. **My "wiring it changes nothing visible" verdict in this row was WRONG** — it rested on the swapped attribution this same report flags in §Corrections: @004b860c (which writes `commandedOn@0x1DC`) is **ThermalSight's** handler, not Searchlight's. Searchlight's is **@004b838c** → toggles `requestedOn@0x1E0`, the field its own Performance reads. There was never a 1995 latent bug | **S — done** | **Verified live**: `BT_BTNTEST=0x14,400,900` → `[light] requested ON` then `[light] reported lightState 0 -> 1`. The lamp lights. (`scratchpad/lamptest.py`) | | 12 | **`0x12`** Thermal sight | IR view | ✅ **handler + body FIXED 2026-07-25 (#61).** `ThermalSight::MessageHandlers` was default-constructed AND it had no `ToggleLamp` at all — the exported body @004b860c had been transcribed into `searchlight.cpp` by mistake. Both now correct: id 3 → `requestedOn@0x1DC`, read by `ThermalSightSimulation` @004b8648. ⚠ Still **no visible IR effect**: `ToggleGlobalThermalVision()` is a marked no-op and the thermal render path (pvision palette flip) is unported, and `IsLocallyViewed()` returns False | **S done** · **L** for the renderer | **Verified live**: `BT_BTNTEST=0x12,400,900` → `[light] thermal sight requested ON` then `reported thermalActive 0 -> 1`. State machine + alarm authentic; screen unchanged (`scratchpad/thermtest.py`) | | 13 | **Numpad cluster with NumLock OFF** | Aim the torso | `L4PADRIO.cpp:583-584` reads `VK_NUMPAD8..9` verbatim; with NumLock off Windows delivers `VK_UP`/`VK_DOWN`/`VK_LEFT`/`VK_RIGHT`, which `bindings.txt:26,27,30,31` map to **throttle and pedals** — "aim up" accelerates the mech. `NUMPAD5/7/9` → `VK_CLEAR/HOME/PRIOR`, unbound | **S** (startup warning) · **M** (extended-key flag) | NumLock off, hold NUMPAD8 with `BT_MPPR_TRACE=1`: `thr=` rises | diff --git a/game/btl4main.cpp b/game/btl4main.cpp index 5b8b611..d60e432 100644 --- a/game/btl4main.cpp +++ b/game/btl4main.cpp @@ -288,6 +288,18 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine { extern int BTJoyConfigWizard(void); BTJoyConfigWizard(); + // + // ONE-SHOT (input-path audit row 10): joyconfig.bat sets BT_JOYCONFIG=1 + // *and* BT_FE_SOLO=1, so the wizard's process hands off to the front end, + // which CreateProcess()es the next generation with the CURRENT environment + // (btl4console.cpp clears only BT_FE_EGG/PODS/SECS/LOOP). The child then + // re-entered the wizard and blocked on console input BEHIND the 3D window + // -- an unrecoverable hang for anyone who configures a joystick. Clear it + // in the Win32 environment (what CreateProcess inherits, matching the + // console's own SetEnvironmentVariableA pattern) the moment the wizard is + // done, so exactly one generation ever runs it. + // + SetEnvironmentVariableA("BT_JOYCONFIG", NULL); } // MP wire-format probe (env BT_NET_PROBE=1): print the exact packet constants