@mpgd/game-runtime 0.4.0 → 0.5.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/dist/ads/index.d.ts +2 -6
- package/dist/ads/index.js +2 -1
- package/dist/game/index.d.ts +5 -0
- package/dist/game/index.js +52 -0
- package/package.json +3 -3
package/dist/ads/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PlatformEvidenceEnvelope, PlatformGateway, PurchaseResult } from '@mpgd/platform';
|
|
1
|
+
import type { PlatformEvidenceEnvelope, PlatformGateway, PlatformPurchasePresentationEvent, PurchaseResult } from '@mpgd/platform';
|
|
2
2
|
import type { GameServicesRewardedAdResult } from '@mpgd/game-services/operations';
|
|
3
3
|
import { type AdPlacementInput, type AdProvider, type AdShowInput, type AdShowResult } from '@mpgd/platform/ads';
|
|
4
4
|
import { type ObserverErrorHandler } from '../observers.js';
|
|
@@ -44,11 +44,7 @@ export declare function createCoordinatedAdProvider(input: {
|
|
|
44
44
|
readonly result: AdShowResult;
|
|
45
45
|
}) => void | Promise<void>;
|
|
46
46
|
}): CoordinatedAdProvider;
|
|
47
|
-
export
|
|
48
|
-
readonly idempotencyKey: string;
|
|
49
|
-
readonly sequence: number;
|
|
50
|
-
readonly state: 'open' | 'closed' | 'not-started' | 'unknown';
|
|
51
|
-
}
|
|
47
|
+
export type PurchasePresentationEvent = PlatformPurchasePresentationEvent;
|
|
52
48
|
export interface CoordinatedPlatformGateway extends PlatformGateway {
|
|
53
49
|
dispose(): void;
|
|
54
50
|
}
|
package/dist/ads/index.js
CHANGED
|
@@ -475,7 +475,8 @@ export function createCoordinatedPlatformGateway(input) {
|
|
|
475
475
|
}
|
|
476
476
|
maybeDetach();
|
|
477
477
|
}
|
|
478
|
-
const
|
|
478
|
+
const purchasePresentation = input.purchasePresentation ?? input.gateway.commerce.presentation;
|
|
479
|
+
const unsubscribe = purchasePresentation?.subscribe((event) => {
|
|
479
480
|
observe(() => {
|
|
480
481
|
const flight = purchases.get(event.idempotencyKey);
|
|
481
482
|
if (flight === undefined || !flight.called || flight.closed) {
|
package/dist/game/index.d.ts
CHANGED
|
@@ -27,6 +27,9 @@ export interface GamePlatformRuntime<T extends PlatformGateway = PlatformGateway
|
|
|
27
27
|
readonly services: S;
|
|
28
28
|
readonly actions?: GameActionCoordinator;
|
|
29
29
|
createGameplayScope(): GameGameplayScope;
|
|
30
|
+
setLoadingProgress(progress: number): void;
|
|
31
|
+
/** Complete host loading before opening a play scene. Rejected starts remain blocked. */
|
|
32
|
+
completeLoading(): Promise<void>;
|
|
30
33
|
/** Reconcile existing journal entries only; this method never opens platform UI. */
|
|
31
34
|
reconcile(): Promise<void>;
|
|
32
35
|
/** Game teardown; native observers and application-owned late recovery may continue. */
|
|
@@ -44,4 +47,6 @@ export declare function createGamePlatformRuntime<T extends PlatformGateway, S e
|
|
|
44
47
|
subscribe(listener: (event: PurchasePresentationEvent) => void): () => void;
|
|
45
48
|
};
|
|
46
49
|
readonly onObserverError?: ObserverErrorHandler;
|
|
50
|
+
/** Must complete synchronously; asynchronous uploads belong at earlier checkpoints. */
|
|
51
|
+
readonly onExit?: () => void;
|
|
47
52
|
}): GamePlatformRuntime<T, S>;
|
package/dist/game/index.js
CHANGED
|
@@ -39,6 +39,47 @@ export function createGamePlatformRuntime(input) {
|
|
|
39
39
|
let actions;
|
|
40
40
|
let unsubscribeResume;
|
|
41
41
|
let disposed = false;
|
|
42
|
+
let unsubscribeExit;
|
|
43
|
+
let startupBlock = input.gateway.gameLoading === undefined
|
|
44
|
+
? undefined
|
|
45
|
+
: execution.acquireBlock({
|
|
46
|
+
reason: 'game-runtime:loading',
|
|
47
|
+
channels: ['simulation', 'gameplay-input', 'audio'],
|
|
48
|
+
});
|
|
49
|
+
let loadingCompletion;
|
|
50
|
+
const setLoadingProgress = (progress) => {
|
|
51
|
+
if (!disposed && loadingCompletion === undefined) {
|
|
52
|
+
observe(() => gateway.gameLoading?.setProgress(Math.max(0, Math.min(100, Number(progress) || 0))), input.onObserverError);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
const completeLoading = () => {
|
|
56
|
+
if (disposed) {
|
|
57
|
+
return Promise.reject(new Error('Game runtime is disposed.'));
|
|
58
|
+
}
|
|
59
|
+
if (loadingCompletion !== undefined) {
|
|
60
|
+
return loadingCompletion;
|
|
61
|
+
}
|
|
62
|
+
const attempt = Promise.resolve().then(async () => {
|
|
63
|
+
if (disposed) {
|
|
64
|
+
throw new Error('Game runtime is disposed.');
|
|
65
|
+
}
|
|
66
|
+
gateway.gameLoading?.setProgress(100);
|
|
67
|
+
await gateway.gameLoading?.complete();
|
|
68
|
+
if (disposed) {
|
|
69
|
+
throw new Error('Game runtime was disposed during loading.');
|
|
70
|
+
}
|
|
71
|
+
const owned = startupBlock;
|
|
72
|
+
startupBlock = undefined;
|
|
73
|
+
owned?.release();
|
|
74
|
+
});
|
|
75
|
+
loadingCompletion = attempt;
|
|
76
|
+
void attempt.catch(() => {
|
|
77
|
+
if (loadingCompletion === attempt) {
|
|
78
|
+
loadingCompletion = undefined;
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
return attempt;
|
|
82
|
+
};
|
|
42
83
|
let hostAudio;
|
|
43
84
|
let unsubscribeHostAudio;
|
|
44
85
|
const gameplayScopes = new Map();
|
|
@@ -108,6 +149,7 @@ export function createGamePlatformRuntime(input) {
|
|
|
108
149
|
}, input.onObserverError);
|
|
109
150
|
}
|
|
110
151
|
unsubscribeActivity?.();
|
|
152
|
+
unsubscribeExit?.();
|
|
111
153
|
gameplayScopes.clear();
|
|
112
154
|
execution.destroy();
|
|
113
155
|
if (unsubscribeHostAudio !== undefined) {
|
|
@@ -156,6 +198,14 @@ export function createGamePlatformRuntime(input) {
|
|
|
156
198
|
...(input.onObserverError === undefined ? {} : { onError: input.onObserverError }),
|
|
157
199
|
});
|
|
158
200
|
unsubscribeResume = gateway.lifecycle.onResume(() => { observe(reconcile, input.onObserverError); });
|
|
201
|
+
unsubscribeExit = gateway.lifecycle.onExit?.(() => {
|
|
202
|
+
observe(() => input.onExit?.(), input.onObserverError);
|
|
203
|
+
dispose();
|
|
204
|
+
});
|
|
205
|
+
if (disposed) {
|
|
206
|
+
unsubscribeExit?.();
|
|
207
|
+
throw new Error('Game exited during runtime initialization.');
|
|
208
|
+
}
|
|
159
209
|
const settings = gateway.gameSettings;
|
|
160
210
|
if (settings !== undefined) {
|
|
161
211
|
const updateMute = (muted) => {
|
|
@@ -182,6 +232,8 @@ export function createGamePlatformRuntime(input) {
|
|
|
182
232
|
presentation,
|
|
183
233
|
services,
|
|
184
234
|
createGameplayScope,
|
|
235
|
+
setLoadingProgress,
|
|
236
|
+
completeLoading,
|
|
185
237
|
...(actions === undefined ? {} : { actions }),
|
|
186
238
|
reconcile,
|
|
187
239
|
dispose,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mpgd/game-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Gameplay execution, scoped UI and action coordination with optional Phaser scene bindings.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -64,8 +64,8 @@
|
|
|
64
64
|
"@types/node": "^24.0.0"
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
|
-
"@mpgd/game-services": "0.
|
|
68
|
-
"@mpgd/platform": "0.
|
|
67
|
+
"@mpgd/game-services": "0.19.0",
|
|
68
|
+
"@mpgd/platform": "0.17.0"
|
|
69
69
|
},
|
|
70
70
|
"repository": {
|
|
71
71
|
"type": "git",
|