Commit Graph
14 Commits
Author SHA1 Message Date
Joe DiPrimaandClaude Fable 5 2cbf9cd88f mkdist: archive the .map beside the .pdb, and stop shipping d3dx9_43.dll twice
Symbols only matter if they survive the build that produced them, so archive
BOTH per version, not just the PDB. The .map is the one that gets used in
practice: tools/symcrash.py resolves btl4+0xNNNN from plain text with no
debugger installed, which is the normal state of an operator box -- this
machine has no cdb either. Warns loudly if either file is missing, since that
silently means a crash in that build can never be read.

Also: d3dx9_43.dll was written into the zip twice. It exists both next to the
exe and in redist/, and both paths land at build/Release/<name> in the archive,
so the entry was duplicated -- about 2 MB wasted and a duplicate-name warning
from Python's zipfile that some extractors also complain about. Redist entries
now skip anything already taken from beside the exe.

Neither the pdb nor the map can reach a zip: .gitignore covers *.pdb and dist/,
and the archive only ever picks up .dll from those directories.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 20:14:22 -05:00
arcattackandClaude Opus 5 12d6862c1f keylight: unship it -- opt-in (BT_KEYLIGHT=1), gone from all player docs
Operator decision: the player base is not on Windows 11, so the RGB keyboard
mirror should not be shipped or promised.  The feature is NOT deleted -- Cyd's
implementation stays intact behind the gate for whoever wants it -- it is
simply off by default and invisible:

  * PadRIO gate flips from opt-out (BT_KEYLIGHT=0 disables) to OPT-IN
    (BT_KEYLIGHT=1 enables).  Default boots produce zero keylight lines, and a
    stub build and a real build now behave identically for players: nothing.
  * Removed from every user-facing artifact: the README paragraph, the
    CONTROLS.md section, the CONTROLS.html footer line.
  * environ.ini template: the entry stays (it is the discoverable home for the
    opt-in) but now says OFF-unless-1, Windows 11 22H2+, and why it is
    unshipped.
  * mkdist's cut-time stub warning removed -- it existed to protect a README
    promise that no longer exists.
  * context/glass-cockpit.md records the decision, the opt-in, and the
    build-time SDK stub gate in one place.

VERIFIED: default boot logs zero keylight lines; BT_KEYLIGHT=1 engages the
feature (the stub on this machine: its one honest log line + the key map).
All three console suites pass; checkctx CLEAN.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 17:06:00 -05:00
arcattackandClaude Opus 5 c4202d92ca zip-doc audit: last stale key claim + a keylight-stub warning at cut time
Full audit of every user-facing artifact in a freshly cut zip (33 checks, all
passing -- README substitutions/controls/migration story, CONTROLS.txt ASCII
flatten, CONTROLS.html loader + wrap, launchers byte-identical, exe carries the
migration):

  * README's intro still said 'Press V any time to toggle the external camera'
    -- the one key claim outside the rewritten controls block, stale for every
    fresh/migrated install since the board took V.  Backtick now.
  * mkdist warns at cut time when the exe carries the KEYLIGHT STUB (build
    machine's SDK < 10.0.22000), because the README promises the RGB feature
    -- nobody should ship a stub build believing that promise ships with it.

The audit zip itself was deleted after verification: its version stamp predates
today's doc commits, and a release cut should rebuild the exe at final HEAD
first (the 554 procedure).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 16:51:17 -05:00
CydandClaude Opus 5 5f88a4eeb2 The keyboard becomes the button board (RP412 bindings port) + a controls page
Ported RP412's bindings design and made it the DEFAULT: the letter and number
rows are the MFD button banks laid out WHERE THEY SIT ON THE PANEL, flight
moves to the numpad so the board stays free, F1-F12 are the map's two columns,
and G/B stay unbound as the physical gap between the lower clusters.  Expressed
in BT's OWN grammar (slew/deflect/set, the Turn channel) -- bindings.txt is a
documented compatibility surface and was not touched.

Coverage: 61 of 72 addresses on the keyboard, and the 11 absent ones are
exactly those the pod never wired (0x16/0x17/0x1E/0x1F column gaps, 0x38-0x3E
intercom/door).  All 72 stay clickable on the panel.

It lands on BT's addresses unreasonably well: 1-4 + QWER are the ENTIRE coolant
system (Condensers 1-6, flush, balance), F6/F7 the display and control-mode
cycles, F9-F12 Generators A-D, F4 the crouch button reconstructed earlier
today.

THE DELIBERATE COST.  A bound key is removed from the authentic 1995
typed-hotkey channel so it cannot double-dispatch, and this board binds nearly
everything -- so 5 (Quad page), z (Eng1), t/y/u/i/o (pilot select) and +/-
(target zoom) are given up.  Unbind a key to get its 1995 meaning back.
Documented in the file header, the markdown and the page.

NEW RULE: A BINDINGS ROW WINS OVER A BUILT-IN CONVENIENCE KEY
(PadRIO::KeyHasBinding).  V and J/K/L are board buttons now, and those polls
read GetAsyncKeyState directly -- without this they would have fired BOTH the
button and the built-in.  View toggle lives on BACKTICK alone.

CONTROLS.MAP rewritten to mirror the board so glass/pod/dev still feel
identical (the 2026-07-21 settlement): 90 bindings, 0 parse complaints.

HAT LABELS CORRECTED [T1].  INPUT_PATH_AUDIT flagged 0x41-0x44 and was right.
Settled from the streamed mapping (BT_CTRLMAP_LOG): elem 66 -> subsys 17
attrID 14, i.e. 0x42 is the TORSO subsystem, not a look; 0x44/0x43/0x41 are the
mapper's LookLeft/LookRight/LookBehind (attrID 10/11/12).  The .RES has no
"TORSO CENTER" string -- its names are LookBehind/Down/Forward/Left/Right -- so
the audit's wording was loose but its substance correct.  Swept L4GLASSWIN,
L4PADPANEL and L4VB16.

docs/CONTROLS.html: interactive keyboard (hover a key for its address and
meaning, MFD clusters outlined), pad diagram, panel map, the under-glass
press-target figure, radar placement thumbnails and the environ.ini table --
modelled on RP412's page, rewritten for BT's addresses and hazards.  mkdist
wraps the fragment in a doctype/charset shell and ships it beside CONTROLS.txt.

Verified: bindings.txt regenerates and loads (74 keys, 0 errors); CONTROLS.MAP
90 bindings, 0 errors; 72/72 panel addresses still dispatch; surround /
exploded / dock / pod / dev boot and simulate with zero faults.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 03:23:27 -05:00
CydandClaude Opus 5 02b1b50e45 RGB keyboard lamp mirror + the shipped controls reference
KEYBOARD LAMP MIRROR (L4KEYLIGHT).  Ported from RP412, itself a port of vRIO's
KeyboardLampMirror.  Keys bound to a lamp address in bindings.txt glow with the
panel palette through Windows Dynamic Lighting -- yellow for the map's side
columns (0x10-0x1F), red for the rest -- flashing in step with the on-screen
buttons (its LampLevel copy matches the FIXED BTLampBrightnessOf).  Per-key
boards light each bound key; zone-lit boards mirror the strongest lamp
board-wide.  All WinRT runs on a private worker thread: watcher, claim, and a
100 ms paint loop that repaints only on change.

RP412's packing hazard does NOT transfer: it forces default struct packing there
because its engine is /Zp1, which would break the WinRT ABI.  BT411's BT_OPTS
carries no /Zp, so only the dialect flags are needed -- /std:c++17
/permissive- per-file, since the project otherwise builds C++14 /permissive.
The scalars-only interface is kept anyway so the isolation survives if packing
is ever added.

Wired in PadRIO: map built from bindings.keyBindings (ActionButton binds only,
first-binding-wins per key), fed from PadRIO::SetLamp, stopped in the dtor
(which hands the LEDs back to Windows).  BT_KEYLIGHT=0 opts out; a machine
without Dynamic Lighting logs once and stays dormant.

Verified live: claimed this machine's 24-zone keyboard and mirrors 25 bound
keys; BT_KEYLIGHT=0 and the pod profile produce zero keylight lines and run
clean.

SHIPPED CONTROLS REFERENCE.  docs/CONTROLS.md carries the keyboard table plus
the full 72-BUTTON POD MAP, grouped by the display each bank surrounds, with the
coolant-valve detent warning (1-5-50-CLOSED, one press past max shuts the loop)
and the jam/eject procedure.  mkdist flattens it to ASCII as CONTROLS.txt at the
zip root, the README's own idiom.  players/README.txt gained pointers to it, to
environ.ini, to -fit, and to the RGB mirror.

KB: context/glass-cockpit.md and docs/GLASS_COCKPIT.md updated for the whole
branch -- layout modes, L4RIOBANK and the under-glass rule, the letterbox fit
and its ordering trap, player-tunable displays, the measured legend grid, the
closed dead-button backlog, and this mirror.

Verified: five-mode regression (surround/exploded/dock/pod/dev) boots and
simulates with zero faults; mkdist writes a 5149-byte pure-ASCII CONTROLS.txt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 02:24:30 -05:00
arcattackandClaude Opus 4.8 f7daf03507 Crash self-report + round-completed latch + PDB archiving
CRASH SELF-REPORT (the #35/#41 heisenbug hunt): every unhandled
exception now logs its code, address, and an EBP-walked stack as
module-relative offsets (btl4+0xNNNN) into BT_LOG before dying --
any field crash on any machine hands us a resolvable stack.  The
walker is per-dereference guarded (a corrupt chain truncates).
BT_CRASHTEST=1 exercises the path end-to-end (verified: forced AV
logs code/addr/5-frame stack).  mkdist archives each build's PDB
next to its zip so offsets resolve per distributed version.

Context: a flaky release-only crash in the mech build path surfaced
during #38 rigs -- it moves with build layout, spares neither peer,
and masks under a debugger heap (13 clean cdb runs vs 2/3 crashes
without).  A real latent memory bug; the self-report will catch it
wherever it strikes next.

ROUND-COMPLETED LATCH (issue #38, awaiting verification): relay-mode
clients refuse RunMission after a round has ended -- the lobby-loop
relaunch owns the next round (in-process round 2 rebuilt every
player/mech entity on stale globals; matchlog caught player=3:1->3:36
in one generation).  Latch sets ONLY when the stop ends a RUNNING
mission (a stray pre-mission StopMission must not poison round 1 --
the first cut ate a legit launch, caught + fixed in rig).  Regression
verification pending (contaminated by the heisenbug above).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 08:51:50 -05:00
arcattackandClaude Opus 4.8 c2ee7299b2 Generic joystick / HOTAS / pedals support: DirectInput 8 layer + capture wizard
Tester ask: configure non-Xbox controllers.  New L4JOY layer (DI8):
enumerates every non-XInput game device (up to 4), DIJOYSTATE2 polling
with range normalization, reacquire-on-loss, 3s hot-plug re-enum.
XInput devices are excluded via the documented RawInput IG_ VID/PID
check (live-verified skipping a real XBOX360 pad -- no double-feed).

bindings.txt grows joydev/joyaxis/joybutton/joyhat rows (device slots
by name substring or ordinal; axes X..RZ/SL0/SL1 with invert/slew/
deadzone; hats as 4-direction buttons with diagonal chords).  PadRIO
runs them through the existing channel/event machinery -- per-binding
edge arrays release automatically on detach (the #24 rule), and a
direct throttle axis maps full lever travel onto the 0..1 channel
while the gait detent still snaps a lever parked in the walk/run dead
band.

BT_JOYCONFIG=1 (joyconfig.bat) runs a console capture wizard: move
each control when prompted (stick/twist/throttle/fire), invert derived
from the move direction, hat look cluster auto-added, output written
between markers in bindings.txt with the rest of the file preserved.

Legacy L4DINPUT DIJoystick (L4CONTROLS=DIJOYSTICK) untouched.
Verified: grammar accept/reject per line, XInput-exclusion live,
30s in-mission no-device polling clean.  Awaiting a tester with real
stick hardware for axis verification.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 16:36:32 -05:00
arcattackandClaude Opus 4.8 eb618fe9e6 BT_EXPIRE: tester builds stop working 14 days after build (compile gate)
Distributed test zips go stale and stray.  BT_EXPIRE (CMake option,
DEFAULT ON so a zip can never accidentally ship without it) makes the
exe refuse to start BT_EXPIRE_DAYS (default 14) after its build day:
boot logs the remaining window, an expired build logs + shows a
"test build expired -- ask the operator for the current build" box and
exits.  btversion.h now stamps BT_BUILD_UNIX (UTC-midnight-rounded so
the only-on-change header churns once per day, not every build) +
BT_BUILD_DATE.  Dev is unaffected: every build re-stamps the day.

-DBT_EXPIRE=OFF builds a non-expiring exe; mkdist reads the cache and
marks such a zip "-noexpire" + prints a warning, and the tester README
expiry note is {EXPIRE}-marker-gated like the steam lines.  Second
compile-gate exception after BT_STEAM (convention updated in
glass-cockpit.md).  Quality gate, not DRM -- a clock rollback defeats it.

Verified live: default build logs "test build window: 14 day(s) left";
a -DBT_EXPIRE_DAYS=0 build logs TEST BUILD EXPIRED and parks on the
explanation box (window title checked) without starting the game;
restored to 14 and rebuilt clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 13:39:19 -05:00
arcattackandClaude Opus 4.8 d53fcf61ad BT_STEAM restored as THE compile gate (license) -- default OFF
The unification flattened BT_STEAM into the always-on exe; the user flagged
the constraint that makes it the one legitimate compile gate: the Steamworks
SDK license bars a public release with Steam inside, so a release build must
exclude it entirely.

- CMakeLists: option(BT_STEAM OFF) restored around the Steamworks block --
  an OFF configure touches no SDK headers, no steam_api.lib, no DLL copy.
  BT_GLASS stays retired (always defined); ONE build dir either way.
  Dev checkout: build/ configured once with -DBT_STEAM=ON.
- mkdist: reads BT_STEAM from build/CMakeCache.txt.  Steam-OFF zips exclude
  play_steam.bat + steam_api.dll (explicitly filtering a STALE DLL left by
  an earlier ON build), strip the README steam section ({STEAM} line
  markers), and take a -nosteam name suffix so the flavors never clobber
  (found live: both stamped 4.11.436 and the release zip overwrote the dev
  zip).

Verified both flavors:
  ON  (dev build):  steam smoke degrades to Winsock without the client;
      zip carries play_steam.bat + steam_api.dll + README steam lines.
  OFF (scratch build-relcheck, deleted after): 0 compile errors; exe dir =
      btl4.exe + OpenAL32.dll only; boots + simulates; BT_STEAM_NET=1 inert
      (transport not in the binary); end-to-end zip has zero steam bits and
      a clean README.

KB: context/glass-cockpit.md unified-build section records the license gate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 10:40:03 -05:00
arcattackandClaude Opus 4.8 dd74d13120 mkdist: redist runtime DLLs + versioned tester README
The first zips (pre-mkdist) shipped d3dx9_43.dll / msvcp140.dll /
vcruntime140.dll next to the exe -- testers without the VC++ / DirectX
redistributables cannot load the exe otherwise.  mkdist missed them (the
clean-extract boot test passed only because this machine has them system-
wide).  Carried into redist/ (recovered from BT411_4.11.341.zip) and
packaged next to the exe.

Also: players/README.txt -- the tester instructions (updated for the
unified build: one exe, clickable cockpit surround, trigger-config regroup,
the settled keymap, play_steam.bat) -- stamped with the version and placed
at the zip root, matching the original zips' shape.

Clean-extract verified on BT411_4.11.435.zip: root has README + 4 bats,
exe dir carries all 4 DLLs, glass-profile boot simulates.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 10:32:11 -05:00
arcattackandClaude Opus 4.8 10ed66bd7c UNIFY: one exe -- retire the pod/glass/steam compile split
The three build dirs (build\ pod, build-glass\, build-steam\) were compile-
time splits of the same game; the tax was real: a stale steam exe shipped 2
days behind, the felt keymap flipped with the boot flavor, and cockpit
clicks were silently dead in the pod build (the trigger-config incident).

Now everything compiles into the single build\ exe:
- CMakeLists: BT_GLASS/BT_STEAM options removed; the glass TUs (PadRIO,
  bindings, panel, glass windows, plasma) + FE/console + Steam transport
  compile unconditionally; both macros always defined (seam markers).
- Steam: steam_api.lib linked with /DELAYLOAD:steam_api.dll + delayimp --
  the DLL maps only when SteamAPI is first called, and every call site
  gates on BT_STEAM_NET=1 (BTSteamNet_* short-circuit on steamActive), so
  machines without the DLL run everything but Steam sessions.
- CABINET GUARD (btl4main): the miniconsole menu previously claimed any
  zero-arg launch -- unified, that would hijack the pod cabinet's boot
  shape.  The menu now requires BT_PLATFORM=glass (or BT_FE_MENU=1);
  platform parse moved above the fe_menu_mode computation.  A bare boot
  behaves exactly like the old pod build (DEV profile, wait for egg).
- Bats (play_solo/join/join_lan/play_steam) + the btoperator.py template:
  single build\ path + BT_PLATFORM=glass.  mkdist: one exe (+DLL scan
  picks up OpenAL32 + steam_api).  build-glass\/build-steam\/build-glass2\
  (a stale Jul-20 cache) deleted.

Verified matrix on the unified exe:
  bare zero-arg boot == old pod exe (same DEV profile + egg-load path;
    the segfault on a missing egg is pre-existing, baselined vs the zip exe)
  glass + BT_BTNTEST scripted click -> [cfgmap] ENTER/EXIT (trigger config)
  runs with steam_api.dll ABSENT (delay-load proven)
  glass zero-arg -> miniconsole menu
  2-node loopback MP: 145 ticks each, cross-connected
  BT_STEAM_NET=1 without Steam client -> "staying on Winsock", game runs

KB: context/glass-cockpit.md compile-gates section rewritten for the
unified model.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 10:28:51 -05:00
arcattackandClaude Opus 4.8 d89ae71db5 mkdist: include the Steam build + play_steam.bat when present
Rebuilt build-steam fresh (was stale at Jul 20 -- predated today's gauge/
trigger-config/keymap fixes); the DLL scan picks up steam_api.dll
automatically.  Clean-extract verified: the steam exe boots to the front-end
menu (Steam client only needed for live lobbies).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 10:12:57 -05:00
arcattackandClaude Opus 4.8 8765ea3571 mkdist: name zips by the BT411_<version> convention (btversion.h)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 10:09:37 -05:00
arcattackandClaude Opus 4.8 f7e674d9cb tools/mkdist.py: tester-distribution zip builder
Packages the player bats (zip root), BOTH exes (build\ pod fallback +
build-glass\ preferred -- the bats auto-select glass = clickable cockpit /
trigger config / bindings.txt keymap), the runtime DLLs next to each exe
(OpenAL32.dll -- the first clean-extract boot test failed without it), and
the git-TRACKED content/ files only (the working tree carries dev junk that
must not ship).  Output: dist/BT411-<date>-<hash>.zip (dist/ gitignored).

Verified: clean-extract boot test -- glass exe boots the GLASS profile and
simulates; pod exe boots as fallback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 10:08:37 -05:00