Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40c35946cd | ||
|
|
47dcc62fc1 | ||
|
|
9754fb1295 | ||
|
|
716e10e3fa | ||
|
|
ab1b3439f4 | ||
|
|
7cd58d3000 | ||
|
|
a28878beaf | ||
|
|
a7722d9ca9 | ||
|
|
bf04e1c079 | ||
|
|
46ff8dd262 |
@@ -82,6 +82,7 @@ precise than anything you can infer.
|
|||||||
| Multiplayer, replication, netcode | `context/multiplayer.md` |
|
| Multiplayer, replication, netcode | `context/multiplayer.md` |
|
||||||
| **The OPERATOR CONSOLE + RELAY** — how to launch it, ports, seats, the round/launch lifecycle, re-arm | `context/operator-console.md` |
|
| **The OPERATOR CONSOLE + RELAY** — how to launch it, ports, seats, the round/launch lifecycle, re-arm | `context/operator-console.md` |
|
||||||
| Running a session as sysop (operator-facing how-to) | `docs/OPERATOR_GUIDE.md` |
|
| Running a session as sysop (operator-facing how-to) | `docs/OPERATOR_GUIDE.md` |
|
||||||
|
| The tester-facing OPEN-ISSUES handout (for Discord) | `docs/OPEN_ISSUES_FOR_TESTERS.txt` — regenerate from the tracker + update the header date before each playtest |
|
||||||
| Pod hardware, monitors, RIO, MFD surfaces, input remap (CONTROLS.MAP/XInput) | `context/pod-hardware.md` |
|
| Pod hardware, monitors, RIO, MFD surfaces, input remap (CONTROLS.MAP/XInput) | `context/pod-hardware.md` |
|
||||||
| Glass cockpit desktop dev layer (BT_GLASS/BT_STEAM gates, PadRIO, miniconsole, Steam) | `context/glass-cockpit.md` |
|
| Glass cockpit desktop dev layer (BT_GLASS/BT_STEAM gates, PadRIO, miniconsole, Steam) | `context/glass-cockpit.md` |
|
||||||
| Steam internet MP (the wire seam, identity tokens, lobby) | `context/steam-networking.md` |
|
| Steam internet MP (the wire seam, identity tokens, lobby) | `context/steam-networking.md` |
|
||||||
|
|||||||
@@ -1384,7 +1384,7 @@ cmHeat(K,ModeSecondaryHeat,58,heatpal.pcc,heatpal2.pcc,Myomers);
|
|||||||
}
|
}
|
||||||
|
|
||||||
#=======================================================================
|
#=======================================================================
|
||||||
# Sunder V1
|
# Denkou (was: Sunder V1)
|
||||||
#=======================================================================
|
#=======================================================================
|
||||||
Snd1Init
|
Snd1Init
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -69,6 +69,23 @@ run\run.cmd [EGG] # default DEV.EGG; cd's to content\ and runs btl4.exe -eg
|
|||||||
⚠ **Keep the `if /I "%%P"=="btl4.exe"` guard** — parsing tasklist with `tokens=2` alone reads its
|
⚠ **Keep the `if /I "%%P"=="btl4.exe"` guard** — parsing tasklist with `tokens=2` alone reads its
|
||||||
*"INFO: No tasks are running"* line as a PID and spins forever with nothing running. Full
|
*"INFO: No tasks are running"* line as a PID and spins forever with nothing running. Full
|
||||||
investigation, including which windows exit vs merely hide: `phases/phase-12-orphan-processes.md`.
|
investigation, including which windows exit vs merely hide: `phases/phase-12-orphan-processes.md`.
|
||||||
|
- **Field-log anatomy — reading a steam night (#157 forensics, 2026-08-10) [T2].** A night's log is
|
||||||
|
a CHAIN of `===== BT411 SESSION` blocks: menu stubs (`args=''`, "first process") alternating with
|
||||||
|
glass generations (`-net 1501 -platform glass`, "relaunched generation"); one glass block = one
|
||||||
|
drop, and boundaries are synchronized across every machine to ±2 s (mission-clock expiry relaunches
|
||||||
|
all peers' menus in the same second). **Stub anatomy:** the steam lobby is CLICK-driven
|
||||||
|
(`steamAction`, btl4fe.cpp), so a menu closed without a click logs exactly **4 boot lines** and
|
||||||
|
exits 0 — a 4-line stub at end-of-night is a player going to bed, NOT a crash (all four machines
|
||||||
|
ended night-14 with identical 4-line stubs at 00:31:2x). 7–8-line stubs = a lobby cycle
|
||||||
|
(`[steamnet] up` + `[lobby] join` + map). `BTCrashFilter` is armed before the first log line and
|
||||||
|
writes a `[crash]` block + module-relative stack on ANY unhandled exception — a silent log end
|
||||||
|
means quit/kill/hang, never an unlogged AV. **`lastrun_<stem>.txt` caveats:** appends stop silently
|
||||||
|
if the file gets locked (the `is_open` guard skips) — a lastrun ending mid-night while the log
|
||||||
|
continues means the file was captured/locked during live troubleshooting, not that the chain died.
|
||||||
|
And the bat sign-off ("The game has exited. If it closed unexpectedly, send…") prints after ANY
|
||||||
|
chain end including a normal quit — it primes players to report quits as crashes; #157 ("did not
|
||||||
|
exit cleanly") was exactly this echo. The arcade-authentic round-end (mission process exits →
|
||||||
|
menu relaunch) itself reads as a crash to first-night players.
|
||||||
- **Why this is a BT411-only hazard [T1].** The 1995 pod shipped ONE folder — `BTL4OPT.EXE` sits
|
- **Why this is a BT411-only hazard [T1].** The 1995 pod shipped ONE folder — `BTL4OPT.EXE` sits
|
||||||
next to `BTL4.RES`/`VIDEO\`/`GAUGE\`/`AUDIO\` (it is still there in `content\`) — and RP411/RP412
|
next to `BTL4.RES`/`VIDEO\`/`GAUGE\`/`AUDIO\` (it is still there in `content\`) — and RP411/RP412
|
||||||
keep that shape (`pack-dist.ps1` copies the exe and every asset dir into one dist root). BT411's
|
keep that shape (`pack-dist.ps1` copies the exe and every asset dir into one dist root). BT411's
|
||||||
|
|||||||
@@ -203,32 +203,6 @@ appears in the cfg like any panel and honours `,noframe`. Verified: a drag wrote
|
|||||||
at 321,222. NB the plasma window blits directly every frame (`GetDC`+`StretchDIBits`), so unlike
|
at 321,222. NB the plasma window blits directly every frame (`GetDC`+`StretchDIBits`), so unlike
|
||||||
the panels it has no `WM_TIMER` focus-throttle to worry about.
|
the panels it has no `WM_TIMER` focus-throttle to worry about.
|
||||||
|
|
||||||
## Glass-panel repaint pump — perf + dirty-skip (2026-08-09/10) [T2 measured]
|
|
||||||
|
|
||||||
The exploded per-display windows are pure CPU/GDI (`ExpandPlaneToBGRA` +
|
|
||||||
`StretchDIBits`), repainted synchronously on the MAIN render thread by
|
|
||||||
`BTGlassPanels_Tick` (~16 Hz). Three layered changes after a playtester reported
|
|
||||||
~20 fps in panels mode vs ~130 fps in the surround:
|
|
||||||
|
|
||||||
1. **HALFTONE → COLORONCOLOR** (`f3d27f5`, in master): the `HALFTONE` stretch
|
|
||||||
(GDI's per-output-pixel resample) × 7 windows per pump was the primary sink;
|
|
||||||
nearest is crisper for the low-res MFD pixels anyway. `BT_GLASS_SMOOTH=1` restores.
|
|
||||||
2. **Per-window dirty-skip** (`c9e25e5`, in master): each window carries a change
|
|
||||||
token = FNV over `SVGA16::PlaneChecksum(mask)` (the shared pixelBuffer masked to
|
|
||||||
every port that can feed the window) + each button's RENDERED lamp brightness +
|
|
||||||
held/latch. The pump re-blits only windows whose token moved: ~31 pumps/2s →
|
|
||||||
4-15 window repaints vs 217-224 always-on. `BT_GLASS_DIRTY=1` logs the tally.
|
|
||||||
3. **Palette generation in the token** (branch `glass-palette-token`): the
|
|
||||||
ColorMapper family (armor rosette tints, adpal/adpal2 damage flash) animates by
|
|
||||||
CLUT writes with ZERO pixel churn — invisible to a pixel checksum.
|
|
||||||
`SVGA16::paletteGeneration` is bumped by the palette writers
|
|
||||||
(`BuildSecondaryColor` only on a REAL entry change, since the flash alternates
|
|
||||||
palettes every Execute writing identical RGB at zero damage; full rebuilds
|
|
||||||
unconditionally) and folded into the token for palette-expanding windows
|
|
||||||
(`monoTint < 0`, or all under `BT_GLASS_MFD_PAL`). Without it the glass radar
|
|
||||||
held stale armor tints between pixel repaints while the D3D surround (which
|
|
||||||
re-expands every frame) tracked live.
|
|
||||||
|
|
||||||
**Turning it OFF: `L4PLASMA=NONE` (also `OFF`/`0`, 2026-08-06) [T2].** `L4GREND` creates a
|
**Turning it OFF: `L4PLASMA=NONE` (also `OFF`/`0`, 2026-08-06) [T2].** `L4GREND` creates a
|
||||||
marquee whenever `L4PLASMA` is set at all (`SCREEN` → the desktop window, anything else → a real
|
marquee whenever `L4PLASMA` is set at all (`SCREEN` → the desktop window, anything else → a real
|
||||||
`PlasmaDisplay` on that serial port), and the GLASS profile force-defaults it to `SCREEN` — so on
|
`PlasmaDisplay` on that serial port), and the GLASS profile force-defaults it to `SCREEN` — so on
|
||||||
|
|||||||
+77
-3
@@ -254,7 +254,22 @@ desktops. Two pieces were added for the cab:
|
|||||||
once, and over CRD you cannot see the panels at all: the log IS the confirmation that a picture
|
once, and over CRD you cannot see the panels at all: the log IS the confirmation that a picture
|
||||||
landed on the right glass.
|
landed on the right glass.
|
||||||
|
|
||||||
### Boot-STABLE panel binding — `monitor:id:` (2026-08-08, staged, pod-untested)
|
### Boot-STABLE panel binding — `monitor:id:` (DEPLOYED + POD-VERIFIED 2026-08-10)
|
||||||
|
**Live on ALPHA-MR since 2026-08-10** — mapping eye-verified over livestream, then the master
|
||||||
|
`C:\bt411\glass_layout.cfg` flipped to the id form (old cfg saved as
|
||||||
|
`glass_layout.cfg.displaynbak`) and the relaunch bound all three windows to the SAME rects with
|
||||||
|
zero mismatch shouts [T2]. The shipped binding:
|
||||||
|
```
|
||||||
|
Secondary / Radar=monitor:id:Default_Monitor,bare # colour radar LCD, USB adapter
|
||||||
|
VGA Port A=monitor:id:UID224795,bare # RAR0005: Heat + Comm + Mfd2 CRTs
|
||||||
|
VGA Port B=monitor:id:UID200195,bare # RAR0005: Mfd1 + Mfd3 CRTs
|
||||||
|
```
|
||||||
|
⚠ **The radar LCD's USB display adapter presents NO EDID** — its only stable identity is the
|
||||||
|
`Default_Monitor` placeholder (`…#Default_Monitor#…&UID256#`). Unique today; a second EDID-less
|
||||||
|
panel would collide, and the per-boot `[glassid]` dump (BT_GLASS_IDS=1, now PERMANENT in
|
||||||
|
`podprofile.ini`) plus the mismatch shout are the diagnosis tools. The Dell (`DEL4025`) is the
|
||||||
|
PRIMARY main-view panel (BT_FIT owns it; not in the layout cfg) — the 08-08 note calling it a
|
||||||
|
"1908FP" service monitor mis-guessed its role.
|
||||||
**Windows renumbers displays.** Nick, after re-cabling + a reboot: *"the order changed … sometimes
|
**Windows renumbers displays.** Nick, after re-cabling + a reboot: *"the order changed … sometimes
|
||||||
they change when one gets turned off and back on, at least how windows SEEs them, even if the
|
they change when one gets turned off and back on, at least how windows SEEs them, even if the
|
||||||
visual desktop tool looks the same."* So the two original binding forms are both boot-fragile —
|
visual desktop tool looks the same."* So the two original binding forms are both boot-fragile —
|
||||||
@@ -480,8 +495,67 @@ instantly and did nothing" actually means "it is still hung". Recover with
|
|||||||
without a GUI):** `DISPLAY\RAR0005\…UID224795`, `DISPLAY\DEL4025\…UID249395` (DELL 1908FP),
|
without a GUI):** `DISPLAY\RAR0005\…UID224795`, `DISPLAY\DEL4025\…UID249395` (DELL 1908FP),
|
||||||
`DISPLAY\RAR0005\…UID200195`. **The two RAR panels share one EDID code and have blank serials**, so
|
`DISPLAY\RAR0005\…UID200195`. **The two RAR panels share one EDID code and have blank serials**, so
|
||||||
`monitor:id:RAR0005` is ambiguous — they must be bound by the per-connector form
|
`monitor:id:RAR0005` is ambiguous — they must be bound by the per-connector form
|
||||||
(`monitor:id:UID224795` / `monitor:id:UID200195`). The Dell's code is unique. The cab's shipped
|
(`monitor:id:UID224795` / `monitor:id:UID200195`). **RESOLVED 2026-08-10:** the full map is
|
||||||
`glass_layout.cfg` still uses the fragile `monitor:DISPLAY4` device-name form.
|
UID224795 = VGA Port A (Heat+Comm+Mfd2), UID200195 = VGA Port B (Mfd1+Mfd3), `Default_Monitor`
|
||||||
|
(EDID-less USB adapter, UID256) = the colour radar LCD, DEL4025 = the PRIMARY main-view panel
|
||||||
|
(not a service monitor). The master cfg now ships the id form (see the binding section above);
|
||||||
|
`glass_layout.cfg.displaynbak` preserves the old DISPLAYn cfg.
|
||||||
|
|
||||||
|
**7. ⚠ cmd redirect trap when scripting over SSH:** `echo VAR=1>> file` appends `VAR=` — cmd
|
||||||
|
parses the digit-adjacent `1>>` as a stream-handle redirect and eats the value (and MSVC putenv
|
||||||
|
treats an EMPTY value as UNSET, so the flag silently never arms). Space it (`echo VAR=1 >>file`,
|
||||||
|
trailing-space payload) or use PowerShell `Add-Content`. Bit us arming BT_GLASS_IDS 2026-08-10.
|
||||||
|
|
||||||
|
**8. Tester-procedure deploy VERIFIED end-to-end (2026-08-10, build 876):** scp zip →
|
||||||
|
`Expand-Archive` → podkit → `schtasks /run` came up clean on the first try — rig config survived,
|
||||||
|
newest-install auto-resolve worked, no hand-edits. The cab genuinely upgrades like a tester box.
|
||||||
|
|
||||||
|
**9a. ⚠ THE RIO's "COM1" IS A USB-SERIAL ADAPTER — the axis-dropout / frozen-boot fault
|
||||||
|
(2026-08-10, minidump-proven) [T1].** `\Device\VCP0` = an FTDI adapter (FTE7PGA5A); COM2 is a
|
||||||
|
second FTDI (A995JTOYA); **two ghost Prolific PL2303 entries also claim "COM1"** from earlier
|
||||||
|
plug-ins (clean them in Device Manager). When the adapter's **USB side** drops mid-operation:
|
||||||
|
brief drop = analog polls go mute (the field "encoder dropouts", revived by hand-jostle /
|
||||||
|
re-enumeration — NOT by button protocol); drop with a write in flight = the 1995 code's
|
||||||
|
**unbounded `GetOverlappedResult` wait in `PCSerialPacket::SendPacket` parked the whole game at
|
||||||
|
0 CPU forever** (three boots on 2026-08-10; stack: NtWaitForSingleObject ← GetOverlappedResult ←
|
||||||
|
SendPacket ← RIO ctor). **Hardened in 4.11.881**: 500 ms bounded write + CancelIo + 3 s sick
|
||||||
|
window (fast-drop, game keeps frame rate, keyboard fallback lives); `txTO=` in the `[rio]` health
|
||||||
|
line counts the events — proven the same evening (`txTO=1 rRetry=4` on a boot that SURVIVED).
|
||||||
|
USB **selective suspend disabled** system-wide on the cab the same day (powercfg) — prime suspect
|
||||||
|
for the random-period idle dropouts. Hardware to-do: FTDI adapter into a REAR motherboard USB
|
||||||
|
port, reseat its USB end. ⚠ A first "print-deadlock" diagnosis of the boot wedge was WRONG
|
||||||
|
(confounded A/B) and is retracted — the dump is the authority.
|
||||||
|
**Day-end state (2026-08-10 evening) + the pick-up plan.** The 881 soak measured the fault
|
||||||
|
precisely: the board answers for **~13 s after each fresh game start** (254 replies at 20 Hz),
|
||||||
|
then goes MUTE on analog for minutes while host→board writes keep completing (`req=3846 rep=254
|
||||||
|
age=240s`, txTO frozen at 1, abandon=0). Direct hand-poll (`tools/probe_rio.ps1`, also at
|
||||||
|
`C:\bt411\probe_rio.ps1` — raw COM1 CHECK/VERSION/ANALOG with correct [cmd][sum&0x7F] framing,
|
||||||
|
DTR/RTS raised): **zero bytes**. USB re-enumeration of BOTH FTDI converters + immediate re-poll:
|
||||||
|
**still zero** — which argues AGAINST the adapter (a wedged adapter RX clears on re-enum) and AT
|
||||||
|
the board. ⚠ [T4 hypothesis for tomorrow]: 13 s ≈ when the mission render load lights up — a
|
||||||
|
**power rail sagging under the cab's full load** would also explain the original mid-combat
|
||||||
|
dropouts (explosion/load transients) and the button "revival" (load dips + harness jostle).
|
||||||
|
Tomorrow, with hands: (1) loopback plug on the FTDI (TX→RX jumper, probe echoes = adapter fully
|
||||||
|
exonerated); (2) 5 V/12 V at the RIO board while a mission loads (the 13 s correlation is the
|
||||||
|
test); (3) swap in one of the spare Prolific adapters as cheap elimination; (4) rear-mobo USB
|
||||||
|
port + ghost-COM cleanup regardless. ⚠ My STALL/RECOVER edge detector in 881 keys on
|
||||||
|
request-age, which the 50 ms poll keeps resetting — it never fires; the `age=` field carries the
|
||||||
|
real signal (fix the keying to reply-age at the next build).
|
||||||
|
**Remote minidump procedure (worked over SSH, no tools install):** 32-bit dump of the wedged pid
|
||||||
|
via `C:\Windows\SysWOW64\rundll32.exe C:\Windows\SysWOW64\comsvcs.dll, MiniDump <pid>
|
||||||
|
C:\bt411\w.dmp full` → scp home → local x86 cdb (`where.exe cdb` resolves the bt411-tools copy)
|
||||||
|
with `-y C:\git\bt411\dist` (the archived per-build PDBs) → `~*k`. A 64-bit comsvcs dump of the
|
||||||
|
WOW64 process shows only 64-bit frames — use the SysWOW64 pair.
|
||||||
|
|
||||||
|
**9. ⚠ `schtasks /end` ORPHANS the game — the dead-RIO trap (2026-08-10):** `/end` kills the
|
||||||
|
batch, but `runpod.bat` `start ""`s the exe DETACHED, so the old btl4.exe survives, keeps COM1
|
||||||
|
open, and the NEXT launch boots with a dead cockpit board: `InitSerialPort() failed
|
||||||
|
[GetLastError() = 5]` (access denied) → `RIO initialization failed!` → buttons dead while the
|
||||||
|
banner still says "hardware RIO" (the banner is the PROFILE, not the init result). Diagnosis:
|
||||||
|
`tasklist /FI "IMAGENAME eq btl4.exe"` showing ≥2 instances. FIXED at the root the same day:
|
||||||
|
`runpod.bat` now `taskkill /F /IM btl4.exe`s FIRST (original saved as `runpod.bat.bak`), making
|
||||||
|
the BT411Run task an idempotent restart — but any hand-rolled launcher on a future cab needs the
|
||||||
|
same line, and a mid-mission `/run` now RESTARTS the game rather than failing quietly.
|
||||||
|
|
||||||
## The 1995 player manual — alignment audit (2026-07-18) [T1, primary source]
|
## The 1995 player manual — alignment audit (2026-07-18) [T1, primary source]
|
||||||
`reference/manual/Tesla40_BT_manual.pdf` (34pp, from Nick). CONFIRMS the reconstruction on
|
`reference/manual/Tesla40_BT_manual.pdf` (34pp, from Nick). CONFIRMS the reconstruction on
|
||||||
|
|||||||
@@ -339,7 +339,7 @@ cavern.egg`. This is every option the original console exposed for a BattleTech
|
|||||||
- experience (4 [T1 game decode]): novice, standard, veteran, expert ← was hardcoded `expert`
|
- experience (4 [T1 game decode]): novice, standard, veteran, expert ← was hardcoded `expert`
|
||||||
- vehicle (18, friendly→tag): Black Hawk=blkhawk, Loki=loki, Nova=bhk1, Mad Cat=madcat, Thor=thor,
|
- vehicle (18, friendly→tag): Black Hawk=blkhawk, Loki=loki, Nova=bhk1, Mad Cat=madcat, Thor=thor,
|
||||||
Owens=owens, Kotori=own1, Summoner=thr1, Hellbringer=lok1, Timber Wolf=mad1, Avatar=avatar,
|
Owens=owens, Kotori=own1, Summoner=thr1, Hellbringer=lok1, Timber Wolf=mad1, Avatar=avatar,
|
||||||
Satyr=ava1, Sunder=sunder, Sunder V1=snd1, Vulture=vulture, Mad Dog=vul1, Mischief=lok2,
|
Satyr=ava1, Sunder=sunder, Denkou=snd1 (#69), Vulture=vulture, Mad Dog=vul1, Mischief=lok2,
|
||||||
Zanin Neko=mad2 (the 18 ModelList names; matches the 2026-07-18 vehicle sweep)
|
Zanin Neko=mad2 (the 18 ModelList names; matches the 2026-07-18 vehicle sweep)
|
||||||
- color/camo (7, display→tag): White=White, Black=Black, Brown=Brown, Red=**Crimson**, Green=Green,
|
- color/camo (7, display→tag): White=White, Black=Black, Brown=Brown, Red=**Crimson**, Green=Green,
|
||||||
Gray=**Grey**, Tan=Tan ← was White/Black/Crimson only
|
Gray=**Grey**, Tan=Tan ← was White/Black/Crimson only
|
||||||
|
|||||||
@@ -0,0 +1,135 @@
|
|||||||
|
=============================================================================
|
||||||
|
BT411 -- ALL OPEN TRACKER ISSUES (59), SORTED FOR PLAYTESTERS
|
||||||
|
Updated 2026-08-10 (evening) -- after Oracle's full annotated sweep, which
|
||||||
|
closed 35 issues. Thank you Oracle: that is what this list is for.
|
||||||
|
=============================================================================
|
||||||
|
HOW TO REPORT: drop a line in Discord with the issue number and PASS /
|
||||||
|
FAIL / what you saw. Screenshots and rough times help; logs get collected
|
||||||
|
as usual.
|
||||||
|
=============================================================================
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
A. FIXED -- PLEASE VERIFY (headliners first: new fixes fielded THIS build)
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
#149 Glass cockpit stutter / hitching ** HEADLINER **
|
||||||
|
First fielded build with the fix. Glass users -- especially Oracle:
|
||||||
|
mission load and play should feel smooth again (repaints now skip
|
||||||
|
windows whose gauges did not change).
|
||||||
|
#150 Double kill credit (one kill counted twice) ** HEADLINER **
|
||||||
|
First fielded build with the fix (868's double-kills were pre-fix).
|
||||||
|
TEST: kill someone with a missile volley -- scoreboard +1, not +2.
|
||||||
|
#69 "Sunder V1" renamed to DENKOU
|
||||||
|
TEST: vehicle picker shows Denkou -- era witnesses, confirm spelling.
|
||||||
|
#71 Closing the Plasma window lost it until restart
|
||||||
|
TEST: X the plasma window -- it minimizes to the taskbar; click restores.
|
||||||
|
#25 Gamepad turn profile (stick X could not turn the mech)
|
||||||
|
TEST: RajelAran -- bind an axis to "Turn" in content\bindings.txt.
|
||||||
|
#27 Autocannon/Gauss dealt the wrong damage type
|
||||||
|
Oracle asked for a RETEST before closing: ballistics should feel/score
|
||||||
|
like solid shot (no blast splash).
|
||||||
|
#38 Mech colors reverted on respawn
|
||||||
|
TEST: custom paint, die, respawn -- colors stay.
|
||||||
|
#39 Controls were live before the game screen appeared
|
||||||
|
TEST: mash fire during load -- nothing happens until the scene shows.
|
||||||
|
#41 (David/Auric) launcher killed before it could run -- forensics armed
|
||||||
|
TEST: if join.bat ever fails again, send content\lastrun_join.txt.
|
||||||
|
#56 MFD buttons drift away from where they are drawn
|
||||||
|
TEST: alt-tab / focus changes mid-round -- buttons stay aligned.
|
||||||
|
#147 Range caret vanished for the session (ticks stayed)
|
||||||
|
Monitoring: if it happens, the log traps the culprit -- just report it.
|
||||||
|
#158 (Sauron) could not move after respawn until throttle cycled
|
||||||
|
TEST when it happens: does cycling the throttle free you?
|
||||||
|
YES -> the #146 release mechanic (expected). NO -> map + spawn spot.
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
B. OPEN -- HELP US REPRODUCE / NARROW (what to note when it happens)
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
#24 Controller hat/look view sticks (recovers after wiggling)
|
||||||
|
#37 Mech torso appears BACKWARDS to other players (transient)
|
||||||
|
#74 8BitDo gamepad works solo, dead in Steam MP (VexUbiquity)
|
||||||
|
#102 Owens: full-throttle run -> hold reverse = animation/audio hang
|
||||||
|
REPRO known (exterior cam, savannah); self-clears on a torso twist.
|
||||||
|
#125 Peers do not see the FIRST destroyed-zone graphic (2nd hit shows it)
|
||||||
|
#126 Peer weapon fire invisible AND silent from some positions
|
||||||
|
#128 Ramming terrain = INSTANT coolant leak -- Oracle re-confirms STILL LIVE
|
||||||
|
#130 Vulture "skating" (gliding without walk animation)
|
||||||
|
#132 (Conn Man) persistent yellow line on the HUD in every mech
|
||||||
|
#135 Coolant leak with NO flashing annunciators -- Oracle re-confirms LIVE
|
||||||
|
#136 A SHUT leaking loop starts/stops leaking with COMPONENT toggles
|
||||||
|
(Oracle marked "Clarify" -- one more description pass requested.)
|
||||||
|
#143 One panel hit -> smoke erupts from MULTIPLE places at once
|
||||||
|
#153 Basic-mode elevation limit only applies after cycling modes (fix pending)
|
||||||
|
#154 Generator Out warning fires on the WRONG EDGE (at recovery, not trip)
|
||||||
|
Refined by Oracle: the stuck-forever gen did NOT reproduce; era rule =
|
||||||
|
gens return at HALF-cooled (red bar portion gone). Threshold audit due.
|
||||||
|
#155 PANIC lit: Eng + Weapon MFD banks dead; Score/Radar/Coolant stay live
|
||||||
|
(Oracle mapped the exact bank split; #50 closed into this ticket.
|
||||||
|
Flicker half = authentic. Next: is the bank capture authentic too?)
|
||||||
|
#159 (Conn Man) lag during drops -- missiles / fire exploding in empty space
|
||||||
|
NOTE: shooter + target + rough time; match receipts measure the rest.
|
||||||
|
#160 Pod cab: RIO axis dropouts + boot wedges (hardware investigation)
|
||||||
|
Cab-side only. Bounded-write hardening is in this build; the cage
|
||||||
|
hardware work continues with Nick.
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
C. STILL-OPEN ERA QUESTIONS
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
#103 Mech-to-mech RAM: armor damage or internals only? (Oracle: don't know;
|
||||||
|
Lynx / Ronin / Draco -- your call. Binary does internals-only.)
|
||||||
|
#133 Generator X-out on the MFD: Oracle recalls an X "under certain
|
||||||
|
circumstances" -- WHICH circumstances? (shutdown / overheat / destroyed)
|
||||||
|
ALSO (#152 rider): did Mid/Adv auto-recentre the torso in the pod, or only
|
||||||
|
Basic? (Oracle: don't know. Binary says: no auto-recentre.)
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
D. DESIGN CALLS -- group opinions wanted
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
#151 Panic eject costs -499: verified REAL 1995 scoring (your death scored
|
||||||
|
as suicide + death penalty). Keep authentic, or house-rule a flat cost?
|
||||||
|
#23 Non-glass screen layout: Oracle proposes widescreen-aware sizing --
|
||||||
|
world view wider + taller, moved up, allowed to overlap the radar.
|
||||||
|
Opinions welcome before anyone prototypes it.
|
||||||
|
#77 Resizable Secondary/Radar window (touch-panel cockpits). Want it?
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
E. KNOWN GAPS -- confirmed and on the list, no need to re-report
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
#49 Radar/map does not draw the arena WALL geometry
|
||||||
|
#76 Glass layout save/restore rework (some windows not saved)
|
||||||
|
#83 INCOMING MISSILE alarm not implemented yet
|
||||||
|
#90 Flames/smoke from serious damage: less than the original (tuning)
|
||||||
|
#104 Mad Dog "NARC 15" label: NARC postdates our 4.10 binary (Oracle: added
|
||||||
|
in a late 4.x release; zero NARC in our decomp) -- content rename due.
|
||||||
|
#105 Sensor damage effects: Oracle's era spec captured (HUD flicker/loss,
|
||||||
|
radar loss) -- implementation pending.
|
||||||
|
#111 Peers see a dying mech's blue warp bubble (should see explosion only)
|
||||||
|
#112 AFC rounds shotgun across zones (should land as one projectile)
|
||||||
|
#113 LRM burn/hit state lasts too long
|
||||||
|
#115 Hot box / target select not connected (Oracle era note: hotbox was a
|
||||||
|
STANDARD-mode feature; absent in Expert -- gating to reconcile)
|
||||||
|
#121 Sensor HEAT has no consequences yet
|
||||||
|
#123 Searchlight: the visible LIGHT + night-fog swap still to come
|
||||||
|
#145 IR "predator vision": decode done, display not implemented
|
||||||
|
#156 Fade-to-black at mission end not happening
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
F. INTERNAL ENGINEERING BACKLOG (nothing to test)
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
#14 Glass panel unreconstructed button handler backlog (census)
|
||||||
|
#17 Cavern upper-butte halves lack a collision stream (authenticity TBD)
|
||||||
|
#21 Weapon recharge arc dark after eng-page use (symptom trail -> #62)
|
||||||
|
#28 Confirm the vital-subsystem-crit death path can fire (machinery in)
|
||||||
|
#30 Weapon "unavailable" (level-7) display verification + accessor cleanup
|
||||||
|
#63 PlayerStatus reads raw 1995 offsets (databinding trap, dormant)
|
||||||
|
#64 damageZone shadow field (half-fixed; consequences disarmed)
|
||||||
|
#65 HandleMessage non-virtual across 10 classes (latent dispatch trap)
|
||||||
|
#72 BTLoadPump swallows WM_QUIT during load (latent)
|
||||||
|
#107 Destroyed HeatSink annunciates nothing (no lamp for condition 0)
|
||||||
|
#117 KB note: per-chassis cockpit bounce was deliberate (Ronin) -- FYI
|
||||||
|
|
||||||
|
=============================================================================
|
||||||
|
End of list. 59 open issues total:
|
||||||
|
12 verify (2 headliners), 17 reproduce, 3 era questions, 3 design calls,
|
||||||
|
14 known gaps, 11 internal.
|
||||||
|
35 issues closed 2026-08-10 on Oracle's annotated sweep.
|
||||||
|
=============================================================================
|
||||||
@@ -2104,19 +2104,7 @@ static unsigned long
|
|||||||
if (svga == NULL) svga = static_cast<SVGA16*>(p->graphicsDisplay);
|
if (svga == NULL) svga = static_cast<SVGA16*>(p->graphicsDisplay);
|
||||||
}
|
}
|
||||||
if (svga != NULL && combined != 0)
|
if (svga != NULL && combined != 0)
|
||||||
{
|
|
||||||
token ^= svga->PlaneChecksum(combined);
|
token ^= svga->PlaneChecksum(combined);
|
||||||
// PALETTE-ANIMATED content (2026-08-10): a palette-expanding window
|
|
||||||
// (the radar; MFDs under BT_GLASS_MFD_PAL) changes colour with ZERO
|
|
||||||
// pixel writes -- the ColorMapper family (armor rosette tints, the
|
|
||||||
// adpal/adpal2 damage flash) writes CLUT entries. Fold the palette
|
|
||||||
// write generation in so those changes repaint; mono-tint windows
|
|
||||||
// don't read the palette and keep their pixel-only token.
|
|
||||||
static int sPalTok = -1;
|
|
||||||
if (sPalTok < 0) sPalTok = getenv("BT_GLASS_MFD_PAL") ? 1 : 0;
|
|
||||||
if (w->monoTint < 0 || sPalTok)
|
|
||||||
token = (token ^ svga->PaletteGeneration()) * 16777619UL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lamps: each button's RENDERED brightness + held/latched, so a flash toggle or a
|
// Lamps: each button's RENDERED brightness + held/latched, so a flash toggle or a
|
||||||
@@ -2133,12 +2121,21 @@ static unsigned long
|
|||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// [rio] health printer (L4RIO.cpp): the serial poll path only COUNTS (kept
|
||||||
|
// I/O-free by design; the 2026-08-10 wedges once blamed on prints there were
|
||||||
|
// really the USB-serial adapter hanging an unbounded overlapped write --
|
||||||
|
// see PCSerialPacket::SendPacket); the report rides this tick, which
|
||||||
|
// already streams [glassperf] every second.
|
||||||
|
extern "C" void BTRioHealthReport();
|
||||||
|
|
||||||
void
|
void
|
||||||
BTGlassPanels_Tick()
|
BTGlassPanels_Tick()
|
||||||
{
|
{
|
||||||
if (gWinCount == 0)
|
if (gWinCount == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
BTRioHealthReport();
|
||||||
|
|
||||||
static unsigned long sLastPaint = 0;
|
static unsigned long sLastPaint = 0;
|
||||||
unsigned long now = GetTickCount();
|
unsigned long now = GetTickCount();
|
||||||
if (now - sLastPaint < (unsigned long)RepaintMilliseconds)
|
if (now - sLastPaint < (unsigned long)RepaintMilliseconds)
|
||||||
|
|||||||
@@ -410,6 +410,20 @@ int PCSerialPacket::ReceivePacket(BYTE *destPtr) // returns len (zero if none)
|
|||||||
// Returns:
|
// Returns:
|
||||||
// void
|
// void
|
||||||
//-------------------------------------------------------------------------
|
//-------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Serial-write health (see the bounded wait below): after a write timeout
|
||||||
|
// the link is SICK for 3 s and writes fast-drop -- one stall, not one per
|
||||||
|
// poll. BTPcspakTxTimeouts feeds the [rio] telemetry.
|
||||||
|
//
|
||||||
|
static unsigned gPcspakTxTimeout = 0;
|
||||||
|
static DWORD gPcspakTxSickUntil = 0;
|
||||||
|
|
||||||
|
extern "C" unsigned
|
||||||
|
BTPcspakTxTimeouts()
|
||||||
|
{
|
||||||
|
return gPcspakTxTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
void PCSerialPacket::SendPacket(BYTE *srcPtr) //sends packet
|
void PCSerialPacket::SendPacket(BYTE *srcPtr) //sends packet
|
||||||
{
|
{
|
||||||
//--------------------------------------------------------------
|
//--------------------------------------------------------------
|
||||||
@@ -418,6 +432,11 @@ void PCSerialPacket::SendPacket(BYTE *srcPtr) //sends packet
|
|||||||
if(!enabled) //if disabled, don't even try
|
if(!enabled) //if disabled, don't even try
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (gPcspakTxSickUntil != 0 && GetTickCount() < gPcspakTxSickUntil)
|
||||||
|
{
|
||||||
|
return; // adapter sick: drop, don't stall
|
||||||
|
}
|
||||||
|
|
||||||
BYTE cmd = *srcPtr; //get the command byte
|
BYTE cmd = *srcPtr; //get the command byte
|
||||||
cmd &= 0x7F; //remove MSB
|
cmd &= 0x7F; //remove MSB
|
||||||
int length = txLengthPtr[cmd]; //get the length in al
|
int length = txLengthPtr[cmd]; //get the length in al
|
||||||
@@ -459,11 +478,37 @@ void PCSerialPacket::SendPacket(BYTE *srcPtr) //sends packet
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DWORD bytesWritten;
|
//
|
||||||
GetOverlappedResult(hComm, &overlapSend, &bytesWritten, TRUE);
|
// BOUNDED completion wait (2026-08-10, ALPHA-MR field minidump). The
|
||||||
if(bytesWritten != length + 1)
|
// RIO's "COM1" is a USB-serial adapter; when its USB link drops with a
|
||||||
|
// write in flight the overlapped write NEVER completes, and the original
|
||||||
|
// GetOverlappedResult(..., TRUE) parked the whole game at 0 CPU forever
|
||||||
|
// (cdb-proven: NtWaitForSingleObject <- GetOverlappedResult <-
|
||||||
|
// SendPacket <- RIO ctor). Now: 500 ms bound (9600 baud needs ~21 ms),
|
||||||
|
// CancelIo on expiry, and a 3 s SICK window that fast-drops writes so a
|
||||||
|
// dead adapter costs one stall, not one per 50 ms poll -- the game stays
|
||||||
|
// at frame rate with a degraded RIO and the keyboard fallback alive.
|
||||||
|
// gPcspakTxTimeout feeds the [rio] health line (txTO=).
|
||||||
|
//
|
||||||
|
DWORD bytesWritten = 0;
|
||||||
|
DWORD wait_result = WaitForSingleObject(overlapSend.hEvent, 500);
|
||||||
|
if (wait_result == WAIT_OBJECT_0)
|
||||||
{
|
{
|
||||||
DEBUG_STREAM << "Not all data was written to serial port!\n" << std::flush;
|
GetOverlappedResult(hComm, &overlapSend, &bytesWritten, FALSE);
|
||||||
|
gPcspakTxSickUntil = 0;
|
||||||
|
if (bytesWritten != (DWORD)(length + 1))
|
||||||
|
{
|
||||||
|
DEBUG_STREAM << "Not all data was written to serial port!\n"
|
||||||
|
<< std::flush;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// No print here -- serial-path discipline; the counter surfaces in
|
||||||
|
// the [rio] health line (txTO=) from the glass-tick printer.
|
||||||
|
CancelIo(hComm);
|
||||||
|
++gPcspakTxTimeout;
|
||||||
|
gPcspakTxSickUntil = GetTickCount() + 3000;
|
||||||
}
|
}
|
||||||
delete buffer;
|
delete buffer;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,11 @@ static LRESULT CALLBACK
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
case WM_CLOSE:
|
case WM_CLOSE:
|
||||||
ShowWindow(window, SW_HIDE); // hide only; the renderer owns it
|
// Minimize, never hide (#71): SW_HIDE left no way back short of a
|
||||||
|
// restart. A minimized window keeps its taskbar button (both framed
|
||||||
|
// and ,noframe styles), so the player can recover the display. The
|
||||||
|
// renderer still owns the window either way.
|
||||||
|
ShowWindow(window, SW_MINIMIZE);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return DefWindowProcW(window, message, wparam, lparam);
|
return DefWindowProcW(window, message, wparam, lparam);
|
||||||
|
|||||||
@@ -1240,6 +1240,105 @@ RIO::~RIO()
|
|||||||
Check_Fpu();
|
Check_Fpu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// [rio] telemetry state (see the tracking block in GetNextEvent). File-scope
|
||||||
|
// statics on purpose: one hardware RIO per process, no header/layout churn --
|
||||||
|
// and the PRINTER (BTRioHealthReport, called from the glass tick) reads ONLY
|
||||||
|
// these statics, never RIO members, so it needs no instance and touches
|
||||||
|
// nothing the serial layer locks.
|
||||||
|
//
|
||||||
|
static int gRioPresent = 0;
|
||||||
|
static unsigned gRioReqCount = 0;
|
||||||
|
static unsigned gRioRepCount = 0;
|
||||||
|
static unsigned gRioBtnCount = 0;
|
||||||
|
static int gRioReqPending = 0;
|
||||||
|
static DWORD gRioLastReqTick = 0;
|
||||||
|
static DWORD gRioLastReplyTick = 0;
|
||||||
|
static int gRioStallActive = 0;
|
||||||
|
static DWORD gRioStallStart = 0;
|
||||||
|
static unsigned gRioStallBtnBase = 0;
|
||||||
|
static unsigned gRioStallCount = 0;
|
||||||
|
static DWORD gRioLastStallMs = 0;
|
||||||
|
static unsigned gRioLastStallBtns = 0;
|
||||||
|
static unsigned gRioLineErrMirror = 0;
|
||||||
|
static unsigned gRioOverrunMirror = 0;
|
||||||
|
static unsigned gRioAbandonMirror = 0;
|
||||||
|
static unsigned gRioRRetryMirror = 0;
|
||||||
|
static unsigned gRioRAbandonMirror = 0;
|
||||||
|
static unsigned gRioRFullMirror = 0;
|
||||||
|
|
||||||
|
extern "C" unsigned BTPcspakTxTimeouts(); // L4PCSPAK.cpp: bounded-write stat
|
||||||
|
|
||||||
|
//
|
||||||
|
// The printer half -- called from BTGlassPanels_Tick beside the [glassperf]
|
||||||
|
// report (a stream site that prints every second without incident), NEVER
|
||||||
|
// from the serial path. Edge lines land at the tick's ~1 s granularity,
|
||||||
|
// ample for the multi-second field dropouts this exists to catch.
|
||||||
|
// BT_RIO_LOG=0 opts out; silent when no hardware RIO ever came up.
|
||||||
|
//
|
||||||
|
extern "C" void
|
||||||
|
BTRioHealthReport()
|
||||||
|
{
|
||||||
|
static int sLog = -1;
|
||||||
|
static DWORD sLastLine = 0;
|
||||||
|
static unsigned sSeenStalls = 0;
|
||||||
|
static int sSawActive = 0;
|
||||||
|
|
||||||
|
if (!gRioPresent)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (sLog < 0)
|
||||||
|
{
|
||||||
|
const char *e = getenv("BT_RIO_LOG");
|
||||||
|
sLog = (e != 0 && *e == '0') ? 0 : 1;
|
||||||
|
}
|
||||||
|
if (!sLog)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD now = GetTickCount();
|
||||||
|
if (gRioStallActive && !sSawActive)
|
||||||
|
{
|
||||||
|
sSawActive = 1;
|
||||||
|
DEBUG_STREAM << "[rio] STALL analog silent "
|
||||||
|
<< (now - gRioStallStart) << "ms and counting req="
|
||||||
|
<< gRioReqCount << " rep=" << gRioRepCount
|
||||||
|
<< "\n" << std::flush;
|
||||||
|
}
|
||||||
|
else if (!gRioStallActive && sSawActive)
|
||||||
|
{
|
||||||
|
sSawActive = 0;
|
||||||
|
}
|
||||||
|
if (sSeenStalls != gRioStallCount)
|
||||||
|
{
|
||||||
|
sSeenStalls = gRioStallCount;
|
||||||
|
DEBUG_STREAM << "[rio] RECOVER stall#" << gRioStallCount
|
||||||
|
<< " lasted " << gRioLastStallMs
|
||||||
|
<< "ms btnDuring=" << gRioLastStallBtns
|
||||||
|
<< "\n" << std::flush;
|
||||||
|
}
|
||||||
|
if (now - sLastLine >= 5000)
|
||||||
|
{
|
||||||
|
sLastLine = now;
|
||||||
|
DEBUG_STREAM << "[rio] req=" << gRioReqCount
|
||||||
|
<< " rep=" << gRioRepCount
|
||||||
|
<< " btn=" << gRioBtnCount
|
||||||
|
<< " age=" << (gRioLastReplyTick != 0
|
||||||
|
? (now - gRioLastReplyTick) : 0)
|
||||||
|
<< "ms stalls=" << gRioStallCount
|
||||||
|
<< " txTO=" << BTPcspakTxTimeouts()
|
||||||
|
<< " lineErr=" << gRioLineErrMirror
|
||||||
|
<< " overrun=" << gRioOverrunMirror
|
||||||
|
<< " abandon=" << gRioAbandonMirror
|
||||||
|
<< " rRetry=" << gRioRRetryMirror
|
||||||
|
<< " rAbandon=" << gRioRAbandonMirror
|
||||||
|
<< " rFull=" << gRioRFullMirror
|
||||||
|
<< "\n" << std::flush;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Logical
|
Logical
|
||||||
RIO::TestInstance() const
|
RIO::TestInstance() const
|
||||||
{
|
{
|
||||||
@@ -1264,6 +1363,36 @@ Logical
|
|||||||
Logical reply, looping;
|
Logical reply, looping;
|
||||||
Word errors;
|
Word errors;
|
||||||
|
|
||||||
|
//
|
||||||
|
// [rio] link-health TRACKING -- pure arithmetic, no I/O here by design.
|
||||||
|
// CORRECTED HISTORY (2026-08-10): the boot wedges first blamed on prints
|
||||||
|
// from this path were actually the USB-serial adapter dropping its link
|
||||||
|
// with a write in flight -- the unbounded GetOverlappedResult in
|
||||||
|
// PCSerialPacket::SendPacket parked the game (minidump-proven; the
|
||||||
|
// print-deadlock A/B was confounded by the adapter behaving that boot).
|
||||||
|
// The count-here / print-from-the-glass-tick split stays anyway: it
|
||||||
|
// keeps this hot polled path I/O-free and the printer at one known
|
||||||
|
// stream site (BTRioHealthReport below, beside [glassperf]).
|
||||||
|
//
|
||||||
|
if (operational)
|
||||||
|
{
|
||||||
|
DWORD now = GetTickCount();
|
||||||
|
if (!gRioStallActive && gRioReqPending
|
||||||
|
&& now - gRioLastReqTick > 500)
|
||||||
|
{
|
||||||
|
gRioStallActive = 1;
|
||||||
|
gRioStallStart = gRioLastReqTick;
|
||||||
|
gRioStallBtnBase = gRioBtnCount;
|
||||||
|
}
|
||||||
|
gRioPresent = 1;
|
||||||
|
gRioLineErrMirror = (unsigned) lineErrorCount;
|
||||||
|
gRioOverrunMirror = (unsigned) overrunCount;
|
||||||
|
gRioAbandonMirror = (unsigned) abandonCount;
|
||||||
|
gRioRRetryMirror = (unsigned) remoteRetryCount;
|
||||||
|
gRioRAbandonMirror = (unsigned) remoteAbandonCount;
|
||||||
|
gRioRFullMirror = (unsigned) remoteFullBufferCount;
|
||||||
|
}
|
||||||
|
|
||||||
//PCSPAKState(this);
|
//PCSPAKState(this);
|
||||||
//
|
//
|
||||||
//cout << flush;
|
//cout << flush;
|
||||||
@@ -1377,6 +1506,20 @@ Logical
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case AnalogReply:
|
case AnalogReply:
|
||||||
|
++gRioRepCount;
|
||||||
|
gRioReqPending = 0;
|
||||||
|
gRioLastReplyTick = GetTickCount();
|
||||||
|
if (gRioStallActive)
|
||||||
|
{
|
||||||
|
// The reply that ended the drought -- record only; the
|
||||||
|
// glass-tick printer emits the RECOVER line. btnDuring
|
||||||
|
// is the field theory's verdict: >0 on every recover =
|
||||||
|
// button traffic resyncs the link; 0 = it self-heals.
|
||||||
|
gRioStallActive = 0;
|
||||||
|
++gRioStallCount;
|
||||||
|
gRioLastStallMs = gRioLastReplyTick - gRioStallStart;
|
||||||
|
gRioLastStallBtns = gRioBtnCount - gRioStallBtnBase;
|
||||||
|
}
|
||||||
destination->Type = RIO::AnalogEvent;
|
destination->Type = RIO::AnalogEvent;
|
||||||
//
|
//
|
||||||
// NOTE: no data is sent in this packet.
|
// NOTE: no data is sent in this packet.
|
||||||
@@ -1423,11 +1566,13 @@ Logical
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ButtonPressed:
|
case ButtonPressed:
|
||||||
|
++gRioBtnCount;
|
||||||
destination->Type = RIO::ButtonPressedEvent;
|
destination->Type = RIO::ButtonPressedEvent;
|
||||||
destination->Data.Unit = receive_buffer[1];
|
destination->Data.Unit = receive_buffer[1];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ButtonReleased:
|
case ButtonReleased:
|
||||||
|
++gRioBtnCount;
|
||||||
destination->Type = RIO::ButtonReleasedEvent;
|
destination->Type = RIO::ButtonReleasedEvent;
|
||||||
destination->Data.Unit = receive_buffer[1];
|
destination->Data.Unit = receive_buffer[1];
|
||||||
break;
|
break;
|
||||||
@@ -1568,6 +1713,9 @@ void
|
|||||||
if (operational && !TestModeActive)
|
if (operational && !TestModeActive)
|
||||||
{
|
{
|
||||||
SendPacket((Byte *) request_analog_string);
|
SendPacket((Byte *) request_analog_string);
|
||||||
|
++gRioReqCount;
|
||||||
|
gRioReqPending = 1;
|
||||||
|
gRioLastReqTick = GetTickCount();
|
||||||
}
|
}
|
||||||
Check_Fpu();
|
Check_Fpu();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5445,7 +5445,6 @@ SVGA16::SVGA16(
|
|||||||
BuildWindows(init_width,init_height,windowed, secondaryIndex, aux1Index, aux2Index);
|
BuildWindows(init_width,init_height,windowed, secondaryIndex, aux1Index, aux2Index);
|
||||||
for (int _i = 0; _i < 10; _i++) // DEV-COMPOSITE: lazily created on first surface draw
|
for (int _i = 0; _i < 10; _i++) // DEV-COMPOSITE: lazily created on first surface draw
|
||||||
mDevSurfaceTex[_i] = NULL;
|
mDevSurfaceTex[_i] = NULL;
|
||||||
paletteGeneration = 0; // GLASS dirty-skip palette tracking
|
|
||||||
//STUBBED: VIDEO RB 1/15/07
|
//STUBBED: VIDEO RB 1/15/07
|
||||||
# if defined(DEBUG)
|
# if defined(DEBUG)
|
||||||
Tell("SVGA16::SVGA16()\n");
|
Tell("SVGA16::SVGA16()\n");
|
||||||
@@ -7191,7 +7190,6 @@ void
|
|||||||
|
|
||||||
svga_palette->paletteData.Valid = True;
|
svga_palette->paletteData.Valid = True;
|
||||||
svga_palette->modified = True;
|
svga_palette->modified = True;
|
||||||
((SVGA16 *) graphicsDisplay)->paletteGeneration++; // glass dirty-skip (full rebuild)
|
|
||||||
|
|
||||||
Check_Fpu();
|
Check_Fpu();
|
||||||
}
|
}
|
||||||
@@ -7236,13 +7234,6 @@ void
|
|||||||
SVGA16Palette
|
SVGA16Palette
|
||||||
*svga_palette(&((SVGA16 *) graphicsDisplay)->palette[paletteID]);
|
*svga_palette(&((SVGA16 *) graphicsDisplay)->palette[paletteID]);
|
||||||
|
|
||||||
// GLASS dirty-skip: track whether this write CHANGES anything -- the
|
|
||||||
// ColorMapper family calls this every Execute (the adpal/adpal2 flash
|
|
||||||
// alternates palettes even at zero damage, usually writing identical RGB),
|
|
||||||
// so bump the generation only on a real change or every glass window that
|
|
||||||
// palette-expands would repaint every pump for nothing.
|
|
||||||
int palette_changed = 0;
|
|
||||||
|
|
||||||
//-------------------------------------------
|
//-------------------------------------------
|
||||||
// If any of the ...TransparentZero modes are used,
|
// If any of the ...TransparentZero modes are used,
|
||||||
// leave color zero undefined for this bit group by
|
// leave color zero undefined for this bit group by
|
||||||
@@ -7284,31 +7275,21 @@ void
|
|||||||
{
|
{
|
||||||
case RedChannel:
|
case RedChannel:
|
||||||
case RedChannelTransparentZero:
|
case RedChannelTransparentZero:
|
||||||
if (destination_triplet->Red != source_triplet->Red)
|
|
||||||
palette_changed = 1;
|
|
||||||
destination_triplet->Red = source_triplet->Red;
|
destination_triplet->Red = source_triplet->Red;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GreenChannel:
|
case GreenChannel:
|
||||||
case GreenChannelTransparentZero:
|
case GreenChannelTransparentZero:
|
||||||
if (destination_triplet->Green != source_triplet->Green)
|
|
||||||
palette_changed = 1;
|
|
||||||
destination_triplet->Green = source_triplet->Green;
|
destination_triplet->Green = source_triplet->Green;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BlueChannel:
|
case BlueChannel:
|
||||||
case BlueChannelTransparentZero:
|
case BlueChannelTransparentZero:
|
||||||
if (destination_triplet->Blue != source_triplet->Blue)
|
|
||||||
palette_changed = 1;
|
|
||||||
destination_triplet->Blue = source_triplet->Blue;
|
destination_triplet->Blue = source_triplet->Blue;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AllChannels:
|
case AllChannels:
|
||||||
case AllChannelsTransparentZero:
|
case AllChannelsTransparentZero:
|
||||||
if (destination_triplet->Red != source_triplet->Red
|
|
||||||
|| destination_triplet->Green != source_triplet->Green
|
|
||||||
|| destination_triplet->Blue != source_triplet->Blue)
|
|
||||||
palette_changed = 1;
|
|
||||||
*destination_triplet = *source_triplet;
|
*destination_triplet = *source_triplet;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -7329,8 +7310,6 @@ void
|
|||||||
|
|
||||||
svga_palette->paletteData.Valid = True;
|
svga_palette->paletteData.Valid = True;
|
||||||
svga_palette->modified = True;
|
svga_palette->modified = True;
|
||||||
if (palette_changed)
|
|
||||||
((SVGA16 *) graphicsDisplay)->paletteGeneration++; // glass dirty-skip
|
|
||||||
|
|
||||||
Check_Fpu();
|
Check_Fpu();
|
||||||
}
|
}
|
||||||
@@ -7434,7 +7413,6 @@ void
|
|||||||
|
|
||||||
svga_palette->paletteData.Valid = True;
|
svga_palette->paletteData.Valid = True;
|
||||||
svga_palette->modified = True;
|
svga_palette->modified = True;
|
||||||
((SVGA16 *) graphicsDisplay)->paletteGeneration++; // glass dirty-skip (full rebuild)
|
|
||||||
|
|
||||||
# if defined(TESTPALETTE)
|
# if defined(TESTPALETTE)
|
||||||
std::cout << "L4GraphicsPort::BuildAuxiliaryPalette for port " <<
|
std::cout << "L4GraphicsPort::BuildAuxiliaryPalette for port " <<
|
||||||
@@ -7545,7 +7523,6 @@ void
|
|||||||
|
|
||||||
svga_palette->paletteData.Valid = True;
|
svga_palette->paletteData.Valid = True;
|
||||||
svga_palette->modified = True;
|
svga_palette->modified = True;
|
||||||
((SVGA16 *) graphicsDisplay)->paletteGeneration++; // glass dirty-skip (full rebuild)
|
|
||||||
|
|
||||||
Check_Fpu();
|
Check_Fpu();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -382,15 +382,6 @@ protected:
|
|||||||
SVGA16Palette
|
SVGA16Palette
|
||||||
palette[PaletteCount];
|
palette[PaletteCount];
|
||||||
|
|
||||||
// GLASS dirty-skip (2026-08-10): bumped by the palette writers
|
|
||||||
// (L4GraphicsPort::BuildSecondaryColor on a REAL entry change; the full
|
|
||||||
// palette rebuilds unconditionally) so the glass repaint token can see
|
|
||||||
// palette-only animation -- the ColorMapper family (armor rosette tints,
|
|
||||||
// damage flash) changes COLORS with zero pixel writes, which the pixel
|
|
||||||
// checksum alone can never catch.
|
|
||||||
unsigned long
|
|
||||||
paletteGeneration;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int NUMGAUGEWINDOWS;
|
int NUMGAUGEWINDOWS;
|
||||||
HWND *gaugeWindows;
|
HWND *gaugeWindows;
|
||||||
@@ -434,11 +425,6 @@ public:
|
|||||||
// glass repaint pump re-blit ONLY the windows whose plane actually changed
|
// glass repaint pump re-blit ONLY the windows whose plane actually changed
|
||||||
// (idle MFDs / static panels skip; the sweeping radar keeps updating).
|
// (idle MFDs / static panels skip; the sweeping radar keeps updating).
|
||||||
unsigned long PlaneChecksum(int mask) const;
|
unsigned long PlaneChecksum(int mask) const;
|
||||||
|
|
||||||
// GLASS dirty-skip: the palette write generation (see paletteGeneration).
|
|
||||||
// Folded into the repaint token of palette-expanding windows so ColorMapper
|
|
||||||
// palette animation (armor tints / damage flash) repaints without pixel churn.
|
|
||||||
unsigned long PaletteGeneration() const { return paletteGeneration; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//########################################################################
|
//########################################################################
|
||||||
|
|||||||
@@ -234,6 +234,36 @@ static LONG WINAPI
|
|||||||
return EXCEPTION_EXECUTE_HANDLER; // die (after the evidence is out)
|
return EXCEPTION_EXECUTE_HANDLER; // die (after the evidence is out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// lastrun_<stem>.txt clean-exit markers (#157, 2026-08-10): the launch record
|
||||||
|
// only ever said "exe reached WinMain", so a night that ended with the player
|
||||||
|
// closing the menu was indistinguishable from a silent death -- and the bat's
|
||||||
|
// sign-off taught players to report normal quits as crashes. The deliberate
|
||||||
|
// exit paths stamp the record themselves now, so the file answers "crash or
|
||||||
|
// quit?" on its own. Name captured at breadcrumb time (cwd is content\ by
|
||||||
|
// then -- the same file the bats bracket).
|
||||||
|
//
|
||||||
|
static char gBTLastrunName[MAX_PATH] = "";
|
||||||
|
|
||||||
|
void
|
||||||
|
BTLastrunNote(const char *note)
|
||||||
|
{
|
||||||
|
if (gBTLastrunName[0] == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SYSTEMTIME lt;
|
||||||
|
GetLocalTime(<);
|
||||||
|
std::ofstream f(gBTLastrunName, std::ios::out | std::ios::app);
|
||||||
|
if (f.is_open())
|
||||||
|
{
|
||||||
|
char stamp[64];
|
||||||
|
wsprintfA(stamp, "[%04d-%02d-%02d %02d:%02d:%02d] ",
|
||||||
|
lt.wYear, lt.wMonth, lt.wDay, lt.wHour, lt.wMinute, lt.wSecond);
|
||||||
|
f << stamp << note << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//===========================================================================//
|
//===========================================================================//
|
||||||
// environ.ini -- the player's settings file (cwd = content\). One KEY=VALUE
|
// environ.ini -- the player's settings file (cwd = content\). One KEY=VALUE
|
||||||
// per line; the real environment always WINS, so a launcher .bat or a shell
|
// per line; the real environment always WINS, so a launcher .bat or a shell
|
||||||
@@ -833,6 +863,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
|||||||
GetComputerNameA(machine, &machine_len);
|
GetComputerNameA(machine, &machine_len);
|
||||||
char lastrun_name[MAX_PATH];
|
char lastrun_name[MAX_PATH];
|
||||||
wsprintfA(lastrun_name, "lastrun_%s.txt", logStem);
|
wsprintfA(lastrun_name, "lastrun_%s.txt", logStem);
|
||||||
|
lstrcpynA(gBTLastrunName, lastrun_name, MAX_PATH);
|
||||||
std::ofstream breadcrumb(lastrun_name, std::ios::out | std::ios::app);
|
std::ofstream breadcrumb(lastrun_name, std::ios::out | std::ios::app);
|
||||||
if (breadcrumb.is_open())
|
if (breadcrumb.is_open())
|
||||||
{
|
{
|
||||||
@@ -1209,6 +1240,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
|||||||
BTFeLaunchSpec fe_spec;
|
BTFeLaunchSpec fe_spec;
|
||||||
if (BTFrontEnd_Run(&fe_spec) != 0 || fe_spec.mode == BTFeLaunchNone)
|
if (BTFrontEnd_Run(&fe_spec) != 0 || fe_spec.mode == BTFeLaunchNone)
|
||||||
{
|
{
|
||||||
|
BTLastrunNote("clean exit: player quit from the menu");
|
||||||
return 0; // quit from the menu
|
return 0; // quit from the menu
|
||||||
}
|
}
|
||||||
char fe_arguments[192];
|
char fe_arguments[192];
|
||||||
|
|||||||
@@ -174,6 +174,8 @@ void
|
|||||||
{
|
{
|
||||||
MarshalLog("window closed by the user -- exiting for real "
|
MarshalLog("window closed by the user -- exiting for real "
|
||||||
"(no relaunch)");
|
"(no relaunch)");
|
||||||
|
extern void BTLastrunNote(const char *);
|
||||||
|
BTLastrunNote("clean exit: player closed the game window");
|
||||||
ExitProcess(0);
|
ExitProcess(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -257,6 +259,16 @@ void
|
|||||||
if (CreateProcessW(exe_path, command_line, NULL, NULL, FALSE,
|
if (CreateProcessW(exe_path, command_line, NULL, NULL, FALSE,
|
||||||
0, NULL, NULL, &startup, &process))
|
0, NULL, NULL, &startup, &process))
|
||||||
{
|
{
|
||||||
|
// Hand the child our foreground right BEFORE we die (#157): Windows
|
||||||
|
// refuses SetForegroundWindow to a process spawned by an exiting
|
||||||
|
// background parent, so the round-end menu could open BEHIND the
|
||||||
|
// desktop clutter -- which is how a normal between-rounds relaunch
|
||||||
|
// read as "the game crashed" from the cockpit seat.
|
||||||
|
if (!AllowSetForegroundWindow(process.dwProcessId))
|
||||||
|
{
|
||||||
|
MarshalLog("foreground handoff denied -- the next window may "
|
||||||
|
"open behind");
|
||||||
|
}
|
||||||
CloseHandle(process.hThread);
|
CloseHandle(process.hThread);
|
||||||
CloseHandle(process.hProcess);
|
CloseHandle(process.hProcess);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -412,7 +412,7 @@ static const CatalogEntry kVehicles[] =
|
|||||||
{ "avatar", "Avatar" },
|
{ "avatar", "Avatar" },
|
||||||
{ "ava1", "Satyr" },
|
{ "ava1", "Satyr" },
|
||||||
{ "sunder", "Sunder" },
|
{ "sunder", "Sunder" },
|
||||||
{ "snd1", "Sunder V1" },
|
{ "snd1", "Denkou" }, // #69: the 4.x-era name (two era witnesses)
|
||||||
{ "vulture", "Vulture" },
|
{ "vulture", "Vulture" },
|
||||||
{ "vul1", "Mad Dog" },
|
{ "vul1", "Mad Dog" },
|
||||||
{ "lok2", "Mischief" },
|
{ "lok2", "Mischief" },
|
||||||
|
|||||||
@@ -1032,6 +1032,12 @@ void
|
|||||||
{
|
{
|
||||||
Check(message);
|
Check(message);
|
||||||
|
|
||||||
|
// #150 -- the binary's ONCE-PER-DEATH latch (local_14 @0x4a0230 body,
|
||||||
|
// part_012.c:14625-14629): IsMechDestroyed() sampled AT ENTRY. The kill
|
||||||
|
// report posts only on the 0->1 edge (!destroyedAtEntry && destroyed-now);
|
||||||
|
// a later salvo of the killing volley takes the inflicted-only path.
|
||||||
|
const int destroyedAtEntry = IsMechDestroyed();
|
||||||
|
|
||||||
// MP DIAGNOSTIC (task #47): confirm the handler runs on the OWNING MASTER
|
// MP DIAGNOSTIC (task #47): confirm the handler runs on the OWNING MASTER
|
||||||
// for a cross-pod (network-delivered) TakeDamage + what state it carries.
|
// for a cross-pod (network-delivered) TakeDamage + what state it carries.
|
||||||
if (getenv("BT_MP_NET"))
|
if (getenv("BT_MP_NET"))
|
||||||
@@ -1242,11 +1248,31 @@ void
|
|||||||
// vital-wreck flag, and the loop-ENTRY zone. This retires
|
// vital-wreck flag, and the loop-ENTRY zone. This retires
|
||||||
// BTPostDamageScore/BTPostKillScore -- see the btplayer.cpp tombstone.
|
// BTPostDamageScore/BTPostKillScore -- see the btplayer.cpp tombstone.
|
||||||
//
|
//
|
||||||
|
// #150 DOUBLE-KILL FIX, two binary-grounded gates (2026-08-10):
|
||||||
|
//
|
||||||
|
// (1) MASTER ONLY. The binary OPENS this handler with the assertion
|
||||||
|
// `if ((flags & 0xc) == 4) Fail("Replicant Mech recieving takedam...",
|
||||||
|
// MECH.CPP, 0x3da)` (part_012.c:14613-14616) -- in 1995 a replicant
|
||||||
|
// executing TakeDamage was a BUG, so the report tail could only ever
|
||||||
|
// run on the victim's master, once. This port deliberately applies
|
||||||
|
// damage on replicants too (local visual responsiveness), so the tail
|
||||||
|
// ALSO ran there -- and the replicant's movement mode can't flip to
|
||||||
|
// wreck until the death record round-trips, so a multi-round killing
|
||||||
|
// volley posted a kill report PER ROUND inside that window. Measured:
|
||||||
|
// one death -> two type-2 arms 1ms apart, kills 0->2 (the field's
|
||||||
|
// "one kill counted twice", #150). Gate the REPORTS to the master;
|
||||||
|
// the local damage application stays.
|
||||||
|
//
|
||||||
|
// (2) ENTRY LATCH. The binary's newly-destroyed test is the 0->1 edge of
|
||||||
|
// IsMechDestroyed() across THIS handler call (local_14), not the
|
||||||
|
// movement-mode-derived deathBlastArmed -- on the master both agree,
|
||||||
|
// but the binary's form is the spec.
|
||||||
|
if (GetInstance() != ReplicantInstance)
|
||||||
{
|
{
|
||||||
extern void BTMechPostCombatReports(void *, void *, float, int, int, int);
|
extern void BTMechPostCombatReports(void *, void *, float, int, int, int);
|
||||||
BTMechPostCombatReports((void *)this, (void *)message,
|
BTMechPostCombatReports((void *)this, (void *)message,
|
||||||
(float)damageTally, zoneDestroyed, reportZone,
|
(float)damageTally, zoneDestroyed, reportZone,
|
||||||
deathBlastArmed && IsMechDestroyed());
|
!destroyedAtEntry && IsMechDestroyed());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -93,8 +93,9 @@ if defined BT_STILL_RUNNING (
|
|||||||
goto btwait
|
goto btwait
|
||||||
)
|
)
|
||||||
echo.
|
echo.
|
||||||
echo The game has exited. If it closed unexpectedly, send the operator
|
echo The game has exited. (Quitting from the menu lands here -- normal.)
|
||||||
echo BOTH of these from the content folder:
|
echo If it CRASHED or would not start, send the operator BOTH of these
|
||||||
|
echo from the content folder:
|
||||||
echo content\lastrun_solo.txt (tiny -- send it EVEN IF there is
|
echo content\lastrun_solo.txt (tiny -- send it EVEN IF there is
|
||||||
echo no solo_*.log at all: that combination
|
echo no solo_*.log at all: that combination
|
||||||
echo is how we spot a blocked launch)
|
echo is how we spot a blocked launch)
|
||||||
|
|||||||
@@ -97,7 +97,10 @@ if defined BT_STILL_RUNNING (
|
|||||||
goto btwait
|
goto btwait
|
||||||
)
|
)
|
||||||
echo.
|
echo.
|
||||||
echo The game has exited. If it closed unexpectedly, send the operator BOTH
|
echo The game has exited.
|
||||||
|
echo (Normal exits land here too: round ends return everyone to the menu,
|
||||||
|
echo and closing the menu window quits -- neither is a crash.)
|
||||||
|
echo If it CRASHED or would not start, send the operator BOTH:
|
||||||
echo content\lastrun_steam.txt (tiny -- send it EVEN IF there is no
|
echo content\lastrun_steam.txt (tiny -- send it EVEN IF there is no
|
||||||
echo steam_*.log at all: that is how we spot
|
echo steam_*.log at all: that is how we spot
|
||||||
echo a launch blocked before it ran)
|
echo a launch blocked before it ran)
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# #150/#151: count type-2 arm executions per kill + measure the eject score arithmetic
|
||||||
|
. /c/git/bt411/scratchpad/night6/bench_common.sh
|
||||||
|
cd /c/git/bt411/content || exit 1
|
||||||
|
taskkill //F //IM btl4.exe >/dev/null 2>&1; sleep 3
|
||||||
|
rm -f sc_a.log sc_b.log sc_r.log
|
||||||
|
mkdir -p /c/git/bt411/scratchpad/night14/mlbak && mv matchlog_*.txt /c/git/bt411/scratchpad/night14/mlbak/ 2>/dev/null
|
||||||
|
bt_expert_egg MP.EGG SC.EGG
|
||||||
|
sed -i "s/^map=.*/map=grass/; s/^time=.*/time=day/; s/^vehicle=.*/vehicle=madcat/" SC.EGG
|
||||||
|
# B: unarmed slow target, respawns on death
|
||||||
|
( export BT_GOTO=enemy BT_GOTO_STOP=80 BT_SCORE_LOG=1 BT_DEATH_LOG=1 BT_MATCHLOG=1 BT_MP_LOG=1
|
||||||
|
bt_launch sc_b.log SC.EGG 0x0C -net 1601 )
|
||||||
|
sleep 2
|
||||||
|
# A: kills B with autofire, then panic-ejects late
|
||||||
|
( export BT_GOTO=enemy BT_GOTO_STOP=100 BT_AUTOFIRE=1 BT_AF_MISSILE=1 BT_AF_PERIOD=4
|
||||||
|
export BT_EJECT_AT=9000
|
||||||
|
export BT_SCORE_LOG=1 BT_DEATH_LOG=1 BT_MATCHLOG=1 BT_MP_LOG=1
|
||||||
|
bt_launch sc_a.log SC.EGG 0x03 -net 1501 )
|
||||||
|
sleep 5
|
||||||
|
python ../tools/btconsole.py SC.EGG 127.0.0.1:1501 127.0.0.1:1601 > sc_r.log 2>&1 &
|
||||||
|
R=$!; sleep 300; kill $R 2>/dev/null; sleep 2
|
||||||
|
bt_kill_ours; sleep 2; taskkill //F //IM btl4.exe >/dev/null 2>&1
|
||||||
|
echo RESULT
|
||||||
|
echo "--- B deaths (kills A should have earned) ---"
|
||||||
|
grep -ac "Mech::Reset" sc_b.log
|
||||||
|
echo "--- A: kill-arm executions + score sequence (matchlog) ---"
|
||||||
|
grep -ah "SCORE\|KILL\|EJECT\|DEATH" matchlog_*.txt 2>/dev/null | head -25
|
||||||
|
echo "--- A: scorepost receipts (producer side) ---"
|
||||||
|
grep -a "scorepost" sc_a.log | head -10
|
||||||
|
grep -a "scorepost" sc_b.log | head -10
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# Hand-poll the RIO board over COM1 -- no game involved. Wire format per
|
||||||
|
# PCSerialPacket::SendPacket: [cmd][checksum = sum & 0x7F].
|
||||||
|
$p = New-Object System.IO.Ports.SerialPort COM1,9600,None,8,One
|
||||||
|
$p.DtrEnable = $true
|
||||||
|
$p.RtsEnable = $true
|
||||||
|
$p.ReadTimeout = 200
|
||||||
|
$p.Open()
|
||||||
|
'PORT OPEN CTS=' + $p.CtsHolding + ' DSR=' + $p.DsrHolding
|
||||||
|
|
||||||
|
function Send-Cmd([byte[]]$bytes, [string]$name) {
|
||||||
|
$p.DiscardInBuffer()
|
||||||
|
$p.Write($bytes, 0, $bytes.Length)
|
||||||
|
Start-Sleep -Milliseconds 700
|
||||||
|
$got = @()
|
||||||
|
while ($p.BytesToRead -gt 0) { $got += [byte]$p.ReadByte() }
|
||||||
|
$hex = ($got | ForEach-Object { $_.ToString('X2') }) -join ' '
|
||||||
|
"$name -> $($got.Count) byte(s): $hex"
|
||||||
|
}
|
||||||
|
|
||||||
|
Send-Cmd @([byte]0x80, [byte]0x00) 'CHECK 0x80'
|
||||||
|
Send-Cmd @([byte]0x81, [byte]0x01) 'VERSION 0x81'
|
||||||
|
Send-Cmd @([byte]0x82, [byte]0x02) 'ANALOG 0x82'
|
||||||
|
Start-Sleep -Seconds 2
|
||||||
|
Send-Cmd @([byte]0x82, [byte]0x02) 'ANALOG again'
|
||||||
|
$p.Close()
|
||||||
|
'DONE'
|
||||||
Reference in New Issue
Block a user