@crowdedkingdoms/crowdyjs 17.14.0-dev.1 → 18.0.0-dev.1
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/MIGRATION.md +92 -1
- package/README.md +93 -162
- package/dist/crowdy-client.d.ts +12 -31
- package/dist/crowdy-client.d.ts.map +1 -1
- package/dist/crowdy-client.js +15 -37
- package/dist/crowdy-dsh/bridge.d.ts.map +1 -1
- package/dist/crowdy-dsh/bridge.js +3 -6
- package/dist/crowdy-studio/controller.d.ts +34 -59
- package/dist/crowdy-studio/controller.d.ts.map +1 -1
- package/dist/crowdy-studio/controller.js +96 -225
- package/dist/crowdy-studio/diagnostics.d.ts +3 -12
- package/dist/crowdy-studio/diagnostics.d.ts.map +1 -1
- package/dist/crowdy-studio/dom-shell.d.ts +2 -4
- package/dist/crowdy-studio/dom-shell.d.ts.map +1 -1
- package/dist/crowdy-studio/dom-shell.js +16 -49
- package/dist/crowdy-studio/embed/hud-layer.d.ts +1 -2
- package/dist/crowdy-studio/embed/hud-layer.d.ts.map +1 -1
- package/dist/crowdy-studio/embed/panel.d.ts +3 -13
- package/dist/crowdy-studio/embed/panel.d.ts.map +1 -1
- package/dist/crowdy-studio/embed/panel.js +3 -6
- package/dist/crowdy-studio/index.d.ts +1 -3
- package/dist/crowdy-studio/index.d.ts.map +1 -1
- package/dist/crowdy-studio/index.js +0 -2
- package/dist/crowdy-studio/models.d.ts +3 -4
- package/dist/crowdy-studio/models.d.ts.map +1 -1
- package/dist/crowdy-studio/starter-projects.d.ts +8 -12
- package/dist/crowdy-studio/starter-projects.d.ts.map +1 -1
- package/dist/crowdy-studio/starter-projects.js +12 -103
- package/dist/domains/appAccess.d.ts +14 -1
- package/dist/domains/appAccess.d.ts.map +1 -1
- package/dist/domains/appAccess.js +37 -1
- package/dist/domains/host.d.ts +3 -3
- package/dist/domains/host.js +3 -3
- package/dist/domains/marketplace.d.ts +9 -70
- package/dist/domains/marketplace.d.ts.map +1 -1
- package/dist/domains/marketplace.js +9 -120
- package/dist/domains/playerWallet.d.ts +6 -12
- package/dist/domains/playerWallet.d.ts.map +1 -1
- package/dist/domains/playerWallet.js +6 -21
- package/dist/errors.d.ts +7 -13
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +6 -10
- package/dist/generated/graphql.d.ts +141 -7083
- package/dist/generated/graphql.d.ts.map +1 -1
- package/dist/generated/graphql.js +5 -313
- package/dist/grid-mods/exec-client-halves.d.ts +19 -3
- package/dist/grid-mods/exec-client-halves.d.ts.map +1 -1
- package/dist/grid-mods/exec-client-halves.js +17 -2
- package/dist/grid-mods/grid-host-calls.d.ts +29 -17
- package/dist/grid-mods/grid-host-calls.d.ts.map +1 -1
- package/dist/grid-mods/grid-host-calls.js +40 -46
- package/dist/grid-mods/mod-runtime.d.ts +18 -11
- package/dist/grid-mods/mod-runtime.d.ts.map +1 -1
- package/dist/grid-mods/mod-runtime.js +4 -2
- package/dist/grid-scope.d.ts +0 -189
- package/dist/grid-scope.d.ts.map +1 -1
- package/dist/grid-scope.js +0 -70
- package/dist/index.d.ts +11 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -16
- package/dist/kit/actions.d.ts +5 -9
- package/dist/kit/actions.d.ts.map +1 -1
- package/dist/kit/actions.js +5 -9
- package/dist/kit/index.d.ts +2 -33
- package/dist/kit/index.d.ts.map +1 -1
- package/dist/kit/index.js +1 -32
- package/dist/kit/kit.d.ts +8 -208
- package/dist/kit/kit.d.ts.map +1 -1
- package/dist/kit/kit.js +5 -163
- package/dist/kit/social.d.ts +1 -3
- package/dist/kit/social.d.ts.map +1 -1
- package/dist/kit/social.js +1 -3
- package/dist/kit/wire.d.ts +16 -5
- package/dist/kit/wire.d.ts.map +1 -1
- package/dist/kit/wire.js +19 -5
- package/dist/live-coding/assets/browser-authoring-index.json +1 -1
- package/dist/live-coding/assets/manifest.json +1 -1
- package/dist/live-coding/browser-authoring-index.generated.d.ts.map +1 -1
- package/dist/live-coding/browser-authoring-index.generated.js +1496 -4386
- package/dist/live-coding/rust-analysis.d.ts.map +1 -1
- package/dist/live-coding/rust-analysis.js +30 -34
- package/dist/player-runtime/client-host-calls.d.ts +8 -0
- package/dist/player-runtime/client-host-calls.d.ts.map +1 -0
- package/dist/player-runtime/client-host-calls.js +28 -0
- package/dist/player-runtime/glue-runtime.d.ts +24 -20
- package/dist/player-runtime/glue-runtime.d.ts.map +1 -1
- package/dist/player-runtime/glue-runtime.js +26 -55
- package/dist/player-runtime/glue-sab.d.ts +2 -2
- package/dist/player-runtime/glue-sab.js +2 -2
- package/dist/player-runtime/player-code-broker.d.ts +70 -43
- package/dist/player-runtime/player-code-broker.d.ts.map +1 -1
- package/dist/player-runtime/player-code-broker.js +159 -64
- package/dist/player-runtime/player-glue-worker.d.ts +8 -9
- package/dist/player-runtime/player-glue-worker.d.ts.map +1 -1
- package/dist/player-runtime/player-glue-worker.js +26 -11
- package/dist/stores/inbox.d.ts +1 -1
- package/dist/stores/inbox.d.ts.map +1 -1
- package/dist/stores/index.d.ts +0 -11
- package/dist/stores/index.d.ts.map +1 -1
- package/dist/stores/index.js +0 -4
- package/dist/stores/session.d.ts +0 -2
- package/dist/stores/session.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/crowdy-studio/model-lint-log.d.ts +0 -76
- package/dist/crowdy-studio/model-lint-log.d.ts.map +0 -1
- package/dist/crowdy-studio/model-lint-log.js +0 -93
- package/dist/crowdy-studio/model-lint.d.ts +0 -61
- package/dist/crowdy-studio/model-lint.d.ts.map +0 -1
- package/dist/crowdy-studio/model-lint.js +0 -45
- package/dist/domains/compute.d.ts +0 -208
- package/dist/domains/compute.d.ts.map +0 -1
- package/dist/domains/compute.js +0 -274
- package/dist/domains/controlPlane.d.ts +0 -38
- package/dist/domains/controlPlane.d.ts.map +0 -1
- package/dist/domains/controlPlane.js +0 -43
- package/dist/domains/gameModel.d.ts +0 -1174
- package/dist/domains/gameModel.d.ts.map +0 -1
- package/dist/domains/gameModel.js +0 -1333
- package/dist/domains/playerCompute.d.ts +0 -91
- package/dist/domains/playerCompute.d.ts.map +0 -1
- package/dist/domains/playerCompute.js +0 -136
- package/dist/domains/playerModel.d.ts +0 -20
- package/dist/domains/playerModel.d.ts.map +0 -1
- package/dist/domains/playerModel.js +0 -54
- package/dist/kit/abilities.d.ts +0 -66
- package/dist/kit/abilities.d.ts.map +0 -1
- package/dist/kit/abilities.js +0 -71
- package/dist/kit/blueprints/combat.d.ts +0 -81
- package/dist/kit/blueprints/combat.d.ts.map +0 -1
- package/dist/kit/blueprints/combat.js +0 -357
- package/dist/kit/blueprints/core.d.ts +0 -222
- package/dist/kit/blueprints/core.d.ts.map +0 -1
- package/dist/kit/blueprints/core.js +0 -185
- package/dist/kit/blueprints/decks.d.ts +0 -50
- package/dist/kit/blueprints/decks.d.ts.map +0 -1
- package/dist/kit/blueprints/decks.js +0 -163
- package/dist/kit/blueprints/economy.d.ts +0 -88
- package/dist/kit/blueprints/economy.d.ts.map +0 -1
- package/dist/kit/blueprints/economy.js +0 -533
- package/dist/kit/blueprints/guild.d.ts +0 -51
- package/dist/kit/blueprints/guild.d.ts.map +0 -1
- package/dist/kit/blueprints/guild.js +0 -50
- package/dist/kit/blueprints/index.d.ts +0 -23
- package/dist/kit/blueprints/index.d.ts.map +0 -1
- package/dist/kit/blueprints/index.js +0 -22
- package/dist/kit/blueprints/inventory.d.ts +0 -84
- package/dist/kit/blueprints/inventory.d.ts.map +0 -1
- package/dist/kit/blueprints/inventory.js +0 -257
- package/dist/kit/blueprints/leaderboards.d.ts +0 -49
- package/dist/kit/blueprints/leaderboards.d.ts.map +0 -1
- package/dist/kit/blueprints/leaderboards.js +0 -126
- package/dist/kit/blueprints/liveops.d.ts +0 -42
- package/dist/kit/blueprints/liveops.d.ts.map +0 -1
- package/dist/kit/blueprints/liveops.js +0 -170
- package/dist/kit/blueprints/locks.d.ts +0 -82
- package/dist/kit/blueprints/locks.d.ts.map +0 -1
- package/dist/kit/blueprints/locks.js +0 -155
- package/dist/kit/blueprints/loot.d.ts +0 -90
- package/dist/kit/blueprints/loot.d.ts.map +0 -1
- package/dist/kit/blueprints/loot.js +0 -229
- package/dist/kit/blueprints/matches.d.ts +0 -84
- package/dist/kit/blueprints/matches.d.ts.map +0 -1
- package/dist/kit/blueprints/matches.js +0 -349
- package/dist/kit/blueprints/moderation.d.ts +0 -32
- package/dist/kit/blueprints/moderation.d.ts.map +0 -1
- package/dist/kit/blueprints/moderation.js +0 -140
- package/dist/kit/blueprints/npcs.d.ts +0 -78
- package/dist/kit/blueprints/npcs.d.ts.map +0 -1
- package/dist/kit/blueprints/npcs.js +0 -125
- package/dist/kit/blueprints/plots.d.ts +0 -63
- package/dist/kit/blueprints/plots.d.ts.map +0 -1
- package/dist/kit/blueprints/plots.js +0 -151
- package/dist/kit/blueprints/progression.d.ts +0 -68
- package/dist/kit/blueprints/progression.d.ts.map +0 -1
- package/dist/kit/blueprints/progression.js +0 -324
- package/dist/kit/blueprints/quests.d.ts +0 -72
- package/dist/kit/blueprints/quests.d.ts.map +0 -1
- package/dist/kit/blueprints/quests.js +0 -285
- package/dist/kit/blueprints/worldsim.d.ts +0 -91
- package/dist/kit/blueprints/worldsim.d.ts.map +0 -1
- package/dist/kit/blueprints/worldsim.js +0 -468
- package/dist/kit/combat.d.ts +0 -141
- package/dist/kit/combat.d.ts.map +0 -1
- package/dist/kit/combat.js +0 -196
- package/dist/kit/decks.d.ts +0 -157
- package/dist/kit/decks.d.ts.map +0 -1
- package/dist/kit/decks.js +0 -186
- package/dist/kit/director.d.ts +0 -88
- package/dist/kit/director.d.ts.map +0 -1
- package/dist/kit/director.js +0 -86
- package/dist/kit/economy.d.ts +0 -289
- package/dist/kit/economy.d.ts.map +0 -1
- package/dist/kit/economy.js +0 -427
- package/dist/kit/engine.d.ts +0 -48
- package/dist/kit/engine.d.ts.map +0 -1
- package/dist/kit/engine.js +0 -113
- package/dist/kit/features.d.ts +0 -60
- package/dist/kit/features.d.ts.map +0 -1
- package/dist/kit/features.js +0 -65
- package/dist/kit/instances.d.ts +0 -52
- package/dist/kit/instances.d.ts.map +0 -1
- package/dist/kit/instances.js +0 -60
- package/dist/kit/inventory.d.ts +0 -129
- package/dist/kit/inventory.d.ts.map +0 -1
- package/dist/kit/inventory.js +0 -200
- package/dist/kit/leaderboards.d.ts +0 -86
- package/dist/kit/leaderboards.d.ts.map +0 -1
- package/dist/kit/leaderboards.js +0 -133
- package/dist/kit/liveops.d.ts +0 -112
- package/dist/kit/liveops.d.ts.map +0 -1
- package/dist/kit/liveops.js +0 -183
- package/dist/kit/loot.d.ts +0 -128
- package/dist/kit/loot.d.ts.map +0 -1
- package/dist/kit/loot.js +0 -160
- package/dist/kit/matches.d.ts +0 -301
- package/dist/kit/matches.d.ts.map +0 -1
- package/dist/kit/matches.js +0 -458
- package/dist/kit/matchmaking.d.ts +0 -57
- package/dist/kit/matchmaking.d.ts.map +0 -1
- package/dist/kit/matchmaking.js +0 -59
- package/dist/kit/minigames.d.ts +0 -30
- package/dist/kit/minigames.d.ts.map +0 -1
- package/dist/kit/minigames.js +0 -38
- package/dist/kit/mobs.d.ts +0 -90
- package/dist/kit/mobs.d.ts.map +0 -1
- package/dist/kit/mobs.js +0 -98
- package/dist/kit/moderation.d.ts +0 -73
- package/dist/kit/moderation.d.ts.map +0 -1
- package/dist/kit/moderation.js +0 -103
- package/dist/kit/movement.d.ts +0 -78
- package/dist/kit/movement.d.ts.map +0 -1
- package/dist/kit/movement.js +0 -103
- package/dist/kit/notifications.d.ts +0 -45
- package/dist/kit/notifications.d.ts.map +0 -1
- package/dist/kit/notifications.js +0 -67
- package/dist/kit/npcs.d.ts +0 -226
- package/dist/kit/npcs.d.ts.map +0 -1
- package/dist/kit/npcs.js +0 -141
- package/dist/kit/objects.d.ts +0 -116
- package/dist/kit/objects.d.ts.map +0 -1
- package/dist/kit/objects.js +0 -119
- package/dist/kit/pets.d.ts +0 -85
- package/dist/kit/pets.d.ts.map +0 -1
- package/dist/kit/pets.js +0 -96
- package/dist/kit/plots.d.ts +0 -85
- package/dist/kit/plots.d.ts.map +0 -1
- package/dist/kit/plots.js +0 -113
- package/dist/kit/progression.d.ts +0 -181
- package/dist/kit/progression.d.ts.map +0 -1
- package/dist/kit/progression.js +0 -265
- package/dist/kit/quests.d.ts +0 -171
- package/dist/kit/quests.d.ts.map +0 -1
- package/dist/kit/quests.js +0 -237
- package/dist/kit/racing.d.ts +0 -85
- package/dist/kit/racing.d.ts.map +0 -1
- package/dist/kit/racing.js +0 -99
- package/dist/kit/shared.d.ts +0 -73
- package/dist/kit/shared.d.ts.map +0 -1
- package/dist/kit/shared.js +0 -101
- package/dist/kit/telemetry.d.ts +0 -66
- package/dist/kit/telemetry.d.ts.map +0 -1
- package/dist/kit/telemetry.js +0 -133
- package/dist/kit/territory.d.ts +0 -97
- package/dist/kit/territory.d.ts.map +0 -1
- package/dist/kit/territory.js +0 -114
- package/dist/kit/worldsim.d.ts +0 -263
- package/dist/kit/worldsim.d.ts.map +0 -1
- package/dist/kit/worldsim.js +0 -268
- package/dist/stores/model.d.ts +0 -81
- package/dist/stores/model.d.ts.map +0 -1
- package/dist/stores/model.js +0 -163
package/dist/kit/matches.js
DELETED
|
@@ -1,458 +0,0 @@
|
|
|
1
|
-
import { CrowdyGraphQLError } from '../errors.js';
|
|
2
|
-
import { encodeBase64, generateCrowdyUuid } from '../utils.js';
|
|
3
|
-
import { matchesNames } from './blueprints/index.js';
|
|
4
|
-
import { kitContainerProperties, kitInvoke, } from './shared.js';
|
|
5
|
-
/**
|
|
6
|
-
* Whether the match's current turn has run out of time — true once the
|
|
7
|
-
* deadline for the open turn has fired. Always false for a match deployed
|
|
8
|
-
* without `turnTimer`, and false for a deadline stranded by an earlier turn,
|
|
9
|
-
* since those record a lower sequence than the one now open.
|
|
10
|
-
*/
|
|
11
|
-
export function turnExpired(match) {
|
|
12
|
-
if (match.turnSeq === undefined || match.turnExpiredSeq === undefined)
|
|
13
|
-
return false;
|
|
14
|
-
return match.turnSeq > 0 && match.turnExpiredSeq >= match.turnSeq;
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Runtime helpers for the {@link matchesBlueprint} conventions: sessions ARE
|
|
18
|
-
* the match primitive — `create` makes a session + a `MatchMeta` + a
|
|
19
|
-
* per-match channel; turn order goes through the platform's session-turn
|
|
20
|
-
* authority; lifecycle functions ping the channel post-commit and
|
|
21
|
-
* {@link onMatchChanged} wraps the notify-to-pull loop (subscribe →
|
|
22
|
-
* `"match_changed"` ping → re-pull state).
|
|
23
|
-
*
|
|
24
|
-
* Obtained via `client.kit(appId).matches`.
|
|
25
|
-
*/
|
|
26
|
-
export class MatchesKit {
|
|
27
|
-
constructor(appId, gameModel, channels, udp, options = {}, engines) {
|
|
28
|
-
this.appId = appId;
|
|
29
|
-
this.gameModel = gameModel;
|
|
30
|
-
this.channels = channels;
|
|
31
|
-
this.udp = udp;
|
|
32
|
-
this.engines = engines;
|
|
33
|
-
/**
|
|
34
|
-
* This client's participant incarnation per session, remembered from
|
|
35
|
-
* {@link create} (a creator starts at 1) and {@link join}, so {@link leave}
|
|
36
|
-
* can send the value the server requires without the caller carrying it.
|
|
37
|
-
*/
|
|
38
|
-
this.incarnations = new Map();
|
|
39
|
-
this.names = matchesNames(options.typePrefix ?? '');
|
|
40
|
-
this.engineModuleName = options.engineModuleName ?? 'match-engine';
|
|
41
|
-
this.actorUuid = options.actorUuid ?? generateCrowdyUuid();
|
|
42
|
-
}
|
|
43
|
-
requireChannels() {
|
|
44
|
-
if (!this.channels) {
|
|
45
|
-
throw new Error('kit.matches needs the channels domain — construct the kit via client.kit(appId)');
|
|
46
|
-
}
|
|
47
|
-
return this.channels;
|
|
48
|
-
}
|
|
49
|
-
requireUdp() {
|
|
50
|
-
if (!this.udp) {
|
|
51
|
-
throw new Error('kit.matches needs the udp domain — construct the kit via client.kit(appId)');
|
|
52
|
-
}
|
|
53
|
-
return this.udp;
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Create a match: a session (the platform match primitive), a per-match
|
|
57
|
-
* notification channel, and the session-scoped `MatchMeta`.
|
|
58
|
-
*
|
|
59
|
-
* @param input.creatorUserId - The calling player's user id (stored so the
|
|
60
|
-
* creator may start/advance/end the match).
|
|
61
|
-
* @param input.seedFromApp - Optional: container types whose app-scoped keyed
|
|
62
|
-
* rows (a `gameModelSeed` with `bindingKey`) are stamped into the match's
|
|
63
|
-
* session at creation, inside the same transaction, so the match starts
|
|
64
|
-
* with its world rows present (chests, spawners, turrets). Same shape as
|
|
65
|
-
* `CreateSessionInput.seedFromApp`: `{ typeNames, initialState? }`. At most
|
|
66
|
-
* 2,000 rows; above that the create is refused and no session exists. Only
|
|
67
|
-
* `'session'`-scoped types; the copies are the only rows the tier's
|
|
68
|
-
* (opt-in) ended-session retention may drop.
|
|
69
|
-
*/
|
|
70
|
-
async create(input) {
|
|
71
|
-
// A kit match talks GraphQL and channel pings; the `actorUuid` here is only
|
|
72
|
-
// the channel-message sender id, never a replicated actor. Under the
|
|
73
|
-
// default `presence: 'actor'` the server would expire every player after
|
|
74
|
-
// the join grace window, so the roster is judged by nothing: players leave
|
|
75
|
-
// with `leave()`, `finish()` ends the session, and an emptied session is
|
|
76
|
-
// abandoned by the empty timeout.
|
|
77
|
-
const session = await this.gameModel.createSession({
|
|
78
|
-
appId: this.appId,
|
|
79
|
-
name: input.displayName ?? `match-${input.mode ?? 'default'}`,
|
|
80
|
-
presence: 'none',
|
|
81
|
-
...(input.seedFromApp ? { seedFromApp: input.seedFromApp } : {}),
|
|
82
|
-
});
|
|
83
|
-
this.incarnations.set(session.sessionId, 1);
|
|
84
|
-
const channel = await this.requireChannels().create({
|
|
85
|
-
appId: this.appId,
|
|
86
|
-
name: `match-${session.sessionId}`,
|
|
87
|
-
description: 'Per-match notification channel (kit.matches notify-to-pull).',
|
|
88
|
-
});
|
|
89
|
-
const meta = await this.gameModel.createContainer({
|
|
90
|
-
appId: this.appId,
|
|
91
|
-
typeName: this.names.metaType,
|
|
92
|
-
displayName: input.displayName ?? `Match ${session.sessionId}`,
|
|
93
|
-
sessionId: session.sessionId,
|
|
94
|
-
properties: [
|
|
95
|
-
{
|
|
96
|
-
key: 'creator_user_id',
|
|
97
|
-
valueType: 'int',
|
|
98
|
-
valueJson: String(input.creatorUserId),
|
|
99
|
-
},
|
|
100
|
-
{ key: 'mode', valueType: 'string', valueJson: JSON.stringify(input.mode ?? '') },
|
|
101
|
-
{
|
|
102
|
-
key: 'max_players',
|
|
103
|
-
valueType: 'int',
|
|
104
|
-
valueJson: String(input.maxPlayers ?? 0),
|
|
105
|
-
},
|
|
106
|
-
{ key: 'channel_id', valueType: 'int', valueJson: String(channel.groupId) },
|
|
107
|
-
],
|
|
108
|
-
});
|
|
109
|
-
return this.toMatch(meta.containerId, session.sessionId, meta.displayName);
|
|
110
|
-
}
|
|
111
|
-
/** List joinable matches (metas still in the lobby state). */
|
|
112
|
-
async open() {
|
|
113
|
-
const metas = await this.gameModel.containers({
|
|
114
|
-
appId: this.appId,
|
|
115
|
-
typeName: this.names.metaType,
|
|
116
|
-
});
|
|
117
|
-
const matches = await Promise.all(metas
|
|
118
|
-
.filter((m) => m.sessionId != null)
|
|
119
|
-
.map((m) => this.toMatch(m.containerId, String(m.sessionId), m.displayName)));
|
|
120
|
-
return matches.filter((m) => m.state === 'lobby');
|
|
121
|
-
}
|
|
122
|
-
/** Read one match by its MatchMeta container. */
|
|
123
|
-
async get(metaId) {
|
|
124
|
-
const meta = await this.gameModel.container({
|
|
125
|
-
appId: this.appId,
|
|
126
|
-
containerId: metaId,
|
|
127
|
-
});
|
|
128
|
-
return this.toMatch(meta.containerId, meta.sessionId != null ? String(meta.sessionId) : '', meta.displayName);
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* Join a match: session participation + the notification channel. A rejoin
|
|
132
|
-
* (you are already in) returns your row with `incarnation + 1`. The
|
|
133
|
-
* incarnation is remembered on this kit instance for {@link leave}.
|
|
134
|
-
*/
|
|
135
|
-
async join(match) {
|
|
136
|
-
const participant = await this.gameModel.joinSession({
|
|
137
|
-
appId: this.appId,
|
|
138
|
-
sessionId: match.sessionId,
|
|
139
|
-
});
|
|
140
|
-
this.incarnations.set(match.sessionId, participant.incarnation);
|
|
141
|
-
if (match.channelId !== '0' && match.channelId !== '') {
|
|
142
|
-
await this.requireChannels().join(match.channelId);
|
|
143
|
-
}
|
|
144
|
-
return participant;
|
|
145
|
-
}
|
|
146
|
-
/**
|
|
147
|
-
* Leave a match: session departure + the notification channel. The server
|
|
148
|
-
* requires the incarnation {@link join} returned to *this* client, so a
|
|
149
|
-
* superseded client can never remove the one that took over; pass it, or
|
|
150
|
-
* let the kit use the one it remembered from `create` / `join` on this
|
|
151
|
-
* instance. A kit session is `presence: 'none'`, so this and {@link finish}
|
|
152
|
-
* are the roster's only exits.
|
|
153
|
-
*
|
|
154
|
-
* @throws {Error} when no incarnation is known for the match on this
|
|
155
|
-
* instance and none was passed.
|
|
156
|
-
* @throws {CrowdyGraphQLError} `SESSION_INCARNATION_STALE` (a newer client
|
|
157
|
-
* of yours rejoined), `SESSION_NOT_PARTICIPANT`, `NOT_FOUND`.
|
|
158
|
-
*/
|
|
159
|
-
async leave(match, incarnation) {
|
|
160
|
-
const known = incarnation ?? this.incarnations.get(match.sessionId);
|
|
161
|
-
if (known === undefined) {
|
|
162
|
-
throw new Error('kit.matches.leave needs the incarnation join() returned — pass it, or join through this kit instance');
|
|
163
|
-
}
|
|
164
|
-
const participant = await this.gameModel.leaveSession({
|
|
165
|
-
appId: this.appId,
|
|
166
|
-
sessionId: match.sessionId,
|
|
167
|
-
incarnation: known,
|
|
168
|
-
});
|
|
169
|
-
this.incarnations.delete(match.sessionId);
|
|
170
|
-
if (match.channelId !== '0' && match.channelId !== '') {
|
|
171
|
-
await this.requireChannels().leave(match.channelId);
|
|
172
|
-
}
|
|
173
|
-
return participant;
|
|
174
|
-
}
|
|
175
|
-
/** Start the match (creator or host). Pings the match channel post-commit. */
|
|
176
|
-
async start(match) {
|
|
177
|
-
return kitInvoke(this.gameModel, {
|
|
178
|
-
appId: String(this.appId),
|
|
179
|
-
functionName: this.names.startFn,
|
|
180
|
-
selfContainerId: match.metaId,
|
|
181
|
-
sessionId: match.sessionId,
|
|
182
|
-
params: {},
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
/** Advance to the next round (creator or host). */
|
|
186
|
-
async advanceRound(match) {
|
|
187
|
-
return kitInvoke(this.gameModel, {
|
|
188
|
-
appId: String(this.appId),
|
|
189
|
-
functionName: this.names.advanceRoundFn,
|
|
190
|
-
selfContainerId: match.metaId,
|
|
191
|
-
sessionId: match.sessionId,
|
|
192
|
-
params: {},
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
/** Whether it is `userId`'s session turn right now. */
|
|
196
|
-
async myTurn(match, userId) {
|
|
197
|
-
const session = await this.gameModel.session({
|
|
198
|
-
appId: this.appId,
|
|
199
|
-
sessionId: match.sessionId,
|
|
200
|
-
});
|
|
201
|
-
return (session.currentTurnUserId != null &&
|
|
202
|
-
String(session.currentTurnUserId) === String(userId));
|
|
203
|
-
}
|
|
204
|
-
/**
|
|
205
|
-
* Pass the turn to the next player via the platform's session-turn
|
|
206
|
-
* authority (current holder, host, or admin — enforced by the service),
|
|
207
|
-
* then ping the match channel so everyone re-pulls.
|
|
208
|
-
*
|
|
209
|
-
* For a match deployed with `turnTimer`, this opens the incoming turn first
|
|
210
|
-
* so it arrives with a deadline already running. Opening it before the
|
|
211
|
-
* handover also means the outgoing holder is still the session's turn user,
|
|
212
|
-
* which is what authorizes them to do it.
|
|
213
|
-
*/
|
|
214
|
-
async endTurn(match, nextUserId) {
|
|
215
|
-
if (match.turnSeq !== undefined)
|
|
216
|
-
await this.beginTurn(match);
|
|
217
|
-
const session = await this.gameModel.setSessionTurn({
|
|
218
|
-
appId: this.appId,
|
|
219
|
-
sessionId: match.sessionId,
|
|
220
|
-
userId: nextUserId,
|
|
221
|
-
});
|
|
222
|
-
await this.notifyChanged(match);
|
|
223
|
-
return session;
|
|
224
|
-
}
|
|
225
|
-
/**
|
|
226
|
-
* Open a turn and arm its deadline (`turnTimer` deployments only) — the
|
|
227
|
-
* deadline replaces any still pending for this match. `endTurn` calls this
|
|
228
|
-
* for you; call it directly to give the current player a fresh clock, e.g.
|
|
229
|
-
* after granting extra time.
|
|
230
|
-
*/
|
|
231
|
-
async beginTurn(match) {
|
|
232
|
-
return kitInvoke(this.gameModel, {
|
|
233
|
-
appId: String(this.appId),
|
|
234
|
-
functionName: this.names.beginTurnFn,
|
|
235
|
-
selfContainerId: match.metaId,
|
|
236
|
-
sessionId: match.sessionId,
|
|
237
|
-
params: {},
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
/**
|
|
241
|
-
* Cancel the pending turn deadline for this match, if any. Returns how many
|
|
242
|
-
* timers were dropped. Ending a match already strands its deadline, so this
|
|
243
|
-
* is only needed when you want turns to stop being timed while play
|
|
244
|
-
* continues.
|
|
245
|
-
*/
|
|
246
|
-
async cancelTurnDeadline(match) {
|
|
247
|
-
return this.gameModel.cancelTimer({
|
|
248
|
-
appId: this.appId,
|
|
249
|
-
dedupeKey: `match_turn:${match.metaId}`,
|
|
250
|
-
});
|
|
251
|
-
}
|
|
252
|
-
/** Find-or-create a player's session-scoped Score row. */
|
|
253
|
-
async ensureScore(match, ownerUserId) {
|
|
254
|
-
const scores = await this.gameModel.containers({
|
|
255
|
-
appId: this.appId,
|
|
256
|
-
typeName: this.names.scoreType,
|
|
257
|
-
sessionId: match.sessionId,
|
|
258
|
-
});
|
|
259
|
-
const mine = scores.find((c) => c.ownerUserId != null && String(c.ownerUserId) === String(ownerUserId));
|
|
260
|
-
if (mine)
|
|
261
|
-
return mine;
|
|
262
|
-
return this.gameModel.createContainer({
|
|
263
|
-
appId: this.appId,
|
|
264
|
-
typeName: this.names.scoreType,
|
|
265
|
-
displayName: `Score ${ownerUserId}`,
|
|
266
|
-
sessionId: match.sessionId,
|
|
267
|
-
properties: [
|
|
268
|
-
{ key: 'owner_user_id', valueType: 'int', valueJson: String(ownerUserId) },
|
|
269
|
-
],
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
/**
|
|
273
|
-
* Add points to a Score row — trusted (host-refereed by default).
|
|
274
|
-
* Resolves with the new points.
|
|
275
|
-
*/
|
|
276
|
-
async score(match, scoreId, points) {
|
|
277
|
-
return kitInvoke(this.gameModel, {
|
|
278
|
-
appId: String(this.appId),
|
|
279
|
-
functionName: this.names.scoreFn,
|
|
280
|
-
selfContainerId: scoreId,
|
|
281
|
-
sessionId: match.sessionId,
|
|
282
|
-
params: { points },
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
/** The match standings, highest points first (client-side sort). */
|
|
286
|
-
async standings(match) {
|
|
287
|
-
const scores = await this.gameModel.containers({
|
|
288
|
-
appId: this.appId,
|
|
289
|
-
typeName: this.names.scoreType,
|
|
290
|
-
sessionId: match.sessionId,
|
|
291
|
-
});
|
|
292
|
-
const rows = await Promise.all(scores.map(async (c) => {
|
|
293
|
-
const props = await kitContainerProperties(this.gameModel, String(this.appId), c.containerId);
|
|
294
|
-
return {
|
|
295
|
-
containerId: c.containerId,
|
|
296
|
-
ownerUserId: c.ownerUserId != null ? String(c.ownerUserId) : null,
|
|
297
|
-
points: Number(props.points ?? 0),
|
|
298
|
-
};
|
|
299
|
-
}));
|
|
300
|
-
return rows.sort((a, b) => b.points - a.points);
|
|
301
|
-
}
|
|
302
|
-
/**
|
|
303
|
-
* Finish the match and record the winner (creator or host). Also drops the
|
|
304
|
-
* pending turn deadline on a `turnTimer` match — `end_match` already
|
|
305
|
-
* strands it, so this just saves the pointless fire and channel ping.
|
|
306
|
-
*
|
|
307
|
-
* When `end_match` succeeds the backing session is ended too
|
|
308
|
-
* (`gameModelEndSession`, reason `completed`): every participant is marked
|
|
309
|
-
* left, admission closes, and the session's events become eligible for
|
|
310
|
-
* retention. The game-authoritative step runs first, so the match is
|
|
311
|
-
* decided whatever happens next; `sessionEnd` on the result says what
|
|
312
|
-
* happened to the session ({@link KitMatchSessionEnd}): `'ended'`, or
|
|
313
|
-
* `'already_ended'` for a replayed finish, or `'forbidden'` when the caller
|
|
314
|
-
* passed `end_match` (creator or the app's elected host) but is not admitted
|
|
315
|
-
* to the session end (session host or app admin only) -- the match is
|
|
316
|
-
* finished, the session is not, and nothing is thrown. Any other refusal of
|
|
317
|
-
* the session end propagates.
|
|
318
|
-
*/
|
|
319
|
-
async finish(match, winnerUserId) {
|
|
320
|
-
const result = await kitInvoke(this.gameModel, {
|
|
321
|
-
appId: String(this.appId),
|
|
322
|
-
functionName: this.names.endFn,
|
|
323
|
-
selfContainerId: match.metaId,
|
|
324
|
-
sessionId: match.sessionId,
|
|
325
|
-
params: { winner_user_id: Number(winnerUserId) },
|
|
326
|
-
});
|
|
327
|
-
if (result.success && match.turnSeq !== undefined) {
|
|
328
|
-
await this.cancelTurnDeadline(match);
|
|
329
|
-
}
|
|
330
|
-
if (result.success) {
|
|
331
|
-
try {
|
|
332
|
-
await this.gameModel.endSession({
|
|
333
|
-
appId: this.appId,
|
|
334
|
-
sessionId: match.sessionId,
|
|
335
|
-
reason: 'completed',
|
|
336
|
-
});
|
|
337
|
-
result.sessionEnd = 'ended';
|
|
338
|
-
}
|
|
339
|
-
catch (error) {
|
|
340
|
-
if (error instanceof CrowdyGraphQLError && error.code === 'SESSION_ENDED') {
|
|
341
|
-
result.sessionEnd = 'already_ended';
|
|
342
|
-
}
|
|
343
|
-
else if (error instanceof CrowdyGraphQLError && error.code === 'FORBIDDEN') {
|
|
344
|
-
result.sessionEnd = 'forbidden';
|
|
345
|
-
}
|
|
346
|
-
else {
|
|
347
|
-
throw error;
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
if (result.sessionEnd !== 'forbidden')
|
|
351
|
-
this.incarnations.delete(match.sessionId);
|
|
352
|
-
}
|
|
353
|
-
return result;
|
|
354
|
-
}
|
|
355
|
-
/**
|
|
356
|
-
* Manually ping the match channel with `"match_changed"` (the lifecycle
|
|
357
|
-
* functions do this automatically via their declared notifications; use
|
|
358
|
-
* this after out-of-band changes such as `endTurn`).
|
|
359
|
-
*/
|
|
360
|
-
async notifyChanged(match) {
|
|
361
|
-
if (match.channelId === '0' || match.channelId === '')
|
|
362
|
-
return false;
|
|
363
|
-
return this.requireUdp().sendChannelMessage({
|
|
364
|
-
channelId: match.channelId,
|
|
365
|
-
uuid: this.actorUuid,
|
|
366
|
-
payload: encodeBase64(new TextEncoder().encode('match_changed')),
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
/**
|
|
370
|
-
* The notify-to-pull loop, wrapped: subscribe to the app's notifications,
|
|
371
|
-
* and on every ping of THIS match's channel re-pull the match state and
|
|
372
|
-
* hand it to `callback`. Returns the unsubscribe function.
|
|
373
|
-
*/
|
|
374
|
-
onMatchChanged(match, callback) {
|
|
375
|
-
return this.requireUdp().subscribe({
|
|
376
|
-
channelMessage: (notification) => {
|
|
377
|
-
if (String(notification.channelId) !== String(match.channelId))
|
|
378
|
-
return;
|
|
379
|
-
void this.get(match.metaId).then(callback);
|
|
380
|
-
},
|
|
381
|
-
}, String(this.appId));
|
|
382
|
-
}
|
|
383
|
-
async toMatch(metaId, sessionId, displayName) {
|
|
384
|
-
const props = await kitContainerProperties(this.gameModel, String(this.appId), metaId);
|
|
385
|
-
return {
|
|
386
|
-
sessionId,
|
|
387
|
-
metaId,
|
|
388
|
-
displayName,
|
|
389
|
-
creatorUserId: Number(props.creator_user_id ?? 0),
|
|
390
|
-
mode: String(props.mode ?? ''),
|
|
391
|
-
state: String(props.state ?? ''),
|
|
392
|
-
round: Number(props.round ?? 0),
|
|
393
|
-
maxPlayers: Number(props.max_players ?? 0),
|
|
394
|
-
winnerUserId: Number(props.winner_user_id ?? 0),
|
|
395
|
-
channelId: String(props.channel_id ?? '0'),
|
|
396
|
-
...(props.tick_count !== undefined
|
|
397
|
-
? { tickCount: Number(props.tick_count) }
|
|
398
|
-
: {}),
|
|
399
|
-
...(props.turn_seq !== undefined ? { turnSeq: Number(props.turn_seq) } : {}),
|
|
400
|
-
...(props.turn_expired_seq !== undefined
|
|
401
|
-
? { turnExpiredSeq: Number(props.turn_expired_seq) }
|
|
402
|
-
: {}),
|
|
403
|
-
};
|
|
404
|
-
}
|
|
405
|
-
// -- Engine path (Wave 2): server-driven lifecycle over the match engine --
|
|
406
|
-
/**
|
|
407
|
-
* Is a match compute engine deployed + enabled (cached per session)? When
|
|
408
|
-
* true the engine owns transitions (ready checks, turn order + timeouts,
|
|
409
|
-
* authoritative scoring); the blueprint's creator-driven functions remain
|
|
410
|
-
* for model-only deployments.
|
|
411
|
-
*/
|
|
412
|
-
engineAvailable() {
|
|
413
|
-
if (!this.engines)
|
|
414
|
-
return Promise.resolve(false);
|
|
415
|
-
return this.engines.has(this.engineModuleName);
|
|
416
|
-
}
|
|
417
|
-
/**
|
|
418
|
-
* Declare ready on an engine match (a MatchMeta container id). The match
|
|
419
|
-
* starts server-side once every expected player is ready.
|
|
420
|
-
*/
|
|
421
|
-
async engineReady(matchId) {
|
|
422
|
-
return this.engineInvoke('ready', { matchId });
|
|
423
|
-
}
|
|
424
|
-
/** Submit your move (the engine validates the turn + resolves). */
|
|
425
|
-
async engineSubmitMove(matchId, params = {}) {
|
|
426
|
-
return this.engineInvoke('submit_move', { matchId, ...params });
|
|
427
|
-
}
|
|
428
|
-
/** Forfeit an engine match. */
|
|
429
|
-
async engineForfeit(matchId) {
|
|
430
|
-
return this.engineInvoke('forfeit', { matchId });
|
|
431
|
-
}
|
|
432
|
-
/** The engine's live view: turn holder, timers, standings, summary. */
|
|
433
|
-
async engineStatus(matchId) {
|
|
434
|
-
return this.engineInvoke('status', { matchId });
|
|
435
|
-
}
|
|
436
|
-
/**
|
|
437
|
-
* Resolve a matchmaking proposal to the match the engine created for it
|
|
438
|
-
* (poll after everyone accepts; see `kit.matchmaking`).
|
|
439
|
-
*/
|
|
440
|
-
async findByProposal(proposalId) {
|
|
441
|
-
if (!this.engines)
|
|
442
|
-
return null;
|
|
443
|
-
const result = await this.engines.invoke(this.engineModuleName, 'find_by_proposal', {
|
|
444
|
-
proposalId,
|
|
445
|
-
});
|
|
446
|
-
return result.success ? String(result.body.matchId ?? '') || null : null;
|
|
447
|
-
}
|
|
448
|
-
async engineInvoke(exportName, params) {
|
|
449
|
-
if (!this.engines) {
|
|
450
|
-
throw new Error('match engine unavailable: compute domain not wired');
|
|
451
|
-
}
|
|
452
|
-
const result = await this.engines.invoke(this.engineModuleName, exportName, params);
|
|
453
|
-
if (!result.success) {
|
|
454
|
-
throw new Error(`matches.${exportName} failed: ${result.reason ?? 'unknown'}`);
|
|
455
|
-
}
|
|
456
|
-
return result.body;
|
|
457
|
-
}
|
|
458
|
-
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import type { Scalars } from '../generated/graphql.js';
|
|
2
|
-
import type { EngineDetector } from './engine.js';
|
|
3
|
-
/** Options for {@link MatchmakingKit}. */
|
|
4
|
-
export interface MatchmakingKitOptions {
|
|
5
|
-
/** The matchmaking module name. Defaults to `'matchmaking'`. */
|
|
6
|
-
moduleName?: string;
|
|
7
|
-
}
|
|
8
|
-
/** Your queue status: where you are queued and any live proposal. */
|
|
9
|
-
export interface KitQueueStatus {
|
|
10
|
-
queuedIn: string | null;
|
|
11
|
-
proposal: {
|
|
12
|
-
proposalId: string;
|
|
13
|
-
mode: string;
|
|
14
|
-
players: string[];
|
|
15
|
-
accepted: string[];
|
|
16
|
-
handedOff: boolean;
|
|
17
|
-
} | null;
|
|
18
|
-
queueDepth: number;
|
|
19
|
-
rating: number;
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Runtime helpers for the matchmaking engine (Wave 2): rating-bucketed
|
|
23
|
-
* queues with widening windows, party blocks, and accept-gated proposals
|
|
24
|
-
* that hand off to the match layer over compute events. After everyone
|
|
25
|
-
* accepts, resolve the created match with `kit.matches.findByProposal`.
|
|
26
|
-
*
|
|
27
|
-
* Obtained via `client.kit(appId).matchmaking`.
|
|
28
|
-
*/
|
|
29
|
-
export declare class MatchmakingKit {
|
|
30
|
-
private readonly engines;
|
|
31
|
-
private readonly moduleName;
|
|
32
|
-
constructor(_appId: Scalars['BigInt']['input'], engines: EngineDetector, options?: MatchmakingKitOptions);
|
|
33
|
-
/** Is the matchmaking engine deployed + enabled (cached per session)? */
|
|
34
|
-
engineAvailable(): Promise<boolean>;
|
|
35
|
-
/**
|
|
36
|
-
* Join a queue (optionally with a party block that must match together,
|
|
37
|
-
* and an explicit rating for games that keep rating on the progression
|
|
38
|
-
* layer).
|
|
39
|
-
*/
|
|
40
|
-
queueJoin(input?: {
|
|
41
|
-
mode?: string;
|
|
42
|
-
rating?: number;
|
|
43
|
-
party?: string[];
|
|
44
|
-
}): Promise<Record<string, unknown>>;
|
|
45
|
-
/** Leave your queue (all modes, or one with `mode`). */
|
|
46
|
-
queueLeave(mode?: string): Promise<Record<string, unknown>>;
|
|
47
|
-
/** Your queue/proposal status. */
|
|
48
|
-
queueStatus(mode?: string): Promise<KitQueueStatus>;
|
|
49
|
-
/** Accept a proposal; when everyone has, the match handoff fires. */
|
|
50
|
-
accept(proposalId: string): Promise<Record<string, unknown>>;
|
|
51
|
-
/** Report the decided result (Elo-lite rating update + proposal close). */
|
|
52
|
-
reportResult(proposalId: string, winnerUserId: string): Promise<Record<string, unknown>>;
|
|
53
|
-
/** Engine totals (queues/proposals/rated players). */
|
|
54
|
-
status(): Promise<Record<string, unknown>>;
|
|
55
|
-
private invoke;
|
|
56
|
-
}
|
|
57
|
-
//# sourceMappingURL=matchmaking.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"matchmaking.d.ts","sourceRoot":"","sources":["../../src/kit/matchmaking.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,0CAA0C;AAC1C,MAAM,WAAW,qBAAqB;IACpC,gEAAgE;IAChE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,qEAAqE;AACrE,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE;QACR,UAAU,EAAE,MAAM,CAAC;QACnB,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;QACnB,SAAS,EAAE,OAAO,CAAC;KACpB,GAAG,IAAI,CAAC;IACT,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;GAOG;AACH,qBAAa,cAAc;IAKvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAJ1B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;gBAGlC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,EACjB,OAAO,EAAE,cAAc,EACxC,OAAO,GAAE,qBAA0B;IAKrC,yEAAyE;IACzE,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC;IAInC;;;;OAIG;IACG,SAAS,CAAC,KAAK,GAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;KAAO;IAIhF,wDAAwD;IAClD,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM;IAI9B,kCAAkC;IAC5B,WAAW,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAUzD,qEAAqE;IAC/D,MAAM,CAAC,UAAU,EAAE,MAAM;IAI/B,2EAA2E;IACrE,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM;IAI3D,sDAAsD;IAChD,MAAM;YAIE,MAAM;CAOrB"}
|
package/dist/kit/matchmaking.js
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Runtime helpers for the matchmaking engine (Wave 2): rating-bucketed
|
|
3
|
-
* queues with widening windows, party blocks, and accept-gated proposals
|
|
4
|
-
* that hand off to the match layer over compute events. After everyone
|
|
5
|
-
* accepts, resolve the created match with `kit.matches.findByProposal`.
|
|
6
|
-
*
|
|
7
|
-
* Obtained via `client.kit(appId).matchmaking`.
|
|
8
|
-
*/
|
|
9
|
-
export class MatchmakingKit {
|
|
10
|
-
constructor(_appId, engines, options = {}) {
|
|
11
|
-
this.engines = engines;
|
|
12
|
-
this.moduleName = options.moduleName ?? 'matchmaking';
|
|
13
|
-
}
|
|
14
|
-
/** Is the matchmaking engine deployed + enabled (cached per session)? */
|
|
15
|
-
engineAvailable() {
|
|
16
|
-
return this.engines.has(this.moduleName);
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Join a queue (optionally with a party block that must match together,
|
|
20
|
-
* and an explicit rating for games that keep rating on the progression
|
|
21
|
-
* layer).
|
|
22
|
-
*/
|
|
23
|
-
async queueJoin(input = {}) {
|
|
24
|
-
return this.invoke('queue_join', input);
|
|
25
|
-
}
|
|
26
|
-
/** Leave your queue (all modes, or one with `mode`). */
|
|
27
|
-
async queueLeave(mode) {
|
|
28
|
-
return this.invoke('queue_leave', mode ? { mode } : {});
|
|
29
|
-
}
|
|
30
|
-
/** Your queue/proposal status. */
|
|
31
|
-
async queueStatus(mode) {
|
|
32
|
-
const body = await this.invoke('queue_status', mode ? { mode } : {});
|
|
33
|
-
return {
|
|
34
|
-
queuedIn: body.queuedIn != null ? String(body.queuedIn) : null,
|
|
35
|
-
proposal: body.proposal ?? null,
|
|
36
|
-
queueDepth: Number(body.queueDepth ?? 0),
|
|
37
|
-
rating: Number(body.rating ?? 0),
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
/** Accept a proposal; when everyone has, the match handoff fires. */
|
|
41
|
-
async accept(proposalId) {
|
|
42
|
-
return this.invoke('accept', { proposalId });
|
|
43
|
-
}
|
|
44
|
-
/** Report the decided result (Elo-lite rating update + proposal close). */
|
|
45
|
-
async reportResult(proposalId, winnerUserId) {
|
|
46
|
-
return this.invoke('report_result', { proposalId, winnerUserId });
|
|
47
|
-
}
|
|
48
|
-
/** Engine totals (queues/proposals/rated players). */
|
|
49
|
-
async status() {
|
|
50
|
-
return this.invoke('status', {});
|
|
51
|
-
}
|
|
52
|
-
async invoke(exportName, params) {
|
|
53
|
-
const result = await this.engines.invoke(this.moduleName, exportName, params);
|
|
54
|
-
if (!result.success) {
|
|
55
|
-
throw new Error(`matchmaking.${exportName} failed: ${result.reason ?? 'unknown'}`);
|
|
56
|
-
}
|
|
57
|
-
return result.body;
|
|
58
|
-
}
|
|
59
|
-
}
|
package/dist/kit/minigames.d.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import type { Scalars } from '../generated/graphql.js';
|
|
2
|
-
import type { EngineDetector, EngineInvokeResult } from './engine.js';
|
|
3
|
-
/** Options for {@link MinigamesKit}. */
|
|
4
|
-
export interface MinigamesKitOptions {
|
|
5
|
-
/** Default module when calls omit one (e.g. your scaffolded game). */
|
|
6
|
-
defaultModuleName?: string;
|
|
7
|
-
}
|
|
8
|
-
/**
|
|
9
|
-
* A thin invoke wrapper for invoke-loop minigames (the Wave 2 `minigame`
|
|
10
|
-
* scaffold pattern): synchronous `play` RPCs with server-bound caller
|
|
11
|
-
* identity, server-held secrets, and per-caller records. Every method
|
|
12
|
-
* resolves the engine envelope — denials come back as
|
|
13
|
-
* `{success: false, reason}` rather than throwing.
|
|
14
|
-
*
|
|
15
|
-
* Obtained via `client.kit(appId).minigames`.
|
|
16
|
-
*/
|
|
17
|
-
export declare class MinigamesKit {
|
|
18
|
-
private readonly engines;
|
|
19
|
-
private readonly defaultModuleName?;
|
|
20
|
-
constructor(_appId: Scalars['BigInt']['input'], engines: EngineDetector, options?: MinigamesKitOptions);
|
|
21
|
-
/** Is a minigame module deployed + enabled (cached per session)? */
|
|
22
|
-
engineAvailable(moduleName?: string): Promise<boolean>;
|
|
23
|
-
/** One round: invoke the game's `play` export with your move. */
|
|
24
|
-
play(params: Record<string, unknown>, moduleName?: string): Promise<EngineInvokeResult>;
|
|
25
|
-
/** Your per-caller record. */
|
|
26
|
-
record(moduleName?: string): Promise<EngineInvokeResult>;
|
|
27
|
-
/** Any other export the game defines (typed result envelope). */
|
|
28
|
-
invoke(exportName: string, params?: Record<string, unknown>, moduleName?: string): Promise<EngineInvokeResult>;
|
|
29
|
-
}
|
|
30
|
-
//# sourceMappingURL=minigames.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"minigames.d.ts","sourceRoot":"","sources":["../../src/kit/minigames.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtE,wCAAwC;AACxC,MAAM,WAAW,mBAAmB;IAClC,sEAAsE;IACtE,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;;GAQG;AACH,qBAAa,YAAY;IAKrB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAJ1B,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAS;gBAG1C,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,EACjB,OAAO,EAAE,cAAc,EACxC,OAAO,GAAE,mBAAwB;IAKnC,oEAAoE;IACpE,eAAe,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAMtD,iEAAiE;IAC3D,IAAI,CACR,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,kBAAkB,CAAC;IAI9B,8BAA8B;IACxB,MAAM,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAI9D,iEAAiE;IAC3D,MAAM,CACV,UAAU,EAAE,MAAM,EAClB,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACpC,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,kBAAkB,CAAC;CAO/B"}
|
package/dist/kit/minigames.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A thin invoke wrapper for invoke-loop minigames (the Wave 2 `minigame`
|
|
3
|
-
* scaffold pattern): synchronous `play` RPCs with server-bound caller
|
|
4
|
-
* identity, server-held secrets, and per-caller records. Every method
|
|
5
|
-
* resolves the engine envelope — denials come back as
|
|
6
|
-
* `{success: false, reason}` rather than throwing.
|
|
7
|
-
*
|
|
8
|
-
* Obtained via `client.kit(appId).minigames`.
|
|
9
|
-
*/
|
|
10
|
-
export class MinigamesKit {
|
|
11
|
-
constructor(_appId, engines, options = {}) {
|
|
12
|
-
this.engines = engines;
|
|
13
|
-
this.defaultModuleName = options.defaultModuleName;
|
|
14
|
-
}
|
|
15
|
-
/** Is a minigame module deployed + enabled (cached per session)? */
|
|
16
|
-
engineAvailable(moduleName) {
|
|
17
|
-
const name = moduleName ?? this.defaultModuleName;
|
|
18
|
-
if (!name)
|
|
19
|
-
return Promise.resolve(false);
|
|
20
|
-
return this.engines.has(name);
|
|
21
|
-
}
|
|
22
|
-
/** One round: invoke the game's `play` export with your move. */
|
|
23
|
-
async play(params, moduleName) {
|
|
24
|
-
return this.invoke('play', params, moduleName);
|
|
25
|
-
}
|
|
26
|
-
/** Your per-caller record. */
|
|
27
|
-
async record(moduleName) {
|
|
28
|
-
return this.invoke('record', {}, moduleName);
|
|
29
|
-
}
|
|
30
|
-
/** Any other export the game defines (typed result envelope). */
|
|
31
|
-
async invoke(exportName, params = {}, moduleName) {
|
|
32
|
-
const name = moduleName ?? this.defaultModuleName;
|
|
33
|
-
if (!name) {
|
|
34
|
-
return { success: false, reason: 'no minigame module configured', body: {} };
|
|
35
|
-
}
|
|
36
|
-
return this.engines.invoke(name, exportName, params);
|
|
37
|
-
}
|
|
38
|
-
}
|