Promotes vpinball_wasm_eval_script from a one-off validation hack to a
supported PinballInstance.evalScript() method (runs arbitrary VBScript
against the live table via the interpreter's own debug-console entry
point) - useful standalone, and it's what confirms the DMD script API
rides the same execution path already proven by real flipper input.
Rewrites the README's Status/Roadmap/Known limitations to reflect
tonight's actual, hands-on-confirmed state: keyboard input, audio, and
VBScript-driven gameplay all verified end-to-end (not just code
review); two more real engine bugs found and fixed (GL back buffer
sized in logical instead of device pixels; back buffer never resynced
on resize, breaking fullscreen); the default table swap and why;
B2S backglass explicitly assessed and declined for now, with the
concrete reasons (no plugin subsystem wired for this build, no test
table to validate against) rather than left as a vague TODO.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Phase A (the hard blocker): the engine now plays a real table live in
the browser via a real per-frame game loop, not just a static render.
Two real, previously-unknown upstream bugs found and fixed along the way:
- RenderDevice::WaitForVSync() unconditionally spawned a real std::thread
every frame, even on __STANDALONE__ builds - a hard crash under
Emscripten's single-threaded runtime (0003).
- The desktop game loop is a blocking native while loop with manual
uSleep throttling, incompatible with a single-threaded WASM main
thread. Adds Player::EmscriptenStepFrame() (one frame, no internal
loop) driven by emscripten_set_main_loop, plus new JS-callable
lifecycle entry points (vpinball_wasm_start/stop/dispose in the new
src/core/EmscriptenBridge.cpp) that bypass the desktop main()/WinMain()
chain entirely, since that chain assumes the process runs exactly one
table to completion then exits (0004).
Verified end-to-end in Chrome: real .vpx load, real shader compilation,
real physics/script engine init, a real running frame loop (observed
advancing), and a clean stop() -> ~Player() teardown mid-session with
no crash or hang.
Phase B: trims the preloaded asset payload from ~51MB to ~11MB by
excluding editor-only bundled example tables and a Monaco code editor
never used by the player runtime, and adds real link-time optimization
(-O2 --closure 1) and --use-preload-cache for repeat visits.