Liquid War 5.6.2

Binary: test/binaries/candidates/liquid-war/LW5/lwwin.exe, image base 0x00400000.

DirectDraw startup across guest threads

Liquid War creates its only window (hwnd 0x18001 cooperatively, 0x20001 with real Workers) on Allegro thread T1. Video-mode selection later runs on the main guest thread. Allegro bridges the two with a synchronous private window message:

Before cross-thread SendMessageA routing, the callback happened to execute on the caller's WebAssembly instance. Correct owner-thread routing exposed that DirectDraw's display width, height, depth, selected-mode flag, cooperative HWND, exclusive flag, and primary palette were mutable WebAssembly globals. Each guest thread has a separate instance, so T1 successfully selected 640x480x8 while the main thread still observed its private 640x480x16 defaults and rejected the driver.

Those fields now live in the atomic shared-memory DX_PROCESS_STATE record. The window callback therefore retains Win32 thread affinity while subsequent DirectDraw calls and GetSystemMetrics see the same process device state. test/test-cross-thread-send.js writes the record from a real owner Worker and asserts that a peer instance reads every field.

Acceptance on 2026-08-26:

Acceptance update on 2026-09-01:

Network startup

Ruled out

The short-lived thread stream seen after entering Net game is not a thread scheduler failure. It is Liquid War retrying 127.0.0.1:8035 after a queued Enter remains logically down across the transition to the network-settings menu and activates Start game there too. Loopback is deliberately local to one emulator process, so that address cannot reach the separate server process.

For headless input, a one-batch keydown followed by di-keyup releases the DirectInput state without leaving a delayed WM_KEYUP. This reaches the settings screen, where 10.77.0.1 can be entered before Start game. The fixed reproduction is encoded in test/test-vlan-match.js.

The current two-process path reaches the waiting room with the player listed, and the server accepts and exchanges protocol bytes. It has not yet been promoted to a gameplay test because the lobby's "Start now"/"Play" activation still needs a reliable headless route.