Commit Graph
9 Commits
Author SHA1 Message Date
valknarandClaude Sonnet 5 1d7c4e7b7d Implement real MsgBox() for VBScript table scripts (browser confirm/alert)
libwinevbs's __LIBWINEVBS__ build of Global_MsgBox() only logged the
prompt text and never set a return value, since it has no host UI to
show a dialog through. Any table script that branches on
MsgBox(...) = vbYes (a common pattern - e.g. core.vbs's own trough
ball-count dialog) always took the "no" branch, since the result
stayed at its zero-initialized default.

Adds a msgbox callback to libwinevbs_callbacks_t, wired on the
vpinball-wasm side to a real window.confirm()/alert() call. Both are
synchronous browser APIs that block JS execution and return a value
immediately, matching what VBScript's MsgBox() needs (its result is
used by the calling statement right away) - the only way to answer it
asynchronously would require Asyncify or a busy-wait poll loop, which
this project has deliberately avoided elsewhere.

Confirmed fixed by hands-on testing: a table's ball-count confirm
dialog now correctly returns Yes/No based on the user's click, instead
of always silently taking the "no" branch.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 19:30:25 +02:00
valknarandClaude Sonnet 5 99ae5b9eda Fix PinMAME busy-wait freeze on browser/window focus loss
core.vbs auto-wires Controller.Pause = True into every table's Paused
event, which Player::OnFocusChanged() fires on focus loss. PinMAME's
own pause handling in usrintrf.c is a "while (g_fPause) { ...;
draw_screen(); ... }" busy-wait written for a real-OS-thread host,
where a separate thread later flips g_fPause back to 0. Under our
single-threaded Emscripten cooperative-scheduling model there is no
second thread to clear it, so entering that loop spins forever at
100% CPU with the tab completely unresponsive.

cpu_run_emscripten_step() now checks g_fPause up front and skips
cpu_timeslice() (and therefore that loop) entirely while paused;
emulation simply doesn't advance until Controller.Pause is set back
to False and the step function is called again next frame.

Confirmed fixed by hands-on testing: unfocusing the browser during a
PinMAME ROM session no longer freezes the tab.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 15:15:48 +02:00
valknarandClaude Sonnet 5 5570955bc5 Fix patches/pinmame/0004: drop duplicate hunk conflicting with 0002
CI / Build wasm engine (push) Successful in 10m51s
0004 was generated via a plain `git diff` against pinmame's pristine
commit, which captured the *cumulative* diff for cpuexec.c - including
patch 0002's time_fence stub hunk, not just 0004's own new cpu_run
splitting code. Applying 0002 then 0004 in sequence (exactly what
setup.sh does) failed: 0004's duplicate hunk expected pre-0002 context
that no longer matched.

Regenerated by diffing the current tree against a baseline with 0001-0003
already applied (matching the vpinball/0006 patch's existing approach)
instead of against pristine directly. Verified: all 4 pinmame patches now
apply cleanly in sequence against a fresh pristine checkout, and the
result is byte-identical to the working tree that was actually built and
tested.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SoSarxLgY33Kax5UNXcafZ
2026-08-23 12:47:51 +02:00
valknarandClaude Sonnet 5 9479cea280 Add real PinMAME (VPinMAME.Controller) integration for ROM-based tables
Statically links the real PinMAME emulation core (libpinmame) instead of
leaving VPinMAME.Controller creation to fail, which was crashing table
scripts on ROM-based tables before they could spawn a ball. PinMAME's own
run_game()->cpu_run() loop is split into a one-shot init, a per-frame step,
and a one-shot teardown (patches/pinmame/0004) so it runs cooperatively on
the same frame callback as vpinball's own loop instead of on a real
std::thread, which hard-aborts under Emscripten's single-threaded runtime -
three smaller wasm32 portability fixes to libpinmame itself round out the
patch set (0001-0003). Adds pinball.loadRom() to supply a ROM zip, written
to the table-relative pinmame/roms/ path vpinball's own plugin already
checks. Confirmed against a real community ROM-based table: Controller
creation and game identification succeed, and a missing ROM now fails
cleanly instead of crashing the page - actual ROM-driven gameplay is still
unconfirmed since no ROM was available (or sought out) to test with.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SoSarxLgY33Kax5UNXcafZ
2026-08-23 12:09:35 +02:00
valknarandClaude Sonnet 5 4638c06b68 Phase E/I: expose evalScript() as a permanent debug API, finalize README
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>
2026-08-22 18:14:49 +02:00
valknarandClaude Sonnet 5 b8bf9f1510 Fix back buffer not resizing on fullscreen toggle
Window::OnResized() (called for every SDL window-resize event, including
entering/leaving browser fullscreen) updated the window's own tracked
pixel size but never propagated it to the window's back buffer render
target, whose stale size kept driving glViewport() - so toggling
fullscreen resized the canvas but rendering stayed pinned to the old,
smaller viewport. Mirrors the resize handling the BGFX backend already
does explicitly elsewhere in RenderDevice.cpp.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-22 18:03:08 +02:00
valknarandClaude Sonnet 5 7c63f01527 Fix GL viewport sizing and ship a real playable default table
The OpenGL back-buffer render target was created with the window's
logical/CSS size instead of its device-pixel size, so on any browser
tab with devicePixelRatio != 1 the GL viewport only covered a fraction
of the canvas's actual backing buffer (rendering anchored bottom-left,
matching GL's viewport origin) - confirmed against a live repro and
fixed to match the convention already used by the BGFX backend
elsewhere in the same file.

Also swap the bundled default table from test000-default-table.vpx
(upstream's rendering/component regression-test fixture, which has no
gameplay script) to exampleTable.vpx - a genuine playable demo table
with working flippers, slingshots, bumpers, targets and a plunger -
confirmed keyboard input and audio now work end-to-end through it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-22 17:56:51 +02:00
valknar a5e5b6f34a Phase A/B: fix the game loop and trim the asset payload
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.
2026-08-22 16:06:17 +02:00
valknar 0a63835689 Scaffold vpinball-wasm: Emscripten port of Visual Pinball
CI / Build wasm engine (push) Failing after 3m30s
CI / Publish to npm registry (push) Skipped
Build scripts, CMake/source patches, npm packaging, and a Gitea CI
workflow to compile the real Visual Pinball engine (SDL3 + WebGL2 +
libwinevbs for real VBScript) to WebAssembly.

The patches are validated end-to-end: the patched engine boots in a
real browser, loads a real .vpx table, compiles its real GLSL shaders,
computes environment map radiance, initializes physics, and starts the
VBScript engine, before hanging on the one deliberately-deferred piece
of work (game loop rewrite around emscripten_set_main_loop), documented
in the README's roadmap.
2026-08-22 14:30:14 +02:00