@flayerlabs/gamemode-cli 0.4.0 → 0.4.2
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,160 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: run-a-game-server
|
|
3
|
+
description: Connects a game with its own authoritative multiplayer server to Flaunch Game Mode with @flayerlabs/gamemode-gate. Use when a game runs its own realtime server or region fleet (custom netcode, Colyseus, Socket.IO, raw WebSockets) and needs the gate, /config, join tickets, awards and the platform submission to line up. Do not use for rules-based games with no server — $build-game-mode covers those.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Run a game server behind a Game Mode gate
|
|
7
|
+
|
|
8
|
+
You have a game whose gameplay lives on your own server. The platform never hosts or relays your
|
|
9
|
+
realtime traffic: you run the server and the gate, and the platform gives your hosted game a strict
|
|
10
|
+
policy that only reaches what you declared. This skill exists because that declaration lives in
|
|
11
|
+
three places that must agree, and because the launch form refuses a gate that does not announce
|
|
12
|
+
itself. Work through the sections in order; each ends with a check you can run.
|
|
13
|
+
|
|
14
|
+
## Know the three services and who hosts them
|
|
15
|
+
|
|
16
|
+
1. Your game bundle: static files you zip and submit. The platform hosts these on Moongate and
|
|
17
|
+
serves them from `https://<deploy-id>.games.moongate.com`. The deploy id is derived from the
|
|
18
|
+
bundle's content, so every re-upload changes the hostname. Never hardcode it; never expect it
|
|
19
|
+
to be stable.
|
|
20
|
+
2. Your gate: one process you deploy (`createGameServerGate()` from `@flayerlabs/gamemode-gate`).
|
|
21
|
+
It owns wallet sessions, the points ledger, spend authorisations and settlement. One process
|
|
22
|
+
serves one chain.
|
|
23
|
+
3. Your game server(s): your realtime processes, up to four public HTTPS origins. Gameplay,
|
|
24
|
+
scoring decisions and anti-abuse are yours; points move only through the gate's award route.
|
|
25
|
+
|
|
26
|
+
## Declare the same origins in all three places
|
|
27
|
+
|
|
28
|
+
The reviewed game-server origins must match exactly — same scheme, same host, no path — in:
|
|
29
|
+
|
|
30
|
+
1. The platform submission form's game server addresses field (one per line). This becomes your
|
|
31
|
+
hosted game's `connect-src`: the browser can only reach origins listed here or your gate.
|
|
32
|
+
2. `gameServerOrigins` in your `createGameServerGate()` options. This puts each origin into the
|
|
33
|
+
set of valid join-ticket audiences.
|
|
34
|
+
3. The `joinTicket(origin)` call in your game client and the `audience` your server verifies.
|
|
35
|
+
|
|
36
|
+
A mismatch fails at a different layer each time: missing from (1) and the fetch dies on CSP with
|
|
37
|
+
"Refused to connect"; missing from (2) and the fetch succeeds but the ticket does not verify;
|
|
38
|
+
wrong in (3) and verification rejects every player. At most four origins, exact HTTPS only —
|
|
39
|
+
wildcards, paths and plain HTTP are refused. Rotating regions? Put them behind stable hostnames;
|
|
40
|
+
the list is not meant to churn.
|
|
41
|
+
|
|
42
|
+
## Serve /config, or the launch form refuses your gate
|
|
43
|
+
|
|
44
|
+
The launch page reads `GET /config` from your gate before it lets anyone launch a coin through
|
|
45
|
+
your game. `startGate()` serves it automatically; `createGameServerGate()` serves it only when
|
|
46
|
+
you pass `announce`. Without it the form reports your server "isn't answering — it may be offline,
|
|
47
|
+
or built before the current Game Mode SDK". Pass it:
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
const app = createGameServerGate({
|
|
51
|
+
// ...pool, sessions, claims, discovery, settlement, gameId, awardToken...
|
|
52
|
+
gameServerOrigins: ['https://us.game.example.com', 'https://eu.game.example.com'],
|
|
53
|
+
announce: () => ({
|
|
54
|
+
chainId: 84532,
|
|
55
|
+
contracts: {
|
|
56
|
+
positionManager: '0x4E7cB1e6800a7B297B38BddcecAF9Ca5b6616FDC',
|
|
57
|
+
spendGatedCalculator: '0x8cbbE6b4cFA5Ccf68399Dbf1429d91A21097ebA5',
|
|
58
|
+
},
|
|
59
|
+
signer: SIGNER_ADDRESS, // the address of your gate's signing key
|
|
60
|
+
settler: SIGNER_ADDRESS,
|
|
61
|
+
walletCapWei: '25000000000000000',
|
|
62
|
+
roundDurationMs: 90_000,
|
|
63
|
+
minLobbyLeadMs: 60_000,
|
|
64
|
+
gateEndsAtGraceS: 10,
|
|
65
|
+
flaunchVariant: 'legacy11',
|
|
66
|
+
requiresEoa: false,
|
|
67
|
+
accepting: true,
|
|
68
|
+
publicLaunchesOpen: true,
|
|
69
|
+
privateLaunches: false,
|
|
70
|
+
}),
|
|
71
|
+
})
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The addresses above are Base Sepolia (chain 84532), read back from the chain. `signer` is what a
|
|
75
|
+
launch writes into its pool as the trusted signer — announce an address you do not hold and every
|
|
76
|
+
coin launched through your game is unplayable. `walletCapWei` must cover a flawless round
|
|
77
|
+
(`maxPointsPerPlayer` times the wei value of a point) or boot refuses.
|
|
78
|
+
|
|
79
|
+
Check: `curl https://<your-gate>/config` returns JSON with your `chainId` and `signer`, and
|
|
80
|
+
`curl https://<your-gate>/health` returns `{"ok":true}`.
|
|
81
|
+
|
|
82
|
+
## Point the gate's environment at the right origins
|
|
83
|
+
|
|
84
|
+
The two origin settings developers most often get backwards:
|
|
85
|
+
|
|
86
|
+
- Allowed origins (browser CORS and the WebSocket `Origin` check) must name BOTH browser callers:
|
|
87
|
+
your game, served from its Moongate origin, and the flaunch page itself, which calls the gate
|
|
88
|
+
directly — `POST /rounds/adopt` comes from the coin page, not from your iframe. Because the
|
|
89
|
+
Moongate hostname changes per upload, use the single-label wildcard for the first:
|
|
90
|
+
`https://*.games.moongate.com,https://flaunch.gg` (plus any preview page origin you test on).
|
|
91
|
+
If your bootstrap follows the single-origin example, split the list:
|
|
92
|
+
`allowedOrigins: required('GAME_ORIGIN').split(',').map((o) => o.trim())`. Missing the page
|
|
93
|
+
origin fails as a CORS preflight error on `/rounds/adopt` the moment a coin page loads.
|
|
94
|
+
- The sign-in domain names the page the player is looking at — the flaunch site embedding your
|
|
95
|
+
game — not your game and not your gate.
|
|
96
|
+
|
|
97
|
+
Generate the signing key, session secret (32+ characters) and award token (32+ characters,
|
|
98
|
+
`openssl rand -hex 32`) fresh per environment. The award token lives on the gate and your game
|
|
99
|
+
server only; it never reaches a browser or a zip.
|
|
100
|
+
|
|
101
|
+
## Wire the market
|
|
102
|
+
|
|
103
|
+
`joinEconomy()` does not find the market on its own. Hand it the one the embedding page pushes,
|
|
104
|
+
or the game has nothing to draw:
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
const embedded = await connectHost()
|
|
108
|
+
const gameMode = await joinEconomy({
|
|
109
|
+
gateUrl: embedded.context.gateUrl,
|
|
110
|
+
roundId: embedded.context.roundId,
|
|
111
|
+
host: embedded.host,
|
|
112
|
+
platform: { market: embedded.market }, // omit this and economy.market stays `unavailable`
|
|
113
|
+
})
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The gate never sends market state, so waiting for it on the live socket waits forever. Subscribe
|
|
117
|
+
to `gameMode.market` for the chart and `marketCapUsd`; a page that pushes nothing leaves the
|
|
118
|
+
status at `unavailable`, which is the cue to draw an empty chart rather than a spinner.
|
|
119
|
+
|
|
120
|
+
## Wire the ticket handshake
|
|
121
|
+
|
|
122
|
+
Browser, after `joinEconomy()`:
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
const gameServerOrigin = 'https://us.game.example.com' // one reviewed origin, chosen by you
|
|
126
|
+
const { ticket } = await gameMode.joinTicket(gameServerOrigin)
|
|
127
|
+
socket.send(JSON.stringify({ type: 'join', ticket })) // first message; never in the URL
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Your server, before accepting any gameplay frame:
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
const playerJoin = verifyPlayerJoinTicket(message.ticket, {
|
|
134
|
+
awardToken,
|
|
135
|
+
gameId,
|
|
136
|
+
roundId,
|
|
137
|
+
audience: gameServerOrigin, // the exact origin the browser connected to
|
|
138
|
+
})
|
|
139
|
+
if (!playerJoin || usedTicketIds.has(playerJoin.jti)) return socket.close(4401, 'invalid join ticket')
|
|
140
|
+
usedTicketIds.add(playerJoin.jti)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Also check the WebSocket `Origin` header against the Moongate wildcard, keep one live connection
|
|
144
|
+
per wallet, and treat every later frame as hostile input. Award points only through
|
|
145
|
+
`POST /internal/rounds/:id/awards` with the award token — see the gate README for the routes.
|
|
146
|
+
|
|
147
|
+
## Strip development fallbacks from the production bundle
|
|
148
|
+
|
|
149
|
+
A production zip that pings `localhost` ports, probes a region list on boot, or falls back to a
|
|
150
|
+
dev wallet endpoint will emit CSP errors on every load and can hang the game on a spinner when the
|
|
151
|
+
fallback wins a race. Gate development-only network paths behind a build flag so the submitted
|
|
152
|
+
bundle contains none of them.
|
|
153
|
+
|
|
154
|
+
## Submit, then verify against the real policy
|
|
155
|
+
|
|
156
|
+
Submit the zip with the gate option on (your gate origin) and the game server addresses filled in.
|
|
157
|
+
The form live-probes your gate's `/health` at submission, so deploy the gate first. Localhost
|
|
158
|
+
cannot rehearse the deployed CSP: after hosting, open the deployed game and confirm every declared
|
|
159
|
+
origin connects and an undeclared one is blocked. Then launch a fresh test coin through the room
|
|
160
|
+
link — a coin launched before a registration change stays bound to what it was launched with.
|
package/dist/scaffold.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"AAGA;;;;;;;;;GASG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAc5D;
|
|
1
|
+
{"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"AAGA;;;;;;;;;GASG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAc5D;AA0CD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAW7D"}
|
package/dist/scaffold.js
CHANGED
|
@@ -37,6 +37,12 @@ async function readCreatorSkill() {
|
|
|
37
37
|
async function readCreatorSkillMetadata() {
|
|
38
38
|
return readAsset('./build-game-mode/agents/openai.yaml', '../../../.agents/skills/build-game-mode/agents/openai.yaml');
|
|
39
39
|
}
|
|
40
|
+
async function readServerSkill() {
|
|
41
|
+
return readAsset('./run-a-game-server/SKILL.md', '../../../.agents/skills/run-a-game-server/SKILL.md');
|
|
42
|
+
}
|
|
43
|
+
async function readServerSkillMetadata() {
|
|
44
|
+
return readAsset('./run-a-game-server/agents/openai.yaml', '../../../.agents/skills/run-a-game-server/agents/openai.yaml');
|
|
45
|
+
}
|
|
40
46
|
async function readAsset(packaged, source) {
|
|
41
47
|
try {
|
|
42
48
|
return await readFile(new URL(packaged, import.meta.url), 'utf8');
|
|
@@ -156,6 +162,8 @@ For the rules and client workflow, read the
|
|
|
156
162
|
`,
|
|
157
163
|
'.agents/skills/build-game-mode/SKILL.md': await readCreatorSkill(),
|
|
158
164
|
'.agents/skills/build-game-mode/agents/openai.yaml': await readCreatorSkillMetadata(),
|
|
165
|
+
'.agents/skills/run-a-game-server/SKILL.md': await readServerSkill(),
|
|
166
|
+
'.agents/skills/run-a-game-server/agents/openai.yaml': await readServerSkillMetadata(),
|
|
159
167
|
'src/game/rules.ts': `import { defineGame, type Decision, type PlayerId, type Refusal } from '@flayerlabs/gamemode-spec';
|
|
160
168
|
import { scheduleWithin } from '@flayerlabs/gamemode-spec/schedule';
|
|
161
169
|
|
|
@@ -386,8 +394,10 @@ SESSION_SECRET=
|
|
|
386
394
|
SIGN_IN_DOMAIN=flaunch.gg
|
|
387
395
|
# This gate's own public https origin — the exact value you submit as the Gate URL.
|
|
388
396
|
GATE_ORIGIN=
|
|
389
|
-
# Comma-separated origins allowed to call this gate: your game's hosted origin(s) and
|
|
390
|
-
# that
|
|
397
|
+
# Comma-separated origins allowed to call this gate: your game's hosted origin(s) and EVERY page
|
|
398
|
+
# that embeds it — the coin page calls /rounds/adopt itself, so a missing page origin is a CORS
|
|
399
|
+
# error at launch. Add a staging/preview launch page's origin while you test against one.
|
|
400
|
+
# One * may span a single DNS label.
|
|
391
401
|
ALLOWED_ORIGINS=https://*.games.moongate.com,https://flaunch.gg
|
|
392
402
|
`,
|
|
393
403
|
'railpack.json': `${JSON.stringify({
|
package/dist/scaffold.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/scaffold.ts
CHANGED
|
@@ -47,6 +47,17 @@ async function readCreatorSkillMetadata(): Promise<string> {
|
|
|
47
47
|
);
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
async function readServerSkill(): Promise<string> {
|
|
51
|
+
return readAsset('./run-a-game-server/SKILL.md', '../../../.agents/skills/run-a-game-server/SKILL.md');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function readServerSkillMetadata(): Promise<string> {
|
|
55
|
+
return readAsset(
|
|
56
|
+
'./run-a-game-server/agents/openai.yaml',
|
|
57
|
+
'../../../.agents/skills/run-a-game-server/agents/openai.yaml',
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
50
61
|
async function readAsset(packaged: string, source: string): Promise<string> {
|
|
51
62
|
try {
|
|
52
63
|
return await readFile(new URL(packaged, import.meta.url), 'utf8');
|
|
@@ -181,6 +192,8 @@ For the rules and client workflow, read the
|
|
|
181
192
|
|
|
182
193
|
'.agents/skills/build-game-mode/SKILL.md': await readCreatorSkill(),
|
|
183
194
|
'.agents/skills/build-game-mode/agents/openai.yaml': await readCreatorSkillMetadata(),
|
|
195
|
+
'.agents/skills/run-a-game-server/SKILL.md': await readServerSkill(),
|
|
196
|
+
'.agents/skills/run-a-game-server/agents/openai.yaml': await readServerSkillMetadata(),
|
|
184
197
|
|
|
185
198
|
'src/game/rules.ts': `import { defineGame, type Decision, type PlayerId, type Refusal } from '@flayerlabs/gamemode-spec';
|
|
186
199
|
import { scheduleWithin } from '@flayerlabs/gamemode-spec/schedule';
|
|
@@ -414,8 +427,10 @@ SESSION_SECRET=
|
|
|
414
427
|
SIGN_IN_DOMAIN=flaunch.gg
|
|
415
428
|
# This gate's own public https origin — the exact value you submit as the Gate URL.
|
|
416
429
|
GATE_ORIGIN=
|
|
417
|
-
# Comma-separated origins allowed to call this gate: your game's hosted origin(s) and
|
|
418
|
-
# that
|
|
430
|
+
# Comma-separated origins allowed to call this gate: your game's hosted origin(s) and EVERY page
|
|
431
|
+
# that embeds it — the coin page calls /rounds/adopt itself, so a missing page origin is a CORS
|
|
432
|
+
# error at launch. Add a staging/preview launch page's origin while you test against one.
|
|
433
|
+
# One * may span a single DNS label.
|
|
419
434
|
ALLOWED_ORIGINS=https://*.games.moongate.com,https://flaunch.gg
|
|
420
435
|
`,
|
|
421
436
|
|