@mpgd/game-runtime 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.
- package/LICENSE +21 -0
- package/README.md +308 -0
- package/dist/actions/index.d.ts +75 -0
- package/dist/actions/index.js +321 -0
- package/dist/channels.d.ts +2 -0
- package/dist/channels.js +3 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.js +122 -0
- package/dist/observers.d.ts +3 -0
- package/dist/observers.js +22 -0
- package/dist/phaser/index.d.ts +26 -0
- package/dist/phaser/index.js +286 -0
- package/dist/platform/index.d.ts +25 -0
- package/dist/platform/index.js +120 -0
- package/dist/ui/index.d.ts +33 -0
- package/dist/ui/index.js +197 -0
- package/docs/phaser.md +106 -0
- package/package.json +81 -0
package/docs/phaser.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Phaser scene binding
|
|
2
|
+
|
|
3
|
+
`@mpgd/game-runtime/phaser` applies headless execution requests to one explicitly
|
|
4
|
+
selected gameplay scene. Keep pause/resume controls in a separate UI scene.
|
|
5
|
+
The adapter never pauses `Phaser.Game`, installs browser lifecycle listeners,
|
|
6
|
+
replaces scene methods, or grants purchases/rewards.
|
|
7
|
+
|
|
8
|
+
```ts
|
|
9
|
+
import { bindPhaserGameScene } from '@mpgd/game-runtime/phaser';
|
|
10
|
+
|
|
11
|
+
// Inside gameplayScene.create(); the binding also applies at the CREATE event,
|
|
12
|
+
// after SceneManager has finished initializing the scene's running status.
|
|
13
|
+
const binding = bindPhaserGameScene({
|
|
14
|
+
controller: applicationRuntime,
|
|
15
|
+
scene: gameplayScene,
|
|
16
|
+
renderingPolicy: 'visibility',
|
|
17
|
+
resetInput: () => { joystick.cancel(); pressedDirections.clear(); },
|
|
18
|
+
audio: gameplayAudioSink,
|
|
19
|
+
uiScope: gameplayViewScope,
|
|
20
|
+
onUnsupportedState: (snapshot, reason) => showIntegrationError(reason),
|
|
21
|
+
onError: reportError,
|
|
22
|
+
});
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
| Runtime request | Phaser mapping |
|
|
26
|
+
| --- | --- |
|
|
27
|
+
| Simulation + gameplay input blocked | `sys.pause()` on the selected running scene; render remains available |
|
|
28
|
+
| Input blocked, simulation allowed | Disable only this scene's pointer, keyboard and gamepad plugins; reset injected input state |
|
|
29
|
+
| Rendering blocked | `sys.setVisible(false)`, keeping simulation policy independent; no sleep/wake mapping |
|
|
30
|
+
| Audio blocked with a sink | Mute that explicit gameplay sink; no user volume changes or global sound manager calls |
|
|
31
|
+
| Simulation blocked, input allowed | Unsupported: required observer receives `simulation-requires-input-block`; previous engine controls remain intact |
|
|
32
|
+
| Audio blocked without a sink | Required observer receives `audio-sink-missing`; supported scene channels still apply |
|
|
33
|
+
|
|
34
|
+
Phaser's paused scenes cannot accept input even when the plugin `enabled` flag
|
|
35
|
+
is true. The core retains independent channels, but this binding cannot preserve
|
|
36
|
+
input during simulation pause. The required unsupported-state observer makes
|
|
37
|
+
this engine limitation explicit; it receives the requested snapshot and a safe
|
|
38
|
+
reason code. Each condition is reported once until it clears. Observation errors/rejected promises go to optional `onError`
|
|
39
|
+
without interrupting other cleanup. Its own failures are consumed.
|
|
40
|
+
|
|
41
|
+
`resetInput` must synchronously clear game-owned pressed keys, touch/joystick
|
|
42
|
+
ownership, and held actions. The binding does not guess a game's input model.
|
|
43
|
+
It runs when input blocking starts (and when a blocked scene wakes), preventing
|
|
44
|
+
missed key-up/touch-end events from replaying stale input after resume.
|
|
45
|
+
|
|
46
|
+
The sink exposes `getMuted()` and `setMuted(boolean)` for gameplay audio only.
|
|
47
|
+
A previously muted sink stays muted. The binding restores only mute changes it
|
|
48
|
+
owns; it never starts sounds, restores volume settings, fades, or resumes all audio.
|
|
49
|
+
|
|
50
|
+
## Ownership and lifetime
|
|
51
|
+
|
|
52
|
+
Use one execution binding per gameplay scene lifetime and route that scene's
|
|
53
|
+
pause/input/visibility policy through the controller. This is a single-writer
|
|
54
|
+
contract, not automatic arbitration of unrelated direct scene writes.
|
|
55
|
+
|
|
56
|
+
The binding records only changes it starts. A previously paused scene is never
|
|
57
|
+
resumed by block release or disposal. Sleeping/stopped scenes are not resumed;
|
|
58
|
+
an external wake reapplies remaining blocks. External sleep revokes the binding's
|
|
59
|
+
pause/visibility ownership. Observable external pause/resume events update ownership;
|
|
60
|
+
a resume while blocked is reconciled immediately. Restoration resumes last so
|
|
61
|
+
resume callbacks can restart a scene without old cleanup overwriting its new binding.
|
|
62
|
+
It does not claim to detect an external pause that
|
|
63
|
+
overlaps an already-owned pause without an observable state transition.
|
|
64
|
+
|
|
65
|
+
`shutdown` and `destroy` remove binding listeners and dispose the supplied view
|
|
66
|
+
scope. Ordinary scene shutdown restores owned input/audio without resuming or
|
|
67
|
+
showing the stopped scene. It does not destroy the shared application runtime.
|
|
68
|
+
A restarted scene must create a fresh binding and scope. A CREATE listener
|
|
69
|
+
handles installation inside `scene.create()` before the first gameplay update.
|
|
70
|
+
|
|
71
|
+
Explicit `dispose()` restores owned state where the scene is still eligible,
|
|
72
|
+
then detaches. Runtime destruction detaches and disposes the scope while keeping
|
|
73
|
+
current engine controls intact: it must not generate a resume or unmute request.
|
|
74
|
+
Late releases and repeated cleanup cannot control a new scene lifetime. Resume
|
|
75
|
+
is driven by controller notifications; no paused-scene update polling is needed.
|
|
76
|
+
|
|
77
|
+
## Validation and distribution
|
|
78
|
+
|
|
79
|
+
The headless fake tests exercise ownership, input flags/reset, channel mapping,
|
|
80
|
+
CREATE handling, shutdown/restart, external sleep/wake, and terminal cleanup.
|
|
81
|
+
`examples/runtime-controls` separately runs real Phaser 4.2.0 in Chromium and
|
|
82
|
+
asserts actual update/render counters and input behavior. Its initial inactive
|
|
83
|
+
scenario verifies the first update is blocked. The audio fixture is a fake sink,
|
|
84
|
+
not a physical-device audio test. Native targets and every Phaser version are
|
|
85
|
+
not covered by that browser result.
|
|
86
|
+
|
|
87
|
+
The implementation was checked against the installed Phaser 4.2.0 source and
|
|
88
|
+
the official [Systems API](https://docs.phaser.io/api-documentation/4.0.0/class/scenes-systems).
|
|
89
|
+
The separate entrypoint keeps Phaser outside headless runtime imports and
|
|
90
|
+
declarations; its optional peer range starts at the tested 4.2.0 version.
|
|
91
|
+
|
|
92
|
+
Install `@mpgd/game-runtime` and `phaser@^4.2.0` to use this binding. It is shipped
|
|
93
|
+
in the same npm tarball as the headless controller. It does not modify
|
|
94
|
+
`phaser-minigame-runtime`'s native compatibility layer or automatically add a
|
|
95
|
+
dependency to generated games.
|
|
96
|
+
|
|
97
|
+
For TypeScript 7 with Phaser 4.2.0, use the starter's `skipLibCheck: true` setting
|
|
98
|
+
for Phaser's existing declaration errors. The headless entrypoints are separately
|
|
99
|
+
tested with `skipLibCheck: false` and no DOM declarations.
|
|
100
|
+
|
|
101
|
+
If the injected audio sink throws while unmuting during disposal/shutdown, scene
|
|
102
|
+
listeners still detach. The handle retains only its failed audio cleanup and a
|
|
103
|
+
subsequent explicit `dispose()` retries it while the runtime is active. Successful
|
|
104
|
+
cleanup stays idempotent; terminal runtime destruction never retries an unmute.
|
|
105
|
+
Complete this explicit cleanup before reusing the same sink for another binding,
|
|
106
|
+
as required by the single-writer ownership contract. No retry timer is installed.
|
package/package.json
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mpgd/game-runtime",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Gameplay execution, scoped UI and action coordination with optional Phaser scene bindings.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"mpgd",
|
|
8
|
+
"phaser",
|
|
9
|
+
"game-development",
|
|
10
|
+
"runtime",
|
|
11
|
+
"lifecycle"
|
|
12
|
+
],
|
|
13
|
+
"type": "module",
|
|
14
|
+
"sideEffects": false,
|
|
15
|
+
"main": "./dist/index.js",
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"default": "./dist/index.js"
|
|
21
|
+
},
|
|
22
|
+
"./ui": {
|
|
23
|
+
"types": "./dist/ui/index.d.ts",
|
|
24
|
+
"default": "./dist/ui/index.js"
|
|
25
|
+
},
|
|
26
|
+
"./platform": {
|
|
27
|
+
"types": "./dist/platform/index.d.ts",
|
|
28
|
+
"default": "./dist/platform/index.js"
|
|
29
|
+
},
|
|
30
|
+
"./actions": {
|
|
31
|
+
"types": "./dist/actions/index.d.ts",
|
|
32
|
+
"default": "./dist/actions/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./phaser": {
|
|
35
|
+
"types": "./dist/phaser/index.d.ts",
|
|
36
|
+
"default": "./dist/phaser/index.js"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"dist",
|
|
41
|
+
"docs"
|
|
42
|
+
],
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"ttsc": "0.18.4",
|
|
45
|
+
"typescript": "7.0.2",
|
|
46
|
+
"vitest": "^4.0.0",
|
|
47
|
+
"phaser": "^4.2.0",
|
|
48
|
+
"@types/node": "^24.0.0"
|
|
49
|
+
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"@mpgd/game-services": "0.15.1"
|
|
52
|
+
},
|
|
53
|
+
"repository": {
|
|
54
|
+
"type": "git",
|
|
55
|
+
"url": "git+https://github.com/imjlk/mpgd-kit.git",
|
|
56
|
+
"directory": "packages/game-runtime"
|
|
57
|
+
},
|
|
58
|
+
"bugs": {
|
|
59
|
+
"url": "https://github.com/imjlk/mpgd-kit/issues"
|
|
60
|
+
},
|
|
61
|
+
"homepage": "https://github.com/imjlk/mpgd-kit/tree/main/packages/game-runtime#readme",
|
|
62
|
+
"publishConfig": {
|
|
63
|
+
"access": "public"
|
|
64
|
+
},
|
|
65
|
+
"peerDependencies": {
|
|
66
|
+
"phaser": "^4.2.0"
|
|
67
|
+
},
|
|
68
|
+
"peerDependenciesMeta": {
|
|
69
|
+
"phaser": {
|
|
70
|
+
"optional": true
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"scripts": {
|
|
74
|
+
"build": "cd ../.. && node tools/run-ttsx.mjs tools/package/build-packages.ts @mpgd/game-runtime",
|
|
75
|
+
"check": "ttsc --noEmit && ttsc --noEmit -p tsconfig.headless.json",
|
|
76
|
+
"test": "pnpm build && vitest run && node test/dist-import.mjs && node test/phaser-dist-import.mjs && ttsc --noEmit -p test/tsconfig.json && ttsc --noEmit -p test/tsconfig.phaser.json && node test/package-import.mjs",
|
|
77
|
+
"lint": "pnpm check",
|
|
78
|
+
"format": "ttsc format",
|
|
79
|
+
"fix": "ttsc fix"
|
|
80
|
+
}
|
|
81
|
+
}
|