@mpgd/cli 0.38.0 → 0.40.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/asset-pack-build.js +4 -1
- package/dist/asset-pack-native-audit.d.ts +5 -0
- package/dist/asset-pack-native-audit.js +137 -0
- package/dist/asset-pack-publish.d.ts +43 -0
- package/dist/asset-pack-publish.js +309 -0
- package/dist/asset-pack-target.d.ts +42 -0
- package/dist/asset-pack-target.js +251 -0
- package/dist/build-targets.d.ts +1 -1
- package/dist/build-targets.js +2 -0
- package/dist/index.js +85 -3
- package/dist/native-build-info.json +2 -2
- package/dist/native-build-target.js +3501 -633
- package/package.json +19 -14
- package/templates/phaser-game/agent/acceptance.md +6 -0
- package/templates/phaser-game/agent/brief.md +4 -0
- package/templates/phaser-game/agent/game-manifest.json +21 -0
- package/templates/phaser-game/docs/MPGD_KIT_WORKFLOWS.md +6 -0
- package/templates/phaser-game/mpgd.targets.json +11 -0
- package/templates/phaser-game/src/main.ts +2 -0
- package/templates/phaser-game/src/platform/buildGateways/oneplay.ts +32 -0
- package/templates/phaser-game/src/platform/runtimeDetector.ts +1 -0
- package/templates/phaser-game/src/runtime/createGame.ts +3 -3
- package/templates/phaser-game/src/scenes/BootScene.ts +7 -1
- package/templates/phaser-game/src/scenes/PlayScene.ts +16 -0
- package/templates/phaser-game/vite.runtime-target-config.ts +2 -0
- package/templates/phaser-game/vite.shared.ts +2 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mpgd/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.40.0",
|
|
4
4
|
"description": "Gunshi CLI for mpgd-kit starter generation and target workflow orchestration.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -40,6 +40,8 @@
|
|
|
40
40
|
"mpgd": "./dist/bin.js"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
+
"@aws-sdk/client-s3": "3.1147.0",
|
|
44
|
+
"@smithy/node-http-handler": "4.12.1",
|
|
43
45
|
"@googleapis/androidpublisher": "42.0.0",
|
|
44
46
|
"@gunshi/plugin-completion": "^0.35.1",
|
|
45
47
|
"@gunshi/plugin-i18n": "^0.35.1",
|
|
@@ -47,6 +49,7 @@
|
|
|
47
49
|
"@xmldom/xmldom": "0.9.12",
|
|
48
50
|
"acorn": "8.17.0",
|
|
49
51
|
"entities": "^8.0.0",
|
|
52
|
+
"fflate": "0.8.3",
|
|
50
53
|
"esbuild": "^0.28.1",
|
|
51
54
|
"gunshi": "^0.35.1",
|
|
52
55
|
"semver": "^7.8.5",
|
|
@@ -54,26 +57,28 @@
|
|
|
54
57
|
"typia": "13.0.2",
|
|
55
58
|
"undici": "^7.29.1",
|
|
56
59
|
"vite": "8.1.3",
|
|
57
|
-
"
|
|
58
|
-
"@mpgd/
|
|
59
|
-
"@mpgd/
|
|
60
|
+
"yauzl": "3.4.0",
|
|
61
|
+
"@mpgd/catalog": "0.8.0",
|
|
62
|
+
"@mpgd/target-config": "0.20.0",
|
|
63
|
+
"@mpgd/phaser-assets": "0.8.0"
|
|
60
64
|
},
|
|
61
65
|
"devDependencies": {
|
|
62
66
|
"@types/node": "^24.0.0",
|
|
63
67
|
"@types/semver": "^7.8.0",
|
|
68
|
+
"@types/yauzl": "3.4.0",
|
|
64
69
|
"ttsc": "0.30.4",
|
|
65
70
|
"typescript": "7.0.2",
|
|
66
|
-
"@mpgd/adapter-ait": "0.13.
|
|
67
|
-
"@mpgd/adapter-browser": "0.
|
|
68
|
-
"@mpgd/adapter-capacitor": "0.7.
|
|
69
|
-
"@mpgd/adapter-devvit": "0.9.
|
|
70
|
-
"@mpgd/
|
|
71
|
-
"@mpgd/
|
|
72
|
-
"@mpgd/game-
|
|
71
|
+
"@mpgd/adapter-ait": "0.13.2",
|
|
72
|
+
"@mpgd/adapter-browser": "0.9.0",
|
|
73
|
+
"@mpgd/adapter-capacitor": "0.7.3",
|
|
74
|
+
"@mpgd/adapter-devvit": "0.9.17",
|
|
75
|
+
"@mpgd/analytics": "0.4.3",
|
|
76
|
+
"@mpgd/adapter-verse8": "0.4.2",
|
|
77
|
+
"@mpgd/game-services": "0.19.0",
|
|
73
78
|
"@mpgd/bridge": "0.11.0",
|
|
74
|
-
"@mpgd/game-
|
|
75
|
-
"@mpgd/i18n": "0.6.
|
|
76
|
-
"@mpgd/platform": "0.
|
|
79
|
+
"@mpgd/game-runtime": "0.5.0",
|
|
80
|
+
"@mpgd/i18n": "0.6.8",
|
|
81
|
+
"@mpgd/platform": "0.17.0"
|
|
77
82
|
},
|
|
78
83
|
"main": "./dist/index.js",
|
|
79
84
|
"types": "./dist/index.d.ts",
|
|
@@ -76,3 +76,9 @@ the snapshot bounds for persistent controls.
|
|
|
76
76
|
bootstrap monetization ports. A test memory store is not durable recovery.
|
|
77
77
|
- Use the real adapter's SDK-driven conformance tests and a game-owned platform
|
|
78
78
|
test environment; generic unit tests do not certify live ad delivery.
|
|
79
|
+
|
|
80
|
+
## ONE play acceptance
|
|
81
|
+
|
|
82
|
+
Build and smoke the configured ONE play target using the game-owned kit path. Validate server-issued reward/checkout binding, raw-byte signed SSV, purchase token API verification, ledger-before-consume/acknowledge, retries without duplicate grants, signed PNS orphan-purchase recovery and atomic cancellation fences. Missing finalization must keep PNS pending; a busy SDK must report native not-started before rejecting a new checkout. Permit five minutes of provider/server clock skew while rejecting timestamps outside the intent window.
|
|
83
|
+
|
|
84
|
+
Use an authenticated server session rather than the SDK player pseudonym. Confirm ad IDs, real payments, lifecycle, HTTPS iframe hosting and PNS/SSV in ONE store with issued ONEconsole credentials. Generic fixture and artifact smoke do not certify real-app commerce.
|
|
@@ -48,3 +48,7 @@ projection. Add SDKs only in target assembly or adapters. All rewarded views use
|
|
|
48
48
|
the game-owned action coordinator and authoritative services, with an
|
|
49
49
|
application-owned durable encrypted operation store. Keep recovery above scenes
|
|
50
50
|
and reconcile existing operations on startup and resume without opening SDK UI.
|
|
51
|
+
|
|
52
|
+
## ONE play server-owned monetization
|
|
53
|
+
|
|
54
|
+
Specify issued placement/product IDs, the authenticated reward and checkout intent routes, durable SSV/intent/ledger stores, an encrypted client operation journal, and reconciliation plus atomic cancellation fencing. Keep monetization disabled until these ports are wired. Purchase/ad callbacks are evidence; backend grants precede consume/acknowledge. Separate confirmed checkout closure from financial completion. Plan real-app acceptance with ONEconsole credentials and hosting headers.
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"microsoft-store",
|
|
11
11
|
"verse8",
|
|
12
12
|
"crazygames",
|
|
13
|
+
"oneplay",
|
|
13
14
|
"android",
|
|
14
15
|
"ios",
|
|
15
16
|
"ait",
|
|
@@ -95,6 +96,26 @@
|
|
|
95
96
|
"SDK earning and physical closure are separate from an authoritative backend ledger grant.",
|
|
96
97
|
"Recover existing claims on startup and resume without reopening SDK UI."
|
|
97
98
|
]
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"id": "commerce.checkout.verified-intent",
|
|
102
|
+
"owner": "src/platform/buildGateways/oneplay.ts",
|
|
103
|
+
"capabilities": [
|
|
104
|
+
"server-issued-checkout-intent",
|
|
105
|
+
"managed-purchase-verification",
|
|
106
|
+
"ledger-before-finalization",
|
|
107
|
+
"signed-notification-recovery",
|
|
108
|
+
"durable-cancellation-fence",
|
|
109
|
+
"native-checkout-presentation",
|
|
110
|
+
"server-issued-ad-request",
|
|
111
|
+
"signed-reward-ssv"
|
|
112
|
+
],
|
|
113
|
+
"gotchas": [
|
|
114
|
+
"ONE play monetization stays unavailable without game-owned authenticated issuer routes and durable intent, receipt, ledger and encrypted client recovery stores.",
|
|
115
|
+
"SDK player IDs are platform asserted, not authenticated backend identities. Client ad or purchase callbacks never grant.",
|
|
116
|
+
"Require completed consume/acknowledge before accepting PNS; implement atomic cancellation fencing and authoritative revocation in the game ledger.",
|
|
117
|
+
"Keep uncertain native outcomes quarantined; SDK resume is not closure proof. Validate issued IDs, SSV, payments and hosting in the real ONE store app."
|
|
118
|
+
]
|
|
98
119
|
}
|
|
99
120
|
],
|
|
100
121
|
"acceptance": [
|
|
@@ -153,3 +153,9 @@ Keep source revision, config, build/smoke results, gameplay evidence, target
|
|
|
153
153
|
preflight, artifact hashes, and target acceptance reports together. A generic
|
|
154
154
|
kit smoke test proves the reusable contract only; it does not replace
|
|
155
155
|
game-owned production credentials, deployment, signing, or store evidence.
|
|
156
|
+
|
|
157
|
+
### ONE play
|
|
158
|
+
|
|
159
|
+
Use `@mpgd/adapter-browser/oneplay` through `PlatformGateway`. Build and smoke the `oneplay` target with the configured kit path. The adapter initializes the H5 SDK before gameplay; `completeLoading()` awaits host acknowledgement. Exit checkpoints must be synchronous through `storage.saveSync`. Use game-owned issued placement IDs and server verification before enabling ads or purchases. Check final HTTPS hosting headers for both ONE store domain families. See the [ONE play guide](https://github.com/imjlk/mpgd-kit/blob/main/docs/ONEPLAY.md) and [official SDK guide](https://onestore-dev.gitbook.io/dev/tools/web-sdk).
|
|
160
|
+
|
|
161
|
+
Wire `rewardRequests` and `commerceServer` in `src/platform/buildGateways/oneplay.ts`. Server-only `@mpgd/game-services/oneplay-reward`, `oneplay-purchase-client`, `oneplay-purchase` and `oneplay-pns` provide signed SSV, authenticated checkout intents, API V7 verification, post-ledger consume/acknowledge and signed notification recovery. The game owns durable stores, routes, an encrypted client journal and atomic cancellation fencing. A completed PNS requires completed finalization. Missing ports keep capabilities unavailable; subscriptions are unsupported. See `commerce.checkout.verified-intent` in the game manifest and the ONE play acceptance notes before enabling policies.
|
|
@@ -33,6 +33,17 @@
|
|
|
33
33
|
"crazyGamesLaunch": "basic",
|
|
34
34
|
"output": "artifacts/crazygames"
|
|
35
35
|
},
|
|
36
|
+
"oneplay": {
|
|
37
|
+
"kind": "web",
|
|
38
|
+
"gameApp": ".",
|
|
39
|
+
"adapter": "oneplay",
|
|
40
|
+
"icon": {
|
|
41
|
+
"profile": "web-preview"
|
|
42
|
+
},
|
|
43
|
+
"installable": false,
|
|
44
|
+
"output": "artifacts/oneplay",
|
|
45
|
+
"authoritativeGameServices": false
|
|
46
|
+
},
|
|
36
47
|
"android": {
|
|
37
48
|
"kind": "capacitor-android",
|
|
38
49
|
"gameApp": ".",
|
|
@@ -49,6 +49,7 @@ async function bootstrap(options: { readonly monetization?: StarterMonetizationP
|
|
|
49
49
|
});
|
|
50
50
|
const viewport = resolveTargetViewportSnapshot({
|
|
51
51
|
...measurement,
|
|
52
|
+
...(platform.viewport === undefined ? {} : { safeAreaInsets: platform.viewport.getState().safeAreaInsets }),
|
|
52
53
|
runtime: runtime.config.runtime,
|
|
53
54
|
orientationPolicy,
|
|
54
55
|
});
|
|
@@ -63,6 +64,7 @@ async function bootstrap(options: { readonly monetization?: StarterMonetizationP
|
|
|
63
64
|
]);
|
|
64
65
|
locale = resolveTargetMpgdLocale({
|
|
65
66
|
capabilities: runtime.capabilities,
|
|
67
|
+
...(platform.gameSettings?.getLocale === undefined ? {} : { preferredLocales: [platform.gameSettings.getLocale()] }),
|
|
66
68
|
fallbackLocale:
|
|
67
69
|
runtime.effectiveConfig?.localization.fallbackLocale
|
|
68
70
|
?? runtime.config.localization.fallbackLocale,
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createOnePlayPlatformGateway,
|
|
3
|
+
type OnePlayGatewayOptions,
|
|
4
|
+
} from '@mpgd/adapter-browser/oneplay';
|
|
5
|
+
import {
|
|
6
|
+
resolveAdPlacementPlatformId,
|
|
7
|
+
resolveProductPlatformId,
|
|
8
|
+
type AdPlacements,
|
|
9
|
+
type ProductCatalog,
|
|
10
|
+
} from '@mpgd/catalog';
|
|
11
|
+
import adPlacementsJson from '@mpgd/catalog/placements.json';
|
|
12
|
+
import productCatalogJson from '@mpgd/catalog/catalog.json';
|
|
13
|
+
import type { PlatformGateway } from '@mpgd/platform';
|
|
14
|
+
import type { RuntimeConfig } from '../runtimeDetector';
|
|
15
|
+
|
|
16
|
+
export async function createBuildGateway(runtime: RuntimeConfig, ports: Pick<OnePlayGatewayOptions, 'rewardRequests' | 'commerceServer'> = {}): Promise<PlatformGateway> {
|
|
17
|
+
const placementIds = Object.fromEntries(
|
|
18
|
+
(adPlacementsJson as AdPlacements).placements.flatMap((placement) => {
|
|
19
|
+
const platformId = resolveAdPlacementPlatformId(placement, runtime.configTarget);
|
|
20
|
+
return placement.type !== 'banner' && platformId !== undefined
|
|
21
|
+
? [[placement.id, { format: placement.type, platformId }]]
|
|
22
|
+
: [];
|
|
23
|
+
}),
|
|
24
|
+
);
|
|
25
|
+
const products = (productCatalogJson as ProductCatalog).products.flatMap((product) => {
|
|
26
|
+
const platformId = resolveProductPlatformId(product, runtime.configTarget);
|
|
27
|
+
return product.type !== 'subscription' && platformId !== undefined
|
|
28
|
+
? [{ id: product.id, platformId, type: product.type }]
|
|
29
|
+
: [];
|
|
30
|
+
});
|
|
31
|
+
return createOnePlayPlatformGateway({ ...ports, placementIds, products });
|
|
32
|
+
}
|
|
@@ -28,15 +28,15 @@ export function createStarterGame(input: {
|
|
|
28
28
|
},
|
|
29
29
|
scene: [BootScene, LobbyScene, PlayScene],
|
|
30
30
|
callbacks: {
|
|
31
|
-
|
|
31
|
+
preBoot(game: Phaser.Game) {
|
|
32
32
|
game.registry.set(starterContextKey, input.context);
|
|
33
|
+
},
|
|
34
|
+
postBoot(game: Phaser.Game) {
|
|
33
35
|
ownGameAudio(game, input.context);
|
|
34
36
|
},
|
|
35
37
|
},
|
|
36
38
|
});
|
|
37
39
|
|
|
38
|
-
game.registry.set(starterContextKey, input.context);
|
|
39
|
-
|
|
40
40
|
return game;
|
|
41
41
|
}
|
|
42
42
|
|
|
@@ -12,11 +12,17 @@ export class BootScene extends Phaser.Scene {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
preload(): void {
|
|
15
|
+
const context = this.registry.get(starterContextKey) as StarterContext;
|
|
16
|
+
this.load.on('progress', (progress: number) =>
|
|
17
|
+
context.gameRuntime.setLoadingProgress(progress * 100),
|
|
18
|
+
);
|
|
15
19
|
loadPhaserAssets(this, starterAssets);
|
|
16
20
|
}
|
|
17
21
|
|
|
18
22
|
create(): void {
|
|
19
23
|
const context = this.registry.get(starterContextKey) as StarterContext;
|
|
20
|
-
|
|
24
|
+
void context.gameRuntime.completeLoading().then(() => {
|
|
25
|
+
this.scene.start(context.launchIntent.entry === 'free-play' ? 'PlayScene' : 'LobbyScene');
|
|
26
|
+
}).catch((error: unknown) => { console.error('[game-loading]', error); });
|
|
21
27
|
}
|
|
22
28
|
}
|
|
@@ -29,6 +29,22 @@ export class PlayScene extends Phaser.Scene {
|
|
|
29
29
|
|
|
30
30
|
create(): void {
|
|
31
31
|
this.context = this.registry.get(starterContextKey) as StarterContext;
|
|
32
|
+
// A host exit callback cannot await a server upload. Keep this local checkpoint synchronous.
|
|
33
|
+
const checkpoint = () => {
|
|
34
|
+
try {
|
|
35
|
+
this.context.platform.storage.saveSync?.({
|
|
36
|
+
key: 'starter-run',
|
|
37
|
+
value: this.state,
|
|
38
|
+
});
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error('[checkpoint]', error);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
const unsubscribeExit = this.context.platform.lifecycle.onExit?.(checkpoint);
|
|
44
|
+
this.events.once('shutdown', () => {
|
|
45
|
+
checkpoint();
|
|
46
|
+
unsubscribeExit?.();
|
|
47
|
+
});
|
|
32
48
|
bindPhaserGameScene({
|
|
33
49
|
controller: this.context.gameRuntime.execution,
|
|
34
50
|
gameplayScope: this.context.gameRuntime.createGameplayScope(),
|
|
@@ -21,6 +21,7 @@ const runtimeKinds = valueSet({
|
|
|
21
21
|
'devvit-web': true,
|
|
22
22
|
'verse8-web': true,
|
|
23
23
|
'crazygames-web': true,
|
|
24
|
+
'oneplay-web': true,
|
|
24
25
|
'wechat-minigame': true,
|
|
25
26
|
'tiktok-minigame': true,
|
|
26
27
|
} satisfies Record<TargetRuntimeKind, true>);
|
|
@@ -34,6 +35,7 @@ const releaseProfiles = valueSet({
|
|
|
34
35
|
devvit: true,
|
|
35
36
|
verse8: true,
|
|
36
37
|
crazygames: true,
|
|
38
|
+
oneplay: true,
|
|
37
39
|
'wechat-minigame': true,
|
|
38
40
|
'tiktok-minigame': true,
|
|
39
41
|
} satisfies Record<ReleaseProfile, true>);
|
|
@@ -152,6 +152,8 @@ export function resolveBuildGatewayModule(input: {
|
|
|
152
152
|
return input.debug && input.buildId === devvitSandboxBuildId
|
|
153
153
|
? 'src/platform/buildGateways/redditSandbox.ts'
|
|
154
154
|
: 'src/platform/buildGateways/reddit.ts';
|
|
155
|
+
case 'oneplay':
|
|
156
|
+
return 'src/platform/buildGateways/oneplay.ts';
|
|
155
157
|
case 'crazygames':
|
|
156
158
|
return 'src/platform/buildGateways/crazygames.ts';
|
|
157
159
|
case 'verse8':
|