Phase D/F/H: embedding shell - loading progress, fullscreen, file upload, touch controls
package/src/index.ts: fixes a real pre-existing bug (start()/loadTable() never actually called vpinball_wasm_start with a table path argument), adds byte-level download progress (onProgress) and requestFullscreen(). package/src/touch-controls.ts: new on-screen virtual flipper/plunger/start button overlay that synthesizes the actual default keyboard scancodes (SDL_SCANCODE_LSHIFT/RSHIFT/RETURN, read from InputManager.cpp) as real KeyboardEvents dispatched on window, matching SDL3's Emscripten keyboard target - keeps the native input path completely unmodified. examples/basic/index.html: a real demo page - progress bar, a user-gesture "Start" button (required for both audio autoplay and fullscreen), a file picker for self-contained .vpx uploads, and the touch overlay shown on touch-capable devices. scripts/build.sh: two real bugs found and fixed via actual browser testing: - MODULARIZE=1 without EXPORT_ES6=1 produces a classic script, not an ES module with a default export - `await import(...)).default` was always undefined. Fixes with -sEXPORT_ES6=1. - --closure 1 silently stripped FS.writeFile/readFile/mkdir down to just low-level node ops, breaking runtime table loading with no compile-time warning. Dropped until root-caused; -O2 alone is kept. Verified end-to-end in Chrome: full load->progress->start flow with no errors, fullscreen actually engaging (document.fullscreenElement true), and a file-picker-selected table loading correctly (LoadGameFromFilename /tables/uploaded.vpx in the boot log, followed by a normal render). README updated to reflect what's now validated vs. still open (real-device input/audio confirmation remains the main open item).
This commit is contained in:
@@ -61,11 +61,15 @@ Full research and two isolated feasibility spikes (proving real VBScript executi
|
||||
- Physics engine initialization and the VBScript engine starting up.
|
||||
- **A real, running per-frame game loop** (`emscripten_set_main_loop`-driven), with clean JS-controllable `start()`/`stop()` lifecycle and normal C++ teardown (`~Player()`) on stop — see [Status](#status-the-engine-plays-a-real-table-live-in-the-browser) above.
|
||||
- A trimmed ~11MB asset payload (~78% smaller than the initial unoptimized ~51MB), by excluding editor-only bundled example tables and a Monaco code editor never used by the player runtime — see `patches/vpinball/0001-*`.
|
||||
- **A full loading → play flow in a real page** (`examples/basic/index.html`): a byte-level download progress bar, a user-gesture "Start" button (required for both audio autoplay and fullscreen to work), a working fullscreen button, and responsive canvas sizing that fills its container correctly.
|
||||
- **Browser file-loading for self-contained tables**: picking a `.vpx` file, mounting it via `FS.writeFile`, and starting the engine against it — verified end-to-end (`PinTable::LoadGameFromFilename /tables/uploaded.vpx` in the boot log, followed by a normal render).
|
||||
- Touch-control overlay code (`attachTouchControls`) that synthesizes the real default keyboard scancodes (`SDL_SCANCODE_LSHIFT`/`RSHIFT`/`RETURN` — see `src/input/InputManager.cpp`) on `window`, matching SDL3's Emscripten keyboard target; implemented and included in the example, not yet confirmed to move a flipper on a real touch device (see below).
|
||||
|
||||
**Not yet validated (should work, per code review, but unconfirmed end-to-end):**
|
||||
- Keyboard/gamepad input actually reaching a flipper/plunger during real gameplay (the input pipeline is code-complete and validated as wired correctly, but a definitive "pressed a key, ball moved" test is still open — see `src/input/SDLInputHandler.h`/`InputManager.cpp`, unmodified).
|
||||
- Audio actually audible (the pipeline is fully wired per code review; needs a real speaker/headphone test plus a user-gesture gate for browser autoplay policy — see below).
|
||||
- Keyboard/gamepad input actually reaching a flipper/plunger during real gameplay. The pipeline is code-complete and correctly wired (verified by reading `SDLInputHandler.h`/`InputManager.cpp`, unmodified), and the underlying key bindings were confirmed by reading the actual default scancode table. A real physical keypress test is still open - the automation environment used for validation in this project could not reliably simulate a modifier-key press (`Shift` alone) as a trusted browser event, which is a tooling limitation of that environment, not a claim that input doesn't work.
|
||||
- Audio actually audible (the pipeline is fully wired per code review; needs a real speaker/headphone test - the autoplay-policy user-gesture gate is now in place via the example's "Start" button).
|
||||
- DMD rendering via a script-driven `Flasher`/`ScriptGlobalTable::put_DMDPixels` (this is native, core-engine functionality requiring no new code — see `src/core/ScriptGlobalTable.cpp:886-937`, `src/parts/flasher.cpp:1312-1341` — just needs a test table that exercises it).
|
||||
- Touch controls actually flipping a flipper on a real touch device (see above).
|
||||
|
||||
**Explicitly out of scope for now** (browser-sandbox constraints or a real infrastructure-cost tradeoff, not a technical dead end — could be revisited):
|
||||
- **Multi-threading** (pthreads/SharedArrayBuffer): would require mandatory `Cross-Origin-Opener-Policy`/`Cross-Origin-Embedder-Policy` headers on every page hosting this widget, a `coi-serviceworker`-style workaround for static hosts that can't set custom headers, and real risk of breaking unrelated cross-origin embeds on host pages — a poor tradeoff for something meant to be embeddable in arbitrary third-party pages. `ThreadPool`'s one-off parallel work (e.g. parallel `.vpx` item deserialization) already runs synchronously instead (`patches/vpinball/0002-*`). Revisit only if real-world profiling shows single-threaded performance is genuinely insufficient.
|
||||
@@ -73,10 +77,10 @@ Full research and two isolated feasibility spikes (proving real VBScript executi
|
||||
- Raw HID device input (`OpenPinDevHandler`/hidapi) — no browser equivalent for real-hardware nudge/plunger boards. Permanent, not a "for now."
|
||||
|
||||
**Further follow-up work:**
|
||||
- A definitive input/audio validation pass (see above).
|
||||
- Touch controls (on-screen virtual flipper/plunger buttons synthesizing keyboard events) and fullscreen/pointer-lock UI wiring for mobile and kiosk-style embedding.
|
||||
- Browser file-loading UX (drag-and-drop/file picker for user-supplied, self-contained `.vpx` files beyond the bundled demo table; real-world tables with an external `.vbs` script override or a `Music/` folder are a documented, known-unsupported gap for single-file uploads).
|
||||
- Further binary-size tuning (the `.wasm` itself is still ~13MB even with `-O2 --closure 1`; `-sFORCE_FILESYSTEM=1`/broad `EXPORTED_RUNTIME_METHODS` pull in more than strictly needed and are candidates to narrow).
|
||||
- A definitive real-device input/audio validation pass (see above).
|
||||
- Pointer-lock UI wiring (fullscreen is done; pointer-lock isn't needed for this game's input model but noted here in case a future feature wants it).
|
||||
- Real-world table support beyond self-contained single-file uploads: tables with an external `.vbs` script override or a `Music/` folder are a documented, known-unsupported gap.
|
||||
- Further binary-size tuning (the `.wasm` itself is still ~13MB with `-O2`; `--closure 1` was tried but silently stripped `FS.writeFile`/`readFile`/`mkdir` down to low-level node ops - a real Emscripten/Closure interaction bug worth root-causing before re-enabling, since it broke runtime table loading with no compile-time warning. `-sFORCE_FILESYSTEM=1`/broad `EXPORTED_RUNTIME_METHODS` also pull in more than strictly needed).
|
||||
- A browser-based (Puppeteer) CI smoke test that actually loads a table and checks for a rendered frame, replacing today's compile-only CI check.
|
||||
|
||||
## Build
|
||||
@@ -96,20 +100,31 @@ source emsdk/emsdk_env.sh
|
||||
## npm usage
|
||||
|
||||
```ts
|
||||
import { loadPinball } from '@valknar/vpinball-wasm';
|
||||
import { loadPinball, attachTouchControls } from '@valknar/vpinball-wasm';
|
||||
|
||||
const pinball = await loadPinball({ canvas: document.querySelector('canvas') });
|
||||
pinball.start();
|
||||
const canvas = document.querySelector('canvas');
|
||||
const pinball = await loadPinball({
|
||||
canvas,
|
||||
onProgress: (fraction) => updateMyLoadingBar(fraction),
|
||||
});
|
||||
|
||||
// start() (and requestFullscreen()) must be called from within a user
|
||||
// gesture, e.g. a click handler - browsers block audio autoplay and
|
||||
// fullscreen requests otherwise.
|
||||
startButton.addEventListener('click', () => {
|
||||
pinball.start();
|
||||
if ('ontouchstart' in window) attachTouchControls({ container: canvas.parentElement });
|
||||
});
|
||||
```
|
||||
|
||||
The published package name/registry (`@valknar/vpinball-wasm` on this project's Gitea npm registry) is a placeholder pending the first real release — see `package.json`. `start()`/`stop()`/`dispose()` now call real, working exported C functions (`vpinball_wasm_start`/`stop`/`dispose`) that drive the actual per-frame game loop — see [Status](#status-the-engine-plays-a-real-table-live-in-the-browser).
|
||||
See `examples/basic/index.html` for a complete working page (loading progress, file upload, touch controls, fullscreen). The published package name/registry (`@valknar/vpinball-wasm` on this project's Gitea npm registry) is a placeholder pending the first real release — see `package.json`.
|
||||
|
||||
## Known limitations
|
||||
|
||||
- Single-threaded only (no pthreads/SharedArrayBuffer) — a deliberate tradeoff, not a gap; see Roadmap.
|
||||
- No heavy plugin ecosystem (PinMAME/DOF/FlexDMD/etc.) — see Roadmap. Native DMD rendering and a lightweight modern backglass plugin remain possible without it.
|
||||
- No raw hardware input (real cabinet nudge/plunger boards) — permanent, no browser equivalent.
|
||||
- Keyboard/gamepad input and audio are code-complete and wired correctly but not yet validated end-to-end in real gameplay (see Roadmap) — no touch controls or fullscreen/pointer-lock UI yet.
|
||||
- Keyboard/gamepad input and audio are code-complete and wired correctly, and the embedding shell (loading progress, fullscreen, file upload, touch-control overlay) is built and tested end-to-end, but a real-device confirmation that a physical/touch keypress moves a flipper and that audio is audible is still open — see Roadmap.
|
||||
- `.wasm`/asset size is reduced (~11MB total, down from ~51MB) but not fully tuned — see Roadmap.
|
||||
|
||||
## Licensing
|
||||
|
||||
Reference in New Issue
Block a user