@wasm-gaming/rsdkv5-wasm 0.1.0

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.
@@ -0,0 +1,28 @@
1
+ // Engine-specific options for RSDKv5U (Sonic Mania).
2
+ export const DEFAULT_RSDKV5_OPTIONS = {
3
+ devMenu: false,
4
+ engineDebugMode: true,
5
+ vsync: true,
6
+ language: 0,
7
+ region: -1,
8
+ };
9
+ export const RSDKV5_OPTIONS_SCHEMA = {
10
+ type: 'object',
11
+ additionalProperties: false,
12
+ properties: {
13
+ devMenu: {
14
+ type: 'boolean',
15
+ default: false,
16
+ description: "Sonic Mania's native dev menu. The launcher provides its own overlay, so keep this off.",
17
+ },
18
+ engineDebugMode: {
19
+ type: 'boolean',
20
+ default: true,
21
+ description: 'Enables the web debug hooks.',
22
+ },
23
+ vsync: { type: 'boolean', default: true },
24
+ language: { type: 'integer', default: 0, minimum: 0 },
25
+ region: { type: 'integer', default: -1 },
26
+ },
27
+ };
28
+ //# sourceMappingURL=rsdkv5.options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rsdkv5.options.js","sourceRoot":"","sources":["../../src/rsdkv5.options.ts"],"names":[],"mappings":"AAAA,qDAAqD;AA8BrD,MAAM,CAAC,MAAM,sBAAsB,GAA4B;IAC7D,OAAO,EAAE,KAAK;IACd,eAAe,EAAE,IAAI;IACrB,KAAK,EAAE,IAAI;IACX,QAAQ,EAAE,CAAC;IACX,MAAM,EAAE,CAAC,CAAC;CACX,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,yFAAyF;SAC5F;QACD,eAAe,EAAE;YACf,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,IAAI;YACb,WAAW,EAAE,8BAA8B;SAC5C;QACD,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE;QACzC,QAAQ,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE;QACrD,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC,EAAE;KACzC;CACF,CAAC"}
@@ -0,0 +1,44 @@
1
+ import type { EngineConfig, EngineInstance, AssetData } from '@wasm-gaming/engine-specs';
2
+ import { manifest } from './rsdkv5.manifest.js';
3
+ export { manifest };
4
+ /** RSDKv5-specific bridge for the launcher's debug/stage-select UI. */
5
+ export interface RsdkDevMenuBridge {
6
+ getStageList(): Array<{
7
+ name: string;
8
+ stages: Array<{
9
+ name: string;
10
+ }>;
11
+ }>;
12
+ loadStage(categoryIdx: number, stageIdx: number): void;
13
+ setPaused(paused: boolean): void;
14
+ }
15
+ export type Rsdkv5Instance = EngineInstance & {
16
+ devMenu: RsdkDevMenuBridge;
17
+ /** True when the working dir is OPFS-backed (persistent) rather than in-memory. */
18
+ persistent: boolean;
19
+ /** Relative storage namespace used under /data (e.g. "sonic-mania"). */
20
+ storageNamespace: string;
21
+ /** Remove persisted game files for this namespace only. */
22
+ purgeStorage(): {
23
+ data: boolean;
24
+ settings: boolean;
25
+ };
26
+ };
27
+ /**
28
+ * Extra (engine-specific) config on top of the contract's EngineConfig: lazy asset
29
+ * providers, invoked only on a cache miss.
30
+ */
31
+ export type Rsdkv5LoadConfig = EngineConfig & {
32
+ dataProvider?: () => Promise<AssetData> | AssetData;
33
+ settingsProvider?: () => Promise<AssetData> | AssetData;
34
+ /** Per-game storage folder under /data used for OPFS/WASMFS files. */
35
+ storageNamespace?: string;
36
+ };
37
+ /** Boot the RSDKv5 engine. */
38
+ export declare function load(config: Rsdkv5LoadConfig): Promise<Rsdkv5Instance>;
39
+ declare const _default: {
40
+ manifest: import("@wasm-gaming/engine-specs").EngineManifest;
41
+ load: typeof load;
42
+ };
43
+ export default _default;
44
+ //# sourceMappingURL=rsdkv5.sdk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rsdkv5.sdk.d.ts","sourceRoot":"","sources":["../../src/rsdkv5.sdk.ts"],"names":[],"mappings":"AAYA,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,wEAAwE;IACxE,gBAAgB,EAAE,MAAM,CAAC;IACzB,2DAA2D;IAC3D,YAAY,IAAI;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC;CACtD,CAAC;AAEF;;;GAGG;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,sEAAsE;IACtE,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,8BAA8B;AAC9B,wBAAsB,IAAI,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,cAAc,CAAC,CAmH5E;;;;;AAED,wBAAkC"}
@@ -0,0 +1,234 @@
1
+ // @wasm-gaming/rsdkv5-wasm — SDK entry point.
2
+ import { manifest } from './rsdkv5.manifest.js';
3
+ import { DEFAULT_RSDKV5_OPTIONS } from './rsdkv5.options.js';
4
+ export { manifest };
5
+ const WORK_ROOT = '/data';
6
+ const DEFAULT_STORAGE_NAMESPACE = 'default';
7
+ /**
8
+ * Serialize engine options into RSDKv5U's Settings.ini format. This is the v5
9
+ * dialect (lowercase keys, 0/1 booleans, `dataFile=` in [Game]) — matching the
10
+ * exact file the proven build preloaded — NOT v4's [Dev]/CamelCase format.
11
+ * `dataFile=Data.rsdk` is load-bearing: it names the pack the engine opens,
12
+ * relative to CWD (the SDK chdir()s into the working dir before callMain).
13
+ */
14
+ function buildSettingsIni(options = {}) {
15
+ const o = { ...DEFAULT_RSDKV5_OPTIONS, ...options };
16
+ const b = (v) => (v ? '1' : '0');
17
+ return [
18
+ '[Game]',
19
+ 'dataFile=Data.rsdk',
20
+ `devMenu=${b(o.devMenu)}`,
21
+ `language=${o.language | 0}`,
22
+ `region=${o.region | 0}`,
23
+ '',
24
+ '[Video]',
25
+ 'windowed=1',
26
+ 'border=1',
27
+ 'exclusiveFS=0',
28
+ `vsync=${b(o.vsync)}`,
29
+ 'tripleBuffering=0',
30
+ 'pixWidth=424',
31
+ 'winWidth=424',
32
+ 'winHeight=240',
33
+ 'fsWidth=0',
34
+ 'fsHeight=0',
35
+ 'refreshRate=60',
36
+ 'shaderSupport=0',
37
+ 'screenShader=0',
38
+ '',
39
+ '[Audio]',
40
+ 'streamsEnabled=1',
41
+ 'streamVolume=0.8',
42
+ 'sfxVolume=1.0',
43
+ '',
44
+ ].join('\n');
45
+ }
46
+ function toUint8(x) {
47
+ if (x == null)
48
+ return null;
49
+ if (typeof x === 'string')
50
+ return new TextEncoder().encode(x);
51
+ if (x instanceof Uint8Array)
52
+ return x;
53
+ if (x instanceof ArrayBuffer)
54
+ return new Uint8Array(x);
55
+ if (ArrayBuffer.isView(x))
56
+ return new Uint8Array(x.buffer, x.byteOffset, x.byteLength);
57
+ throw new TypeError('asset must be Uint8Array | ArrayBuffer | string');
58
+ }
59
+ /** Normalize a user-provided storage namespace into a safe relative path. */
60
+ function normalizeStorageNamespace(namespace) {
61
+ if (typeof namespace !== 'string' || !namespace.trim())
62
+ return DEFAULT_STORAGE_NAMESPACE;
63
+ const cleaned = namespace
64
+ .split('/')
65
+ .map((segment) => segment.trim())
66
+ .filter(Boolean)
67
+ .map((segment) => segment.replace(/[^A-Za-z0-9._-]/g, '_'))
68
+ .filter(Boolean)
69
+ .join('/');
70
+ return cleaned || DEFAULT_STORAGE_NAMESPACE;
71
+ }
72
+ /** Best-effort mkdir -p for the Emscripten FS layer. */
73
+ function ensureDir(Module, path) {
74
+ const parts = path.split('/').filter(Boolean);
75
+ let current = '';
76
+ for (const part of parts) {
77
+ current += `/${part}`;
78
+ try {
79
+ Module.FS.mkdir(current);
80
+ }
81
+ catch {
82
+ /* already exists */
83
+ }
84
+ }
85
+ }
86
+ /** Mount the game working directory. */
87
+ function mountWorkingDir(Module, storageNamespace) {
88
+ const workDir = `${WORK_ROOT}/${storageNamespace}`;
89
+ ensureDir(Module, workDir);
90
+ return { persistent: false, workDir };
91
+ }
92
+ /** True if `path` exists in the (mounted) filesystem. */
93
+ function fileExists(Module, path) {
94
+ try {
95
+ Module.FS.stat(path);
96
+ return true;
97
+ }
98
+ catch {
99
+ return false;
100
+ }
101
+ }
102
+ /** Delete a file if present; returns true when something was removed. */
103
+ function deleteFileIfExists(Module, path) {
104
+ if (!fileExists(Module, path))
105
+ return false;
106
+ try {
107
+ Module.FS.unlink(path);
108
+ return true;
109
+ }
110
+ catch {
111
+ return false;
112
+ }
113
+ }
114
+ /** Boot the RSDKv5 engine. */
115
+ export async function load(config) {
116
+ const { canvas, assets, onEvent } = config;
117
+ const options = config.options;
118
+ if (!canvas)
119
+ throw new Error('rsdkv5: config.canvas is required');
120
+ // Emscripten's SDL2 port locates the canvas via document.querySelector('#canvas').
121
+ if (canvas.id !== 'canvas')
122
+ canvas.id = 'canvas';
123
+ const emit = (e) => {
124
+ try {
125
+ onEvent?.(e);
126
+ }
127
+ catch { /* host handler must not break us */ }
128
+ };
129
+ const jsUrl = config.jsUrl ?? new URL('./rsdkv5.js', import.meta.url).href;
130
+ const wasmUrl = config.wasmUrl ?? new URL('./rsdkv5.wasm', import.meta.url).href;
131
+ // RSDKv5 uses the same key map preset used by the shared launcher script.
132
+ if (typeof window !== 'undefined')
133
+ window.__gamepadKeyMap = manifest.input;
134
+ const mod = await import(/* @vite-ignore */ jsUrl);
135
+ const createRSDKv5 = mod.default;
136
+ const Module = await createRSDKv5({
137
+ canvas,
138
+ noInitialRun: true,
139
+ locateFile: (path) => (path.endsWith('.wasm') ? wasmUrl : path),
140
+ print: (...a) => console.log('[rsdkv5]', ...a),
141
+ printErr: (...a) => console.error('[rsdkv5]', ...a),
142
+ onAbort: (reason) => emit({ type: 'error', error: new Error(`rsdkv5 aborted: ${reason}`) }),
143
+ });
144
+ const storageNamespace = normalizeStorageNamespace(config.storageNamespace);
145
+ const { persistent, workDir } = mountWorkingDir(Module, storageNamespace);
146
+ const dataPath = `${workDir}/Data.rsdk`;
147
+ // Capital S: RSDKv5U opens "Settings.ini" and the Emscripten FS is
148
+ // case-sensitive (v4 used lowercase settings.ini — do not copy that here).
149
+ const settingsPath = `${workDir}/Settings.ini`;
150
+ // Data.rsdk — precedence: explicit asset > lazy provider > existing file.
151
+ let dataBytes = toUint8(assets?.data);
152
+ if (!dataBytes && config.dataProvider) {
153
+ dataBytes = toUint8(await config.dataProvider());
154
+ }
155
+ if (dataBytes) {
156
+ Module.FS.writeFile(dataPath, dataBytes);
157
+ }
158
+ else if (!fileExists(Module, dataPath)) {
159
+ throw new Error('rsdkv5: no Data.rsdk — provide assets.data or dataProvider');
160
+ }
161
+ // settings.ini — explicit asset > lazy provider > generated from options.
162
+ let settingsBytes = toUint8(assets?.settings);
163
+ if (!settingsBytes && config.settingsProvider) {
164
+ settingsBytes = toUint8(await config.settingsProvider());
165
+ }
166
+ if (settingsBytes) {
167
+ Module.FS.writeFile(settingsPath, settingsBytes);
168
+ }
169
+ else if (!fileExists(Module, settingsPath)) {
170
+ Module.FS.writeFile(settingsPath, new TextEncoder().encode(buildSettingsIni(options)));
171
+ }
172
+ Module.FS.chdir(workDir);
173
+ const setPaused = (paused) => {
174
+ if (typeof Module.web_devmenu_set_paused === 'function')
175
+ Module.web_devmenu_set_paused(paused);
176
+ };
177
+ const devMenu = {
178
+ getStageList() {
179
+ if (typeof Module.web_devmenu_get_stage_list !== 'function')
180
+ return [];
181
+ try {
182
+ return JSON.parse(Module.web_devmenu_get_stage_list());
183
+ }
184
+ catch (e) {
185
+ console.error('[rsdkv5] getStageList failed', e);
186
+ return [];
187
+ }
188
+ },
189
+ loadStage(categoryIdx, stageIdx) {
190
+ if (typeof Module.web_devmenu_load_stage === 'function') {
191
+ Module.web_devmenu_load_stage(categoryIdx | 0, stageIdx | 0);
192
+ }
193
+ },
194
+ setPaused,
195
+ };
196
+ // No 'UsingCWD' argv — that's a v4-ism; v5U's main() doesn't parse it. The
197
+ // engine resolves Settings.ini/dataFile relative to the FS CWD set above.
198
+ Module.callMain([]);
199
+ emit({ type: 'ready' });
200
+ return {
201
+ start() { },
202
+ pause() { setPaused(true); },
203
+ resume() { setPaused(false); },
204
+ reset() {
205
+ throw new Error('rsdkv5: reset() is not supported — destroy() and load() again');
206
+ },
207
+ setInput(preset) {
208
+ if (typeof window !== 'undefined') {
209
+ window.__gamepadKeyMap = preset ?? manifest.input;
210
+ }
211
+ },
212
+ destroy() {
213
+ try {
214
+ Module.pauseMainLoop?.();
215
+ }
216
+ catch { /* noop */ }
217
+ try {
218
+ setPaused(true);
219
+ }
220
+ catch { /* noop */ }
221
+ },
222
+ devMenu,
223
+ persistent,
224
+ storageNamespace,
225
+ purgeStorage() {
226
+ return {
227
+ data: deleteFileIfExists(Module, dataPath),
228
+ settings: deleteFileIfExists(Module, settingsPath),
229
+ };
230
+ },
231
+ };
232
+ }
233
+ export default { manifest, load };
234
+ //# sourceMappingURL=rsdkv5.sdk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rsdkv5.sdk.js","sourceRoot":"","sources":["../../src/rsdkv5.sdk.ts"],"names":[],"mappings":"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"}
Binary file
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@wasm-gaming/rsdkv5-wasm",
3
+ "version": "0.1.0",
4
+ "description": "RSDKv5 (Retro Software Development Kit v5) compiled to WebAssembly, with a JS SDK conforming to the wasm-gaming engine contract. Runs Sonic Mania by loading Data.rsdk at runtime.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/wasm-gaming/rsdkv5-wasm.git"
8
+ },
9
+ "bugs": {
10
+ "url": "https://github.com/wasm-gaming/rsdkv5-wasm/issues"
11
+ },
12
+ "homepage": "https://github.com/wasm-gaming/rsdkv5-wasm#readme",
13
+ "type": "module",
14
+ "main": "./dist/rsdkv5/rsdkv5.sdk.js",
15
+ "types": "./dist/rsdkv5/rsdkv5.sdk.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/rsdkv5/rsdkv5.sdk.d.ts",
19
+ "import": "./dist/rsdkv5/rsdkv5.sdk.js"
20
+ },
21
+ "./options": {
22
+ "types": "./dist/rsdkv5/rsdkv5.options.d.ts",
23
+ "import": "./dist/rsdkv5/rsdkv5.options.js"
24
+ },
25
+ "./manifest": "./dist/manifest.json"
26
+ },
27
+ "files": [
28
+ "dist/rsdkv5/",
29
+ "!dist/rsdkv5/*.data",
30
+ "!dist/rsdkv5/*.rsdk",
31
+ "dist/manifest.json",
32
+ "src/rsdkv5.manifest.ts",
33
+ "src/rsdkv5.options.ts",
34
+ "src/rsdkv5.sdk.ts",
35
+ "README.md"
36
+ ],
37
+ "comment:scripts": "Build logic lives in the Makefile, not here (make build / build-sdk / build-wasm / preview).",
38
+ "scripts": {},
39
+ "peerDependencies": {
40
+ "@wasm-gaming/engine-specs": "*"
41
+ },
42
+ "peerDependenciesMeta": {
43
+ "@wasm-gaming/engine-specs": {
44
+ "optional": true
45
+ }
46
+ },
47
+ "devDependencies": {
48
+ "@wasm-gaming/engine-specs": "^0.1.0",
49
+ "typescript": "^5.5.0"
50
+ },
51
+ "engineContract": "https://github.com/wasm-gaming/engine-specs",
52
+ "license": "MIT"
53
+ }
@@ -0,0 +1,44 @@
1
+ // The rsdkv5 EngineManifest — typed against the contract, so a drift from
2
+ // @wasm-gaming/engine-specs is a compile error here. `npm run build:manifest`
3
+ // serializes this to dist/manifest.json (the artifact CI attaches to a Release).
4
+
5
+ import type { EngineManifest } from '@wasm-gaming/engine-specs';
6
+ import { RSDKV5_OPTIONS_SCHEMA } from './rsdkv5.options.js';
7
+
8
+ export const manifest: EngineManifest = {
9
+ id: 'rsdkv5',
10
+ version: '0.1.0',
11
+ name: 'Retro Software Development Kit v5',
12
+ artifacts: {
13
+ // Relative to the manifest (dist/manifest.json); the engine files live in
14
+ // the dist/rsdkv5/ subfolder.
15
+ wasm: 'rsdkv5/rsdkv5.wasm',
16
+ js: 'rsdkv5/rsdkv5.js',
17
+ },
18
+ assets: [
19
+ {
20
+ key: 'data',
21
+ // Lives under the OPFS-backed (persistent) working dir; the engine reads it
22
+ // via CWD.
23
+ mountPath: '/data/Data.rsdk',
24
+ required: true,
25
+ accept: ['.rsdk'],
26
+ description:
27
+ 'RSDKv5 game data pack. Sonic Mania uses this at runtime to select content.',
28
+ },
29
+ {
30
+ key: 'settings',
31
+ mountPath: '/data/Settings.ini',
32
+ required: false,
33
+ accept: ['.ini'],
34
+ description:
35
+ 'Engine settings. Omitted → the SDK generates one from config.options.',
36
+ },
37
+ ],
38
+ input: 'rsdkv5',
39
+ video: { baseWidth: 424, baseHeight: 240, aspect: '16:9' },
40
+ options: RSDKV5_OPTIONS_SCHEMA,
41
+ capabilities: { saveStates: false, sram: false, coreSelectable: false },
42
+ };
43
+
44
+ export default manifest;
@@ -0,0 +1,58 @@
1
+ // Engine-specific options for RSDKv5U (Sonic Mania).
2
+
3
+ // This is the authoritative description of what `EngineConfig.options` accepts
4
+ // for this engine. It provides both:
5
+ // - `Rsdkv5Options` — the compile-time type (for TS hosts/launchers)
6
+ // - `RSDKV5_OPTIONS_SCHEMA` — the JSON Schema mirrored into manifest.json's
7
+ // `options` (for runtime host UI + validation)
8
+ // - `DEFAULT_RSDKV5_OPTIONS` — defaults the SDK falls back to.
9
+ //
10
+ // At runtime these options are serialized into Sonic Mania's `Settings.ini`
11
+ // when the host does not pass an explicit `settings` asset.
12
+
13
+ import type { JSONSchema } from '@wasm-gaming/engine-specs';
14
+
15
+ export interface Rsdkv5Options {
16
+ /**
17
+ * Sonic Mania's native dev menu. Kept off by default: the launcher owns its
18
+ * own overlay, and leaving this on can hijack boot flow.
19
+ */
20
+ devMenu?: boolean;
21
+ /** Enables the debug hooks exposed by the web build. */
22
+ engineDebugMode?: boolean;
23
+ /** VSync the engine's SDL window. */
24
+ vsync?: boolean;
25
+ /** Language numeric value used by the build's settings.ini. */
26
+ language?: number;
27
+ /** Region numeric value used by the build's settings.ini. */
28
+ region?: number;
29
+ }
30
+
31
+ export const DEFAULT_RSDKV5_OPTIONS: Required<Rsdkv5Options> = {
32
+ devMenu: false,
33
+ engineDebugMode: true,
34
+ vsync: true,
35
+ language: 0,
36
+ region: -1,
37
+ };
38
+
39
+ export const RSDKV5_OPTIONS_SCHEMA: JSONSchema = {
40
+ type: 'object',
41
+ additionalProperties: false,
42
+ properties: {
43
+ devMenu: {
44
+ type: 'boolean',
45
+ default: false,
46
+ description:
47
+ "Sonic Mania's native dev menu. The launcher provides its own overlay, so keep this off.",
48
+ },
49
+ engineDebugMode: {
50
+ type: 'boolean',
51
+ default: true,
52
+ description: 'Enables the web debug hooks.',
53
+ },
54
+ vsync: { type: 'boolean', default: true },
55
+ language: { type: 'integer', default: 0, minimum: 0 },
56
+ region: { type: 'integer', default: -1 },
57
+ },
58
+ };