@thegeem/protocol 0.1.24 → 0.1.26
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 +22 -4
- package/dist/index.d.cts +48 -1
- package/dist/index.d.ts +48 -1
- package/dist/index.js +20 -4
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -20,7 +20,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
CHARACTER_IDS: () => CHARACTER_IDS,
|
|
23
24
|
ClientMsg: () => ClientMsg,
|
|
25
|
+
EMOTE_IDS: () => EMOTE_IDS,
|
|
24
26
|
GAMES: () => GAMES,
|
|
25
27
|
GAME_IDS: () => GAME_IDS,
|
|
26
28
|
LIMITS: () => LIMITS,
|
|
@@ -69,6 +71,8 @@ var MODES = [
|
|
|
69
71
|
{ id: "snag", gameId: "snag", label: "Snag", onlineEligible: true }
|
|
70
72
|
];
|
|
71
73
|
var modesForGame = (gameId) => MODES.filter((m) => m.gameId === gameId);
|
|
74
|
+
var EMOTE_IDS = ["laugh", "wow", "think", "gg"];
|
|
75
|
+
var CHARACTER_IDS = ["m", "f"];
|
|
72
76
|
var ROOM_ALPHABET = "ACDEFGHJKMNPQRTUVWXY2346789";
|
|
73
77
|
var UNSAFE_CHARS = /[\p{Cc}<>]/gu;
|
|
74
78
|
var DisplayName = import_zod.z.string().trim().min(1).max(LIMITS.MAX_NAME_LEN).transform((s) => s.replace(UNSAFE_CHARS, "").trim()).refine((s) => s.length >= 1, "name is empty after sanitizing");
|
|
@@ -90,9 +94,10 @@ var Avatar = import_zod.z.object({
|
|
|
90
94
|
emoji: import_zod.z.string().min(1).max(16).transform((s) => s.replace(UNSAFE_CHARS, "")).refine((s) => s.length >= 1, "empty emoji"),
|
|
91
95
|
color: import_zod.z.string().regex(/^#[0-9a-fA-F]{6}$/, "color must be #rrggbb")
|
|
92
96
|
});
|
|
97
|
+
var Character = import_zod.z.enum(CHARACTER_IDS);
|
|
93
98
|
var ClientMsg = import_zod.z.discriminatedUnion("t", [
|
|
94
99
|
// room / lobby
|
|
95
|
-
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 }),
|
|
100
|
+
import_zod.z.object({ t: import_zod.z.literal("createRoom"), name: DisplayName.optional(), avatar: Avatar.optional(), game: import_zod.z.enum(GAME_IDS).optional(), character: Character.optional(), pv: Pv, idToken: IdToken, platform: Platform }),
|
|
96
101
|
// Host sets/changes their display name AFTER createRoom (for anonymous one-tap
|
|
97
102
|
// hosts who started nameless). Host-only; the relay re-broadcasts the lobby.
|
|
98
103
|
// SUPERSEDED by setProfile (kept working for shipped builds).
|
|
@@ -105,9 +110,9 @@ var ClientMsg = import_zod.z.discriminatedUnion("t", [
|
|
|
105
110
|
// Note: mid-game propagation depends on what the running game's state carries — trivia's
|
|
106
111
|
// view carries names+avatars so it updates live; dama/ersimha views are id-keyed (clients
|
|
107
112
|
// map ids→names from the lobby roster), so a change there lands on the next lobby.
|
|
108
|
-
import_zod.z.object({ t: import_zod.z.literal("setProfile"), name: DisplayName.optional(), avatar: Avatar.optional() }),
|
|
109
|
-
import_zod.z.object({ t: import_zod.z.literal("joinRoom"), code: RoomCode, name: DisplayName, avatar: Avatar.optional(), pv: Pv, idToken: IdToken, platform: Platform }),
|
|
110
|
-
import_zod.z.object({ t: import_zod.z.literal("resume"), code: RoomCode, token: import_zod.z.string().max(120), avatar: Avatar.optional(), pv: Pv, idToken: IdToken, platform: Platform }),
|
|
113
|
+
import_zod.z.object({ t: import_zod.z.literal("setProfile"), name: DisplayName.optional(), avatar: Avatar.optional(), character: Character.optional() }),
|
|
114
|
+
import_zod.z.object({ t: import_zod.z.literal("joinRoom"), code: RoomCode, name: DisplayName, avatar: Avatar.optional(), character: Character.optional(), pv: Pv, idToken: IdToken, platform: Platform }),
|
|
115
|
+
import_zod.z.object({ t: import_zod.z.literal("resume"), code: RoomCode, token: import_zod.z.string().max(120), avatar: Avatar.optional(), character: Character.optional(), pv: Pv, idToken: IdToken, platform: Platform }),
|
|
111
116
|
import_zod.z.object({ t: import_zod.z.literal("leaveRoom") }),
|
|
112
117
|
import_zod.z.object({ t: import_zod.z.literal("approve"), playerId: PlayerId }),
|
|
113
118
|
import_zod.z.object({ t: import_zod.z.literal("reject"), playerId: PlayerId }),
|
|
@@ -150,6 +155,12 @@ var ClientMsg = import_zod.z.discriminatedUnion("t", [
|
|
|
150
155
|
// false = the first two approved joiners take the board and the host WATCHES
|
|
151
156
|
// (yourColor:null) while keeping every host power. Needs 2 approved joiners.
|
|
152
157
|
hostPlays: import_zod.z.boolean().optional(),
|
|
158
|
+
// Name the seats explicitly instead of taking them by join order (gs#47 Ask 2, layered on per
|
|
159
|
+
// D-037). Exactly two DISTINCT ids, both approved members of the room (the host may be one):
|
|
160
|
+
// `players[0]` plays black and moves first, `players[1]` plays white; everyone else in the room
|
|
161
|
+
// spectates. When present this OVERRIDES `hostPlays` — an explicit seating beats an implicit one.
|
|
162
|
+
// Violations are rejected with `bad_players`. Consumed by دامة (the only fixed-seat game today).
|
|
163
|
+
players: import_zod.z.array(PlayerId).length(2).optional(),
|
|
153
164
|
// PREFERRED: pick a mode from the registry (see MODES). The server maps it to the engine
|
|
154
165
|
// rules. Send this and omit buzzerMode/noHost. Falls back to the legacy flags below if omitted.
|
|
155
166
|
modeId: import_zod.z.enum(MODE_IDS).optional(),
|
|
@@ -173,6 +184,11 @@ var ClientMsg = import_zod.z.discriminatedUnion("t", [
|
|
|
173
184
|
import_zod.z.object({ t: import_zod.z.literal("noHostNobody") }),
|
|
174
185
|
// player actions
|
|
175
186
|
import_zod.z.object({ t: import_zod.z.literal("buzz") }),
|
|
187
|
+
// «طقطقة» (D-038): send one of the four closed emotes. Server-enforced 5s per-player throttle on
|
|
188
|
+
// its OWN rate lane — a client-side throttle is trivially bypassed, and this is a family app, so
|
|
189
|
+
// Shape B applies exactly as it does to buzz arrival order. Over-rate emotes are dropped silently.
|
|
190
|
+
// Ephemeral: relayed, never stored. Allowed only while a game whose driver permits emotes is running.
|
|
191
|
+
import_zod.z.object({ t: import_zod.z.literal("sendEmote"), emoteId: import_zod.z.enum(EMOTE_IDS) }),
|
|
176
192
|
import_zod.z.object({ t: import_zod.z.literal("answer"), index: import_zod.z.number().int().min(0).max(3) }),
|
|
177
193
|
// trivia is 4-option (0–3)
|
|
178
194
|
import_zod.z.object({ t: import_zod.z.literal("useHelp"), helpType: HelpType }),
|
|
@@ -223,7 +239,9 @@ var ClientMsg = import_zod.z.discriminatedUnion("t", [
|
|
|
223
239
|
]);
|
|
224
240
|
// Annotate the CommonJS export names for ESM import in node:
|
|
225
241
|
0 && (module.exports = {
|
|
242
|
+
CHARACTER_IDS,
|
|
226
243
|
ClientMsg,
|
|
244
|
+
EMOTE_IDS,
|
|
227
245
|
GAMES,
|
|
228
246
|
GAME_IDS,
|
|
229
247
|
LIMITS,
|
package/dist/index.d.cts
CHANGED
|
@@ -83,6 +83,24 @@ interface ModeInfo {
|
|
|
83
83
|
declare const MODES: readonly ModeInfo[];
|
|
84
84
|
/** The modes available for a given game (for a client's mode picker). */
|
|
85
85
|
declare const modesForGame: (gameId: GameId) => ModeInfo[];
|
|
86
|
+
/**
|
|
87
|
+
* «طقطقة» (tagtaga) emotes — D-038 (filed as D-036 in gs#50), design `geem-design DESIGN.md § tagtaga`.
|
|
88
|
+
* A CLOSED, POSITIVE-ONLY set of exactly four: ههههه · يا سلام · قاعد أفكر · لعب زين. The ids match the
|
|
89
|
+
* art masters (`art/tagtaga/Tagtaga-<emoteId>-<character>.png`), so the wire addresses the sprite directly.
|
|
90
|
+
* ⚠ Never add hurry-up / gloat / crying / angry — that's a RULING, not an oversight: «think» exists so a
|
|
91
|
+
* slow player can speak about THEMSELVES rather than anyone pressuring anyone. A closed enum with no
|
|
92
|
+
* free-form payload is also what keeps emotes off the UGC/moderation surface entirely.
|
|
93
|
+
*/
|
|
94
|
+
declare const EMOTE_IDS: readonly ["laugh", "wow", "think", "gg"];
|
|
95
|
+
type EmoteId = (typeof EMOTE_IDS)[number];
|
|
96
|
+
/**
|
|
97
|
+
* Which character sprite a player is drawn as: `m` = ghutra, `f` = hijab (the `-m`/`-f` art suffix).
|
|
98
|
+
* Both carry the identical emote set. It's a display preference, not identity: set it on room entry
|
|
99
|
+
* or change it any time via `setProfile`, and the server stamps it on that player's outgoing emotes
|
|
100
|
+
* so receivers can pick the right sprite without a roster lookup.
|
|
101
|
+
*/
|
|
102
|
+
declare const CHARACTER_IDS: readonly ["m", "f"];
|
|
103
|
+
type CharacterId = (typeof CHARACTER_IDS)[number];
|
|
86
104
|
/** Unambiguous room-code alphabet — no 0/O, 1/I/L. */
|
|
87
105
|
declare const ROOM_ALPHABET = "ACDEFGHJKMNPQRTUVWXY2346789";
|
|
88
106
|
declare const HelpType: z.ZodEnum<["removeTwoAnswers", "changeQuestion", "extraTime", "doublePoints", "stealPoints", "restPlayer", "tripLevel"]>;
|
|
@@ -100,6 +118,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
|
|
|
100
118
|
color: string;
|
|
101
119
|
}>>;
|
|
102
120
|
game: z.ZodOptional<z.ZodEnum<["seedha", "snag", "trivia", "dama", "ersimha"]>>;
|
|
121
|
+
character: z.ZodOptional<z.ZodEnum<["m", "f"]>>;
|
|
103
122
|
pv: z.ZodOptional<z.ZodNumber>;
|
|
104
123
|
idToken: z.ZodOptional<z.ZodString>;
|
|
105
124
|
platform: z.ZodOptional<z.ZodEnum<["web", "ios", "android"]>>;
|
|
@@ -111,6 +130,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
|
|
|
111
130
|
color: string;
|
|
112
131
|
} | undefined;
|
|
113
132
|
game?: "seedha" | "snag" | "trivia" | "dama" | "ersimha" | undefined;
|
|
133
|
+
character?: "m" | "f" | undefined;
|
|
114
134
|
pv?: number | undefined;
|
|
115
135
|
idToken?: string | undefined;
|
|
116
136
|
platform?: "web" | "ios" | "android" | undefined;
|
|
@@ -122,6 +142,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
|
|
|
122
142
|
color: string;
|
|
123
143
|
} | undefined;
|
|
124
144
|
game?: "seedha" | "snag" | "trivia" | "dama" | "ersimha" | undefined;
|
|
145
|
+
character?: "m" | "f" | undefined;
|
|
125
146
|
pv?: number | undefined;
|
|
126
147
|
idToken?: string | undefined;
|
|
127
148
|
platform?: "web" | "ios" | "android" | undefined;
|
|
@@ -147,6 +168,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
|
|
|
147
168
|
emoji: string;
|
|
148
169
|
color: string;
|
|
149
170
|
}>>;
|
|
171
|
+
character: z.ZodOptional<z.ZodEnum<["m", "f"]>>;
|
|
150
172
|
}, "strip", z.ZodTypeAny, {
|
|
151
173
|
t: "setProfile";
|
|
152
174
|
name?: string | undefined;
|
|
@@ -154,6 +176,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
|
|
|
154
176
|
emoji: string;
|
|
155
177
|
color: string;
|
|
156
178
|
} | undefined;
|
|
179
|
+
character?: "m" | "f" | undefined;
|
|
157
180
|
}, {
|
|
158
181
|
t: "setProfile";
|
|
159
182
|
name?: string | undefined;
|
|
@@ -161,6 +184,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
|
|
|
161
184
|
emoji: string;
|
|
162
185
|
color: string;
|
|
163
186
|
} | undefined;
|
|
187
|
+
character?: "m" | "f" | undefined;
|
|
164
188
|
}>, z.ZodObject<{
|
|
165
189
|
t: z.ZodLiteral<"joinRoom">;
|
|
166
190
|
code: z.ZodString;
|
|
@@ -175,6 +199,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
|
|
|
175
199
|
emoji: string;
|
|
176
200
|
color: string;
|
|
177
201
|
}>>;
|
|
202
|
+
character: z.ZodOptional<z.ZodEnum<["m", "f"]>>;
|
|
178
203
|
pv: z.ZodOptional<z.ZodNumber>;
|
|
179
204
|
idToken: z.ZodOptional<z.ZodString>;
|
|
180
205
|
platform: z.ZodOptional<z.ZodEnum<["web", "ios", "android"]>>;
|
|
@@ -186,6 +211,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
|
|
|
186
211
|
emoji: string;
|
|
187
212
|
color: string;
|
|
188
213
|
} | undefined;
|
|
214
|
+
character?: "m" | "f" | undefined;
|
|
189
215
|
pv?: number | undefined;
|
|
190
216
|
idToken?: string | undefined;
|
|
191
217
|
platform?: "web" | "ios" | "android" | undefined;
|
|
@@ -197,6 +223,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
|
|
|
197
223
|
emoji: string;
|
|
198
224
|
color: string;
|
|
199
225
|
} | undefined;
|
|
226
|
+
character?: "m" | "f" | undefined;
|
|
200
227
|
pv?: number | undefined;
|
|
201
228
|
idToken?: string | undefined;
|
|
202
229
|
platform?: "web" | "ios" | "android" | undefined;
|
|
@@ -214,6 +241,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
|
|
|
214
241
|
emoji: string;
|
|
215
242
|
color: string;
|
|
216
243
|
}>>;
|
|
244
|
+
character: z.ZodOptional<z.ZodEnum<["m", "f"]>>;
|
|
217
245
|
pv: z.ZodOptional<z.ZodNumber>;
|
|
218
246
|
idToken: z.ZodOptional<z.ZodString>;
|
|
219
247
|
platform: z.ZodOptional<z.ZodEnum<["web", "ios", "android"]>>;
|
|
@@ -225,6 +253,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
|
|
|
225
253
|
emoji: string;
|
|
226
254
|
color: string;
|
|
227
255
|
} | undefined;
|
|
256
|
+
character?: "m" | "f" | undefined;
|
|
228
257
|
pv?: number | undefined;
|
|
229
258
|
idToken?: string | undefined;
|
|
230
259
|
platform?: "web" | "ios" | "android" | undefined;
|
|
@@ -236,6 +265,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
|
|
|
236
265
|
emoji: string;
|
|
237
266
|
color: string;
|
|
238
267
|
} | undefined;
|
|
268
|
+
character?: "m" | "f" | undefined;
|
|
239
269
|
pv?: number | undefined;
|
|
240
270
|
idToken?: string | undefined;
|
|
241
271
|
platform?: "web" | "ios" | "android" | undefined;
|
|
@@ -326,6 +356,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
|
|
|
326
356
|
totalRounds: z.ZodNumber;
|
|
327
357
|
categoryIds: z.ZodArray<z.ZodString, "many">;
|
|
328
358
|
hostPlays: z.ZodOptional<z.ZodBoolean>;
|
|
359
|
+
players: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
329
360
|
modeId: z.ZodOptional<z.ZodEnum<["solo", "oneDevice", "snag", "multiplayer"]>>;
|
|
330
361
|
buzzerMode: z.ZodOptional<z.ZodEnum<["off", "solo", "race", "controller"]>>;
|
|
331
362
|
noHost: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -336,6 +367,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
|
|
|
336
367
|
game?: "seedha" | "snag" | "trivia" | "dama" | "ersimha" | undefined;
|
|
337
368
|
modeId?: "snag" | "solo" | "oneDevice" | "multiplayer" | undefined;
|
|
338
369
|
hostPlays?: boolean | undefined;
|
|
370
|
+
players?: string[] | undefined;
|
|
339
371
|
buzzerMode?: "solo" | "off" | "race" | "controller" | undefined;
|
|
340
372
|
noHost?: boolean | undefined;
|
|
341
373
|
}, {
|
|
@@ -345,6 +377,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
|
|
|
345
377
|
game?: "seedha" | "snag" | "trivia" | "dama" | "ersimha" | undefined;
|
|
346
378
|
modeId?: "snag" | "solo" | "oneDevice" | "multiplayer" | undefined;
|
|
347
379
|
hostPlays?: boolean | undefined;
|
|
380
|
+
players?: string[] | undefined;
|
|
348
381
|
buzzerMode?: "solo" | "off" | "race" | "controller" | undefined;
|
|
349
382
|
noHost?: boolean | undefined;
|
|
350
383
|
}>, z.ZodObject<{
|
|
@@ -407,6 +440,15 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
|
|
|
407
440
|
t: "buzz";
|
|
408
441
|
}, {
|
|
409
442
|
t: "buzz";
|
|
443
|
+
}>, z.ZodObject<{
|
|
444
|
+
t: z.ZodLiteral<"sendEmote">;
|
|
445
|
+
emoteId: z.ZodEnum<["laugh", "wow", "think", "gg"]>;
|
|
446
|
+
}, "strip", z.ZodTypeAny, {
|
|
447
|
+
t: "sendEmote";
|
|
448
|
+
emoteId: "laugh" | "wow" | "think" | "gg";
|
|
449
|
+
}, {
|
|
450
|
+
t: "sendEmote";
|
|
451
|
+
emoteId: "laugh" | "wow" | "think" | "gg";
|
|
410
452
|
}>, z.ZodObject<{
|
|
411
453
|
t: z.ZodLiteral<"answer">;
|
|
412
454
|
index: z.ZodNumber;
|
|
@@ -827,6 +869,11 @@ type ServerMsg = {
|
|
|
827
869
|
} | {
|
|
828
870
|
t: 'roomClosed';
|
|
829
871
|
reason: string;
|
|
872
|
+
} | {
|
|
873
|
+
t: 'emote';
|
|
874
|
+
from: string;
|
|
875
|
+
emoteId: EmoteId;
|
|
876
|
+
character: CharacterId;
|
|
830
877
|
} | {
|
|
831
878
|
t: 'lobby';
|
|
832
879
|
code: string;
|
|
@@ -891,4 +938,4 @@ type ServerMsg = {
|
|
|
891
938
|
message: string;
|
|
892
939
|
};
|
|
893
940
|
|
|
894
|
-
export { type AvatarView, type BuzzerGameMode, type CategoryView, ClientMsg, type DamaMoveView, type DamaPieceView, type DamaStateView, type Effect, type ErsimhaStateView, 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 };
|
|
941
|
+
export { type AvatarView, type BuzzerGameMode, CHARACTER_IDS, type CategoryView, type CharacterId, ClientMsg, type DamaMoveView, type DamaPieceView, type DamaStateView, EMOTE_IDS, type Effect, type EmoteId, type ErsimhaStateView, 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
|
@@ -83,6 +83,24 @@ interface ModeInfo {
|
|
|
83
83
|
declare const MODES: readonly ModeInfo[];
|
|
84
84
|
/** The modes available for a given game (for a client's mode picker). */
|
|
85
85
|
declare const modesForGame: (gameId: GameId) => ModeInfo[];
|
|
86
|
+
/**
|
|
87
|
+
* «طقطقة» (tagtaga) emotes — D-038 (filed as D-036 in gs#50), design `geem-design DESIGN.md § tagtaga`.
|
|
88
|
+
* A CLOSED, POSITIVE-ONLY set of exactly four: ههههه · يا سلام · قاعد أفكر · لعب زين. The ids match the
|
|
89
|
+
* art masters (`art/tagtaga/Tagtaga-<emoteId>-<character>.png`), so the wire addresses the sprite directly.
|
|
90
|
+
* ⚠ Never add hurry-up / gloat / crying / angry — that's a RULING, not an oversight: «think» exists so a
|
|
91
|
+
* slow player can speak about THEMSELVES rather than anyone pressuring anyone. A closed enum with no
|
|
92
|
+
* free-form payload is also what keeps emotes off the UGC/moderation surface entirely.
|
|
93
|
+
*/
|
|
94
|
+
declare const EMOTE_IDS: readonly ["laugh", "wow", "think", "gg"];
|
|
95
|
+
type EmoteId = (typeof EMOTE_IDS)[number];
|
|
96
|
+
/**
|
|
97
|
+
* Which character sprite a player is drawn as: `m` = ghutra, `f` = hijab (the `-m`/`-f` art suffix).
|
|
98
|
+
* Both carry the identical emote set. It's a display preference, not identity: set it on room entry
|
|
99
|
+
* or change it any time via `setProfile`, and the server stamps it on that player's outgoing emotes
|
|
100
|
+
* so receivers can pick the right sprite without a roster lookup.
|
|
101
|
+
*/
|
|
102
|
+
declare const CHARACTER_IDS: readonly ["m", "f"];
|
|
103
|
+
type CharacterId = (typeof CHARACTER_IDS)[number];
|
|
86
104
|
/** Unambiguous room-code alphabet — no 0/O, 1/I/L. */
|
|
87
105
|
declare const ROOM_ALPHABET = "ACDEFGHJKMNPQRTUVWXY2346789";
|
|
88
106
|
declare const HelpType: z.ZodEnum<["removeTwoAnswers", "changeQuestion", "extraTime", "doublePoints", "stealPoints", "restPlayer", "tripLevel"]>;
|
|
@@ -100,6 +118,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
|
|
|
100
118
|
color: string;
|
|
101
119
|
}>>;
|
|
102
120
|
game: z.ZodOptional<z.ZodEnum<["seedha", "snag", "trivia", "dama", "ersimha"]>>;
|
|
121
|
+
character: z.ZodOptional<z.ZodEnum<["m", "f"]>>;
|
|
103
122
|
pv: z.ZodOptional<z.ZodNumber>;
|
|
104
123
|
idToken: z.ZodOptional<z.ZodString>;
|
|
105
124
|
platform: z.ZodOptional<z.ZodEnum<["web", "ios", "android"]>>;
|
|
@@ -111,6 +130,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
|
|
|
111
130
|
color: string;
|
|
112
131
|
} | undefined;
|
|
113
132
|
game?: "seedha" | "snag" | "trivia" | "dama" | "ersimha" | undefined;
|
|
133
|
+
character?: "m" | "f" | undefined;
|
|
114
134
|
pv?: number | undefined;
|
|
115
135
|
idToken?: string | undefined;
|
|
116
136
|
platform?: "web" | "ios" | "android" | undefined;
|
|
@@ -122,6 +142,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
|
|
|
122
142
|
color: string;
|
|
123
143
|
} | undefined;
|
|
124
144
|
game?: "seedha" | "snag" | "trivia" | "dama" | "ersimha" | undefined;
|
|
145
|
+
character?: "m" | "f" | undefined;
|
|
125
146
|
pv?: number | undefined;
|
|
126
147
|
idToken?: string | undefined;
|
|
127
148
|
platform?: "web" | "ios" | "android" | undefined;
|
|
@@ -147,6 +168,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
|
|
|
147
168
|
emoji: string;
|
|
148
169
|
color: string;
|
|
149
170
|
}>>;
|
|
171
|
+
character: z.ZodOptional<z.ZodEnum<["m", "f"]>>;
|
|
150
172
|
}, "strip", z.ZodTypeAny, {
|
|
151
173
|
t: "setProfile";
|
|
152
174
|
name?: string | undefined;
|
|
@@ -154,6 +176,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
|
|
|
154
176
|
emoji: string;
|
|
155
177
|
color: string;
|
|
156
178
|
} | undefined;
|
|
179
|
+
character?: "m" | "f" | undefined;
|
|
157
180
|
}, {
|
|
158
181
|
t: "setProfile";
|
|
159
182
|
name?: string | undefined;
|
|
@@ -161,6 +184,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
|
|
|
161
184
|
emoji: string;
|
|
162
185
|
color: string;
|
|
163
186
|
} | undefined;
|
|
187
|
+
character?: "m" | "f" | undefined;
|
|
164
188
|
}>, z.ZodObject<{
|
|
165
189
|
t: z.ZodLiteral<"joinRoom">;
|
|
166
190
|
code: z.ZodString;
|
|
@@ -175,6 +199,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
|
|
|
175
199
|
emoji: string;
|
|
176
200
|
color: string;
|
|
177
201
|
}>>;
|
|
202
|
+
character: z.ZodOptional<z.ZodEnum<["m", "f"]>>;
|
|
178
203
|
pv: z.ZodOptional<z.ZodNumber>;
|
|
179
204
|
idToken: z.ZodOptional<z.ZodString>;
|
|
180
205
|
platform: z.ZodOptional<z.ZodEnum<["web", "ios", "android"]>>;
|
|
@@ -186,6 +211,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
|
|
|
186
211
|
emoji: string;
|
|
187
212
|
color: string;
|
|
188
213
|
} | undefined;
|
|
214
|
+
character?: "m" | "f" | undefined;
|
|
189
215
|
pv?: number | undefined;
|
|
190
216
|
idToken?: string | undefined;
|
|
191
217
|
platform?: "web" | "ios" | "android" | undefined;
|
|
@@ -197,6 +223,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
|
|
|
197
223
|
emoji: string;
|
|
198
224
|
color: string;
|
|
199
225
|
} | undefined;
|
|
226
|
+
character?: "m" | "f" | undefined;
|
|
200
227
|
pv?: number | undefined;
|
|
201
228
|
idToken?: string | undefined;
|
|
202
229
|
platform?: "web" | "ios" | "android" | undefined;
|
|
@@ -214,6 +241,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
|
|
|
214
241
|
emoji: string;
|
|
215
242
|
color: string;
|
|
216
243
|
}>>;
|
|
244
|
+
character: z.ZodOptional<z.ZodEnum<["m", "f"]>>;
|
|
217
245
|
pv: z.ZodOptional<z.ZodNumber>;
|
|
218
246
|
idToken: z.ZodOptional<z.ZodString>;
|
|
219
247
|
platform: z.ZodOptional<z.ZodEnum<["web", "ios", "android"]>>;
|
|
@@ -225,6 +253,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
|
|
|
225
253
|
emoji: string;
|
|
226
254
|
color: string;
|
|
227
255
|
} | undefined;
|
|
256
|
+
character?: "m" | "f" | undefined;
|
|
228
257
|
pv?: number | undefined;
|
|
229
258
|
idToken?: string | undefined;
|
|
230
259
|
platform?: "web" | "ios" | "android" | undefined;
|
|
@@ -236,6 +265,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
|
|
|
236
265
|
emoji: string;
|
|
237
266
|
color: string;
|
|
238
267
|
} | undefined;
|
|
268
|
+
character?: "m" | "f" | undefined;
|
|
239
269
|
pv?: number | undefined;
|
|
240
270
|
idToken?: string | undefined;
|
|
241
271
|
platform?: "web" | "ios" | "android" | undefined;
|
|
@@ -326,6 +356,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
|
|
|
326
356
|
totalRounds: z.ZodNumber;
|
|
327
357
|
categoryIds: z.ZodArray<z.ZodString, "many">;
|
|
328
358
|
hostPlays: z.ZodOptional<z.ZodBoolean>;
|
|
359
|
+
players: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
329
360
|
modeId: z.ZodOptional<z.ZodEnum<["solo", "oneDevice", "snag", "multiplayer"]>>;
|
|
330
361
|
buzzerMode: z.ZodOptional<z.ZodEnum<["off", "solo", "race", "controller"]>>;
|
|
331
362
|
noHost: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -336,6 +367,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
|
|
|
336
367
|
game?: "seedha" | "snag" | "trivia" | "dama" | "ersimha" | undefined;
|
|
337
368
|
modeId?: "snag" | "solo" | "oneDevice" | "multiplayer" | undefined;
|
|
338
369
|
hostPlays?: boolean | undefined;
|
|
370
|
+
players?: string[] | undefined;
|
|
339
371
|
buzzerMode?: "solo" | "off" | "race" | "controller" | undefined;
|
|
340
372
|
noHost?: boolean | undefined;
|
|
341
373
|
}, {
|
|
@@ -345,6 +377,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
|
|
|
345
377
|
game?: "seedha" | "snag" | "trivia" | "dama" | "ersimha" | undefined;
|
|
346
378
|
modeId?: "snag" | "solo" | "oneDevice" | "multiplayer" | undefined;
|
|
347
379
|
hostPlays?: boolean | undefined;
|
|
380
|
+
players?: string[] | undefined;
|
|
348
381
|
buzzerMode?: "solo" | "off" | "race" | "controller" | undefined;
|
|
349
382
|
noHost?: boolean | undefined;
|
|
350
383
|
}>, z.ZodObject<{
|
|
@@ -407,6 +440,15 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
|
|
|
407
440
|
t: "buzz";
|
|
408
441
|
}, {
|
|
409
442
|
t: "buzz";
|
|
443
|
+
}>, z.ZodObject<{
|
|
444
|
+
t: z.ZodLiteral<"sendEmote">;
|
|
445
|
+
emoteId: z.ZodEnum<["laugh", "wow", "think", "gg"]>;
|
|
446
|
+
}, "strip", z.ZodTypeAny, {
|
|
447
|
+
t: "sendEmote";
|
|
448
|
+
emoteId: "laugh" | "wow" | "think" | "gg";
|
|
449
|
+
}, {
|
|
450
|
+
t: "sendEmote";
|
|
451
|
+
emoteId: "laugh" | "wow" | "think" | "gg";
|
|
410
452
|
}>, z.ZodObject<{
|
|
411
453
|
t: z.ZodLiteral<"answer">;
|
|
412
454
|
index: z.ZodNumber;
|
|
@@ -827,6 +869,11 @@ type ServerMsg = {
|
|
|
827
869
|
} | {
|
|
828
870
|
t: 'roomClosed';
|
|
829
871
|
reason: string;
|
|
872
|
+
} | {
|
|
873
|
+
t: 'emote';
|
|
874
|
+
from: string;
|
|
875
|
+
emoteId: EmoteId;
|
|
876
|
+
character: CharacterId;
|
|
830
877
|
} | {
|
|
831
878
|
t: 'lobby';
|
|
832
879
|
code: string;
|
|
@@ -891,4 +938,4 @@ type ServerMsg = {
|
|
|
891
938
|
message: string;
|
|
892
939
|
};
|
|
893
940
|
|
|
894
|
-
export { type AvatarView, type BuzzerGameMode, type CategoryView, ClientMsg, type DamaMoveView, type DamaPieceView, type DamaStateView, type Effect, type ErsimhaStateView, 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 };
|
|
941
|
+
export { type AvatarView, type BuzzerGameMode, CHARACTER_IDS, type CategoryView, type CharacterId, ClientMsg, type DamaMoveView, type DamaPieceView, type DamaStateView, EMOTE_IDS, type Effect, type EmoteId, type ErsimhaStateView, 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
|
@@ -34,6 +34,8 @@ var MODES = [
|
|
|
34
34
|
{ id: "snag", gameId: "snag", label: "Snag", onlineEligible: true }
|
|
35
35
|
];
|
|
36
36
|
var modesForGame = (gameId) => MODES.filter((m) => m.gameId === gameId);
|
|
37
|
+
var EMOTE_IDS = ["laugh", "wow", "think", "gg"];
|
|
38
|
+
var CHARACTER_IDS = ["m", "f"];
|
|
37
39
|
var ROOM_ALPHABET = "ACDEFGHJKMNPQRTUVWXY2346789";
|
|
38
40
|
var UNSAFE_CHARS = /[\p{Cc}<>]/gu;
|
|
39
41
|
var DisplayName = z.string().trim().min(1).max(LIMITS.MAX_NAME_LEN).transform((s) => s.replace(UNSAFE_CHARS, "").trim()).refine((s) => s.length >= 1, "name is empty after sanitizing");
|
|
@@ -55,9 +57,10 @@ var Avatar = z.object({
|
|
|
55
57
|
emoji: z.string().min(1).max(16).transform((s) => s.replace(UNSAFE_CHARS, "")).refine((s) => s.length >= 1, "empty emoji"),
|
|
56
58
|
color: z.string().regex(/^#[0-9a-fA-F]{6}$/, "color must be #rrggbb")
|
|
57
59
|
});
|
|
60
|
+
var Character = z.enum(CHARACTER_IDS);
|
|
58
61
|
var ClientMsg = z.discriminatedUnion("t", [
|
|
59
62
|
// room / lobby
|
|
60
|
-
z.object({ t: z.literal("createRoom"), name: DisplayName.optional(), avatar: Avatar.optional(), game: z.enum(GAME_IDS).optional(), pv: Pv, idToken: IdToken, platform: Platform }),
|
|
63
|
+
z.object({ t: z.literal("createRoom"), name: DisplayName.optional(), avatar: Avatar.optional(), game: z.enum(GAME_IDS).optional(), character: Character.optional(), pv: Pv, idToken: IdToken, platform: Platform }),
|
|
61
64
|
// Host sets/changes their display name AFTER createRoom (for anonymous one-tap
|
|
62
65
|
// hosts who started nameless). Host-only; the relay re-broadcasts the lobby.
|
|
63
66
|
// SUPERSEDED by setProfile (kept working for shipped builds).
|
|
@@ -70,9 +73,9 @@ var ClientMsg = z.discriminatedUnion("t", [
|
|
|
70
73
|
// Note: mid-game propagation depends on what the running game's state carries — trivia's
|
|
71
74
|
// view carries names+avatars so it updates live; dama/ersimha views are id-keyed (clients
|
|
72
75
|
// map ids→names from the lobby roster), so a change there lands on the next lobby.
|
|
73
|
-
z.object({ t: z.literal("setProfile"), name: DisplayName.optional(), avatar: Avatar.optional() }),
|
|
74
|
-
z.object({ t: z.literal("joinRoom"), code: RoomCode, name: DisplayName, avatar: Avatar.optional(), pv: Pv, idToken: IdToken, platform: Platform }),
|
|
75
|
-
z.object({ t: z.literal("resume"), code: RoomCode, token: z.string().max(120), avatar: Avatar.optional(), pv: Pv, idToken: IdToken, platform: Platform }),
|
|
76
|
+
z.object({ t: z.literal("setProfile"), name: DisplayName.optional(), avatar: Avatar.optional(), character: Character.optional() }),
|
|
77
|
+
z.object({ t: z.literal("joinRoom"), code: RoomCode, name: DisplayName, avatar: Avatar.optional(), character: Character.optional(), pv: Pv, idToken: IdToken, platform: Platform }),
|
|
78
|
+
z.object({ t: z.literal("resume"), code: RoomCode, token: z.string().max(120), avatar: Avatar.optional(), character: Character.optional(), pv: Pv, idToken: IdToken, platform: Platform }),
|
|
76
79
|
z.object({ t: z.literal("leaveRoom") }),
|
|
77
80
|
z.object({ t: z.literal("approve"), playerId: PlayerId }),
|
|
78
81
|
z.object({ t: z.literal("reject"), playerId: PlayerId }),
|
|
@@ -115,6 +118,12 @@ var ClientMsg = z.discriminatedUnion("t", [
|
|
|
115
118
|
// false = the first two approved joiners take the board and the host WATCHES
|
|
116
119
|
// (yourColor:null) while keeping every host power. Needs 2 approved joiners.
|
|
117
120
|
hostPlays: z.boolean().optional(),
|
|
121
|
+
// Name the seats explicitly instead of taking them by join order (gs#47 Ask 2, layered on per
|
|
122
|
+
// D-037). Exactly two DISTINCT ids, both approved members of the room (the host may be one):
|
|
123
|
+
// `players[0]` plays black and moves first, `players[1]` plays white; everyone else in the room
|
|
124
|
+
// spectates. When present this OVERRIDES `hostPlays` — an explicit seating beats an implicit one.
|
|
125
|
+
// Violations are rejected with `bad_players`. Consumed by دامة (the only fixed-seat game today).
|
|
126
|
+
players: z.array(PlayerId).length(2).optional(),
|
|
118
127
|
// PREFERRED: pick a mode from the registry (see MODES). The server maps it to the engine
|
|
119
128
|
// rules. Send this and omit buzzerMode/noHost. Falls back to the legacy flags below if omitted.
|
|
120
129
|
modeId: z.enum(MODE_IDS).optional(),
|
|
@@ -138,6 +147,11 @@ var ClientMsg = z.discriminatedUnion("t", [
|
|
|
138
147
|
z.object({ t: z.literal("noHostNobody") }),
|
|
139
148
|
// player actions
|
|
140
149
|
z.object({ t: z.literal("buzz") }),
|
|
150
|
+
// «طقطقة» (D-038): send one of the four closed emotes. Server-enforced 5s per-player throttle on
|
|
151
|
+
// its OWN rate lane — a client-side throttle is trivially bypassed, and this is a family app, so
|
|
152
|
+
// Shape B applies exactly as it does to buzz arrival order. Over-rate emotes are dropped silently.
|
|
153
|
+
// Ephemeral: relayed, never stored. Allowed only while a game whose driver permits emotes is running.
|
|
154
|
+
z.object({ t: z.literal("sendEmote"), emoteId: z.enum(EMOTE_IDS) }),
|
|
141
155
|
z.object({ t: z.literal("answer"), index: z.number().int().min(0).max(3) }),
|
|
142
156
|
// trivia is 4-option (0–3)
|
|
143
157
|
z.object({ t: z.literal("useHelp"), helpType: HelpType }),
|
|
@@ -187,7 +201,9 @@ var ClientMsg = z.discriminatedUnion("t", [
|
|
|
187
201
|
z.object({ t: z.literal("skipWord") })
|
|
188
202
|
]);
|
|
189
203
|
export {
|
|
204
|
+
CHARACTER_IDS,
|
|
190
205
|
ClientMsg,
|
|
206
|
+
EMOTE_IDS,
|
|
191
207
|
GAMES,
|
|
192
208
|
GAME_IDS,
|
|
193
209
|
LIMITS,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thegeem/protocol",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.26",
|
|
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",
|