@crowdedkingdoms/crowdyjs 8.7.0 → 8.8.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.
Files changed (42) hide show
  1. package/MIGRATION.md +42 -0
  2. package/README.md +2 -2
  3. package/dist/index.d.ts +2 -2
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +2 -2
  6. package/dist/kit/decks.d.ts +33 -1
  7. package/dist/kit/decks.d.ts.map +1 -1
  8. package/dist/kit/decks.js +49 -1
  9. package/dist/kit/director.d.ts +88 -0
  10. package/dist/kit/director.d.ts.map +1 -0
  11. package/dist/kit/director.js +86 -0
  12. package/dist/kit/economy.d.ts +46 -1
  13. package/dist/kit/economy.d.ts.map +1 -1
  14. package/dist/kit/economy.js +63 -1
  15. package/dist/kit/index.d.ts +6 -2
  16. package/dist/kit/index.d.ts.map +1 -1
  17. package/dist/kit/index.js +6 -2
  18. package/dist/kit/instances.d.ts +52 -0
  19. package/dist/kit/instances.d.ts.map +1 -0
  20. package/dist/kit/instances.js +60 -0
  21. package/dist/kit/kit.d.ts +16 -0
  22. package/dist/kit/kit.d.ts.map +1 -1
  23. package/dist/kit/kit.js +12 -4
  24. package/dist/kit/leaderboards.d.ts +21 -1
  25. package/dist/kit/leaderboards.d.ts.map +1 -1
  26. package/dist/kit/leaderboards.js +40 -1
  27. package/dist/kit/matches.d.ts +33 -1
  28. package/dist/kit/matches.d.ts.map +1 -1
  29. package/dist/kit/matches.js +56 -1
  30. package/dist/kit/matchmaking.d.ts +57 -0
  31. package/dist/kit/matchmaking.d.ts.map +1 -0
  32. package/dist/kit/matchmaking.js +59 -0
  33. package/dist/kit/minigames.d.ts +30 -0
  34. package/dist/kit/minigames.d.ts.map +1 -0
  35. package/dist/kit/minigames.js +38 -0
  36. package/dist/kit/quests.d.ts +57 -0
  37. package/dist/kit/quests.d.ts.map +1 -1
  38. package/dist/kit/quests.js +72 -0
  39. package/dist/kit/wire.d.ts +37 -0
  40. package/dist/kit/wire.d.ts.map +1 -1
  41. package/dist/kit/wire.js +50 -0
  42. package/package.json +1 -1
@@ -0,0 +1,57 @@
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
@@ -0,0 +1 @@
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"}
@@ -0,0 +1,59 @@
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
+ }
@@ -0,0 +1,30 @@
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
@@ -0,0 +1 @@
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"}
@@ -0,0 +1,38 @@
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
+ }
@@ -87,6 +87,63 @@ export declare class QuestsKit {
87
87
  }>;
88
88
  /** List a player's quest progress rows. */
89
89
  mine(ownerUserId: Scalars['BigInt']['input']): Promise<KitQuestProgress[]>;
90
+ /**
91
+ * STUDIO (admin) — define an ordered tutorial chain as quest defs. Steps
92
+ * are plain quests whose `questId` encodes the chain + index
93
+ * (`"<chain>:<i>"`), so no new server surface is involved: the sequencing
94
+ * is a read-side convention enforced by {@link tutorial} /
95
+ * {@link acceptNextTutorialStep} (a step is `locked` until every earlier
96
+ * step completes).
97
+ */
98
+ defineTutorial(input: {
99
+ /** Chain id (one app can ship several tutorials). Defaults to `'ftue'`. */
100
+ chain?: string;
101
+ steps: Array<{
102
+ displayName: string;
103
+ targetCount?: number;
104
+ rewardItemId?: string;
105
+ rewardQty?: number;
106
+ rewardGold?: number;
107
+ }>;
108
+ }): Promise<{
109
+ __typename?: "GmContainer";
110
+ containerId: string;
111
+ appId: string;
112
+ sessionId: string | null;
113
+ typeName: string;
114
+ displayName: string;
115
+ description: string | null;
116
+ ownerUserId: string | null;
117
+ metadataJson: string;
118
+ }[]>;
119
+ /** One tutorial step joined with the player's progress. */
120
+ /**
121
+ * A player's view of a tutorial chain: steps in order, each `locked`
122
+ * (an earlier step is incomplete), `active` (the first incomplete step),
123
+ * or `complete`. The client shows/drives only the `active` step; the
124
+ * trusted advance authority is unchanged (players still cannot complete
125
+ * their own quests).
126
+ */
127
+ tutorial(ownerUserId: Scalars['BigInt']['input'], chain?: string): Promise<Array<{
128
+ stepIndex: number;
129
+ def: KitQuestDef;
130
+ progress: KitQuestProgress | null;
131
+ status: 'locked' | 'active' | 'complete';
132
+ }>>;
133
+ /**
134
+ * Ensure the player's ACTIVE tutorial step has a progress row (accepting
135
+ * it when missing) and return the step. Returns null when the chain is
136
+ * complete. Calling this for a locked step is impossible by construction —
137
+ * it always targets the first incomplete step.
138
+ */
139
+ acceptNextTutorialStep(ownerUserId: Scalars['BigInt']['input'], chain?: string, options?: {
140
+ sessionId?: string;
141
+ }): Promise<{
142
+ stepIndex: number;
143
+ def: KitQuestDef;
144
+ progress: KitQuestProgress | null;
145
+ status: "locked" | "active" | "complete";
146
+ } | null>;
90
147
  /** Read one progress row. */
91
148
  state(progressId: string): Promise<KitQuestProgress>;
92
149
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"quests.d.ts","sourceRoot":"","sources":["../../src/kit/quests.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"quests.d.ts","sourceRoot":"","sources":["../../src/kit/quests.ts"],"names":[],"mappings":"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"}
@@ -106,6 +106,78 @@ export class QuestsKit {
106
106
  const mine = containers.filter((c) => c.ownerUserId != null && String(c.ownerUserId) === String(ownerUserId));
107
107
  return Promise.all(mine.map((c) => this.state(c.containerId)));
108
108
  }
109
+ // -- FTUE / tutorial step sequencing (Wave 2) -----------------------------
110
+ /**
111
+ * STUDIO (admin) — define an ordered tutorial chain as quest defs. Steps
112
+ * are plain quests whose `questId` encodes the chain + index
113
+ * (`"<chain>:<i>"`), so no new server surface is involved: the sequencing
114
+ * is a read-side convention enforced by {@link tutorial} /
115
+ * {@link acceptNextTutorialStep} (a step is `locked` until every earlier
116
+ * step completes).
117
+ */
118
+ async defineTutorial(input) {
119
+ const chain = input.chain ?? 'ftue';
120
+ const created = [];
121
+ for (const [index, step] of input.steps.entries()) {
122
+ created.push(await this.defineQuest({
123
+ questId: `${chain}:${index}`,
124
+ displayName: step.displayName,
125
+ targetCount: step.targetCount ?? 1,
126
+ rewardItemId: step.rewardItemId,
127
+ rewardQty: step.rewardQty,
128
+ rewardGold: step.rewardGold,
129
+ }));
130
+ }
131
+ return created;
132
+ }
133
+ /** One tutorial step joined with the player's progress. */
134
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars -- documented shape
135
+ /**
136
+ * A player's view of a tutorial chain: steps in order, each `locked`
137
+ * (an earlier step is incomplete), `active` (the first incomplete step),
138
+ * or `complete`. The client shows/drives only the `active` step; the
139
+ * trusted advance authority is unchanged (players still cannot complete
140
+ * their own quests).
141
+ */
142
+ async tutorial(ownerUserId, chain = 'ftue') {
143
+ const prefix = `${chain}:`;
144
+ const defs = (await this.catalog())
145
+ .filter((def) => def.questId.startsWith(prefix))
146
+ .map((def) => ({ def, stepIndex: Number(def.questId.slice(prefix.length)) }))
147
+ .filter(({ stepIndex }) => Number.isFinite(stepIndex))
148
+ .sort((a, b) => a.stepIndex - b.stepIndex);
149
+ const progressRows = await this.mine(ownerUserId);
150
+ let blocked = false;
151
+ return defs.map(({ def, stepIndex }) => {
152
+ const progress = progressRows.find((p) => p.questId === def.questId) ?? null;
153
+ const complete = progress?.completed === true;
154
+ const status = complete
155
+ ? 'complete'
156
+ : blocked
157
+ ? 'locked'
158
+ : 'active';
159
+ if (!complete)
160
+ blocked = true;
161
+ return { stepIndex, def, progress, status };
162
+ });
163
+ }
164
+ /**
165
+ * Ensure the player's ACTIVE tutorial step has a progress row (accepting
166
+ * it when missing) and return the step. Returns null when the chain is
167
+ * complete. Calling this for a locked step is impossible by construction —
168
+ * it always targets the first incomplete step.
169
+ */
170
+ async acceptNextTutorialStep(ownerUserId, chain = 'ftue', options = {}) {
171
+ const steps = await this.tutorial(ownerUserId, chain);
172
+ const active = steps.find((step) => step.status === 'active');
173
+ if (!active)
174
+ return null;
175
+ if (active.progress)
176
+ return active;
177
+ await this.accept(ownerUserId, active.def.containerId, options);
178
+ const refreshed = await this.tutorial(ownerUserId, chain);
179
+ return refreshed.find((step) => step.stepIndex === active.stepIndex) ?? null;
180
+ }
109
181
  /** Read one progress row. */
110
182
  async state(progressId) {
111
183
  const container = await this.gameModel.container({
@@ -60,6 +60,12 @@ export declare function engineLanes(): Record<string, (state: EnginePose) => boo
60
60
  export declare const EVENT_CONTACT_DAMAGE = 77;
61
61
  /** Weather/season transition from a world engine (kit-sim weather). */
62
62
  export declare const EVENT_WEATHER = 90;
63
+ /** Turn changed (kit-play turns; match engines). */
64
+ export declare const EVENT_TURN = 91;
65
+ /** Score / match summary (kit-play score). */
66
+ export declare const EVENT_SCORE = 92;
67
+ /** Match proposal (matchmaking → matches handoff). */
68
+ export declare const EVENT_PROPOSAL = 93;
63
69
  /** Split an engine server-event payload into its type + JSON body. */
64
70
  export declare function parseEngineEvent(bytes: Uint8Array): {
65
71
  eventType: number;
@@ -84,4 +90,35 @@ export interface WeatherEvent {
84
90
  }
85
91
  /** Parse a weather event; null when the payload is another type. */
86
92
  export declare function parseWeatherEvent(bytes: Uint8Array): WeatherEvent | null;
93
+ /** A parsed type-91 turn-changed event (match engines). */
94
+ export interface TurnEvent {
95
+ actorId: string;
96
+ round: number;
97
+ turnInRound: number;
98
+ body: Record<string, unknown>;
99
+ }
100
+ /** Parse a turn event; null when the payload is another type. */
101
+ export declare function parseTurnEvent(bytes: Uint8Array): TurnEvent | null;
102
+ /** A parsed type-92 score/summary event (match engines). */
103
+ export interface ScoreEvent {
104
+ /** Per-actor standings when present. */
105
+ standings: Array<{
106
+ actorId: string;
107
+ score: number;
108
+ rank: number;
109
+ }>;
110
+ winnerId: string | null;
111
+ body: Record<string, unknown>;
112
+ }
113
+ /** Parse a score event; null when the payload is another type. */
114
+ export declare function parseScoreEvent(bytes: Uint8Array): ScoreEvent | null;
115
+ /** A parsed type-93 match-proposal event (matchmaking handoff). */
116
+ export interface ProposalEvent {
117
+ proposalId: string;
118
+ mode: string;
119
+ players: string[];
120
+ body: Record<string, unknown>;
121
+ }
122
+ /** Parse a proposal event; null when the payload is another type. */
123
+ export declare function parseProposalEvent(bytes: Uint8Array): ProposalEvent | null;
87
124
  //# sourceMappingURL=wire.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"wire.d.ts","sourceRoot":"","sources":["../../src/kit/wire.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,eAAO,MAAM,UAAU,KAAK,CAAC;AAE7B,4EAA4E;AAC5E,eAAO,MAAM,aAAa,IAAS,CAAC;AACpC,eAAO,MAAM,QAAQ,IAAS,CAAC;AAC/B,eAAO,MAAM,QAAQ,IAAS,CAAC;AAC/B,eAAO,MAAM,cAAc,IAAS,CAAC;AAErC,uEAAuE;AACvE,MAAM,WAAW,UAAU;IACzB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,0EAA0E;IAC1E,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,gDAAgD;AAChD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,UAAU,CAiBtE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU,GAAG,IAAI,CAqBrE;AAED,sEAAsE;AACtE,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAI3D;AAED;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,UAAU,CAAC,UAAU,CAiBlD,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC,CAM5E;AAMD,wEAAwE;AACxE,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,uEAAuE;AACvE,eAAO,MAAM,aAAa,KAAK,CAAC;AAEhC,sEAAsE;AACtE,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,UAAU,GAChB;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAAG,IAAI,CAY7D;AAED,6CAA6C;AAC7C,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,2EAA2E;AAC3E,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,GAAG,kBAAkB,GAAG,IAAI,CAS/E;AAED,iDAAiD;AACjD,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,sEAAsE;IACtE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED,oEAAoE;AACpE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,UAAU,GAAG,YAAY,GAAG,IAAI,CASxE"}
1
+ {"version":3,"file":"wire.d.ts","sourceRoot":"","sources":["../../src/kit/wire.ts"],"names":[],"mappings":"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"}
package/dist/kit/wire.js CHANGED
@@ -114,6 +114,12 @@ export function engineLanes() {
114
114
  export const EVENT_CONTACT_DAMAGE = 77;
115
115
  /** Weather/season transition from a world engine (kit-sim weather). */
116
116
  export const EVENT_WEATHER = 90;
117
+ /** Turn changed (kit-play turns; match engines). */
118
+ export const EVENT_TURN = 91;
119
+ /** Score / match summary (kit-play score). */
120
+ export const EVENT_SCORE = 92;
121
+ /** Match proposal (matchmaking → matches handoff). */
122
+ export const EVENT_PROPOSAL = 93;
117
123
  /** Split an engine server-event payload into its type + JSON body. */
118
124
  export function parseEngineEvent(bytes) {
119
125
  if (bytes.length < 2)
@@ -154,3 +160,47 @@ export function parseWeatherEvent(bytes) {
154
160
  body: parsed.body,
155
161
  };
156
162
  }
163
+ /** Parse a turn event; null when the payload is another type. */
164
+ export function parseTurnEvent(bytes) {
165
+ const parsed = parseEngineEvent(bytes);
166
+ if (!parsed || parsed.eventType !== EVENT_TURN)
167
+ return null;
168
+ return {
169
+ actorId: String(parsed.body.actorId ?? ''),
170
+ round: Number(parsed.body.round ?? 0),
171
+ turnInRound: Number(parsed.body.turnInRound ?? 0),
172
+ body: parsed.body,
173
+ };
174
+ }
175
+ /** Parse a score event; null when the payload is another type. */
176
+ export function parseScoreEvent(bytes) {
177
+ const parsed = parseEngineEvent(bytes);
178
+ if (!parsed || parsed.eventType !== EVENT_SCORE)
179
+ return null;
180
+ const standings = Array.isArray(parsed.body.standings)
181
+ ? parsed.body.standings.map((s) => ({
182
+ actorId: String(s.actorId ?? ''),
183
+ score: Number(s.score ?? 0),
184
+ rank: Number(s.rank ?? 0),
185
+ }))
186
+ : [];
187
+ return {
188
+ standings,
189
+ winnerId: parsed.body.winnerId != null ? String(parsed.body.winnerId) : null,
190
+ body: parsed.body,
191
+ };
192
+ }
193
+ /** Parse a proposal event; null when the payload is another type. */
194
+ export function parseProposalEvent(bytes) {
195
+ const parsed = parseEngineEvent(bytes);
196
+ if (!parsed || parsed.eventType !== EVENT_PROPOSAL)
197
+ return null;
198
+ return {
199
+ proposalId: String(parsed.body.proposalId ?? ''),
200
+ mode: String(parsed.body.mode ?? ''),
201
+ players: Array.isArray(parsed.body.players)
202
+ ? parsed.body.players.map(String)
203
+ : [],
204
+ body: parsed.body,
205
+ };
206
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crowdedkingdoms/crowdyjs",
3
- "version": "8.7.0",
3
+ "version": "8.8.0",
4
4
  "description": "Client SDK for Crowded Kingdoms GraphQL API with UDP proxy support",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",