@pop-party/create-game 1.0.0 → 1.2.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/README.md +5 -0
- package/package.json +1 -1
- package/src/cli-arguments.js +3 -1
- package/src/generate-game.js +4 -9
- package/starter/content/art/manifest.json +8604 -8502
- package/starter/content/content-bundle.json +6 -4
package/README.md
CHANGED
|
@@ -19,6 +19,11 @@ the configured immutable release; later runs keep that independent local copy.
|
|
|
19
19
|
Production never reads the development workspace. Both commands validate the
|
|
20
20
|
complete selected release before opening a port.
|
|
21
21
|
|
|
22
|
+
The resulting service immediately receives the engine-owned stage, controller,
|
|
23
|
+
room lifecycle, and authenticated core tools. Its `src/stage`,
|
|
24
|
+
`src/controller`, and `src/tools` folders begin as empty additive plugin
|
|
25
|
+
boundaries; they do not contain placeholder pages or copies of engine code.
|
|
26
|
+
|
|
22
27
|
`pop-party migrate` previews the registered, contiguous game migration path.
|
|
23
28
|
It writes nothing unless `--output <new-directory>` is supplied, never mutates
|
|
24
29
|
the source bundle, runs every migration twice to reject nondeterministic output,
|
package/package.json
CHANGED
package/src/cli-arguments.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
+
const { version: DEFAULT_ENGINE_VERSION } = require("../package.json");
|
|
4
|
+
|
|
3
5
|
const HELP_TEXT = [
|
|
4
6
|
"Usage: npm create @pop-party/game <name> [options]",
|
|
5
7
|
"",
|
|
@@ -38,7 +40,7 @@ function parseCreateGameArguments(argv = []) {
|
|
|
38
40
|
if (!displayName) throw new Error("Game display name is required");
|
|
39
41
|
return Object.freeze({
|
|
40
42
|
displayName,
|
|
41
|
-
engineVersion: options.engineVersion ||
|
|
43
|
+
engineVersion: options.engineVersion || DEFAULT_ENGINE_VERSION,
|
|
42
44
|
starterRoot: options.starterRoot || undefined,
|
|
43
45
|
targetRoot: options.targetRoot || undefined
|
|
44
46
|
});
|
package/src/generate-game.js
CHANGED
|
@@ -55,11 +55,6 @@ function generatedContributionSource(kind) {
|
|
|
55
55
|
return `"use strict";\n\n// Add ${kind} contributions as { id: "game-namespace.name", value: ... }.\nmodule.exports = Object.freeze([]);\n`;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
function generatedBootstrapRendererSource({ gameId, role }) {
|
|
59
|
-
const kind = role === "stage" ? "Stage" : "Controller";
|
|
60
|
-
return `"use strict";\n\nmodule.exports = Object.freeze([{\n id: ${JSON.stringify(`${gameId}.bootstrap-${role}`)},\n value: Object.freeze({\n renderBootstrap({ game }) {\n if (game.id !== ${JSON.stringify(gameId)}) throw new Error("${kind} renderer received another game");\n return Object.freeze({\n heading: game.displayName,\n message: ${JSON.stringify(`${kind} service is ready.`)}\n });\n }\n })\n}]);\n`;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
58
|
function generatedRenderBlueprintSource(gameId) {
|
|
64
59
|
return `services:\n - type: web\n name: ${gameId}\n runtime: node\n plan: starter\n numInstances: 1\n buildCommand: npm install --no-audit --no-fund && npm run build\n startCommand: npm start\n healthCheckPath: /health\n maxShutdownDelaySeconds: 300\n envVars:\n - key: NODE_ENV\n value: production\n`;
|
|
65
60
|
}
|
|
@@ -69,7 +64,7 @@ function generatedPluginSource(pluginNamespace) {
|
|
|
69
64
|
}
|
|
70
65
|
|
|
71
66
|
function generatedConfigTestSource({ engineVersion, gameId, pluginNamespace }) {
|
|
72
|
-
return `"use strict";\n\nconst assert = require("node:assert/strict");\nconst test = require("node:test");\nconst { createGameApplicationRuntime } = require("@pop-party/engine/server/application");\nconst { createGameReadinessRuntime } = require("@pop-party/engine/server/readiness");\nconst game = require("../game.config");\n\ntest("game configuration owns an exact engine and plugin boundary", async () => {\n assert.equal(game.gameId, ${JSON.stringify(gameId)});\n assert.equal(game.engineCompatibility, ${JSON.stringify(engineVersion)});\n assert.equal(game.plugin.namespace, ${JSON.stringify(pluginNamespace)});\n assert.equal(game.gameData, null);\n assert.
|
|
67
|
+
return `"use strict";\n\nconst assert = require("node:assert/strict");\nconst test = require("node:test");\nconst { createGameApplicationRuntime } = require("@pop-party/engine/server/application");\nconst { createGameReadinessRuntime } = require("@pop-party/engine/server/readiness");\nconst game = require("../game.config");\n\ntest("game configuration owns an exact engine and plugin boundary", async () => {\n assert.equal(game.gameId, ${JSON.stringify(gameId)});\n assert.equal(game.engineCompatibility, ${JSON.stringify(engineVersion)});\n assert.equal(game.plugin.namespace, ${JSON.stringify(pluginNamespace)});\n assert.equal(game.gameData, null);\n assert.deepEqual({\n actions: game.registrations.actions,\n stageRenderers: game.registrations.stageRenderers,\n controllerRenderers: game.registrations.controllerRenderers,\n stateSchemas: game.registrations.stateSchemas,\n validators: game.registrations.validators,\n migrations: game.registrations.migrations,\n toolPanels: game.registrations.toolPanels,\n diagnostics: game.registrations.diagnostics\n }, {\n actions: [],\n stageRenderers: [],\n controllerRenderers: [],\n stateSchemas: [],\n validators: [],\n migrations: [],\n toolPanels: [],\n diagnostics: []\n });\n const readiness = createGameReadinessRuntime({ gameDefinition: game, engineVersion: ${JSON.stringify(engineVersion)} });\n const active = await readiness.check();\n assert.equal(active.release.gameId, ${JSON.stringify(gameId)});\n assert.equal(active.release.engineVersion, ${JSON.stringify(engineVersion)});\n assert.ok(active.gameData.defaultGameFlow.states.length > 0);\n assert.ok(active.gameData.defaultArtCompositions.length > 0);\n assert.equal(readiness.state.status, "ready");\n});\n\ntest("generated game starts with the complete engine-owned application", async () => {\n const runtime = createGameApplicationRuntime({\n gameDefinition: game,\n engineVersion: ${JSON.stringify(engineVersion)},\n workspaceRoot: process.cwd(),\n host: "127.0.0.1",\n port: 0\n });\n try {\n const startup = await runtime.start();\n const health = await fetch(\`${"${startup.localUrl}"}/health\`);\n const stage = await fetch(\`${"${startup.localUrl}"}/stage\`);\n const controller = await fetch(\`${"${startup.localUrl}"}/controller\`);\n const tools = await fetch(\`${"${startup.localUrl}"}/tools\`);\n const flow = await fetch(\`${"${startup.localUrl}"}/api/game-flow\`);\n assert.equal(health.status, 200);\n assert.equal((await health.json()).game.id, ${JSON.stringify(gameId)});\n assert.equal(stage.status, 200);\n assert.match(await stage.text(), /id="stageScreen"/);\n assert.equal(controller.status, 200);\n assert.match(await controller.text(), /id="controllerScreen"/);\n assert.equal(tools.status, 200);\n assert.match(await tools.text(), /id="toolDashboardBar"/);\n assert.equal(flow.status, 200);\n assert.ok((await flow.json()).flow.states.length > 0);\n } finally {\n await runtime.stop();\n }\n});\n`;
|
|
73
68
|
}
|
|
74
69
|
|
|
75
70
|
function rewriteBundleGameId(contentRoot, gameId) {
|
|
@@ -115,13 +110,13 @@ function generateGame(options = {}) {
|
|
|
115
110
|
writeText(stagingRoot, "package.json", `${JSON.stringify(manifest, null, 2)}\n`);
|
|
116
111
|
writeText(stagingRoot, "game.config.js", generatedConfigSource({ displayName, engineVersion, gameId }));
|
|
117
112
|
writeText(stagingRoot, "src/actions/index.js", generatedContributionSource("server and flow action"));
|
|
118
|
-
writeText(stagingRoot, "src/stage/index.js",
|
|
119
|
-
writeText(stagingRoot, "src/controller/index.js",
|
|
113
|
+
writeText(stagingRoot, "src/stage/index.js", generatedContributionSource("stage renderer"));
|
|
114
|
+
writeText(stagingRoot, "src/controller/index.js", generatedContributionSource("controller renderer"));
|
|
120
115
|
writeText(stagingRoot, "src/tools/index.js", generatedContributionSource("authenticated tool panel"));
|
|
121
116
|
writeText(stagingRoot, "src/plugin/index.js", generatedPluginSource(pluginNamespace));
|
|
122
117
|
writeText(stagingRoot, "tests/config.test.js", generatedConfigTestSource({ engineVersion, gameId, pluginNamespace }));
|
|
123
118
|
writeText(stagingRoot, ".gitignore", "node_modules/\n.env\n.pop-party/\ndist/\noutputs/\n");
|
|
124
|
-
writeText(stagingRoot, "README.md", `# ${displayName}\n\nIndependent Pop Party game using \`@pop-party/engine@${engineVersion}\`.\n\nRun \`npm run dev\` locally or \`npm start\` in production. Development seeds ignored \`.pop-party/content\` once and then preserves that independent local copy; production uses the configured active store. The engine validates the selected release before binding the service port. Game-owned contributions live under \`src/actions\`, \`src/stage\`, \`src/controller\`, and \`src/tools\`; register them through the namespaced plugin in \`src/plugin\`. Content and starter blobs under \`content\` are independent copies owned by this game
|
|
119
|
+
writeText(stagingRoot, "README.md", `# ${displayName}\n\nIndependent Pop Party game using \`@pop-party/engine@${engineVersion}\`.\n\nRun \`npm run dev\` locally or \`npm start\` in production. Development seeds ignored \`.pop-party/content\` once and then preserves that independent local copy; production uses the configured active store. The engine validates the selected release before binding the service port and supplies the complete stage, controller, room lifecycle, and authenticated core tools. Game-owned contributions live under \`src/actions\`, \`src/stage\`, \`src/controller\`, and \`src/tools\`; register them through the namespaced plugin in \`src/plugin\`. Content and starter blobs under \`content\` are independent copies owned by this game.\n`);
|
|
125
120
|
writeText(stagingRoot, "render.yaml", generatedRenderBlueprintSource(gameId));
|
|
126
121
|
writeText(stagingRoot, "DEPLOYMENT.md", `# ${displayName} deployment\n\nThis game deploys as one independent Render web service defined by \`render.yaml\`. Keep \`numInstances: 1\` and autoscaling disabled while rooms are in-memory. Render builds with \`npm install --no-audit --no-fund && npm run build\`, starts with \`npm start\`, and checks \`/health\`.\n\nProduction must use reviewed provider credentials and an immutable active release. Do not point \`POP_PARTY_CONTENT_ROOT\` at \`.pop-party/content\`; that override is only for the engine-owned local development command. Configure provider, OAuth, and content-writer secrets in Render rather than committing them.\n`);
|
|
127
122
|
writeText(stagingRoot, "LICENSE", fs.readFileSync(path.join(__dirname, "..", "LICENSE"), "utf8"));
|