@jgengine/convex 0.18.0 → 0.19.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.
package/dist/server.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import type { Auth, DataModelFromSchemaDefinition, DocumentByName, GenericMutationCtx } from "convex/server";
1
+ import type { Auth, FunctionReference, DataModelFromSchemaDefinition, DocumentByName, GenericMutationCtx, GenericQueryCtx } from "convex/server";
2
2
  import type { GenericId } from "convex/values";
3
- import type { ChatMessage } from "@jgengine/core/game/chat";
3
+ import { type ChatMessage } from "@jgengine/core/game/chat";
4
4
  import type { ChatSendOutcome } from "@jgengine/core/multiplayer/chatContract";
5
5
  import { type PoseSyncRules } from "@jgengine/core/multiplayer/presenceModel";
6
6
  import type { CommandScope } from "@jgengine/core/runtime/commandRunner";
@@ -8,7 +8,7 @@ import type { GameRuntime } from "@jgengine/core/runtime/gameRuntime";
8
8
  import type { LeaderboardIncrement } from "@jgengine/core/runtime/hostPersistence";
9
9
  import { type MatchmakingMode } from "@jgengine/core/runtime/hostPolicy";
10
10
  import type { SaveConfig } from "@jgengine/core/runtime/save";
11
- import type { GameRuntimeSnapshot } from "@jgengine/core/runtime/snapshot";
11
+ import type { GameRuntimeSnapshot, RuntimePlayerRow } from "@jgengine/core/runtime/snapshot";
12
12
  /** @internal Re-export shared host-policy pure helpers so existing convex imports keep working. */
13
13
  export { JG_MAX_MEMBERS_PER_SERVER, canJoinPrivateServer, isListablePublicly, validateSlotsPerServer, type MatchmakingMode, } from "@jgengine/core/runtime/hostPolicy";
14
14
  /** @internal */
@@ -19,6 +19,7 @@ export declare function jgengineTables(): {
19
19
  gameId: string;
20
20
  joinCode?: string | undefined;
21
21
  lastSavedAt?: number | undefined;
22
+ memberCount?: number | undefined;
22
23
  memberUserIds: string[];
23
24
  mode?: string | undefined;
24
25
  modeConfig?: any;
@@ -32,6 +33,7 @@ export declare function jgengineTables(): {
32
33
  slotsPerServer: number;
33
34
  status: "closed" | "open" | "running";
34
35
  tickAnchorMs: number;
36
+ topology?: "rooms" | "shared" | undefined;
35
37
  updatedAt: number;
36
38
  visibility?: "private" | "public" | undefined;
37
39
  }, {
@@ -41,6 +43,8 @@ export declare function jgengineTables(): {
41
43
  modeConfig: import("convex/values").VAny<any, "optional", string>;
42
44
  visibility: import("convex/values").VUnion<"private" | "public" | undefined, [import("convex/values").VLiteral<"public", "required">, import("convex/values").VLiteral<"private", "required">], "optional", never>;
43
45
  joinCode: import("convex/values").VString<string | undefined, "optional">;
46
+ topology: import("convex/values").VUnion<"rooms" | "shared" | undefined, [import("convex/values").VLiteral<"shared", "required">, import("convex/values").VLiteral<"rooms", "required">], "optional", never>;
47
+ memberCount: import("convex/values").VFloat64<number | undefined, "optional">;
44
48
  memberUserIds: import("convex/values").VArray<string[], import("convex/values").VString<string, "required">, "required">;
45
49
  slotsPerServer: import("convex/values").VFloat64<number, "required">;
46
50
  save: import("convex/values").VUnion<"none" | {
@@ -61,29 +65,92 @@ export declare function jgengineTables(): {
61
65
  dirtyAt: import("convex/values").VFloat64<number | undefined, "optional">;
62
66
  createdAt: import("convex/values").VFloat64<number, "required">;
63
67
  updatedAt: import("convex/values").VFloat64<number, "required">;
64
- }, "required", "createdAt" | "dirtyAt" | "gameId" | "joinCode" | "lastSavedAt" | "memberUserIds" | "mode" | "modeConfig" | "revision" | "save" | "save.auto" | "save.scope" | "serverState" | "sessionPlayers" | "slotsPerServer" | "status" | "tickAnchorMs" | "updatedAt" | "visibility" | `modeConfig.${string}` | `serverState.${string}` | `sessionPlayers.${string}`>, {
68
+ }, "required", "createdAt" | "dirtyAt" | "gameId" | "joinCode" | "lastSavedAt" | "memberCount" | "memberUserIds" | "mode" | "modeConfig" | "revision" | "save" | "save.auto" | "save.scope" | "serverState" | "sessionPlayers" | "slotsPerServer" | "status" | "tickAnchorMs" | "topology" | "updatedAt" | "visibility" | `modeConfig.${string}` | `serverState.${string}` | `sessionPlayers.${string}`>, {
65
69
  by_dirty: ["dirtyAt", "_creationTime"];
66
70
  by_game_and_status: ["gameId", "status", "_creationTime"];
67
71
  by_game_status_tick: ["gameId", "status", "tickAnchorMs", "_creationTime"];
68
72
  by_status: ["status", "_creationTime"];
69
73
  }, {}, {}>;
74
+ jgServerCapacity: import("convex/server").TableDefinition<import("convex/values").VObject<{
75
+ gameId: string;
76
+ memberCount: number;
77
+ mode?: string | undefined;
78
+ serverId: GenericId<"jgGameServers">;
79
+ slotsPerServer: number;
80
+ status: "closed" | "open" | "running";
81
+ }, {
82
+ serverId: import("convex/values").VId<GenericId<"jgGameServers">, "required">;
83
+ gameId: import("convex/values").VString<string, "required">;
84
+ status: import("convex/values").VUnion<"closed" | "open" | "running", [import("convex/values").VLiteral<"open", "required">, import("convex/values").VLiteral<"running", "required">, import("convex/values").VLiteral<"closed", "required">], "required", never>;
85
+ mode: import("convex/values").VString<string | undefined, "optional">;
86
+ memberCount: import("convex/values").VFloat64<number, "required">;
87
+ slotsPerServer: import("convex/values").VFloat64<number, "required">;
88
+ }, "required", "gameId" | "memberCount" | "mode" | "serverId" | "slotsPerServer" | "status">, {
89
+ by_server: ["serverId", "_creationTime"];
90
+ }, {}, {}>;
91
+ jgServerMembers: import("convex/server").TableDefinition<import("convex/values").VObject<{
92
+ joinedAt: number;
93
+ serverId: GenericId<"jgGameServers">;
94
+ sessionIds?: string[] | undefined;
95
+ userId: string;
96
+ }, {
97
+ serverId: import("convex/values").VId<GenericId<"jgGameServers">, "required">;
98
+ userId: import("convex/values").VString<string, "required">;
99
+ joinedAt: import("convex/values").VFloat64<number, "required">;
100
+ /** Live client sessions holding this membership; absent on legacy and host-driven joins. */
101
+ sessionIds: import("convex/values").VArray<string[] | undefined, import("convex/values").VString<string, "required">, "optional">;
102
+ }, "required", "joinedAt" | "serverId" | "sessionIds" | "userId">, {
103
+ by_server: ["serverId", "_creationTime"];
104
+ by_server_and_user: ["serverId", "userId", "_creationTime"];
105
+ }, {}, {}>;
106
+ jgRateLimits: import("convex/server").TableDefinition<import("convex/values").VObject<{
107
+ count: number;
108
+ expiresAt: number;
109
+ key: string;
110
+ startedAt: number;
111
+ }, {
112
+ key: import("convex/values").VString<string, "required">;
113
+ startedAt: import("convex/values").VFloat64<number, "required">;
114
+ count: import("convex/values").VFloat64<number, "required">;
115
+ expiresAt: import("convex/values").VFloat64<number, "required">;
116
+ }, "required", "count" | "expiresAt" | "key" | "startedAt">, {
117
+ by_expires: ["expiresAt", "_creationTime"];
118
+ by_key: ["key", "_creationTime"];
119
+ }, {}, {}>;
120
+ jgClientErrors: import("convex/server").TableDefinition<import("convex/values").VObject<{
121
+ createdAt: number;
122
+ details?: any;
123
+ message: string;
124
+ userId: string;
125
+ }, {
126
+ userId: import("convex/values").VString<string, "required">;
127
+ message: import("convex/values").VString<string, "required">;
128
+ details: import("convex/values").VAny<any, "optional", string>;
129
+ createdAt: import("convex/values").VFloat64<number, "required">;
130
+ }, "required", "createdAt" | "details" | "message" | "userId" | `details.${string}`>, {
131
+ by_created: ["createdAt", "_creationTime"];
132
+ }, {}, {}>;
70
133
  jgPlayerProfiles: import("convex/server").TableDefinition<import("convex/values").VObject<{
71
134
  createdAt: number;
72
135
  dirtyAt?: number | undefined;
73
136
  gameId: string;
74
137
  playerState: any;
75
138
  revision: number;
139
+ sessionServerId?: GenericId<"jgGameServers"> | undefined;
140
+ sessionState?: any;
76
141
  updatedAt: number;
77
142
  userId: string;
78
143
  }, {
79
144
  userId: import("convex/values").VString<string, "required">;
80
145
  gameId: import("convex/values").VString<string, "required">;
81
146
  playerState: import("convex/values").VAny<any, "required", string>;
147
+ sessionState: import("convex/values").VAny<any, "optional", string>;
148
+ sessionServerId: import("convex/values").VId<GenericId<"jgGameServers"> | undefined, "optional">;
82
149
  revision: import("convex/values").VFloat64<number, "required">;
83
150
  dirtyAt: import("convex/values").VFloat64<number | undefined, "optional">;
84
151
  createdAt: import("convex/values").VFloat64<number, "required">;
85
152
  updatedAt: import("convex/values").VFloat64<number, "required">;
86
- }, "required", "createdAt" | "dirtyAt" | "gameId" | "playerState" | "revision" | "updatedAt" | "userId" | `playerState.${string}`>, {
153
+ }, "required", "createdAt" | "dirtyAt" | "gameId" | "playerState" | "revision" | "sessionServerId" | "sessionState" | "updatedAt" | "userId" | `playerState.${string}` | `sessionState.${string}`>, {
87
154
  by_user_and_game: ["userId", "gameId", "_creationTime"];
88
155
  }, {}, {}>;
89
156
  jgWorldChunks: import("convex/server").TableDefinition<import("convex/values").VObject<{
@@ -136,8 +203,12 @@ export declare function jgengineTables(): {
136
203
  by_server_and_action: ["serverId", "action", "_creationTime"];
137
204
  }, {}, {}>;
138
205
  jgPoses: import("convex/server").TableDefinition<import("convex/values").VObject<{
206
+ chunkKey?: string | undefined;
207
+ homeGameId?: string | undefined;
139
208
  kind?: string | undefined;
140
209
  label?: string | undefined;
210
+ lastWorldSnapshotAt?: number | undefined;
211
+ ownerActorId?: string | undefined;
141
212
  revokedAt?: number | undefined;
142
213
  rotationPitch: number;
143
214
  rotationY: number;
@@ -150,6 +221,10 @@ export declare function jgengineTables(): {
150
221
  z: number;
151
222
  }, {
152
223
  serverId: import("convex/values").VString<string, "required">;
224
+ homeGameId: import("convex/values").VString<string | undefined, "optional">;
225
+ ownerActorId: import("convex/values").VString<string | undefined, "optional">;
226
+ lastWorldSnapshotAt: import("convex/values").VFloat64<number | undefined, "optional">;
227
+ chunkKey: import("convex/values").VString<string | undefined, "optional">;
153
228
  userId: import("convex/values").VString<string, "required">;
154
229
  /** One row per live session, so a second tab is a second row rather than a fight over one. */
155
230
  sessionId: import("convex/values").VString<string | undefined, "optional">;
@@ -165,13 +240,21 @@ export declare function jgengineTables(): {
165
240
  updatedAt: import("convex/values").VFloat64<number, "required">;
166
241
  /** Set when the session was displaced or left; the reaper deletes the row once it goes idle. */
167
242
  revokedAt: import("convex/values").VFloat64<number | undefined, "optional">;
168
- }, "required", "kind" | "label" | "revokedAt" | "rotationPitch" | "rotationY" | "serverId" | "sessionId" | "updatedAt" | "userId" | "x" | "y" | "z">, {
243
+ }, "required", "chunkKey" | "homeGameId" | "kind" | "label" | "lastWorldSnapshotAt" | "ownerActorId" | "revokedAt" | "rotationPitch" | "rotationY" | "serverId" | "sessionId" | "updatedAt" | "userId" | "x" | "y" | "z">, {
244
+ by_home_game: ["homeGameId", "_creationTime"];
245
+ by_home_game_revoked_updated: ["homeGameId", "revokedAt", "updatedAt", "_creationTime"];
246
+ by_revoked: ["revokedAt", "_creationTime"];
247
+ by_revoked_updated: ["revokedAt", "updatedAt", "_creationTime"];
169
248
  by_server: ["serverId", "_creationTime"];
249
+ by_server_and_chunk: ["serverId", "chunkKey", "_creationTime"];
170
250
  by_server_and_user: ["serverId", "userId", "_creationTime"];
251
+ by_server_user_revoked_updated: ["serverId", "userId", "revokedAt", "updatedAt", "_creationTime"];
171
252
  by_updated: ["updatedAt", "_creationTime"];
253
+ by_user: ["userId", "_creationTime"];
172
254
  }, {}, {}>;
173
255
  jgChatMessages: import("convex/server").TableDefinition<import("convex/values").VObject<{
174
256
  at: number;
257
+ authorName?: string | undefined;
175
258
  body: string;
176
259
  channelId: string;
177
260
  serverId: string;
@@ -181,9 +264,11 @@ export declare function jgengineTables(): {
181
264
  channelId: import("convex/values").VString<string, "required">;
182
265
  userId: import("convex/values").VString<string, "required">;
183
266
  body: import("convex/values").VString<string, "required">;
267
+ authorName: import("convex/values").VString<string | undefined, "optional">;
184
268
  at: import("convex/values").VFloat64<number, "required">;
185
- }, "required", "at" | "body" | "channelId" | "serverId" | "userId">, {
186
- by_server_channel: ["serverId", "channelId", "_creationTime"];
269
+ }, "required", "at" | "authorName" | "body" | "channelId" | "serverId" | "userId">, {
270
+ by_at: ["at", "_creationTime"];
271
+ by_server_channel: ["serverId", "channelId", "at", "_creationTime"];
187
272
  }, {}, {}>;
188
273
  };
189
274
  /**
@@ -225,6 +310,7 @@ declare const schemaForTypes: import("convex/server").SchemaDefinition<{
225
310
  gameId: string;
226
311
  joinCode?: string | undefined;
227
312
  lastSavedAt?: number | undefined;
313
+ memberCount?: number | undefined;
228
314
  memberUserIds: string[];
229
315
  mode?: string | undefined;
230
316
  modeConfig?: any;
@@ -238,6 +324,7 @@ declare const schemaForTypes: import("convex/server").SchemaDefinition<{
238
324
  slotsPerServer: number;
239
325
  status: "closed" | "open" | "running";
240
326
  tickAnchorMs: number;
327
+ topology?: "rooms" | "shared" | undefined;
241
328
  updatedAt: number;
242
329
  visibility?: "private" | "public" | undefined;
243
330
  }, {
@@ -247,6 +334,8 @@ declare const schemaForTypes: import("convex/server").SchemaDefinition<{
247
334
  modeConfig: import("convex/values").VAny<any, "optional", string>;
248
335
  visibility: import("convex/values").VUnion<"private" | "public" | undefined, [import("convex/values").VLiteral<"public", "required">, import("convex/values").VLiteral<"private", "required">], "optional", never>;
249
336
  joinCode: import("convex/values").VString<string | undefined, "optional">;
337
+ topology: import("convex/values").VUnion<"rooms" | "shared" | undefined, [import("convex/values").VLiteral<"shared", "required">, import("convex/values").VLiteral<"rooms", "required">], "optional", never>;
338
+ memberCount: import("convex/values").VFloat64<number | undefined, "optional">;
250
339
  memberUserIds: import("convex/values").VArray<string[], import("convex/values").VString<string, "required">, "required">;
251
340
  slotsPerServer: import("convex/values").VFloat64<number, "required">;
252
341
  save: import("convex/values").VUnion<"none" | {
@@ -267,29 +356,92 @@ declare const schemaForTypes: import("convex/server").SchemaDefinition<{
267
356
  dirtyAt: import("convex/values").VFloat64<number | undefined, "optional">;
268
357
  createdAt: import("convex/values").VFloat64<number, "required">;
269
358
  updatedAt: import("convex/values").VFloat64<number, "required">;
270
- }, "required", "createdAt" | "dirtyAt" | "gameId" | "joinCode" | "lastSavedAt" | "memberUserIds" | "mode" | "modeConfig" | "revision" | "save" | "save.auto" | "save.scope" | "serverState" | "sessionPlayers" | "slotsPerServer" | "status" | "tickAnchorMs" | "updatedAt" | "visibility" | `modeConfig.${string}` | `serverState.${string}` | `sessionPlayers.${string}`>, {
359
+ }, "required", "createdAt" | "dirtyAt" | "gameId" | "joinCode" | "lastSavedAt" | "memberCount" | "memberUserIds" | "mode" | "modeConfig" | "revision" | "save" | "save.auto" | "save.scope" | "serverState" | "sessionPlayers" | "slotsPerServer" | "status" | "tickAnchorMs" | "topology" | "updatedAt" | "visibility" | `modeConfig.${string}` | `serverState.${string}` | `sessionPlayers.${string}`>, {
271
360
  by_dirty: ["dirtyAt", "_creationTime"];
272
361
  by_game_and_status: ["gameId", "status", "_creationTime"];
273
362
  by_game_status_tick: ["gameId", "status", "tickAnchorMs", "_creationTime"];
274
363
  by_status: ["status", "_creationTime"];
275
364
  }, {}, {}>;
365
+ jgServerCapacity: import("convex/server").TableDefinition<import("convex/values").VObject<{
366
+ gameId: string;
367
+ memberCount: number;
368
+ mode?: string | undefined;
369
+ serverId: GenericId<"jgGameServers">;
370
+ slotsPerServer: number;
371
+ status: "closed" | "open" | "running";
372
+ }, {
373
+ serverId: import("convex/values").VId<GenericId<"jgGameServers">, "required">;
374
+ gameId: import("convex/values").VString<string, "required">;
375
+ status: import("convex/values").VUnion<"closed" | "open" | "running", [import("convex/values").VLiteral<"open", "required">, import("convex/values").VLiteral<"running", "required">, import("convex/values").VLiteral<"closed", "required">], "required", never>;
376
+ mode: import("convex/values").VString<string | undefined, "optional">;
377
+ memberCount: import("convex/values").VFloat64<number, "required">;
378
+ slotsPerServer: import("convex/values").VFloat64<number, "required">;
379
+ }, "required", "gameId" | "memberCount" | "mode" | "serverId" | "slotsPerServer" | "status">, {
380
+ by_server: ["serverId", "_creationTime"];
381
+ }, {}, {}>;
382
+ jgServerMembers: import("convex/server").TableDefinition<import("convex/values").VObject<{
383
+ joinedAt: number;
384
+ serverId: GenericId<"jgGameServers">;
385
+ sessionIds?: string[] | undefined;
386
+ userId: string;
387
+ }, {
388
+ serverId: import("convex/values").VId<GenericId<"jgGameServers">, "required">;
389
+ userId: import("convex/values").VString<string, "required">;
390
+ joinedAt: import("convex/values").VFloat64<number, "required">;
391
+ /** Live client sessions holding this membership; absent on legacy and host-driven joins. */
392
+ sessionIds: import("convex/values").VArray<string[] | undefined, import("convex/values").VString<string, "required">, "optional">;
393
+ }, "required", "joinedAt" | "serverId" | "sessionIds" | "userId">, {
394
+ by_server: ["serverId", "_creationTime"];
395
+ by_server_and_user: ["serverId", "userId", "_creationTime"];
396
+ }, {}, {}>;
397
+ jgRateLimits: import("convex/server").TableDefinition<import("convex/values").VObject<{
398
+ count: number;
399
+ expiresAt: number;
400
+ key: string;
401
+ startedAt: number;
402
+ }, {
403
+ key: import("convex/values").VString<string, "required">;
404
+ startedAt: import("convex/values").VFloat64<number, "required">;
405
+ count: import("convex/values").VFloat64<number, "required">;
406
+ expiresAt: import("convex/values").VFloat64<number, "required">;
407
+ }, "required", "count" | "expiresAt" | "key" | "startedAt">, {
408
+ by_expires: ["expiresAt", "_creationTime"];
409
+ by_key: ["key", "_creationTime"];
410
+ }, {}, {}>;
411
+ jgClientErrors: import("convex/server").TableDefinition<import("convex/values").VObject<{
412
+ createdAt: number;
413
+ details?: any;
414
+ message: string;
415
+ userId: string;
416
+ }, {
417
+ userId: import("convex/values").VString<string, "required">;
418
+ message: import("convex/values").VString<string, "required">;
419
+ details: import("convex/values").VAny<any, "optional", string>;
420
+ createdAt: import("convex/values").VFloat64<number, "required">;
421
+ }, "required", "createdAt" | "details" | "message" | "userId" | `details.${string}`>, {
422
+ by_created: ["createdAt", "_creationTime"];
423
+ }, {}, {}>;
276
424
  jgPlayerProfiles: import("convex/server").TableDefinition<import("convex/values").VObject<{
277
425
  createdAt: number;
278
426
  dirtyAt?: number | undefined;
279
427
  gameId: string;
280
428
  playerState: any;
281
429
  revision: number;
430
+ sessionServerId?: GenericId<"jgGameServers"> | undefined;
431
+ sessionState?: any;
282
432
  updatedAt: number;
283
433
  userId: string;
284
434
  }, {
285
435
  userId: import("convex/values").VString<string, "required">;
286
436
  gameId: import("convex/values").VString<string, "required">;
287
437
  playerState: import("convex/values").VAny<any, "required", string>;
438
+ sessionState: import("convex/values").VAny<any, "optional", string>;
439
+ sessionServerId: import("convex/values").VId<GenericId<"jgGameServers"> | undefined, "optional">;
288
440
  revision: import("convex/values").VFloat64<number, "required">;
289
441
  dirtyAt: import("convex/values").VFloat64<number | undefined, "optional">;
290
442
  createdAt: import("convex/values").VFloat64<number, "required">;
291
443
  updatedAt: import("convex/values").VFloat64<number, "required">;
292
- }, "required", "createdAt" | "dirtyAt" | "gameId" | "playerState" | "revision" | "updatedAt" | "userId" | `playerState.${string}`>, {
444
+ }, "required", "createdAt" | "dirtyAt" | "gameId" | "playerState" | "revision" | "sessionServerId" | "sessionState" | "updatedAt" | "userId" | `playerState.${string}` | `sessionState.${string}`>, {
293
445
  by_user_and_game: ["userId", "gameId", "_creationTime"];
294
446
  }, {}, {}>;
295
447
  jgWorldChunks: import("convex/server").TableDefinition<import("convex/values").VObject<{
@@ -342,8 +494,12 @@ declare const schemaForTypes: import("convex/server").SchemaDefinition<{
342
494
  by_server_and_action: ["serverId", "action", "_creationTime"];
343
495
  }, {}, {}>;
344
496
  jgPoses: import("convex/server").TableDefinition<import("convex/values").VObject<{
497
+ chunkKey?: string | undefined;
498
+ homeGameId?: string | undefined;
345
499
  kind?: string | undefined;
346
500
  label?: string | undefined;
501
+ lastWorldSnapshotAt?: number | undefined;
502
+ ownerActorId?: string | undefined;
347
503
  revokedAt?: number | undefined;
348
504
  rotationPitch: number;
349
505
  rotationY: number;
@@ -356,6 +512,10 @@ declare const schemaForTypes: import("convex/server").SchemaDefinition<{
356
512
  z: number;
357
513
  }, {
358
514
  serverId: import("convex/values").VString<string, "required">;
515
+ homeGameId: import("convex/values").VString<string | undefined, "optional">;
516
+ ownerActorId: import("convex/values").VString<string | undefined, "optional">;
517
+ lastWorldSnapshotAt: import("convex/values").VFloat64<number | undefined, "optional">;
518
+ chunkKey: import("convex/values").VString<string | undefined, "optional">;
359
519
  userId: import("convex/values").VString<string, "required">;
360
520
  /** One row per live session, so a second tab is a second row rather than a fight over one. */
361
521
  sessionId: import("convex/values").VString<string | undefined, "optional">;
@@ -371,13 +531,21 @@ declare const schemaForTypes: import("convex/server").SchemaDefinition<{
371
531
  updatedAt: import("convex/values").VFloat64<number, "required">;
372
532
  /** Set when the session was displaced or left; the reaper deletes the row once it goes idle. */
373
533
  revokedAt: import("convex/values").VFloat64<number | undefined, "optional">;
374
- }, "required", "kind" | "label" | "revokedAt" | "rotationPitch" | "rotationY" | "serverId" | "sessionId" | "updatedAt" | "userId" | "x" | "y" | "z">, {
534
+ }, "required", "chunkKey" | "homeGameId" | "kind" | "label" | "lastWorldSnapshotAt" | "ownerActorId" | "revokedAt" | "rotationPitch" | "rotationY" | "serverId" | "sessionId" | "updatedAt" | "userId" | "x" | "y" | "z">, {
535
+ by_home_game: ["homeGameId", "_creationTime"];
536
+ by_home_game_revoked_updated: ["homeGameId", "revokedAt", "updatedAt", "_creationTime"];
537
+ by_revoked: ["revokedAt", "_creationTime"];
538
+ by_revoked_updated: ["revokedAt", "updatedAt", "_creationTime"];
375
539
  by_server: ["serverId", "_creationTime"];
540
+ by_server_and_chunk: ["serverId", "chunkKey", "_creationTime"];
376
541
  by_server_and_user: ["serverId", "userId", "_creationTime"];
542
+ by_server_user_revoked_updated: ["serverId", "userId", "revokedAt", "updatedAt", "_creationTime"];
377
543
  by_updated: ["updatedAt", "_creationTime"];
544
+ by_user: ["userId", "_creationTime"];
378
545
  }, {}, {}>;
379
546
  jgChatMessages: import("convex/server").TableDefinition<import("convex/values").VObject<{
380
547
  at: number;
548
+ authorName?: string | undefined;
381
549
  body: string;
382
550
  channelId: string;
383
551
  serverId: string;
@@ -387,14 +555,18 @@ declare const schemaForTypes: import("convex/server").SchemaDefinition<{
387
555
  channelId: import("convex/values").VString<string, "required">;
388
556
  userId: import("convex/values").VString<string, "required">;
389
557
  body: import("convex/values").VString<string, "required">;
558
+ authorName: import("convex/values").VString<string | undefined, "optional">;
390
559
  at: import("convex/values").VFloat64<number, "required">;
391
- }, "required", "at" | "body" | "channelId" | "serverId" | "userId">, {
392
- by_server_channel: ["serverId", "channelId", "_creationTime"];
560
+ }, "required", "at" | "authorName" | "body" | "channelId" | "serverId" | "userId">, {
561
+ by_at: ["at", "_creationTime"];
562
+ by_server_channel: ["serverId", "channelId", "at", "_creationTime"];
393
563
  }, {}, {}>;
394
564
  }, true>;
395
565
  /** Data model of the {@link jgengineTables} schema — the shape a host's own Convex ctx must satisfy to
396
566
  * call the exported persistence helpers. */
397
567
  export type JGDataModel = DataModelFromSchemaDefinition<typeof schemaForTypes>;
568
+ /** Read-only context accepted by engine host queries. */
569
+ export type JGQueryCtx = GenericQueryCtx<JGDataModel>;
398
570
  /** Mutation ctx accepted by {@link loadServerSnapshot} / {@link persistServerSnapshot}. */
399
571
  export type JGMutationCtx = GenericMutationCtx<JGDataModel>;
400
572
  /** A `jgGameServers` row — the server handle the persistence helpers load from and write back to. */
@@ -407,6 +579,10 @@ export declare const DEFAULT_CONVEX_POSE_RULES: PoseSyncRules;
407
579
  export declare function resolveActor(ctx: {
408
580
  auth: Auth;
409
581
  }, claimedExternalId: string | undefined, mode: JgAuthMode): Promise<string | null>;
582
+ /** Most live client sessions one shared-topology membership remembers; the oldest drop first. */
583
+ export declare const MAX_MEMBER_SESSIONS = 16;
584
+ /** Longest accepted client `sessionId`. */
585
+ export declare const MAX_SESSION_ID_LENGTH = 128;
410
586
  /**
411
587
  * How much of a server to hydrate. Both fields default to "everything", which is a document read per
412
588
  * member plus one per chunk — the cost that makes a large shared world unaffordable per mutation.
@@ -460,6 +636,22 @@ export type RunCommandArgs = {
460
636
  * {@link CommandDef} declares through its own `scope`; omit both to hydrate the whole world.
461
637
  */
462
638
  scope?: LoadSnapshotScope;
639
+ /**
640
+ * Trusted host override after game-specific authorization; never forward a client-supplied actor.
641
+ * The actor must be a member; when they might have no live session (a closed tab, an agent), call
642
+ * {@link GameServerHelpers.ensureJoined} first.
643
+ */
644
+ actorUserId?: string;
645
+ beforePersist?: (ctx: JGMutationCtx, loaded: LoadedServerSnapshot) => Promise<void>;
646
+ };
647
+ /** Outcome of a join: the server joined and whether the player's profile was created, or why it was refused. */
648
+ export type EnsureJoinedOutcome = {
649
+ ok: true;
650
+ serverId: GenericId<"jgGameServers">;
651
+ isNew: boolean;
652
+ } | {
653
+ ok: false;
654
+ reason: "full" | "closed" | "unauthorized";
463
655
  };
464
656
  /** A server resolved for runtime work: its row, its registered runtime, and its hydrated snapshot. */
465
657
  export type LoadedServerSnapshot = {
@@ -473,6 +665,7 @@ export type LoadedServerSnapshot = {
473
665
  * table writes in one transaction, which a pre-registered mutation cannot do.
474
666
  */
475
667
  export type GameServerHelpers = {
668
+ ensureServer: (ctx: JGMutationCtx, gameId: string) => Promise<ServerDoc>;
476
669
  loadSnapshot: (ctx: JGMutationCtx, serverId: string, scope?: LoadSnapshotScope) => Promise<LoadedServerSnapshot | null>;
477
670
  applyCommand: (args: {
478
671
  gameId: string;
@@ -490,7 +683,22 @@ export type GameServerHelpers = {
490
683
  reason: string;
491
684
  };
492
685
  persistSnapshot: (ctx: JGMutationCtx, server: ServerDoc, snapshot: GameRuntimeSnapshot, save?: SaveConfig) => Promise<boolean>;
686
+ /**
687
+ * Apply a command as the actor. Refuses non-members with "Not a member of this server"; host
688
+ * mutations acting for a user who might have no live session call {@link ensureJoined} first.
689
+ */
493
690
  runCommand: (ctx: JGMutationCtx, args: RunCommandArgs) => Promise<RunCommandOutcome>;
691
+ /**
692
+ * Join a trusted, already-authorized actor through the same path as the `joinServer` mutation.
693
+ * Idempotent: an existing member of `serverId` returns at once. Pins no client session, so it never
694
+ * keeps a membership alive past the user's own session leaves.
695
+ */
696
+ ensureJoined: (ctx: JGMutationCtx, args: {
697
+ gameId: string;
698
+ userId: string;
699
+ serverId?: string;
700
+ }) => Promise<EnsureJoinedOutcome>;
701
+ resetPlayerProfile: (ctx: JGMutationCtx, serverId: string, userId: string) => Promise<RuntimePlayerRow>;
494
702
  };
495
703
  /** How many running servers one `tickActiveServers` transaction may hydrate, tick, and persist. */
496
704
  export declare const DEFAULT_MAX_SERVERS_PER_TICK = 32;
@@ -499,10 +707,9 @@ export declare const MAX_CHUNKS_PER_QUERY = 64;
499
707
  /**
500
708
  * Build the hosted-server Convex functions for a set of game runtimes.
501
709
  *
502
- * **Supported world size.** One server is one Convex document holding the world row, the member
503
- * roster, and every member's session state, and the host rewrites that document whenever the world
504
- * changes. `slotsPerServer` is therefore capped at {@link JG_MAX_MEMBERS_PER_SERVER} (256) and a
505
- * larger value throws here, at wiring time, rather than at the write that overflows the document.
710
+ * Room topology keeps its roster and session map in the server document and caps capacity at
711
+ * {@link JG_MAX_MEMBERS_PER_SERVER}. Shared topology stores membership, capacity, and per-player
712
+ * session state separately; player-only commands and roster changes do not rewrite the world row.
506
713
  * World geometry that outgrows one row belongs in `jgWorldChunks`, hydrated by key
507
714
  * ({@link LoadSnapshotScope}) and read by clients through `getChunks` — not in `serverState`.
508
715
  *
@@ -514,6 +721,7 @@ export declare function createGameServerFunctions(options?: {
514
721
  runtimes?: GameRuntime[];
515
722
  auth?: JgAuthMode;
516
723
  slotsPerServer?: number;
724
+ topology?: "shared" | "rooms";
517
725
  matchmaking?: MatchmakingMode;
518
726
  maxServersPerTick?: number;
519
727
  allowedFeedActions?: readonly string[];
@@ -525,14 +733,13 @@ export declare function createGameServerFunctions(options?: {
525
733
  mode?: string | undefined;
526
734
  modeConfig?: any;
527
735
  serverId?: GenericId<"jgGameServers"> | undefined;
736
+ sessionId?: string | undefined;
528
737
  visibility?: "private" | "public" | undefined;
529
- }, Promise<{
530
- serverId: GenericId<"jgGameServers">;
531
- isNew: boolean;
532
- }>>;
738
+ }, Promise<EnsureJoinedOutcome>>;
533
739
  leaveServer: import("convex/server").RegisteredMutation<"public", {
534
740
  externalId?: string | undefined;
535
741
  serverId: GenericId<"jgGameServers">;
742
+ sessionId?: string | undefined;
536
743
  }, Promise<null>>;
537
744
  runCommand: import("convex/server").RegisteredMutation<"public", {
538
745
  command: string;
@@ -579,8 +786,17 @@ export declare function createGameServerFunctions(options?: {
579
786
  mode: string | undefined;
580
787
  memberCount: number;
581
788
  slotsPerServer: number;
582
- revision: number;
583
- updatedAt: number;
789
+ } | null>>;
790
+ getServerCapacity: import("convex/server").RegisteredQuery<"public", {
791
+ externalId?: string | undefined;
792
+ serverId: GenericId<"jgGameServers">;
793
+ }, Promise<{
794
+ _id: GenericId<"jgGameServers">;
795
+ gameId: string;
796
+ status: "closed" | "open" | "running";
797
+ mode: string | undefined;
798
+ memberCount: number;
799
+ slotsPerServer: number;
584
800
  } | null>>;
585
801
  getChunks: import("convex/server").RegisteredQuery<"public", {
586
802
  chunkKeys: string[];
@@ -727,6 +943,7 @@ export declare function createPresenceFunctions(options?: {
727
943
  list: import("convex/server").RegisteredQuery<"public", {
728
944
  externalId?: string | undefined;
729
945
  serverId: string;
946
+ viewerChunkKey?: string | undefined;
730
947
  }, Promise<PresenceListRow[]>>;
731
948
  sync: import("convex/server").RegisteredMutation<"public", {
732
949
  externalId?: string | undefined;
@@ -751,6 +968,111 @@ export declare function createPresenceFunctions(options?: {
751
968
  reaped: number;
752
969
  }>>;
753
970
  };
971
+ /** One active player delivered to an online-system batch. */
972
+ export type OnlinePlayer = {
973
+ serverId: string;
974
+ userId: string;
975
+ homeGameId?: string;
976
+ };
977
+ /**
978
+ * Scan one bounded presence page and schedule its handler and continuation in separate transactions.
979
+ * @capability online-player-batches Schedule bounded active-player or home-game work in separate transactions.
980
+ */
981
+ export declare function forEachOnlinePlayer(ctx: JGMutationCtx, options: {
982
+ batchSize?: number;
983
+ handler: FunctionReference<"mutation", "internal", {
984
+ players: OnlinePlayer[];
985
+ nowMs: number;
986
+ }>;
987
+ continuation: FunctionReference<"mutation", "internal", {
988
+ cursor?: string | null;
989
+ nowMs?: number;
990
+ }>;
991
+ cursor?: string | null;
992
+ nowMs?: number;
993
+ freshWindowMs?: number;
994
+ /** Deliver one representative per home game when several agents share a simulation. */
995
+ groupBy?: "userId" | "homeGameId";
996
+ }): Promise<{
997
+ scheduled: number;
998
+ remaining: boolean;
999
+ }>;
1000
+ /**
1001
+ * Consume one request in a fixed rate window; concurrent callers serialize on the indexed key.
1002
+ * @capability host-rate-limit Enforce indexed per-key request windows inside authoritative mutations.
1003
+ */
1004
+ export declare function rateLimit(ctx: JGMutationCtx, args: {
1005
+ key: string;
1006
+ windowMs: number;
1007
+ max: number;
1008
+ nowMs?: number;
1009
+ }): Promise<{
1010
+ ok: boolean;
1011
+ retryAfterMs: number;
1012
+ }>;
1013
+ /**
1014
+ * Write validated chat in a host transaction after the caller resolves its actor and world.
1015
+ * @capability host-chat-send Validate, rate-limit and persist chat inside an authorized host mutation.
1016
+ */
1017
+ export declare function sendChatMessage(ctx: JGMutationCtx, args: {
1018
+ serverId: string;
1019
+ userId: string;
1020
+ body: string;
1021
+ channelId?: string;
1022
+ authorName?: string;
1023
+ maxBodyLength?: number;
1024
+ minIntervalMs?: number;
1025
+ }): Promise<ChatSendOutcome>;
1026
+ /**
1027
+ * Bounded recent chat for a resolved server; authorization belongs to the calling query.
1028
+ * @capability host-chat-history Read bounded channel history inside an authorized host query.
1029
+ */
1030
+ export declare function recentChatMessages(ctx: {
1031
+ db: Pick<JGQueryCtx["db"], "query">;
1032
+ }, args: {
1033
+ serverId: string;
1034
+ channelId?: string;
1035
+ limit?: number;
1036
+ }): Promise<{
1037
+ _creationTime: number;
1038
+ _id: GenericId<"jgChatMessages">;
1039
+ at: number;
1040
+ authorName?: string | undefined;
1041
+ body: string;
1042
+ channelId: string;
1043
+ serverId: string;
1044
+ userId: string;
1045
+ }[]>;
1046
+ /**
1047
+ * Authenticated, bounded client diagnostics and bounded retention sweeps.
1048
+ * @capability client-error-reporting Accept authenticated bounded diagnostics with cooldown and retention sweeps.
1049
+ */
1050
+ export declare function createClientErrorFunctions(): {
1051
+ reportClientError: import("convex/server").RegisteredMutation<"public", {
1052
+ agentPrompt?: string | undefined;
1053
+ componentStack?: string | undefined;
1054
+ kind?: string | undefined;
1055
+ message: string;
1056
+ pathname?: string | undefined;
1057
+ stack?: string | undefined;
1058
+ url?: string | undefined;
1059
+ userAgent?: string | undefined;
1060
+ }, Promise<{
1061
+ ok: boolean;
1062
+ reason: string;
1063
+ } | {
1064
+ reason?: undefined;
1065
+ ok: boolean;
1066
+ }>>;
1067
+ pruneClientErrors: import("convex/server").RegisteredMutation<"internal", {}, Promise<{
1068
+ deleted: number;
1069
+ remaining: boolean;
1070
+ }>>;
1071
+ pruneRateLimits: import("convex/server").RegisteredMutation<"internal", {}, Promise<{
1072
+ deleted: number;
1073
+ remaining: boolean;
1074
+ }>>;
1075
+ };
754
1076
  /** @internal */
755
1077
  export declare function createChatFunctions(options?: {
756
1078
  auth?: JgAuthMode;
@@ -763,19 +1085,33 @@ export declare function createChatFunctions(options?: {
763
1085
  externalId?: string | undefined;
764
1086
  serverId: string;
765
1087
  }, Promise<ChatMessage[]>>;
1088
+ recent: import("convex/server").RegisteredQuery<"public", {
1089
+ channelId: string;
1090
+ externalId?: string | undefined;
1091
+ serverId: string;
1092
+ }, Promise<ChatMessage[]>>;
766
1093
  sendMessage: import("convex/server").RegisteredMutation<"public", {
767
1094
  body: string;
768
1095
  channelId: string;
769
1096
  externalId?: string | undefined;
770
1097
  serverId: string;
771
1098
  }, Promise<ChatSendOutcome>>;
1099
+ pruneChatMessages: import("convex/server").RegisteredMutation<"internal", {}, Promise<{
1100
+ deleted: number;
1101
+ remaining: boolean;
1102
+ }>>;
772
1103
  };
773
1104
  export type JgCronSpec = {
774
1105
  name: string;
775
1106
  intervalSeconds: number;
776
- functionKey: "tickActiveServers" | "flushDirtyServers" | "reapIdlePresence";
1107
+ functionKey: "tickActiveServers" | "flushDirtyServers" | "reapIdlePresence" | "pruneChatMessages" | "pruneClientErrors" | "pruneRateLimits";
777
1108
  /** Which factory's exports the function lives in, i.e. which `convex/*.ts` file to reach it through. */
778
- module: "runtime" | "presence";
1109
+ module: "runtime" | "presence" | "chat" | "clientErrors";
779
1110
  };
780
1111
  /** @internal */
781
- export declare function jgengineCronSpecs(): readonly JgCronSpec[];
1112
+ export declare function jgengineCronSpecs(options?: {
1113
+ runtimes?: readonly Pick<GameRuntime, "hasTick">[];
1114
+ intervalSeconds?: Partial<Record<JgCronSpec["functionKey"], number>>;
1115
+ chat?: boolean;
1116
+ clientErrors?: boolean;
1117
+ }): readonly JgCronSpec[];