@thegeem/protocol 0.1.17 → 0.1.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -41,12 +41,14 @@ var LIMITS = {
41
41
  MAX_PLAYERS: 12,
42
42
  MAX_MSG_PER_SEC: 8
43
43
  };
44
- var PROTOCOL_VERSION = 2;
44
+ var PROTOCOL_VERSION = 3;
45
45
  var MIN_SUPPORTED_PV = 1;
46
- var GAME_IDS = ["seedha", "snag", "trivia"];
46
+ var GAME_IDS = ["seedha", "snag", "trivia", "dama"];
47
47
  var GAMES = [
48
48
  { id: "seedha", label: "Seedha", minPv: 2 },
49
49
  { id: "snag", label: "Snag", minPv: 2 },
50
+ { id: "dama", label: "Dama", minPv: 3 },
51
+ // engine #2 (D-027) — offered only to pv≥3 clients
50
52
  { id: "trivia", label: "Trivia", minPv: 1, deprecated: true }
51
53
  ];
52
54
  var gamesForPv = (pv) => GAMES.filter((g) => !g.deprecated && g.minPv <= pv);
@@ -85,7 +87,7 @@ var Avatar = import_zod.z.object({
85
87
  });
86
88
  var ClientMsg = import_zod.z.discriminatedUnion("t", [
87
89
  // room / lobby
88
- import_zod.z.object({ t: import_zod.z.literal("createRoom"), name: DisplayName.optional(), avatar: Avatar.optional(), pv: Pv, idToken: IdToken, platform: Platform }),
90
+ import_zod.z.object({ t: import_zod.z.literal("createRoom"), name: DisplayName.optional(), avatar: Avatar.optional(), game: import_zod.z.enum(GAME_IDS).optional(), pv: Pv, idToken: IdToken, platform: Platform }),
89
91
  // Host sets/changes their display name AFTER createRoom (for anonymous one-tap
90
92
  // hosts who started nameless). Host-only; the relay re-broadcasts the lobby.
91
93
  import_zod.z.object({ t: import_zod.z.literal("renameHost"), name: DisplayName }),
@@ -116,7 +118,8 @@ var ClientMsg = import_zod.z.discriminatedUnion("t", [
116
118
  game: import_zod.z.enum(GAME_IDS).optional(),
117
119
  // which game to play; defaults to 'trivia'
118
120
  totalRounds: import_zod.z.number().int().min(1).max(50),
119
- categoryIds: import_zod.z.array(import_zod.z.string().max(60)).min(1).max(12),
121
+ categoryIds: import_zod.z.array(import_zod.z.string().max(60)).min(0).max(12),
122
+ // empty for games without categories (Dama); trivia rejects empty in-handler
120
123
  // PREFERRED: pick a mode from the registry (see MODES). The server maps it to the engine
121
124
  // rules. Send this and omit buzzerMode/noHost. Falls back to the legacy flags below if omitted.
122
125
  modeId: import_zod.z.enum(MODE_IDS).optional(),
@@ -153,7 +156,19 @@ var ClientMsg = import_zod.z.discriminatedUnion("t", [
153
156
  // Report the CURRENT question as bad (wrong answer / typo / offensive). The relay resolves WHICH
154
157
  // question from its authoritative room state — the client never sees the question id (anti-cheat),
155
158
  // so it only sends an optional `reason`. Only meaningful while a question is on screen.
156
- import_zod.z.object({ t: import_zod.z.literal("reportQuestion"), reason: import_zod.z.string().max(500).optional() })
159
+ import_zod.z.object({ t: import_zod.z.literal("reportQuestion"), reason: import_zod.z.string().max(500).optional() }),
160
+ // ── Dama slice (gameId 'dama', engine #2 — D-027) ──
161
+ // Make a Dama move: from → to (board coords, 0–7). The relay validates legality against the engine
162
+ // (Shape B) and rejects anything not in legalMoves. If two capture chains share from+to (rare), the
163
+ // relay picks the max-capture one; a client MAY send the full landing `path` to disambiguate.
164
+ import_zod.z.object({
165
+ t: import_zod.z.literal("damaMove"),
166
+ from: import_zod.z.object({ r: import_zod.z.number().int().min(0).max(7), c: import_zod.z.number().int().min(0).max(7) }),
167
+ to: import_zod.z.object({ r: import_zod.z.number().int().min(0).max(7), c: import_zod.z.number().int().min(0).max(7) }),
168
+ path: import_zod.z.array(import_zod.z.object({ r: import_zod.z.number().int().min(0).max(7), c: import_zod.z.number().int().min(0).max(7) })).max(12).optional()
169
+ }),
170
+ // Resign the current Dama game (concede) → opponent wins.
171
+ import_zod.z.object({ t: import_zod.z.literal("damaResign") })
157
172
  ]);
158
173
  // Annotate the CommonJS export names for ESM import in node:
159
174
  0 && (module.exports = {
package/dist/index.d.cts CHANGED
@@ -33,7 +33,7 @@ declare const LIMITS: {
33
33
  * `seedha`/`snag`). Raise the floor SLOWLY to retire ancient builds. Unversioned clients (no `pv`,
34
34
  * e.g. the legacy web harness) are treated as the floor and never rejected.
35
35
  */
36
- declare const PROTOCOL_VERSION = 2;
36
+ declare const PROTOCOL_VERSION = 3;
37
37
  declare const MIN_SUPPORTED_PV = 1;
38
38
  /**
39
39
  * Geem is a PLATFORM of GAMES (jackbox-style). The trivia board split (D-023) into TWO player-facing
@@ -42,7 +42,7 @@ declare const MIN_SUPPORTED_PV = 1;
42
42
  * pre-pv2 clients (they never saw seedha/snag), and what old clients send on startGame. Every room
43
43
  * runs one game, identified by `gameId`.
44
44
  */
45
- declare const GAME_IDS: readonly ["seedha", "snag", "trivia"];
45
+ declare const GAME_IDS: readonly ["seedha", "snag", "trivia", "dama"];
46
46
  type GameId = (typeof GAME_IDS)[number];
47
47
  /**
48
48
  * The GAME REGISTRY — player-facing games + the version gating (D-024/D-025). A client renders a tile
@@ -96,6 +96,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
96
96
  emoji: string;
97
97
  color: string;
98
98
  }>>;
99
+ game: z.ZodOptional<z.ZodEnum<["seedha", "snag", "trivia", "dama"]>>;
99
100
  pv: z.ZodOptional<z.ZodNumber>;
100
101
  idToken: z.ZodOptional<z.ZodString>;
101
102
  platform: z.ZodOptional<z.ZodEnum<["web", "ios", "android"]>>;
@@ -106,6 +107,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
106
107
  emoji: string;
107
108
  color: string;
108
109
  } | undefined;
110
+ game?: "seedha" | "snag" | "trivia" | "dama" | undefined;
109
111
  pv?: number | undefined;
110
112
  idToken?: string | undefined;
111
113
  platform?: "web" | "ios" | "android" | undefined;
@@ -116,6 +118,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
116
118
  emoji: string;
117
119
  color: string;
118
120
  } | undefined;
121
+ game?: "seedha" | "snag" | "trivia" | "dama" | undefined;
119
122
  pv?: number | undefined;
120
123
  idToken?: string | undefined;
121
124
  platform?: "web" | "ios" | "android" | undefined;
@@ -286,7 +289,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
286
289
  activity: "configuring" | "idle";
287
290
  }>, z.ZodObject<{
288
291
  t: z.ZodLiteral<"startGame">;
289
- game: z.ZodOptional<z.ZodEnum<["seedha", "snag", "trivia"]>>;
292
+ game: z.ZodOptional<z.ZodEnum<["seedha", "snag", "trivia", "dama"]>>;
290
293
  totalRounds: z.ZodNumber;
291
294
  categoryIds: z.ZodArray<z.ZodString, "many">;
292
295
  modeId: z.ZodOptional<z.ZodEnum<["solo", "oneDevice", "snag", "multiplayer"]>>;
@@ -296,16 +299,16 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
296
299
  t: "startGame";
297
300
  categoryIds: string[];
298
301
  totalRounds: number;
302
+ game?: "seedha" | "snag" | "trivia" | "dama" | undefined;
299
303
  modeId?: "snag" | "solo" | "oneDevice" | "multiplayer" | undefined;
300
- game?: "seedha" | "snag" | "trivia" | undefined;
301
304
  buzzerMode?: "solo" | "off" | "race" | "controller" | undefined;
302
305
  noHost?: boolean | undefined;
303
306
  }, {
304
307
  t: "startGame";
305
308
  categoryIds: string[];
306
309
  totalRounds: number;
310
+ game?: "seedha" | "snag" | "trivia" | "dama" | undefined;
307
311
  modeId?: "snag" | "solo" | "oneDevice" | "multiplayer" | undefined;
308
- game?: "seedha" | "snag" | "trivia" | undefined;
309
312
  buzzerMode?: "solo" | "off" | "race" | "controller" | undefined;
310
313
  noHost?: boolean | undefined;
311
314
  }>, z.ZodObject<{
@@ -419,6 +422,72 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
419
422
  }, {
420
423
  t: "reportQuestion";
421
424
  reason?: string | undefined;
425
+ }>, z.ZodObject<{
426
+ t: z.ZodLiteral<"damaMove">;
427
+ from: z.ZodObject<{
428
+ r: z.ZodNumber;
429
+ c: z.ZodNumber;
430
+ }, "strip", z.ZodTypeAny, {
431
+ r: number;
432
+ c: number;
433
+ }, {
434
+ r: number;
435
+ c: number;
436
+ }>;
437
+ to: z.ZodObject<{
438
+ r: z.ZodNumber;
439
+ c: z.ZodNumber;
440
+ }, "strip", z.ZodTypeAny, {
441
+ r: number;
442
+ c: number;
443
+ }, {
444
+ r: number;
445
+ c: number;
446
+ }>;
447
+ path: z.ZodOptional<z.ZodArray<z.ZodObject<{
448
+ r: z.ZodNumber;
449
+ c: z.ZodNumber;
450
+ }, "strip", z.ZodTypeAny, {
451
+ r: number;
452
+ c: number;
453
+ }, {
454
+ r: number;
455
+ c: number;
456
+ }>, "many">>;
457
+ }, "strip", z.ZodTypeAny, {
458
+ t: "damaMove";
459
+ from: {
460
+ r: number;
461
+ c: number;
462
+ };
463
+ to: {
464
+ r: number;
465
+ c: number;
466
+ };
467
+ path?: {
468
+ r: number;
469
+ c: number;
470
+ }[] | undefined;
471
+ }, {
472
+ t: "damaMove";
473
+ from: {
474
+ r: number;
475
+ c: number;
476
+ };
477
+ to: {
478
+ r: number;
479
+ c: number;
480
+ };
481
+ path?: {
482
+ r: number;
483
+ c: number;
484
+ }[] | undefined;
485
+ }>, z.ZodObject<{
486
+ t: z.ZodLiteral<"damaResign">;
487
+ }, "strip", z.ZodTypeAny, {
488
+ t: "damaResign";
489
+ }, {
490
+ t: "damaResign";
422
491
  }>]>;
423
492
  type ClientMsg = z.infer<typeof ClientMsg>;
424
493
  /** A player's privacy-safe avatar (emoji + circle color) for in-room display. */
@@ -574,6 +643,36 @@ type Effect = {
574
643
  } | {
575
644
  t: 'endGame';
576
645
  };
646
+ /** A Dama piece as clients see it. */
647
+ interface DamaPieceView {
648
+ color: 'black' | 'white';
649
+ king: boolean;
650
+ }
651
+ /** A legal Dama move offered to the player to move (for highlighting). `captures` = pieces it takes. */
652
+ interface DamaMoveView {
653
+ from: {
654
+ r: number;
655
+ c: number;
656
+ };
657
+ to: {
658
+ r: number;
659
+ c: number;
660
+ };
661
+ captures: number;
662
+ path: {
663
+ r: number;
664
+ c: number;
665
+ }[];
666
+ }
667
+ /** The authoritative Dama board pushed to a client. `board[r][c]` is null on empty/light squares; r=0 is the top. */
668
+ interface DamaStateView {
669
+ board: (DamaPieceView | null)[][];
670
+ turn: 'black' | 'white';
671
+ winner: 'black' | 'white' | null;
672
+ yourColor: 'black' | 'white' | null;
673
+ legalMoves: DamaMoveView[];
674
+ moveCount: number;
675
+ }
577
676
  type ServerMsg = {
578
677
  t: 'roomCreated';
579
678
  code: string;
@@ -618,6 +717,11 @@ type ServerMsg = {
618
717
  view: GameStateView;
619
718
  you: YouContext;
620
719
  teamConnected: boolean[];
720
+ } | {
721
+ t: 'damaState';
722
+ gameId: 'dama';
723
+ view: DamaStateView;
724
+ you: YouContext;
621
725
  } | {
622
726
  t: 'fx';
623
727
  effects: Effect[];
@@ -634,4 +738,4 @@ type ServerMsg = {
634
738
  message: string;
635
739
  };
636
740
 
637
- export { type AvatarView, type BuzzerGameMode, type CategoryView, ClientMsg, type Effect, GAMES, GAME_IDS, type GameId, type GameInfo, type GameStateView, type GameStatus, type GameplayFlow, type HelpName, LIMITS, type LobbyPlayer, MIN_SUPPORTED_PV, MODES, MODE_IDS, type ModeId, type ModeInfo, PROTOCOL_VERSION, type QuestionView, ROOM_ALPHABET, type ServerMsg, type SoundName, type TeamSlotView, type TeamView, type YouContext, gameIdForPv, gamesForPv, modesForGame };
741
+ export { type AvatarView, type BuzzerGameMode, type CategoryView, ClientMsg, type DamaMoveView, type DamaPieceView, type DamaStateView, type Effect, GAMES, GAME_IDS, type GameId, type GameInfo, type GameStateView, type GameStatus, type GameplayFlow, type HelpName, LIMITS, type LobbyPlayer, MIN_SUPPORTED_PV, MODES, MODE_IDS, type ModeId, type ModeInfo, PROTOCOL_VERSION, type QuestionView, ROOM_ALPHABET, type ServerMsg, type SoundName, type TeamSlotView, type TeamView, type YouContext, gameIdForPv, gamesForPv, modesForGame };
package/dist/index.d.ts CHANGED
@@ -33,7 +33,7 @@ declare const LIMITS: {
33
33
  * `seedha`/`snag`). Raise the floor SLOWLY to retire ancient builds. Unversioned clients (no `pv`,
34
34
  * e.g. the legacy web harness) are treated as the floor and never rejected.
35
35
  */
36
- declare const PROTOCOL_VERSION = 2;
36
+ declare const PROTOCOL_VERSION = 3;
37
37
  declare const MIN_SUPPORTED_PV = 1;
38
38
  /**
39
39
  * Geem is a PLATFORM of GAMES (jackbox-style). The trivia board split (D-023) into TWO player-facing
@@ -42,7 +42,7 @@ declare const MIN_SUPPORTED_PV = 1;
42
42
  * pre-pv2 clients (they never saw seedha/snag), and what old clients send on startGame. Every room
43
43
  * runs one game, identified by `gameId`.
44
44
  */
45
- declare const GAME_IDS: readonly ["seedha", "snag", "trivia"];
45
+ declare const GAME_IDS: readonly ["seedha", "snag", "trivia", "dama"];
46
46
  type GameId = (typeof GAME_IDS)[number];
47
47
  /**
48
48
  * The GAME REGISTRY — player-facing games + the version gating (D-024/D-025). A client renders a tile
@@ -96,6 +96,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
96
96
  emoji: string;
97
97
  color: string;
98
98
  }>>;
99
+ game: z.ZodOptional<z.ZodEnum<["seedha", "snag", "trivia", "dama"]>>;
99
100
  pv: z.ZodOptional<z.ZodNumber>;
100
101
  idToken: z.ZodOptional<z.ZodString>;
101
102
  platform: z.ZodOptional<z.ZodEnum<["web", "ios", "android"]>>;
@@ -106,6 +107,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
106
107
  emoji: string;
107
108
  color: string;
108
109
  } | undefined;
110
+ game?: "seedha" | "snag" | "trivia" | "dama" | undefined;
109
111
  pv?: number | undefined;
110
112
  idToken?: string | undefined;
111
113
  platform?: "web" | "ios" | "android" | undefined;
@@ -116,6 +118,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
116
118
  emoji: string;
117
119
  color: string;
118
120
  } | undefined;
121
+ game?: "seedha" | "snag" | "trivia" | "dama" | undefined;
119
122
  pv?: number | undefined;
120
123
  idToken?: string | undefined;
121
124
  platform?: "web" | "ios" | "android" | undefined;
@@ -286,7 +289,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
286
289
  activity: "configuring" | "idle";
287
290
  }>, z.ZodObject<{
288
291
  t: z.ZodLiteral<"startGame">;
289
- game: z.ZodOptional<z.ZodEnum<["seedha", "snag", "trivia"]>>;
292
+ game: z.ZodOptional<z.ZodEnum<["seedha", "snag", "trivia", "dama"]>>;
290
293
  totalRounds: z.ZodNumber;
291
294
  categoryIds: z.ZodArray<z.ZodString, "many">;
292
295
  modeId: z.ZodOptional<z.ZodEnum<["solo", "oneDevice", "snag", "multiplayer"]>>;
@@ -296,16 +299,16 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
296
299
  t: "startGame";
297
300
  categoryIds: string[];
298
301
  totalRounds: number;
302
+ game?: "seedha" | "snag" | "trivia" | "dama" | undefined;
299
303
  modeId?: "snag" | "solo" | "oneDevice" | "multiplayer" | undefined;
300
- game?: "seedha" | "snag" | "trivia" | undefined;
301
304
  buzzerMode?: "solo" | "off" | "race" | "controller" | undefined;
302
305
  noHost?: boolean | undefined;
303
306
  }, {
304
307
  t: "startGame";
305
308
  categoryIds: string[];
306
309
  totalRounds: number;
310
+ game?: "seedha" | "snag" | "trivia" | "dama" | undefined;
307
311
  modeId?: "snag" | "solo" | "oneDevice" | "multiplayer" | undefined;
308
- game?: "seedha" | "snag" | "trivia" | undefined;
309
312
  buzzerMode?: "solo" | "off" | "race" | "controller" | undefined;
310
313
  noHost?: boolean | undefined;
311
314
  }>, z.ZodObject<{
@@ -419,6 +422,72 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
419
422
  }, {
420
423
  t: "reportQuestion";
421
424
  reason?: string | undefined;
425
+ }>, z.ZodObject<{
426
+ t: z.ZodLiteral<"damaMove">;
427
+ from: z.ZodObject<{
428
+ r: z.ZodNumber;
429
+ c: z.ZodNumber;
430
+ }, "strip", z.ZodTypeAny, {
431
+ r: number;
432
+ c: number;
433
+ }, {
434
+ r: number;
435
+ c: number;
436
+ }>;
437
+ to: z.ZodObject<{
438
+ r: z.ZodNumber;
439
+ c: z.ZodNumber;
440
+ }, "strip", z.ZodTypeAny, {
441
+ r: number;
442
+ c: number;
443
+ }, {
444
+ r: number;
445
+ c: number;
446
+ }>;
447
+ path: z.ZodOptional<z.ZodArray<z.ZodObject<{
448
+ r: z.ZodNumber;
449
+ c: z.ZodNumber;
450
+ }, "strip", z.ZodTypeAny, {
451
+ r: number;
452
+ c: number;
453
+ }, {
454
+ r: number;
455
+ c: number;
456
+ }>, "many">>;
457
+ }, "strip", z.ZodTypeAny, {
458
+ t: "damaMove";
459
+ from: {
460
+ r: number;
461
+ c: number;
462
+ };
463
+ to: {
464
+ r: number;
465
+ c: number;
466
+ };
467
+ path?: {
468
+ r: number;
469
+ c: number;
470
+ }[] | undefined;
471
+ }, {
472
+ t: "damaMove";
473
+ from: {
474
+ r: number;
475
+ c: number;
476
+ };
477
+ to: {
478
+ r: number;
479
+ c: number;
480
+ };
481
+ path?: {
482
+ r: number;
483
+ c: number;
484
+ }[] | undefined;
485
+ }>, z.ZodObject<{
486
+ t: z.ZodLiteral<"damaResign">;
487
+ }, "strip", z.ZodTypeAny, {
488
+ t: "damaResign";
489
+ }, {
490
+ t: "damaResign";
422
491
  }>]>;
423
492
  type ClientMsg = z.infer<typeof ClientMsg>;
424
493
  /** A player's privacy-safe avatar (emoji + circle color) for in-room display. */
@@ -574,6 +643,36 @@ type Effect = {
574
643
  } | {
575
644
  t: 'endGame';
576
645
  };
646
+ /** A Dama piece as clients see it. */
647
+ interface DamaPieceView {
648
+ color: 'black' | 'white';
649
+ king: boolean;
650
+ }
651
+ /** A legal Dama move offered to the player to move (for highlighting). `captures` = pieces it takes. */
652
+ interface DamaMoveView {
653
+ from: {
654
+ r: number;
655
+ c: number;
656
+ };
657
+ to: {
658
+ r: number;
659
+ c: number;
660
+ };
661
+ captures: number;
662
+ path: {
663
+ r: number;
664
+ c: number;
665
+ }[];
666
+ }
667
+ /** The authoritative Dama board pushed to a client. `board[r][c]` is null on empty/light squares; r=0 is the top. */
668
+ interface DamaStateView {
669
+ board: (DamaPieceView | null)[][];
670
+ turn: 'black' | 'white';
671
+ winner: 'black' | 'white' | null;
672
+ yourColor: 'black' | 'white' | null;
673
+ legalMoves: DamaMoveView[];
674
+ moveCount: number;
675
+ }
577
676
  type ServerMsg = {
578
677
  t: 'roomCreated';
579
678
  code: string;
@@ -618,6 +717,11 @@ type ServerMsg = {
618
717
  view: GameStateView;
619
718
  you: YouContext;
620
719
  teamConnected: boolean[];
720
+ } | {
721
+ t: 'damaState';
722
+ gameId: 'dama';
723
+ view: DamaStateView;
724
+ you: YouContext;
621
725
  } | {
622
726
  t: 'fx';
623
727
  effects: Effect[];
@@ -634,4 +738,4 @@ type ServerMsg = {
634
738
  message: string;
635
739
  };
636
740
 
637
- export { type AvatarView, type BuzzerGameMode, type CategoryView, ClientMsg, type Effect, GAMES, GAME_IDS, type GameId, type GameInfo, type GameStateView, type GameStatus, type GameplayFlow, type HelpName, LIMITS, type LobbyPlayer, MIN_SUPPORTED_PV, MODES, MODE_IDS, type ModeId, type ModeInfo, PROTOCOL_VERSION, type QuestionView, ROOM_ALPHABET, type ServerMsg, type SoundName, type TeamSlotView, type TeamView, type YouContext, gameIdForPv, gamesForPv, modesForGame };
741
+ export { type AvatarView, type BuzzerGameMode, type CategoryView, ClientMsg, type DamaMoveView, type DamaPieceView, type DamaStateView, type Effect, GAMES, GAME_IDS, type GameId, type GameInfo, type GameStateView, type GameStatus, type GameplayFlow, type HelpName, LIMITS, type LobbyPlayer, MIN_SUPPORTED_PV, MODES, MODE_IDS, type ModeId, type ModeInfo, PROTOCOL_VERSION, type QuestionView, ROOM_ALPHABET, type ServerMsg, type SoundName, type TeamSlotView, type TeamView, type YouContext, gameIdForPv, gamesForPv, modesForGame };
package/dist/index.js CHANGED
@@ -6,12 +6,14 @@ var LIMITS = {
6
6
  MAX_PLAYERS: 12,
7
7
  MAX_MSG_PER_SEC: 8
8
8
  };
9
- var PROTOCOL_VERSION = 2;
9
+ var PROTOCOL_VERSION = 3;
10
10
  var MIN_SUPPORTED_PV = 1;
11
- var GAME_IDS = ["seedha", "snag", "trivia"];
11
+ var GAME_IDS = ["seedha", "snag", "trivia", "dama"];
12
12
  var GAMES = [
13
13
  { id: "seedha", label: "Seedha", minPv: 2 },
14
14
  { id: "snag", label: "Snag", minPv: 2 },
15
+ { id: "dama", label: "Dama", minPv: 3 },
16
+ // engine #2 (D-027) — offered only to pv≥3 clients
15
17
  { id: "trivia", label: "Trivia", minPv: 1, deprecated: true }
16
18
  ];
17
19
  var gamesForPv = (pv) => GAMES.filter((g) => !g.deprecated && g.minPv <= pv);
@@ -50,7 +52,7 @@ var Avatar = z.object({
50
52
  });
51
53
  var ClientMsg = z.discriminatedUnion("t", [
52
54
  // room / lobby
53
- z.object({ t: z.literal("createRoom"), name: DisplayName.optional(), avatar: Avatar.optional(), pv: Pv, idToken: IdToken, platform: Platform }),
55
+ z.object({ t: z.literal("createRoom"), name: DisplayName.optional(), avatar: Avatar.optional(), game: z.enum(GAME_IDS).optional(), pv: Pv, idToken: IdToken, platform: Platform }),
54
56
  // Host sets/changes their display name AFTER createRoom (for anonymous one-tap
55
57
  // hosts who started nameless). Host-only; the relay re-broadcasts the lobby.
56
58
  z.object({ t: z.literal("renameHost"), name: DisplayName }),
@@ -81,7 +83,8 @@ var ClientMsg = z.discriminatedUnion("t", [
81
83
  game: z.enum(GAME_IDS).optional(),
82
84
  // which game to play; defaults to 'trivia'
83
85
  totalRounds: z.number().int().min(1).max(50),
84
- categoryIds: z.array(z.string().max(60)).min(1).max(12),
86
+ categoryIds: z.array(z.string().max(60)).min(0).max(12),
87
+ // empty for games without categories (Dama); trivia rejects empty in-handler
85
88
  // PREFERRED: pick a mode from the registry (see MODES). The server maps it to the engine
86
89
  // rules. Send this and omit buzzerMode/noHost. Falls back to the legacy flags below if omitted.
87
90
  modeId: z.enum(MODE_IDS).optional(),
@@ -118,7 +121,19 @@ var ClientMsg = z.discriminatedUnion("t", [
118
121
  // Report the CURRENT question as bad (wrong answer / typo / offensive). The relay resolves WHICH
119
122
  // question from its authoritative room state — the client never sees the question id (anti-cheat),
120
123
  // so it only sends an optional `reason`. Only meaningful while a question is on screen.
121
- z.object({ t: z.literal("reportQuestion"), reason: z.string().max(500).optional() })
124
+ z.object({ t: z.literal("reportQuestion"), reason: z.string().max(500).optional() }),
125
+ // ── Dama slice (gameId 'dama', engine #2 — D-027) ──
126
+ // Make a Dama move: from → to (board coords, 0–7). The relay validates legality against the engine
127
+ // (Shape B) and rejects anything not in legalMoves. If two capture chains share from+to (rare), the
128
+ // relay picks the max-capture one; a client MAY send the full landing `path` to disambiguate.
129
+ z.object({
130
+ t: z.literal("damaMove"),
131
+ from: z.object({ r: z.number().int().min(0).max(7), c: z.number().int().min(0).max(7) }),
132
+ to: z.object({ r: z.number().int().min(0).max(7), c: z.number().int().min(0).max(7) }),
133
+ path: z.array(z.object({ r: z.number().int().min(0).max(7), c: z.number().int().min(0).max(7) })).max(12).optional()
134
+ }),
135
+ // Resign the current Dama game (concede) → opponent wins.
136
+ z.object({ t: z.literal("damaResign") })
122
137
  ]);
123
138
  export {
124
139
  ClientMsg,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thegeem/protocol",
3
- "version": "0.1.17",
3
+ "version": "0.1.19",
4
4
  "description": "Geem's wire protocol — shared zod schemas + TypeScript types for talking to the Geem game server over Socket.IO. The source of truth all clients implement against.",
5
5
  "license": "UNLICENSED",
6
6
  "homepage": "https://geem.tv",