From 57524d4a686999279a1eb765dff3b3cb23147de4 Mon Sep 17 00:00:00 2001 From: Cyd Date: Fri, 3 Jul 2026 14:45:20 -0500 Subject: [PATCH] Phase 3c: fix production vr_sync abort; full game runs through renderer Root cause: vpx_max_postboot_acks=200, a Phase-2 bring-up guard, capped how many post-boot replies the device would ever feed. A real BattleTech session issues an unbounded stream of sync/frame/render replies and aborted with "velocirender_receive timed out - sends_wo_rcv" at exactly the 200th ack (143 sync + 55 frame-ack + 2 render). Cap is now effectively unlimited (0x7fffffff; override VPX_MAX_ACKS for diagnostics). After the fix the full game (game.conf, RIO disabled, cycles=max) runs indefinitely: 2500+ syncs, 1264+ frame-acks, no abort, into L4VIDEO content load (Thor mech + terrain from ALPHA_1/REL410/BT/VIDEO/GEO -- 841 real .bgf models; test.egg is only mission params). Window still shows background: the game uses the full DPL hierarchy (instance/object/lod/geogroup + DCS transforms) which the flat flyk-tuned scene walk doesn't traverse yet -- that is Phase 3d, documented in PHASE3-PROGRESS.md. Co-Authored-By: Claude Opus 4.8 --- emulator/PHASE3-PROGRESS.md | 75 +++++++++++++++++++++++++++------- emulator/game.conf | 25 ++++++++++++ emulator/vpx-device/vpxlog.cpp | 10 ++++- 3 files changed, 95 insertions(+), 15 deletions(-) create mode 100644 emulator/game.conf diff --git a/emulator/PHASE3-PROGRESS.md b/emulator/PHASE3-PROGRESS.md index d12e9b4..ff9eb11 100644 --- a/emulator/PHASE3-PROGRESS.md +++ b/emulator/PHASE3-PROGRESS.md @@ -1,10 +1,14 @@ # Phase 3 — Render Backend: Progress -**Status (2026-07-03): Phase 3a + 3b complete. 3a — the render command stream -is fully captured and decoded, and a captured DIVRGB frame reconstructed to -pixels offline. 3b — a live OpenGL window built into the emulated board draws -each frame in real time inside DOSBox-X. First images ever produced from the -Rel 4.10 VPX protocol without a real board.** +**Status (2026-07-03): Phase 3a + 3b + 3c complete. 3a — the render command +stream is fully captured and decoded, and a captured DIVRGB frame +reconstructed to pixels offline. 3b — a live OpenGL window built into the +emulated board draws each frame in real time inside DOSBox-X. 3c — the +production `vr_sync` abort is fixed and the full BattleTech v4.10 game now runs +indefinitely through the emulated board, drawing 1000s of frames (was hard- +capped at 200). First images ever produced from the Rel 4.10 VPX protocol +without a real board. Next (3d): traverse the game's DPL hierarchy + DCS +transforms so its mechs actually appear.** ![decoded DIVRGB calibration screen](divrgb-decoded.png) @@ -108,14 +112,57 @@ before its first receive, which the POLL_THRESHOLD gating stalls. This is not a 3b issue (the `flyk` clean-launch path renders fine); it is the same production-sync item still open from Phase 2. -## Remaining +## 3c. The full game runs through the live renderer (sync abort fixed) -1. **Production-path vr_sync timeout** (`sends_wo_rcv`): the game's - `btdpl.ini`/netnub launch path. Needed before the game itself renders. -2. **Content**: `test.egg` carries no models, so even past sync the game has - no `buttee.bgf`/`mslr.bgf` geometry to draw — needs a real mission/content - tree from the pod image. -3. **DOS/4GW sync variant** for the CYCLE `flyk yip.scn` fixture (Red Planet +**The production `vr_sync` abort is fixed, and BattleTech v4.10 now runs +indefinitely through the emulated board + live renderer.** Root cause was a +Phase-2 bring-up guard left in the device: `vpx_max_postboot_acks = 200` +capped how many post-boot replies the device would ever feed. A real game +session issues an unbounded stream of sync/frame/render replies; the game +aborted with `velocirender_receive timed out — sends_wo_rcv` the instant the +device fell silent — at *exactly* the 200th ack (measured: 143 sync + 55 +frame-ack + 2 render = 200). The cap is now effectively unlimited +(`0x7fffffff`, override with `VPX_MAX_ACKS`). Same run after the fix: 2500+ +syncs, 1264+ frame-acks, no abort. The game progresses past sync into +`L4VIDEO.cpp` content/entity setup (loading the Thor mech and terrain) and +draws continuously. + +Contrary to the earlier note, the content **is** on the production image: 841 +`.bgf` models under `ALPHA_1/REL410/BT/VIDEO/GEO` (incl. `BUTTEE.BGF`, +`MSLR.BGF`, the `THR*` Thor mech). `test.egg` is only mission parameters (as +the pod owner said); the geometry loads from `VIDEO/GEO` regardless. + +Game render config: `game.conf` (RIO disabled so the missing cockpit board +doesn't stall the control manager; `cycles=max`). + +### What the game's wire stream looks like (vs. flyk) + +The game geometry path is heavier and different from flyk's flat DIVRGB: +- **action 26 (0x1A) dominates** — texture texel upload. 32-byte headers + `[node][size][w][h]` (`0x1000`=64×64, `0x4000`=128×128 tiles) then 256-byte + texel rows. 2880+ of these = the mission texture set streaming in. +- action 28 (0x1C): material/light parameter blocks. +- actions 23/25 (verts/conns) still carry mesh geometry but are a small + fraction of the stream. +- The scene graph is the **full DPL hierarchy** — zone → dcs → instance → + object → lod → geogroup → geometry — with **DCS transform matrices** + positioning each mech/prop. flyk's DIVRGB put geometry directly in + geogroups at world coordinates. + +## Remaining (Phase 3d: game geometry on screen) + +The window currently shows only the view background for the game because the +live backend's `scene_publish_frame()` walks the *flat* flyk layout +(geogroup→geometry at world coords). To draw the game's mechs it must: + +1. **Traverse the full hierarchy** (instance/object/lod/geogroup) and **apply + DCS transform matrices** (accumulate the 4×4s down the graph) so each mech + lands at its world position; pick a LOD. +2. **Arena content search path**: the game logs `couldn't load object + thr_tshd.bgf` — that object lives in `VIDEO/GEO/ARENA/` and `…/POLAR/` + (arena-specific), not the top `GEO` dir; the geometry search path needs the + selected arena subdir (a `setenv`/config detail, not protocol). +3. **Texturing** (action 26 texel maps + SVT), lighting, depth/material. + Current backend is flat-shaded untextured polys. +4. **DOS/4GW sync variant** for the CYCLE `flyk yip.scn` fixture (Red Planet geometry) — the older action-check sync. -4. Texturing (`set_texmap_texels`, action 23-data + SVT), lighting, and - depth/material niceties; current backend is flat-shaded untextured polys. diff --git a/emulator/game.conf b/emulator/game.conf new file mode 100644 index 0000000..bd58d09 --- /dev/null +++ b/emulator/game.conf @@ -0,0 +1,25 @@ +[sdl] +output=opengl +[dosbox] +memsize=32 +machine=svga_s3 +[cpu] +core=normal +cputype=pentium +cycles=max +[serial] +serial1=disabled +serial2=disabled +[autoexec] +mount c "C:\VWE\TeslaRel410\ALPHA_1" +c: +cd \REL410\BT +set VIDEOFORMAT=svga +set BLASTER=A220 I5 D1 H5 P330 T6 +set TEMP=c:\ +call setenv.bat r s n n +32rtm.exe -x +btl4opt.exe -egg test.egg +32rtm.exe -u +echo ALPHA1-RUN-DONE +pause diff --git a/emulator/vpx-device/vpxlog.cpp b/emulator/vpx-device/vpxlog.cpp index 20ad767..2f41369 100644 --- a/emulator/vpx-device/vpxlog.cpp +++ b/emulator/vpx-device/vpxlog.cpp @@ -212,7 +212,13 @@ static void queue_version_request(void) { * action code. The post-boot vr_init reply is ignored by the caller * (DPL_HOST.C), so any action != vr_draw_scene_action satisfies it. */ static int postboot_acks = 0; -static int vpx_max_postboot_acks = 200; /* safety cap */ +/* Post-boot reply cap. This was a Phase-2 bring-up guard against a runaway + * reply loop while the protocol was still being reversed. The protocol is now + * solid and a real game session issues an unbounded number of sync/frame/ + * render replies -- BattleTech aborts with "velocirender_receive timed out - + * sends_wo_rcv" the instant the device stops answering. Default is now + * effectively unlimited; override with VPX_MAX_ACKS only for diagnostics. */ +static int vpx_max_postboot_acks = 0x7fffffff; static int empty_polls = 0; static const int POLL_THRESHOLD = 6; /* consecutive empty polls => blocking receive */ static void queue_render_ack_node(unsigned char action, unsigned node) { @@ -723,6 +729,8 @@ void VPXLOG_Init(void) { vpx_respond = (r && r[0] && r[0] != '0'); const char *h = getenv("VPX_HANDSHAKES"); if (h && atoi(h) > 0) vpx_max_handshakes = atoi(h); + const char *ma = getenv("VPX_MAX_ACKS"); + if (ma && atoi(ma) > 0) vpx_max_postboot_acks = atoi(ma); const char *fd = getenv("VPX_FIFODUMP"); if (fd && fd[0]) {