The walk/run clip often faced away from the direction of travel:
- deltaToDir16 used 0 = +X with rounding, while 1.13c clips start at
south-west; units further reduced it with dir16 % 8 and the renderer
with % dirCount, so 8-direction monsters showed the wrong row.
- re-pathing every frame while the mouse was held made the first path
point the centre of the current cell, so the unit stepped back and
turned around before moving on.
- server position packets rubber-banded the local player towards a
lagging position, and spawnOrSnap reset the facing to 0.
Unit facing is now the 1.13c dir64 (D2Common 0x6FD5CF40), set once per
path segment and kept while idle, mapped to the clip row with the D2CMP
and moveToWorldSubTile paths from the rounded cell. The local player
follows D2Client's position gate 0x6FADA1B0: out-of-tolerance positions
send C2S 0x5F instead of moving the predictor, 0x15 is the authoritative
snap, and replay follows the recorded positions.
- Verify 1.13c Bnclient.dll (0x6ff2ed41: cmp ecx, 0x2bf20) sends 4-byte
SID_NULL (FF 00 04 00) every 180,000 ms (3 minutes) once the BNCS
handshake starts until the BNCS socket closes, and confirm D2MCPClient.dll
has no client-side MCP keepalive packet.
- Start a 180,000 ms (BNCS_KEEPALIVE_INTERVAL_MS) periodic SID_NULL timer in
BncsSession.handshake() and cancel it on close() or stream disconnect.
- Echo SID_NULL in FakeRealm wireBncs (matching PvPGN bnetd _client_pingreq)
and add FakeClock unit tests for 180s SID_NULL keepalive and >6m idle
persistence on character select and lobby screens.
- Add lobby-idle subcommand to tools/d2-bot.ts for live WSS idle verification.
Fixes#541
Adds tools/lib/vendor-scenarios.ts (inspect, vendor-open, vendor-buy-sell,
vendor-repair, stash-gold, gamble, vendor-verify) driven through
MapService pathfinding, plus a --report=<json> flag for d2-bot.
Captures recorded with WebBotThree (clvl 1, 2 gold); replay --strict is
clean (unknown=0 opaque=0 errors=0).
Split the mixed src/ and scripts/ trees into five compiler-enforced modules
aligned with the 1.13c DLL architecture, plus tools/:
- src/common/ (D2Common/Storm/D2Lang): formats, MPQ, data tables, DRLG,
items/skills/units metadata, RNG, pack contracts. No DOM/Node.
- src/netproto/ (D2Net/Bnclient/D2MCPClient): BNCS/MCP/D2GS protocol; external
code imports only src/netproto/index.ts.
- src/server/ (D2Game): authoritative simulation + LocalLoopbackAdapter.
All 23 reverse deps into client removed; no Math.random /
Date.now / performance.now.
- src/client/ (D2Client/D2Gfx/D2Win/D2Launch/D2Sound): render, UI, frontend,
audio, session, toolbar and the play.html entry.
- src/baker/ Node-only offline packers/extractors + unified `npm run bake`.
- tools/ bot, replay, unlock, verify/audit/benchmark CLIs.
play.html is now the only HTML entry; index.html, acts*.html and bnet.html
plus legacy entry glue (src/main.ts, act-scene, net-scene, lockstep src/net)
are removed.
Adds tsconfig.{common,netproto,server,client,baker}.json wired into
`npm run typecheck`, and tests/arch/boundaries.test.ts enforcing the module
boundaries with zero whitelist exceptions.
Verification: typecheck 6/6 clean; vitest 556 files / 6640 tests pass;
npm run build clean; bake --verify (136/136 levels, universal DT1s);
netproto-replay --strict 0 unknown/opaque/errors; d2-bot walk hardSnaps=0;
audit-levels 136/136; verify-play 9/9 gates at 800x600 and 1024x768.