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` |
|
||||
| **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` |
|
||||
| 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` |
|
||||
| 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` |
|
||||
|
||||
@@ -1384,7 +1384,7 @@ cmHeat(K,ModeSecondaryHeat,58,heatpal.pcc,heatpal2.pcc,Myomers);
|
||||
}
|
||||
|
||||
#=======================================================================
|
||||
# Sunder V1
|
||||
# Denkou (was: Sunder V1)
|
||||
#=======================================================================
|
||||
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
|
||||
*"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`.
|
||||
- **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
|
||||
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
|
||||
|
||||
+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
|
||||
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
|
||||
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 —
|
||||
@@ -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),
|
||||
`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:UID224795` / `monitor:id:UID200195`). The Dell's code is unique. The cab's shipped
|
||||
`glass_layout.cfg` still uses the fragile `monitor:DISPLAY4` device-name form.
|
||||
(`monitor:id:UID224795` / `monitor:id:UID200195`). **RESOLVED 2026-08-10:** the full map is
|
||||
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]
|
||||
`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`
|
||||
- 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,
|
||||
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)
|
||||
- color/camo (7, display→tag): White=White, Black=Black, Brown=Brown, Red=**Crimson**, Green=Green,
|
||||
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.
|
||||
=============================================================================
|
||||
@@ -2121,12 +2121,21 @@ static unsigned long
|
||||
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
|
||||
BTGlassPanels_Tick()
|
||||
{
|
||||
if (gWinCount == 0)
|
||||
return;
|
||||
|
||||
BTRioHealthReport();
|
||||
|
||||
static unsigned long sLastPaint = 0;
|
||||
unsigned long now = GetTickCount();
|
||||
if (now - sLastPaint < (unsigned long)RepaintMilliseconds)
|
||||
|
||||
@@ -410,6 +410,20 @@ int PCSerialPacket::ReceivePacket(BYTE *destPtr) // returns len (zero if none)
|
||||
// Returns:
|
||||
// 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
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
@@ -418,6 +432,11 @@ void PCSerialPacket::SendPacket(BYTE *srcPtr) //sends packet
|
||||
if(!enabled) //if disabled, don't even try
|
||||
return;
|
||||
|
||||
if (gPcspakTxSickUntil != 0 && GetTickCount() < gPcspakTxSickUntil)
|
||||
{
|
||||
return; // adapter sick: drop, don't stall
|
||||
}
|
||||
|
||||
BYTE cmd = *srcPtr; //get the command byte
|
||||
cmd &= 0x7F; //remove MSB
|
||||
int length = txLengthPtr[cmd]; //get the length in al
|
||||
@@ -459,11 +478,37 @@ void PCSerialPacket::SendPacket(BYTE *srcPtr) //sends packet
|
||||
return;
|
||||
}
|
||||
}
|
||||
DWORD bytesWritten;
|
||||
GetOverlappedResult(hComm, &overlapSend, &bytesWritten, TRUE);
|
||||
if(bytesWritten != length + 1)
|
||||
//
|
||||
// BOUNDED completion wait (2026-08-10, ALPHA-MR field minidump). The
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,11 @@ static LRESULT CALLBACK
|
||||
return 0;
|
||||
|
||||
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 DefWindowProcW(window, message, wparam, lparam);
|
||||
|
||||
@@ -1240,6 +1240,105 @@ RIO::~RIO()
|
||||
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
|
||||
RIO::TestInstance() const
|
||||
{
|
||||
@@ -1264,6 +1363,36 @@ Logical
|
||||
Logical reply, looping;
|
||||
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);
|
||||
//
|
||||
//cout << flush;
|
||||
@@ -1377,6 +1506,20 @@ Logical
|
||||
break;
|
||||
|
||||
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;
|
||||
//
|
||||
// NOTE: no data is sent in this packet.
|
||||
@@ -1423,11 +1566,13 @@ Logical
|
||||
break;
|
||||
|
||||
case ButtonPressed:
|
||||
++gRioBtnCount;
|
||||
destination->Type = RIO::ButtonPressedEvent;
|
||||
destination->Data.Unit = receive_buffer[1];
|
||||
break;
|
||||
|
||||
case ButtonReleased:
|
||||
++gRioBtnCount;
|
||||
destination->Type = RIO::ButtonReleasedEvent;
|
||||
destination->Data.Unit = receive_buffer[1];
|
||||
break;
|
||||
@@ -1568,6 +1713,9 @@ void
|
||||
if (operational && !TestModeActive)
|
||||
{
|
||||
SendPacket((Byte *) request_analog_string);
|
||||
++gRioReqCount;
|
||||
gRioReqPending = 1;
|
||||
gRioLastReqTick = GetTickCount();
|
||||
}
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
@@ -234,6 +234,36 @@ static LONG WINAPI
|
||||
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
|
||||
// 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);
|
||||
char lastrun_name[MAX_PATH];
|
||||
wsprintfA(lastrun_name, "lastrun_%s.txt", logStem);
|
||||
lstrcpynA(gBTLastrunName, lastrun_name, MAX_PATH);
|
||||
std::ofstream breadcrumb(lastrun_name, std::ios::out | std::ios::app);
|
||||
if (breadcrumb.is_open())
|
||||
{
|
||||
@@ -1209,6 +1240,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
||||
BTFeLaunchSpec fe_spec;
|
||||
if (BTFrontEnd_Run(&fe_spec) != 0 || fe_spec.mode == BTFeLaunchNone)
|
||||
{
|
||||
BTLastrunNote("clean exit: player quit from the menu");
|
||||
return 0; // quit from the menu
|
||||
}
|
||||
char fe_arguments[192];
|
||||
|
||||
@@ -174,6 +174,8 @@ void
|
||||
{
|
||||
MarshalLog("window closed by the user -- exiting for real "
|
||||
"(no relaunch)");
|
||||
extern void BTLastrunNote(const char *);
|
||||
BTLastrunNote("clean exit: player closed the game window");
|
||||
ExitProcess(0);
|
||||
}
|
||||
}
|
||||
@@ -257,6 +259,16 @@ void
|
||||
if (CreateProcessW(exe_path, command_line, NULL, NULL, FALSE,
|
||||
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.hProcess);
|
||||
}
|
||||
|
||||
@@ -412,7 +412,7 @@ static const CatalogEntry kVehicles[] =
|
||||
{ "avatar", "Avatar" },
|
||||
{ "ava1", "Satyr" },
|
||||
{ "sunder", "Sunder" },
|
||||
{ "snd1", "Sunder V1" },
|
||||
{ "snd1", "Denkou" }, // #69: the 4.x-era name (two era witnesses)
|
||||
{ "vulture", "Vulture" },
|
||||
{ "vul1", "Mad Dog" },
|
||||
{ "lok2", "Mischief" },
|
||||
|
||||
@@ -1032,6 +1032,12 @@ void
|
||||
{
|
||||
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
|
||||
// for a cross-pod (network-delivered) TakeDamage + what state it carries.
|
||||
if (getenv("BT_MP_NET"))
|
||||
@@ -1242,11 +1248,31 @@ void
|
||||
// vital-wreck flag, and the loop-ENTRY zone. This retires
|
||||
// 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);
|
||||
BTMechPostCombatReports((void *)this, (void *)message,
|
||||
(float)damageTally, zoneDestroyed, reportZone,
|
||||
deathBlastArmed && IsMechDestroyed());
|
||||
!destroyedAtEntry && IsMechDestroyed());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -93,8 +93,9 @@ if defined BT_STILL_RUNNING (
|
||||
goto btwait
|
||||
)
|
||||
echo.
|
||||
echo The game has exited. If it closed unexpectedly, send the operator
|
||||
echo BOTH of these from the content folder:
|
||||
echo The game has exited. (Quitting from the menu lands here -- normal.)
|
||||
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 no solo_*.log at all: that combination
|
||||
echo is how we spot a blocked launch)
|
||||
|
||||
@@ -97,7 +97,10 @@ if defined BT_STILL_RUNNING (
|
||||
goto btwait
|
||||
)
|
||||
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 steam_*.log at all: that is how we spot
|
||||
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