@wasm-gaming/rsdkv4-wasm 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -54,16 +54,77 @@ The launcher decides how Escape behaves and draws its own overlay. It should
54
54
  intercept Escape in the **capture phase** if it wants to repurpose it (the engine
55
55
  ships with `DevMenu=false`, so the native in-canvas menu stays suppressed).
56
56
 
57
+ > ⚠️ A capture-phase listener on `window` that calls `stopPropagation()` also
58
+ > stops the **bubble**-phase listener SDL registers there — i.e. it takes the
59
+ > keyboard away from the game. Swallow only the keys you actually handle, and
60
+ > only while your overlay is open. (This is exactly how the shared demo
61
+ > template's pause menu used to kill the d-pad; see
62
+ > [src/demo/components/esc-menu.html](src/demo/components/esc-menu.html).)
63
+
64
+ ### Start menu — the host can own that too
65
+ `instance.game` exposes RSDKv4's Start Menu as data (the `web_*` embind functions
66
+ in `WebGame.cpp`), so a host can draw its own save / character / options screens
67
+ instead of the engine's in-canvas text menus. The logic mirrors
68
+ `Debug.cpp` move for move — same globals, same `saveRAM`, same
69
+ `InitStartingStage()` — so a game started this way lands in the state the native
70
+ menu would have produced.
71
+
72
+ ```js
73
+ const engine = await load({ canvas, assets: { data },
74
+ options: { skipStartMenu: true }, // hide the engine's own screens
75
+ });
76
+
77
+ engine.game.type(); // 1 = Sonic 1, 2 = Sonic 2
78
+ engine.game.players(); // ["SONIC", "TAILS", "KNUCKLES", "SONIC AND TAILS"]
79
+ engine.game.saveSlots(); // [{ slot, empty, character, lives, score, emeralds, list, zone }]
80
+ engine.game.start(0, 1); // slot 0 as Tails — a slot with data resumes it
81
+ engine.game.start(null, 0); // no-save mode
82
+ engine.game.deleteSave(2);
83
+
84
+ engine.game.options(); // the engine's GAME OPTIONS screen, as data
85
+ engine.game.setOption('spindash', 1); // written through to the game's save file
86
+ ```
87
+
88
+ `load()` resolves only once `Engine::Init` has run, so all of the above is safe to
89
+ call the moment it returns. The demo's [start-screens.ts](src/demo/start-screens.ts)
90
+ is a worked example.
91
+
92
+ ### Pausing has an owner
93
+ `pause(owner?)` / `resume(owner?)` — the **first** caller to pause owns it, and only
94
+ that owner's `resume()` lifts it. A pause overlay therefore can't resume a game a
95
+ start screen (or the host) had already frozen:
96
+
97
+ ```js
98
+ engine.pause('menu'); // overlay opens
99
+ engine.resume('menu'); // …closes: only resumes if 'menu' was the one that paused
100
+ ```
101
+
102
+ Pausing stops the **audio** too: `masterPaused` only freezes the logic loop, so
103
+ without that the music kept playing over the overlay — and `destroy()` kept
104
+ playing it after the host returned to its launcher.
105
+
57
106
  ### Options
58
107
  Engine-specific options are described in [src/rsdkv4.options.ts](src/rsdkv4.options.ts)
59
108
  (`Rsdkv4Options` type + `RSDKV4_OPTIONS_SCHEMA`, mirrored into
60
109
  [src/rsdkv4.manifest.ts](src/rsdkv4.manifest.ts)'s `options`). When the host doesn't pass an explicit
61
110
  `settings` asset, the SDK **generates `settings.ini` from `config.options`**
62
- (`devMenu`, `engineDebugMode`, `vsync`, `startingCategory/Scene/Player`).
111
+ (`devMenu`, `engineDebugMode`, `vsync`, `skipStartMenu`,
112
+ `startingCategory/Scene/Player`).
113
+
114
+ > `startingCategory/Scene/Player` default to **255 = unset**, which is what the
115
+ > engine reads as "boot normally". Any other value — *including 0* — makes
116
+ > `Engine::Init` skip the start-menu flow and force that stage.
63
117
 
64
118
  ### Other niceties
65
119
  - **Canvas id guard** — forces `canvas.id = "canvas"` because Emscripten's SDL2
66
120
  port locates the canvas via `querySelector('#canvas')`.
121
+ - **Stale-engine self-heal** — `rsdkv4.js`/`.wasm` are big and most static hosts
122
+ send nothing but `Last-Modified`, so browsers keep a build for hours. Fresh SDK
123
+ against a cached engine is a miserable failure mode: the bridges it calls
124
+ aren't there and every getter quietly answers "empty" — no save slots, no
125
+ characters, an empty pause menu. So `load()` checks the module for the bridges
126
+ it needs and, if any are missing, refetches once with a cache-busting query and
127
+ says so in the console. A healthy build loads the engine exactly once.
67
128
 
68
129
  > Audio autoplay-unlock and the gamepad→keyboard translator are cross-cutting host
69
130
  > concerns (the app's shared input script); the SDK only exposes the `input`
@@ -73,14 +134,19 @@ Engine-specific options are described in [src/rsdkv4.options.ts](src/rsdkv4.opti
73
134
 
74
135
  Built with **`-sWASMFS`** — Emscripten's modern filesystem, replacing MEMFS. The
75
136
  game working dir `/data` is mounted on **OPFS** (persistent) via the `WebFS.cpp`
76
- `web_mount_opfs` helper when the page is **cross-origin isolated**; otherwise it
77
- falls back to the WASMFS in-memory backend so the engine still boots. `Data.rsdk`
137
+ `web_mount_opfs` helper when the page is **cross-origin isolated** *and* the wasm
138
+ build can create the OPFS backend from the main thread (see the caveat below —
139
+ today's build can't); otherwise it falls back to the WASMFS in-memory backend so
140
+ the engine still boots. `Data.rsdk`
78
141
  and the generated `settings.ini` are written there and read via CWD (the engine
79
142
  is booted with the `UsingCWD` arg).
80
143
 
81
144
  ### Backend selection — `config.persist`
82
- - `undefined` (default) — **auto**: OPFS when cross-origin isolated, else in-memory.
83
- - `'opfs'` — **force** OPFS; warn + fall back to in-memory if unavailable.
145
+ - `undefined` (default) — **auto**: OPFS when cross-origin isolated *and* the build
146
+ supports the mount, else in-memory.
147
+ - `'opfs'` — **ask for** OPFS; warn + fall back to in-memory if unavailable. (It
148
+ can't be *forced*: a build that can't mount OPFS would abort the module, so the
149
+ SDK checks first and falls back rather than trying.)
84
150
  - `null` — **force the in-memory** WASMFS backend (the MEMFS-equivalent fallback;
85
151
  non-persistent).
86
152
 
@@ -95,6 +161,32 @@ is booted with the `UsingCWD` arg).
95
161
  This prevents cross-game reuse collisions and allows purging one game's files
96
162
  without touching the others.
97
163
 
164
+ ### `/data` *is* the OPFS root
165
+ `web_mount_opfs` mounts the backend returned by `wasmfs_create_opfs_backend()`,
166
+ whose root is `navigator.storage.getDirectory()`. So the WASM path `/data/<ns>/…`
167
+ and the OPFS path `<ns>/…` a host reaches from JS are **the same file**:
168
+
169
+ | host, from JS (OPFS API) | engine, inside WASMFS |
170
+ | ---------------------------- | ----------------------------- |
171
+ | `rsdkv4/Sonic1/Data.rsdk` | `/data/rsdkv4/Sonic1/Data.rsdk` |
172
+
173
+ A launcher can therefore write a player's `Data.rsdk` straight into the folder the
174
+ engine will use as its working dir and then load with
175
+ `storageNamespace: 'rsdkv4/Sonic1'` and **no `assets.data` at all** — the SDK finds
176
+ the file already persisted and skips the copy. That is what this repo's demo does
177
+ (see [Try it locally](#try-it-locally)); it keeps one copy of a ~40 MB pack instead
178
+ of two, and the game's save data lands next to it in the same folder.
179
+
180
+ ### Save data survives even without the mount
181
+ The engine writes its save file (`SData.bin`) into the working dir, which is
182
+ in-memory on every build that can't mount OPFS — so game saves would die with the
183
+ page and every slot would read "NEW GAME" forever. The *host* side of OPFS has no
184
+ such limitation (the plain async API works on any main thread), so the SDK
185
+ mirrors that one file itself: it copies any stored `SData.bin` into the working
186
+ dir before the engine boots, and copies it back out on pause, on `destroy()`, and
187
+ every 10s in between. Saves therefore persist per `storageNamespace` even while
188
+ the working dir does not. `persist: null` turns the mirror off with everything else.
189
+
98
190
  ### Skip re-fetch when already persisted
99
191
  When the working dir is OPFS-backed, the SDK checks whether `/data/Data.rsdk`
100
192
  (and `settings.ini`) already exist and **reuses them instead of re-downloading**.
@@ -126,15 +218,20 @@ const purged = sonic2.purgeStorage();
126
218
  console.log(purged); // { data: true|false, settings: true|false }
127
219
  ```
128
220
 
129
- > Persistence only actually engages under cross-origin isolation (see the OPFS
130
- > caveat below), so the skip-fetch benefit needs COOP/COEP + `-pthread` to be real.
131
-
132
- > ⚠️ **OPFS caveat (unverified):** OPFS sync-access handles only exist in Workers,
133
- > so Emscripten proxies them to a thread — reliable OPFS needs **`-pthread`** in
134
- > the build plus **COOP/COEP** headers on the host. This build is single-threaded,
135
- > so today OPFS persistence is best-effort and gated on `crossOriginIsolated`.
136
- > Adding `-pthread` interacts with SDL2 + `emscripten_set_main_loop` and must be
137
- > validated with a real build.
221
+ > Persistence doesn't engage with the current build (see the OPFS caveat below),
222
+ > so the skip-fetch benefit isn't real yet — every load fetches.
223
+
224
+ > ⚠️ **OPFS caveat:** OPFS sync-access handles only exist in Workers, so WASMFS's
225
+ > OPFS backend spawns a proxy worker — and it **refuses to do that from the main
226
+ > browser thread** unless the build has **`-sASYNCIFY`** or **`-sJSPI`**. It
227
+ > doesn't fail softly either: `wasmfs_create_opfs_backend()` asserts, and the
228
+ > failed assert `abort()`s the module, so there is nothing left to fall back with.
229
+ > The SDK mounts by calling into the module from the page (main thread) and this
230
+ > build has neither flag, so `web_opfs_supported()` (WebFS.cpp) reports 0 and the
231
+ > SDK never attempts the mount — COOP/COEP alone changes nothing. Making it real
232
+ > means either building with Asyncify/JSPI, or `-pthread` + performing the mount
233
+ > from the engine thread; both interact with SDL2 + `emscripten_set_main_loop` and
234
+ > must be validated with a real build.
138
235
 
139
236
  ## Build
140
237
 
@@ -149,7 +246,9 @@ make build-wasm # local: runs scripts/build.sh inside emscripten/emsdk (Docker
149
246
 
150
247
  - `build-lib` compiles the SDK/options/manifest (`.js` + `.d.ts`) → `dist/rsdkv4/`.
151
248
  - `build-manifest` serializes the typed manifest to `dist/manifest.json`.
152
- - `build-demo` compiles `src/demo/demo.ts` → `dist/demo.js`, copies
249
+ - `build-demo` compiles `src/demo/{demo,library}.ts` → `dist/`, copies the shared
250
+ demo template from `@wasm-gaming/engine-specs` → `dist/demo/`, overwrites its
251
+ launcher with `src/demo/components/launcher.html` (the two-game one), copies
153
252
  `src/demo/index.html` → `dist/index.html`, seeds `dist/settings.ini`.
154
253
  - **`scripts/build.sh`** does not call Docker — it runs the WASM build steps
155
254
  directly and expects an Emscripten SDK on PATH. In CI it runs inside an
@@ -176,40 +275,94 @@ dist/
176
275
  │ └── rsdkv4.manifest.js(+ .d.ts)
177
276
  ├── manifest.json # declarative manifest (artifacts → rsdkv4/rsdkv4.*)
178
277
  ├── index.html # demo shell (import map → ./rsdkv4/rsdkv4.sdk.js)
179
- ├── demo.js
278
+ ├── demo.js # window.SDK + window.rsdkv4Library
279
+ ├── library.js # the two-game OPFS library (src/demo/library.ts)
280
+ ├── start-screens.js # save-select screen (src/demo/start-screens.ts)
281
+ ├── assets/ # demo artwork (game logos), from src/demo/assets/
282
+ ├── demo/ # shared template (engine-specs); launcher.html and
283
+ │ # esc-menu.html replaced by our own copies
180
284
  ├── settings.ini # seeded from src/settings.default.ini if absent
181
- └── Data.rsdk # dev-provided (git-ignored)
285
+ └── Data.rsdk # unused by the demo; kept for manual experiments
182
286
  ```
183
287
 
184
288
  ## Try it locally
185
289
 
186
290
  ```bash
187
- make build # produce dist/ (TypeScript + WASM)
188
- cp /path/to/Data.rsdk dist/ # git-ignored dev file the demo auto-loads
189
- cp /path/to/settings.ini dist/ # OPTIONAL — else seeded/generated
190
- make preview # serves dist/ on :8080
191
- # open http://localhost:8080/
291
+ make build # produce dist/ (TypeScript + WASM)
292
+ make preview # serves dist/ on :8024 with COOP/COEP
293
+ # open http://localhost:8024/
294
+ ```
295
+
296
+ ### The launcher is a two-game library
297
+
298
+ One WASM, two games — so the demo launcher is not a ROM picker but a shelf with
299
+ one slot per game. Drop **Sonic 1's** `Data.rsdk` on one slot and **Sonic 2's** on
300
+ the other (drag-and-drop or the file picker), pick a slot, press **Play**. Each
301
+ pack is stored once, in the browser, and never uploaded:
302
+
303
+ ```
304
+ OPFS root
305
+ └── rsdkv4/
306
+ ├── Sonic1/
307
+ │ ├── Data.rsdk ← what you dropped
308
+ │ ├── settings.ini ← written by the SDK on first launch
309
+ │ └── … ← that game's save data
310
+ └── Sonic2/…
192
311
  ```
193
312
 
194
- The demo fetches `./Data.rsdk` (required) and, if present, `./settings.ini`
195
- (optional) from `dist/`, mounting both into WASMFS. Without a `Data.rsdk` file the
196
- demo shows a pick / drag-and-drop prompt; without a `settings.ini` file the SDK
197
- synthesizes one from `config.options`.
313
+ Because [`/data` is the OPFS root](#data-is-the-opfs-root), that folder *is* the
314
+ engine's working dir: launching loads with `storageNamespace: 'rsdkv4/Sonic1'` and
315
+ no assets, so nothing is copied or re-read. **Replace** swaps the pack; **Remove**
316
+ deletes only `Data.rsdk`, leaving settings and saves for when it comes back.
317
+
318
+ A page that isn't cross-origin isolated can't reach OPFS from WASM (see the caveat
319
+ above), so the SDK falls back to an in-memory working dir and pulls the bytes
320
+ through `dataProvider()` — the library serves them from the same stored copy, so
321
+ the games still launch. Saves still survive, through the
322
+ [save-data mirror](#save-data-survives-even-without-the-mount).
323
+
324
+ ### The game's own menus, in HTML
325
+
326
+ Launching goes straight to a **save-select screen** — the demo's, not the engine's
327
+ (it boots with `skipStartMenu: true`). It is laid out like Sonic Mania's: a row of
328
+ portrait cards, status on top, character in the middle, chaos emeralds along the
329
+ bottom, `NO SAVE` first. File and character are the *same* choice: **←/→ picks the
330
+ file, ↑/↓ picks the player** (Sonic, Tails, Knuckles, Sonic & Tails) on any unused
331
+ file, Enter starts. A used file starts as the character it was saved with. Everything but the game logo
332
+ (`src/demo/assets/`) is drawn in CSS, and it drives the engine through
333
+ `instance.game`, so the resulting game is the same one the native text menus
334
+ would have started.
335
+
336
+ **Escape** opens the pause overlay, which pauses the engine *and its music* and
337
+ shows only what RSDKv4 can actually do live:
338
+
339
+ - **Jump to** — buttons for the title screen, the game's stage menu, level select
340
+ and special stages 1-8. Each closes the overlay and warps straight away, so
341
+ there is no "now press resume" step
342
+ - **Game options** — the engine's own GAME OPTIONS (spindash, speed caps, S1
343
+ spikes, item box set, super forms), read and written through the same globals
344
+ and save file the native screen uses
345
+
346
+ The pieces: [src/demo/library.ts](src/demo/library.ts) (storage),
347
+ [src/demo/start-screens.ts](src/demo/start-screens.ts) (save select),
348
+ [src/demo/components/launcher.html](src/demo/components/launcher.html) (the slots),
349
+ [src/demo/components/esc-menu.html](src/demo/components/esc-menu.html) (pause overlay),
350
+ [src/demo/index.html](src/demo/index.html) (wiring to `SDK.load`).
198
351
 
199
352
  ## Live demo (GitHub Pages)
200
353
 
201
354
  The `pages` job in [.github/workflows/build.yml](.github/workflows/build.yml)
202
- publishes `dist/` to GitHub Pages on pushes to `main` (and manual runs). Since
203
- `Data.rsdk` is git-ignored, the live demo opens on the pick / drag-and-drop
204
- prompt — drop a `Data.rsdk` to boot.
355
+ publishes `dist/` to GitHub Pages on pushes to `main` (and manual runs). No game
356
+ data ships with it, so the live demo opens on two empty slots — drop each game's
357
+ `Data.rsdk` once and they stay in that browser.
205
358
 
206
359
  - **One-time setup:** repo *Settings → Pages → Build and deployment → Source =
207
360
  GitHub Actions*. The workflow also calls `actions/configure-pages` with
208
361
  `enablement: true`, so first deploy can bootstrap Pages automatically when the
209
362
  repo-level Pages site does not exist yet.
210
- - **OPFS note:** GitHub Pages can't set COOP/COEP headers, so the page isn't
211
- cross-origin isolated — OPFS stays off and the SDK uses the in-memory WASMFS
212
- backend (see the filesystem caveat above).
363
+ - **OPFS note:** OPFS stays off and the SDK uses the in-memory WASMFS backend —
364
+ GitHub Pages can't set COOP/COEP headers, and this build couldn't mount OPFS
365
+ even isolated (see the filesystem caveat above).
213
366
 
214
367
  ## Status
215
368
 
@@ -219,9 +372,9 @@ prompt — drop a `Data.rsdk` to boot.
219
372
  the `web_devmenu_*` embind bridge, `callMain`/`FS`/`ccall`, no `--preload-file`).
220
373
  - ⏳ **End-to-end runtime in a browser** (boot from a real `Data.rsdk`) — not yet
221
374
  verified.
222
- - ⏳ **OPFS persistence** — gated on `crossOriginIsolated`; almost certainly needs
223
- a `-pthread` build + COOP/COEP to actually engage (see the caveat above). Until
224
- then the SDK falls back to the in-memory WASMFS backend.
375
+ - ⏳ **OPFS persistence** — not engaged: WASMFS's OPFS backend can't be created on
376
+ the main browser thread without an Asyncify/JSPI build (see the caveat above),
377
+ so the SDK detects that and falls back to the in-memory WASMFS backend.
225
378
 
226
379
  ## License
227
380
 
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "id": "rsdkv4",
3
- "version": "0.1.0",
3
+ "version": "0.1.5",
4
4
  "name": "Retro Software Development Kit v4",
5
+ "description": "RSDKv4 compiled to WebAssembly via Emscripten. One game-agnostic WASM binary runs both Sonic 1 and Sonic 2 — the difference is only which Data.rsdk the host supplies at runtime. Ships none of the game: the player provides their own Data.rsdk, and it never leaves their browser.",
5
6
  "artifacts": {
6
7
  "wasm": "rsdkv4/rsdkv4.wasm",
7
8
  "js": "rsdkv4/rsdkv4.js"
@@ -26,7 +27,17 @@
26
27
  "description": "Engine settings. Omitted → the SDK generates one from config.options."
27
28
  }
28
29
  ],
29
- "input": "rsdkv4",
30
+ "input": {
31
+ "up": "ArrowUp",
32
+ "down": "ArrowDown",
33
+ "left": "ArrowLeft",
34
+ "right": "ArrowRight",
35
+ "a": "KeyZ",
36
+ "b": "KeyX",
37
+ "c": "KeyC",
38
+ "start": "Enter",
39
+ "select": "ShiftLeft"
40
+ },
30
41
  "video": {
31
42
  "baseWidth": 424,
32
43
  "baseHeight": 240,
@@ -50,20 +61,28 @@
50
61
  "type": "boolean",
51
62
  "default": true
52
63
  },
64
+ "skipStartMenu": {
65
+ "type": "boolean",
66
+ "default": false,
67
+ "description": "Skip the engine's own save/character/options screens — set it when the host renders them from instance.game."
68
+ },
53
69
  "startingCategory": {
54
70
  "type": "integer",
55
71
  "minimum": 0,
56
- "default": 0
72
+ "default": 255,
73
+ "description": "0-based stage-list category; 255 = unset (normal boot flow)."
57
74
  },
58
75
  "startingScene": {
59
76
  "type": "integer",
60
77
  "minimum": 0,
61
- "default": 0
78
+ "default": 255,
79
+ "description": "0-based scene in the category; 255 = unset (normal boot flow)."
62
80
  },
63
81
  "startingPlayer": {
64
82
  "type": "integer",
65
83
  "minimum": 0,
66
- "default": 0
84
+ "default": 255,
85
+ "description": "Player/character index; 255 = unset (normal boot flow)."
67
86
  }
68
87
  }
69
88
  },
@@ -1 +1 @@
1
- {"version":3,"file":"rsdkv4.manifest.d.ts","sourceRoot":"","sources":["../../src/rsdkv4.manifest.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAGhE,eAAO,MAAM,QAAQ,EAAE,cAkCtB,CAAC;AAEF,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"rsdkv4.manifest.d.ts","sourceRoot":"","sources":["../../src/rsdkv4.manifest.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAGhE,eAAO,MAAM,QAAQ,EAAE,cA+CtB,CAAC;AAEF,eAAe,QAAQ,CAAC"}
@@ -4,8 +4,9 @@
4
4
  import { RSDKV4_OPTIONS_SCHEMA } from './rsdkv4.options.js';
5
5
  export const manifest = {
6
6
  id: 'rsdkv4',
7
- version: '0.1.0',
7
+ version: '0.1.5',
8
8
  name: 'Retro Software Development Kit v4',
9
+ description: 'RSDKv4 compiled to WebAssembly via Emscripten. One game-agnostic WASM binary runs both Sonic 1 and Sonic 2 — the difference is only which Data.rsdk the host supplies at runtime. Ships none of the game: the player provides their own Data.rsdk, and it never leaves their browser.',
9
10
  artifacts: {
10
11
  // Relative to the manifest (dist/manifest.json); the engine files live in
11
12
  // the dist/rsdkv4/ subfolder.
@@ -30,7 +31,18 @@ export const manifest = {
30
31
  description: 'Engine settings. Omitted → the SDK generates one from config.options.',
31
32
  },
32
33
  ],
33
- input: 'rsdkv4',
34
+ // RSDKv4 keyboard defaults. The key names are KeyboardEvent.code values.
35
+ input: {
36
+ up: 'ArrowUp',
37
+ down: 'ArrowDown',
38
+ left: 'ArrowLeft',
39
+ right: 'ArrowRight',
40
+ a: 'KeyZ',
41
+ b: 'KeyX',
42
+ c: 'KeyC',
43
+ start: 'Enter',
44
+ select: 'ShiftLeft',
45
+ },
34
46
  video: { baseWidth: 424, baseHeight: 240, aspect: '16:9' },
35
47
  options: RSDKV4_OPTIONS_SCHEMA,
36
48
  capabilities: { saveStates: false, sram: false, coreSelectable: false },
@@ -1 +1 @@
1
- {"version":3,"file":"rsdkv4.manifest.js","sourceRoot":"","sources":["../../src/rsdkv4.manifest.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,8EAA8E;AAC9E,iFAAiF;AAGjF,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,MAAM,CAAC,MAAM,QAAQ,GAAmB;IACtC,EAAE,EAAE,QAAQ;IACZ,OAAO,EAAE,OAAO;IAChB,IAAI,EAAE,mCAAmC;IACzC,SAAS,EAAE;QACT,0EAA0E;QAC1E,8BAA8B;QAC9B,IAAI,EAAE,oBAAoB;QAC1B,EAAE,EAAE,kBAAkB;KACvB;IACD,MAAM,EAAE;QACN;YACE,GAAG,EAAE,MAAM;YACX,4EAA4E;YAC5E,0CAA0C;YAC1C,SAAS,EAAE,iBAAiB;YAC5B,QAAQ,EAAE,IAAI;YACd,MAAM,EAAE,CAAC,OAAO,CAAC;YACjB,WAAW,EACT,iFAAiF;SACpF;QACD;YACE,GAAG,EAAE,UAAU;YACf,SAAS,EAAE,oBAAoB;YAC/B,QAAQ,EAAE,KAAK;YACf,MAAM,EAAE,CAAC,MAAM,CAAC;YAChB,WAAW,EACT,uEAAuE;SAC1E;KACF;IACD,KAAK,EAAE,QAAQ;IACf,KAAK,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE;IAC1D,OAAO,EAAE,qBAAqB;IAC9B,YAAY,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE;CACxE,CAAC;AAEF,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"rsdkv4.manifest.js","sourceRoot":"","sources":["../../src/rsdkv4.manifest.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,8EAA8E;AAC9E,iFAAiF;AAGjF,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,MAAM,CAAC,MAAM,QAAQ,GAAmB;IACtC,EAAE,EAAE,QAAQ;IACZ,OAAO,EAAE,OAAO;IAChB,IAAI,EAAE,mCAAmC;IACzC,WAAW,EACT,uRAAuR;IACzR,SAAS,EAAE;QACT,0EAA0E;QAC1E,8BAA8B;QAC9B,IAAI,EAAE,oBAAoB;QAC1B,EAAE,EAAE,kBAAkB;KACvB;IACD,MAAM,EAAE;QACN;YACE,GAAG,EAAE,MAAM;YACX,4EAA4E;YAC5E,0CAA0C;YAC1C,SAAS,EAAE,iBAAiB;YAC5B,QAAQ,EAAE,IAAI;YACd,MAAM,EAAE,CAAC,OAAO,CAAC;YACjB,WAAW,EACT,iFAAiF;SACpF;QACD;YACE,GAAG,EAAE,UAAU;YACf,SAAS,EAAE,oBAAoB;YAC/B,QAAQ,EAAE,KAAK;YACf,MAAM,EAAE,CAAC,MAAM,CAAC;YAChB,WAAW,EACT,uEAAuE;SAC1E;KACF;IACD,yEAAyE;IACzE,KAAK,EAAE;QACL,EAAE,EAAE,SAAS;QACb,IAAI,EAAE,WAAW;QACjB,IAAI,EAAE,WAAW;QACjB,KAAK,EAAE,YAAY;QACnB,CAAC,EAAE,MAAM;QACT,CAAC,EAAE,MAAM;QACT,CAAC,EAAE,MAAM;QACT,KAAK,EAAE,OAAO;QACd,MAAM,EAAE,WAAW;KACpB;IACD,KAAK,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE;IAC1D,OAAO,EAAE,qBAAqB;IAC9B,YAAY,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE;CACxE,CAAC;AAEF,eAAe,QAAQ,CAAC"}
@@ -10,6 +10,12 @@ export interface Rsdkv4Options {
10
10
  engineDebugMode?: boolean;
11
11
  /** VSync the engine's SDL window. */
12
12
  vsync?: boolean;
13
+ /**
14
+ * Skip RSDKv4's in-canvas Start Menu (save select / character select / game
15
+ * options). Set this when the host draws those screens itself from
16
+ * `instance.game` — otherwise the engine's own menu runs underneath.
17
+ */
18
+ skipStartMenu?: boolean;
13
19
  /** Boot directly into a stage: 0-based stage-list category. */
14
20
  startingCategory?: number;
15
21
  /** Boot directly into a stage: 0-based scene within the category. */
@@ -17,6 +23,12 @@ export interface Rsdkv4Options {
17
23
  /** Starting player/character index. */
18
24
  startingPlayer?: number;
19
25
  }
26
+ /**
27
+ * RSDKv4 reads 255 as "not set" for the starting stage/player (see Userdata.cpp:
28
+ * anything else — **including 0** — makes Engine::Init skip the start-menu flow
29
+ * and force InitStartingStage). So "boot normally" has to be 255, not 0.
30
+ */
31
+ export declare const RSDKV4_UNSET = 255;
20
32
  export declare const DEFAULT_RSDKV4_OPTIONS: Required<Rsdkv4Options>;
21
33
  export declare const RSDKV4_OPTIONS_SCHEMA: JSONSchema;
22
34
  //# sourceMappingURL=rsdkv4.options.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"rsdkv4.options.d.ts","sourceRoot":"","sources":["../../src/rsdkv4.options.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAE5D,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,4EAA4E;IAC5E,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,qCAAqC;IACrC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,+DAA+D;IAC/D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,qEAAqE;IACrE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,uCAAuC;IACvC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAAC,aAAa,CAO1D,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,UAoBnC,CAAC"}
1
+ {"version":3,"file":"rsdkv4.options.d.ts","sourceRoot":"","sources":["../../src/rsdkv4.options.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAE5D,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,4EAA4E;IAC5E,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,qCAAqC;IACrC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,+DAA+D;IAC/D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,qEAAqE;IACrE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,uCAAuC;IACvC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;GAIG;AACH,eAAO,MAAM,YAAY,MAAM,CAAC;AAEhC,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAAC,aAAa,CAQ1D,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,UAyCnC,CAAC"}
@@ -10,13 +10,20 @@
10
10
  // At runtime these options are serialized into RSDKv4's `settings.ini` (see the
11
11
  // SDK's settings serializer) when the host does not pass an explicit `settings`
12
12
  // asset.
13
+ /**
14
+ * RSDKv4 reads 255 as "not set" for the starting stage/player (see Userdata.cpp:
15
+ * anything else — **including 0** — makes Engine::Init skip the start-menu flow
16
+ * and force InitStartingStage). So "boot normally" has to be 255, not 0.
17
+ */
18
+ export const RSDKV4_UNSET = 255;
13
19
  export const DEFAULT_RSDKV4_OPTIONS = {
14
20
  devMenu: false,
15
21
  engineDebugMode: true,
16
22
  vsync: true,
17
- startingCategory: 0,
18
- startingScene: 0,
19
- startingPlayer: 0,
23
+ skipStartMenu: false,
24
+ startingCategory: RSDKV4_UNSET,
25
+ startingScene: RSDKV4_UNSET,
26
+ startingPlayer: RSDKV4_UNSET,
20
27
  };
21
28
  export const RSDKV4_OPTIONS_SCHEMA = {
22
29
  type: 'object',
@@ -33,9 +40,29 @@ export const RSDKV4_OPTIONS_SCHEMA = {
33
40
  description: 'Enables the web devmenu embind bridge (stage list / warp / pause).',
34
41
  },
35
42
  vsync: { type: 'boolean', default: true },
36
- startingCategory: { type: 'integer', minimum: 0, default: 0 },
37
- startingScene: { type: 'integer', minimum: 0, default: 0 },
38
- startingPlayer: { type: 'integer', minimum: 0, default: 0 },
43
+ skipStartMenu: {
44
+ type: 'boolean',
45
+ default: false,
46
+ description: "Skip the engine's own save/character/options screens — set it when the host renders them from instance.game.",
47
+ },
48
+ startingCategory: {
49
+ type: 'integer',
50
+ minimum: 0,
51
+ default: RSDKV4_UNSET,
52
+ description: '0-based stage-list category; 255 = unset (normal boot flow).',
53
+ },
54
+ startingScene: {
55
+ type: 'integer',
56
+ minimum: 0,
57
+ default: RSDKV4_UNSET,
58
+ description: '0-based scene in the category; 255 = unset (normal boot flow).',
59
+ },
60
+ startingPlayer: {
61
+ type: 'integer',
62
+ minimum: 0,
63
+ default: RSDKV4_UNSET,
64
+ description: 'Player/character index; 255 = unset (normal boot flow).',
65
+ },
39
66
  },
40
67
  };
41
68
  //# sourceMappingURL=rsdkv4.options.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"rsdkv4.options.js","sourceRoot":"","sources":["../../src/rsdkv4.options.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,EAAE;AACF,+EAA+E;AAC/E,qCAAqC;AACrC,+EAA+E;AAC/E,8EAA8E;AAC9E,6EAA6E;AAC7E,iEAAiE;AACjE,EAAE;AACF,gFAAgF;AAChF,gFAAgF;AAChF,SAAS;AAuBT,MAAM,CAAC,MAAM,sBAAsB,GAA4B;IAC7D,OAAO,EAAE,KAAK;IACd,eAAe,EAAE,IAAI;IACrB,KAAK,EAAE,IAAI;IACX,gBAAgB,EAAE,CAAC;IACnB,aAAa,EAAE,CAAC;IAChB,cAAc,EAAE,CAAC;CAClB,CAAC;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAAe;IAC/C,IAAI,EAAE,QAAQ;IACd,oBAAoB,EAAE,KAAK;IAC3B,UAAU,EAAE;QACV,OAAO,EAAE;YACP,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,KAAK;YACd,WAAW,EACT,mHAAmH;SACtH;QACD,eAAe,EAAE;YACf,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,IAAI;YACb,WAAW,EAAE,oEAAoE;SAClF;QACD,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE;QACzC,gBAAgB,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE;QAC7D,aAAa,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE;QAC1D,cAAc,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE;KAC5D;CACF,CAAC"}
1
+ {"version":3,"file":"rsdkv4.options.js","sourceRoot":"","sources":["../../src/rsdkv4.options.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,EAAE;AACF,+EAA+E;AAC/E,qCAAqC;AACrC,+EAA+E;AAC/E,8EAA8E;AAC9E,6EAA6E;AAC7E,iEAAiE;AACjE,EAAE;AACF,gFAAgF;AAChF,gFAAgF;AAChF,SAAS;AA6BT;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,GAAG,CAAC;AAEhC,MAAM,CAAC,MAAM,sBAAsB,GAA4B;IAC7D,OAAO,EAAE,KAAK;IACd,eAAe,EAAE,IAAI;IACrB,KAAK,EAAE,IAAI;IACX,aAAa,EAAE,KAAK;IACpB,gBAAgB,EAAE,YAAY;IAC9B,aAAa,EAAE,YAAY;IAC3B,cAAc,EAAE,YAAY;CAC7B,CAAC;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAAe;IAC/C,IAAI,EAAE,QAAQ;IACd,oBAAoB,EAAE,KAAK;IAC3B,UAAU,EAAE;QACV,OAAO,EAAE;YACP,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,KAAK;YACd,WAAW,EACT,mHAAmH;SACtH;QACD,eAAe,EAAE;YACf,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,IAAI;YACb,WAAW,EAAE,oEAAoE;SAClF;QACD,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE;QACzC,aAAa,EAAE;YACb,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,KAAK;YACd,WAAW,EACT,8GAA8G;SACjH;QACD,gBAAgB,EAAE;YAChB,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,CAAC;YACV,OAAO,EAAE,YAAY;YACrB,WAAW,EAAE,8DAA8D;SAC5E;QACD,aAAa,EAAE;YACb,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,CAAC;YACV,OAAO,EAAE,YAAY;YACrB,WAAW,EAAE,gEAAgE;SAC9E;QACD,cAAc,EAAE;YACd,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,CAAC;YACV,OAAO,EAAE,YAAY;YACrB,WAAW,EAAE,yDAAyD;SACvE;KACF;CACF,CAAC"}
@@ -12,8 +12,65 @@ export interface RsdkDevMenuBridge {
12
12
  loadStage(categoryIdx: number, stageIdx: number): void;
13
13
  setPaused(paused: boolean): void;
14
14
  }
15
- export type Rsdkv4Instance = EngineInstance & {
15
+ /** One of RSDKv4's four save slots, as the engine's own save-select screen sees it. */
16
+ export interface RsdkSaveSlot {
17
+ /** 0-3. */
18
+ slot: number;
19
+ /** No game stored here — starting it means picking a character first. */
20
+ empty: boolean;
21
+ /** Character index into `players()`: 0 Sonic, 1 Tails, 2 Knuckles, 3 Sonic & Tails. */
22
+ character: number;
23
+ lives: number;
24
+ score: number;
25
+ emeralds: number;
26
+ /** Stage list the save resumes into (1 regular, 3 special), or -1 when empty. */
27
+ list: number;
28
+ /** 0-based scene within `list` — index it against `devMenu.getStageList()`. */
29
+ zone: number;
30
+ }
31
+ /** A row of the engine's GAME OPTIONS screen (differs between Sonic 1 and 2). */
32
+ export interface RsdkGameOption {
33
+ key: string;
34
+ label: string;
35
+ value: number;
36
+ type: 'boolean' | 'enum';
37
+ /** Choice labels, for `type: 'enum'` (item box sets). */
38
+ values?: string[];
39
+ }
40
+ /**
41
+ * The engine's Start Menu as data, so a host can render save-slot / character /
42
+ * game-option screens itself. Everything here mirrors what RSDKv4's own in-canvas
43
+ * menu does — see WebGame.cpp.
44
+ */
45
+ export interface RsdkGameBridge {
46
+ /** 1 = Sonic 1, 2 = Sonic 2, 0 = unrecognised pack. */
47
+ type(): number;
48
+ /** Playable characters from the pack's GameConfig, in engine order. */
49
+ players(): string[];
50
+ saveSlots(): RsdkSaveSlot[];
51
+ /**
52
+ * Start a game the way the native menu would: a slot with data continues it,
53
+ * an empty slot starts a new game as `player`, and `slot: null` plays without
54
+ * saving. Resumes the engine if the SDK had it paused.
55
+ */
56
+ start(slot: number | null, player?: number): void;
57
+ /** Wipe a slot back to "NEW GAME". */
58
+ deleteSave(slot: number): void;
59
+ options(): RsdkGameOption[];
60
+ /** Set an option; written through to the engine and its save file, as the engine does. */
61
+ setOption(key: string, value: number): void;
62
+ }
63
+ export type Rsdkv4Instance = Omit<EngineInstance, 'pause' | 'resume'> & {
64
+ /**
65
+ * Pause, on behalf of `owner` (default 'host'). The first caller to pause owns
66
+ * it and only that owner's `resume()` lifts it, so a pause overlay can't
67
+ * resume a game a start screen — or the host — had already frozen. Audio stops
68
+ * with the engine.
69
+ */
70
+ pause(owner?: string): void;
71
+ resume(owner?: string): void;
16
72
  devMenu: RsdkDevMenuBridge;
73
+ game: RsdkGameBridge;
17
74
  /** True when the working dir is OPFS-backed (persistent) rather than in-memory. */
18
75
  persistent: boolean;
19
76
  /** Relative storage namespace used under /data (e.g. "sonic1", "sonic2"). */
@@ -37,6 +94,8 @@ export type Rsdkv4LoadConfig = EngineConfig & {
37
94
  * Examples: "sonic1", "sonic2", "my-pack/v1".
38
95
  */
39
96
  storageNamespace?: string;
97
+ /** Deprecated alias for `canvasEl`, kept for hosts written against 0.0.x. */
98
+ canvas?: HTMLCanvasElement;
40
99
  };
41
100
  /** Boot the RSDKv4 engine. */
42
101
  export declare function load(config: Rsdkv4LoadConfig): Promise<Rsdkv4Instance>;
@@ -1 +1 @@
1
- {"version":3,"file":"rsdkv4.sdk.d.ts","sourceRoot":"","sources":["../../src/rsdkv4.sdk.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACzF,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAGhD,OAAO,EAAE,QAAQ,EAAE,CAAC;AA+GpB,uEAAuE;AACvE,MAAM,WAAW,iBAAiB;IAChC,YAAY,IAAI,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC,CAAC;IACzE,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACvD,SAAS,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC;CAClC;AAED,MAAM,MAAM,cAAc,GAAG,cAAc,GAAG;IAC5C,OAAO,EAAE,iBAAiB,CAAC;IAC3B,mFAAmF;IACnF,UAAU,EAAE,OAAO,CAAC;IACpB,6EAA6E;IAC7E,gBAAgB,EAAE,MAAM,CAAC;IACzB,2DAA2D;IAC3D,YAAY,IAAI;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC;CACtD,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG,YAAY,GAAG;IAC5C,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC,SAAS,CAAC,GAAG,SAAS,CAAC;IACpD,gBAAgB,CAAC,EAAE,MAAM,OAAO,CAAC,SAAS,CAAC,GAAG,SAAS,CAAC;IACxD;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,8BAA8B;AAC9B,wBAAsB,IAAI,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,cAAc,CAAC,CA+H5E;;;;;AAED,wBAAkC"}
1
+ {"version":3,"file":"rsdkv4.sdk.d.ts","sourceRoot":"","sources":["../../src/rsdkv4.sdk.ts"],"names":[],"mappings":"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"}