Commit Graph
23 Commits
Author SHA1 Message Date
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
arcattackandClaude Opus 5 8edce41f88 bindings: one-time migration to the board-2 layout -- the two-population fork is gone
Operator decision: "make this version devour their custom settings and re-output
the correct binding file with the updates -- I'd like to eliminate competing
code paths."  Before this, the preserved-bindings.txt convention meant upgraders
stayed frozen on whatever layout their file was written under, forever: two
player populations, two sets of true documentation, and every future default
improvement reaching only fresh installs.

THE MIGRATION (PadBindingProfile::MigrateBindingsFile, run once at load):
  * A file carrying the new "# bindings-board 2" marker is left alone -- the
    check is the first thing that runs, so this is a no-op forever after.
  * Otherwise: every row matching the UNION OF EVERY DEFAULT SET EVER SHIPPED
    (85 canonical rows, harvested from c2ee729..1cda880 and embedded as a
    table; comments stripped, whitespace collapsed, uppercased) is dropped --
    those rows were never a player's choice, they were just the old board.
  * Rows the player actually authored are CARRIED with their original text and
    comments, and WIN over the new defaults: the losing default row is emitted
    commented out as "# (yours wins) ...", keyed by the control ("KEY T",
    "PAD A", "PADAXIS LX") so key rows only displace key rows.
  * The joystick wizard's marker-delimited section is preserved VERBATIM (the
    same markers L4JOY rewrites between), so HOTAS players migrate without
    re-running the wizard.
  * The previous file is saved as bindings.old.txt first.  Restoring it over
    bindings.txt just re-migrates next boot -- deliberate: the old WORLD is not
    restorable, only the player's own rows persist.  That is the point.

VERIFIED end-to-end with the real exe (three planted scenarios):
  A. old-world file (8 old-default rows + 2 authored rows incl. a rate tweak +
     a wizard section): both authored rows carried with comments, both
     conflicting new defaults stood down with "(yours wins)", wizard section
     byte-preserved, backup written, log line states all counts, and the
     migrated file parses with ZERO malformed lines.
  B. already-migrated file: byte-identical md5 after a full boot, no migration
     line, no backup touched.  (First attempt at this check clobbered its own
     evidence by running case C first -- re-run properly.)
  C. no file: fresh defaults now carry the marker as line 1, no migration.
  The operator's real bindings.txt was stashed before testing and restored
  untouched; it will migrate on their own next launch, as intended.

Docs flipped to the new reality: the README's "your keys DO NOT change"
upgrade promise is replaced by the migration story (customs + stick kept, old
stock keys replaced, bindings.old.txt escape hatch); CONTROLS.md gains the same
note; context/glass-cockpit.md records the mechanism, the 85-row table's
provenance, and why restoring the backup re-migrates.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 14:19:13 -05:00
arcattackandClaude Opus 5 150961176c merge fix-ups: the four review findings + a build portability gate the merge exposed
Applied on top of the glass-cockpit-refit merge, all from the pre-merge review:

1. README CONTROLS TABLE REWRITTEN for the new default board.  It still taught
   "W/S throttle, A/D turn" while the defaults moved flight to the numpad and
   handed the letter rows to the MFD banks -- a fresh install would read
   controls that do not work.  The new table teaches the numpad flight block,
   the panel-on-your-keys groups, F7 for control mode (was M), backtick for the
   camera (was V), the coolant valves on 1-3/QWE with the detent warning kept,
   and PgUp/PgDn volume.  UPGRADERS ARE TOLD EXPLICITLY that their preserved
   bindings.txt keeps their old keys, and that deleting it opts into the board.

2. BACKTICK VIEW-TOGGLE GUARDED.  The bindings-row-wins rule (KeyHasBinding)
   covered V and J/K/L but not backtick itself, which polled unconditionally --
   and BACKTICK is a nameable, unbound key, so binding it to a button
   double-dispatched (button + camera).  Both halves of the view poll are now
   guarded.

3. ENVIRON.INI REFUSES ONE-SHOTS.  The loader applied ANY key=value line, and
   BT_JOYCONFIG in the file would defeat the #66 one-shot in a sneaky way: the
   wizard DELETES the process var after running, so in every relaunched child
   there is no real env var left to win over the file -- capture wizard at
   every mission start.  BT_JOYCONFIG is now skipped with the reason in the
   template header, which also warns that test/debug hooks (BT_MP_FORCE_DMG
   and friends) do not belong in a file that silently persists forever.

4. VOLUME DOCUMENTED EVERYWHERE IT WAS MISSING.  CONTROLS.md gains the
   PgUp/PgDn row (the old "- / = (see below)" cell pointed at a note that never
   mentioned volume) and the note that the keys moved so they no longer share a
   key with anything in any layout; the bindings template header reserves
   PgUp/PgDn informally.

5. L4KEYLIGHT BUILD GATE (found by building the merge on this machine).  The
   C++/WinRT Dynamic Lighting TU does not compile against SDK 10.0.19041 --
   its bundled cppwinrt fails inside winrt/base itself (C2039 'wait_for').
   CMake now detects an SDK older than 10.0.22000 and builds a dormant stub
   with the same five-function scalar interface (logs once at Start, identical
   behaviour otherwise) -- extending the feature's own runtime philosophy
   ("no Dynamic Lighting -> log once, stay dormant") to build time.  Dynamic
   Lighting is a Windows 11 22H2 feature, so nothing real is lost on an older
   build machine, and machines with a newer SDK build the real mirror
   unchanged.

VERIFIED on the merged tree (4.11.572):
  * build clean -- 0 compile errors; the 40 /FORCE-tolerated unresolved
    externals are byte-identical to every pre-merge build (20
    CreateStreamedSubsystem + 20 DefaultData; the earlier claim that all 40
    were CSS was shorthand -- corrected here for the record).
  * solo boot: environ.ini template written + loaded, mode resolver announces
    the surround, historical bands L276/R276/T223/B336 confirmed, keylight
    stub logs its dormant line, BT_SHOT capture shows the under-glass button
    treatment and the corrected hat labels, 0 faults.
  * PgUp/PgDn volume PROVEN LIVE via injected keys: 5%->10%->15%->10%, saved
    to volume.cfg.  (Side catch: content\volume.cfg had been sitting at 0.00
    from an old test -- anyone using this tree had a silent game; reset.)
  * full relay cycle on the merged exe: 2 pods staged, launch pair, mission,
    StopMission, round RESET -- all clean.
  * all three console suites still pass; checkctx CLEAN.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 13:24:22 -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 5 da617e6f8f README + release notes: match reports UPLOAD THEMSELVES -- stop asking testers to send them
The operator caught this: I had carried the old "please send the operator your
matchlog" instruction into the release notes, and the shipped README has said
it for a while.  It is out of date.

Since 2026-07-22 the client auto-uploads its match forensic log to the relay
after every round: btl4main.cpp:914 calls BTRelayUploadMatchLog() once
RunMissions() returns, and the console receives it on route -9 and saves it as
matchlogs/<peer>_<name> (btconsole.py:1026-1042).  content/matchlogs/ is
already full of them.

So the instruction was making every tester hunt down and attach a file the
operator already had.  Corrected to state the real contract, including the
gaps the auto-upload genuinely cannot cover:

  * RELAY modes only -- join.bat and join_lan.bat both set BT_RELAY, so both
    upload.  play_solo.bat and play_steam.bat set none, so those still need a
    manual send.
  * The upload fires when a round ends CLEANLY (after RunMissions returns), so
    a mid-round CRASH loses it -- exactly the case we most want the file for.
  * join.log is NEVER uploaded -- no mechanism exists -- so that one is still
    a manual ask, and is now called out separately rather than buried.

Also added the same correction to dist/RELEASE_NOTES_4.11.550.md.

The zip cut before this (BT411_4.11.550.zip) carries the stale README and must
be re-cut.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 19:56:56 -05:00
arcattackandClaude Opus 5 4360908d38 README (the shipped tester doc): repair two corrupted paths + bring it current with today's wave
Asked to double-check the launchers and make sure the user-facing docs in the
zip are up to date.  players/README.txt is the ONLY user-facing doc mkdist
ships (content/VIDEO/RESULT2.TXT is a game asset), and it was last touched
2026-07-24 -- before all of today's work.

1. CORRUPTED PATHS (real defect, worst possible placement).  The file
   contained literal CONTROL BYTES where two paths should be:

       offset 440  0x08  ...personal files (content<BS>indings.txt with...
       offset 490  0x0b  ...joystick setup, content<VT>olume.cfg with...

   i.e. `content\bindings.txt` and `content\volume.cfg` had been written
   through a string that interpreted \b as backspace and \v as vertical tab.
   Testers read "contentindings.txt" / "contentolume.cfg" -- files that do not
   exist -- in the UPGRADE paragraph, the one place that tells them which
   personal files to preserve when extracting over an old install.  Repaired
   by byte, and verified: 0 control bytes remain in all four packaging
   variants.  (The other two occurrences, content\join.log and a later
   content\bindings.txt, were undamaged -- \j and the second \b happened to
   survive.)

2. CONTENT now current with today's 13-issue wave, tester-relevant parts only:
   * AMMO BAY FIRE is now LETHAL (#46) -- flagged prominently as a gameplay
     change, with the ~10 s fuse, what it costs, and the EJECT-hold escape.
     Previously these fires were harmless; a tester who ignores one now dies.
   * ENG-page buttons FLASH on a jam or bay fire (#47) -- the restored pod
     annunciator, so testers know to look for it.
   * KNOWN ISSUE + WORKAROUND for #62: the BUS MODE button's second press
     detaches a weapon from its generator and the auto re-attach is dead, so a
     weapon can go permanently dark.  Documented with the recovery (press a
     GENERATOR SELECT A/B/C/D button) to pre-empt a wave of "my weapon broke"
     reports.

Verified by simulating mkdist's own marker processing across all four
steam-on/off x expire-on/off variants: new content survives every path, no
leftover {VERSION}/{STEAM}/{EXPIRE} markers reach the tester, no control bytes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 19:03:37 -05:00
arcattack 37a520d8a0 README: control mode vs experience level are different systems + NumLock note
The same careful tester hit the BASIC-mode trap twice because 'expert/
veteran modes' (menu experience) sound like control modes (M key).
Spelled out the distinction + the numpad NumLock dependency.
2026-07-24 12:47:15 -05:00
arcattack b710a848c2 README: upgrade = extract-over-top (preserves bindings.txt + volume.cfg)
The advertised upgrade flow was delete-and-unzip, which wipes the two
per-player files (key/joystick bindings incl. joyconfig output, saved
volume) every version.  The zip carries neither file, so extract-over-
top preserves them by construction -- now the documented flow, with the
copy-out fallback for clean-delete preferrers.
2026-07-24 12:16:28 -05:00
arcattackandClaude Opus 4.8 ef9a648c89 README: BASIC control mode does not steer with pedals (Ferret's 510 report)
Field report (new tester, 510): keyboard A/D + arrows 'not working' --
can twist and drive but not turn.  Root-caused live via key injection:
fresh spawns start in BASIC control mode, where steering rides the
STICK (authentic rookie mode) and the pedal-bound keys are inert by
design; one M press (MID) makes A/D steer.  (A false regression-bisect
chase established the metric noise came from an attached pad's stick
drift nudging BASIC steering.)  README now warns about the default
mode; kept the env-gated [padwrite] diagnostic from the hunt.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 11:57:44 -05:00
arcattack 3ee980ba46 README: coolant flush/valve keys + the valve detent trap (max -> CLOSED)
The 1-5-50-0 cycle's closed detent one press past max is what cooked
both SRM6s in the 2026-07-23 field session -- players need the warning
and the shared-supply note.
2026-07-23 17:32:51 -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 2edd1b5363 Issue #31: wire the EJECT button (EjectAmmo msg 0xb @004bb9b8) -- the in-mission unjam
Disasm-faithful transcription of the export-gap handler onto
ProjectileWeapon (MESSAGE_ENTRY id 0xb, the #19 pattern).  PRESS arms
the ~3s UpdateEject countdown (bin alarm -> Ejecting(3); gate 2 parks
the weapon offline mid-eject -- authentic).  HOLD to completion =
DumpAmmo jettisons the bay -> NoAmmo.  TAP (release early) cycles ONE
round out via FeedAmmo and returns the weapon to Loading -- clears a
jam at the cost of a round (and recovers a FailureHeat 7 while rounds
remain).  Binary structure kept exactly incl. the press-no-bin
fall-through into the release block.

AmmoBin: Ejecting(3)/Ejected(4) alarm levels decoded + named
SetAmmoState accessor (databinding rule).  Always-on forensics:
'[weap] EJECT tap' + '[ammo] bay DUMPED overboard (N rounds)'.

Rig-verified live (BT_EJECTTEST=1 / =hold on LRM15): taps eject one
round each and return the weapon to service (15->8 across cycles);
hold dumps all 16 -> NoAmmo.  Awaiting a human press of the actual
ENG-page button on a jammed weapon.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 13:49:11 -05:00
arcattackandClaude Opus 4.8 225fe43f0c CORRECTION: EJECT tap IS the in-mission unjam (EjectAmmo @004bb9b8 decoded)
Old-timer testimony (tap = eject one round, hold = eject the bay, eject
clears a jam) checked against the binary: raw disasm of the export-gap
handler @004bb9b8 confirms it byte-for-byte.  PRESS arms the ~3s
UpdateEject countdown (hold-to-completion = DumpAmmo whole bay ->
NoAmmo); RELEASE before completion (TAP) ejects ONE round (@004bd4f4)
and, with ammo remaining, sets weaponAlarm 3 + restarts the reload --
the weapon returns to service.  A tap even recovers a FailureHeat
level-7 launcher while rounds remain (empty-bin gate 2 re-pins 7 only
when the bay is dry).

The earlier 'jams are mission-permanent / no unjam exists' claim read
only the HOLD path -- corrected + swept: projweap.cpp case-5/jam-log/
CheckForJam comments, players/README.txt, decomp-reference.md
(message-tables entry), open-questions.md (state-7 recovery note).
Gitea #31 updated; wiring the handler now restores the pod's actual
jam-recovery mechanic.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 12:21:06 -05:00
arcattackandClaude Opus 4.8 420d739d75 README: jam behavior + where the jam lamp lives (field-confirmed)
Live verification closed the loop on issue #30: heat-jammed SRMs light
the authentic BTEJAM lamp on the weapon panel's ENG DATA page; the
default view shows nothing (authentic).  Testers get told where to look.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 10:30:17 -05:00
arcattackandClaude Opus 4.8 4e0fcbf328 Issue #26: runtime master volume (-/= keys, persisted)
The game audio plays hot with no in-game control -- testers couldn't
hear voice chat without the Windows mixer (playtest night).  The -/=
keys now step the OpenAL listener gain (the master scale every source
inherits) in 5% steps, clamped 0..150%, edge-detected in the per-frame
poll with a foreground guard; the value persists to content\volume.cfg
and reloads at boot (BT_AUDIO_VOLUME env still wins when set; default
stays 0.6).  README updated.

Verified: volume.cfg 0.25 -> boot log "master gain=0.25"; key stepping
needs a live focused session (awaiting live verification).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 00:53:07 -05:00
arcattackandClaude Opus 4.8 8368163b04 Lobby loop: auto-rejoin between rounds + seat reclaim + live mission edits
The between-rounds arcade flow (first-playtest-night field request): at
mission end every pod exited, seats dropped, and every round required
everyone to re-run join.bat with no way to adjust the mission.

1. AUTO-REJOIN: StopMission (operator END / mission clock -- NOT a
   window close) sets gBTMissionStoppedByConsole; after the matchlog
   upload btl4main relaunches the pod into the join wait with the same
   cmdline (BT_CALLSIGN/BT_MECH ride the inherited environment).

2. SEAT RECLAIM: the assigned tag is mirrored file-scope
   (BTRelaySelfTag) and re-presented as the 3rd SEAT_REQUEST payload
   field (BT_SEAT_CLAIM); the relay holds a dropped seat for its tag
   for 90s and a returning player gets their EXACT seat back -- static
   ordering across rounds, the real-pod model.  Non-claim joiners skip
   reclaim-held seats.

3. LIVE MISSION EDITS: the console gains "Apply mission settings"
   (enabled while a session runs) writing arena/time/weather/length
   into the session egg; the relay re-reads the file at round reset and
   egg release (roster shape locked mid-session).

Verified 2-pod 3-round: pods self-relaunched after each clock end; the
SECOND-to-rejoin still reclaimed its original seat (ordering held); the
between-rounds edit landed ("mission length now 75s").  Known edge
noted in the KB: a stale ready flag from a not-yet-exited old conn can
satisfy the launch gate mid-rejoin -- operators launch on green dots.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 00:28:56 -05:00
arcattackandClaude Opus 4.8 fcedc046cf Walk-up callsign/mech request: join menu -> relay -> the session egg
The 1995 front-desk conversation, over the internet.  join.bat/
join_lan.bat now open a JOIN-GAME menu (the FE in BT_FE_JOIN trim:
CALLSIGN edit + the 18-mech list + JOIN); the choice relaunches into the
normal join with BT_CALLSIGN/BT_MECH env, rides the relay SEAT_REQUEST
as {callsign NUL mech NUL} (empty payload = roster defaults, wire-
compatible), and the relay validates the mech tag, HOLDS egg delivery
until every pod's console pad is connected, rewrites the egg via
eggmodel (vehicle= + rasterized callsign bitmaps, graceful no-PySide6
fallback) and streams it to all pads -- so every player's egg copy
carries every player's callsign.

The hold is gated on CONSOLE-PAD count, not game-side registration: a
pod HELLOs only after parsing the egg's roster, so a registration gate
deadlocks (hit live in the first run; pods animate in WAITING FOR
MISSION ASSIGNMENT during the hold).

Verified 2-node e2e (env-injected requests): thor/vulture requested over
roster defaults bhk1/ava1 -> relay held 1/2, released at 2/2, rewrote
both seats (relay log), the delivered egg carries vehicle=thor/vulture +
name=VIPER/MONGOOSE, both pods launched and built [cyl] tables for both
requested mechs.  Join-menu layout screenshot-verified; the menu's
click-through relaunch awaits live human verification.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 13:58:51 -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 753540de96 MP match forensics: per-peer matchlog + relay auto-upload + matchcheck
For the 8-player playtest.  Damage authority is VICTIM-side, so no single
peer sees the whole match: every -net instance now writes a compact
machine-parseable matchlog_<date>_<time>_<pid>.txt (auto-armed by -net;
BT_MATCHLOG=1/0 overrides; solo silent), and at mission end a relay-mode
pod dials the relay game port (the seat-request throwaway-dial pattern,
envelope route -9) and streams the file back -- the relay saves every
peer's copy under matchlogs/ on the operator's machine, so testers send
nothing by hand.  tools/matchcheck.py <dir> reconciles all of them:
damage claimed (FIRE/PROJ/SPLASH/RAM, shooter-side) vs applied (DMG,
victim-side authoritative), kill/death replication across observers,
PLAYER_DEAD counts, scores, version skew, replicant-application and
unattributed-damage anomalies, mid-mission disconnects.

Hooks at the authoritative sites: Mech::TakeDamageMessageHandler (DMG,
post-base, zone + post-application damageLevel), MechWeapon::
SendDamageMessage (FIRE), projectile impact/splash/collision dispatch
(PROJ/SPLASH/RAM), wreck entry (DEATH -- a ONE-SHOT latch at
MovementMode 9, NOT the transition: a replicant arrives at 9 straight
from the type-6 record header and never runs the transition branch),
BTPlayer vehicle/death/score handlers (VEHICLE/PLAYER_DEAD/SCORE), zone
crit cascade (CRIT), APP.cpp RunningMission (MISSION), L4NET host
handlers (PEER_UP/PEER_DOWN).  Every line t=/w=/st=-stamped + fflushed.

Verified 2-node: mesh run pairs A's 22 FIRE 1:1 with B's 22 DMG (same
amounts, cylinder-resolved zones, ~200ms latency); force-damage kill run
logs 103 DMG + 3 DEATH inst=M + 3 PLAYER_DEAD across three respawn
cycles victim-side and the kill SCOREs shooter-side; relay-mode run
auto-uploaded BOTH peers' files at the mission-clock stop and matchcheck
produced the full report with exactly one (correct) anomaly -- the force
hook's claim-less damage, the unattributed-damage detector working.
Relay gained a route-specific 8MB cap for the upload frame (game frames
stay capped at 1600).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 13:22:10 -05:00
arcattackandClaude Opus 4.8 aaa252a4a3 play_solo.bat: open the mission menu (mech/map/options pickers)
play_solo hardcoded -egg ARENA1.EGG, bypassing the miniconsole front end
that already carries the full pickers: SOLO/RAW-SOLO/HOST-LAN/JOIN-LAN
modes, 8 maps, all 18 mechs, 4 experience levels, colours/badges/patches,
drop zones, advanced damage, time-of-day, weather and mission length.

The bat now launches zero-arg (BT_PLATFORM=glass -> the menu); the menu
relaunches the exe with the generated egg and the cockpit/gauge env
(BT_START_INSIDE/BT_DEV_GAUGES) inherits into the mission child.  Menu-boot
verified with the bat's exact env; the solo menu->mission->menu cycle was
verified at FE landing.  README + the btoperator generated-bat template
updated to match.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 10:49:01 -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