@jgengine/convex 0.18.0 → 0.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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,89 @@ 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
+ userId: string;
95
+ }, {
96
+ serverId: import("convex/values").VId<GenericId<"jgGameServers">, "required">;
97
+ userId: import("convex/values").VString<string, "required">;
98
+ joinedAt: import("convex/values").VFloat64<number, "required">;
99
+ }, "required", "joinedAt" | "serverId" | "userId">, {
100
+ by_server: ["serverId", "_creationTime"];
101
+ by_server_and_user: ["serverId", "userId", "_creationTime"];
102
+ }, {}, {}>;
103
+ jgRateLimits: import("convex/server").TableDefinition<import("convex/values").VObject<{
104
+ count: number;
105
+ expiresAt: number;
106
+ key: string;
107
+ startedAt: number;
108
+ }, {
109
+ key: import("convex/values").VString<string, "required">;
110
+ startedAt: import("convex/values").VFloat64<number, "required">;
111
+ count: import("convex/values").VFloat64<number, "required">;
112
+ expiresAt: import("convex/values").VFloat64<number, "required">;
113
+ }, "required", "count" | "expiresAt" | "key" | "startedAt">, {
114
+ by_expires: ["expiresAt", "_creationTime"];
115
+ by_key: ["key", "_creationTime"];
116
+ }, {}, {}>;
117
+ jgClientErrors: import("convex/server").TableDefinition<import("convex/values").VObject<{
118
+ createdAt: number;
119
+ details?: any;
120
+ message: string;
121
+ userId: string;
122
+ }, {
123
+ userId: import("convex/values").VString<string, "required">;
124
+ message: import("convex/values").VString<string, "required">;
125
+ details: import("convex/values").VAny<any, "optional", string>;
126
+ createdAt: import("convex/values").VFloat64<number, "required">;
127
+ }, "required", "createdAt" | "details" | "message" | "userId" | `details.${string}`>, {
128
+ by_created: ["createdAt", "_creationTime"];
129
+ }, {}, {}>;
70
130
  jgPlayerProfiles: import("convex/server").TableDefinition<import("convex/values").VObject<{
71
131
  createdAt: number;
72
132
  dirtyAt?: number | undefined;
73
133
  gameId: string;
74
134
  playerState: any;
75
135
  revision: number;
136
+ sessionServerId?: GenericId<"jgGameServers"> | undefined;
137
+ sessionState?: any;
76
138
  updatedAt: number;
77
139
  userId: string;
78
140
  }, {
79
141
  userId: import("convex/values").VString<string, "required">;
80
142
  gameId: import("convex/values").VString<string, "required">;
81
143
  playerState: import("convex/values").VAny<any, "required", string>;
144
+ sessionState: import("convex/values").VAny<any, "optional", string>;
145
+ sessionServerId: import("convex/values").VId<GenericId<"jgGameServers"> | undefined, "optional">;
82
146
  revision: import("convex/values").VFloat64<number, "required">;
83
147
  dirtyAt: import("convex/values").VFloat64<number | undefined, "optional">;
84
148
  createdAt: import("convex/values").VFloat64<number, "required">;
85
149
  updatedAt: import("convex/values").VFloat64<number, "required">;
86
- }, "required", "createdAt" | "dirtyAt" | "gameId" | "playerState" | "revision" | "updatedAt" | "userId" | `playerState.${string}`>, {
150
+ }, "required", "createdAt" | "dirtyAt" | "gameId" | "playerState" | "revision" | "sessionServerId" | "sessionState" | "updatedAt" | "userId" | `playerState.${string}` | `sessionState.${string}`>, {
87
151
  by_user_and_game: ["userId", "gameId", "_creationTime"];
88
152
  }, {}, {}>;
89
153
  jgWorldChunks: import("convex/server").TableDefinition<import("convex/values").VObject<{
@@ -136,8 +200,12 @@ export declare function jgengineTables(): {
136
200
  by_server_and_action: ["serverId", "action", "_creationTime"];
137
201
  }, {}, {}>;
138
202
  jgPoses: import("convex/server").TableDefinition<import("convex/values").VObject<{
203
+ chunkKey?: string | undefined;
204
+ homeGameId?: string | undefined;
139
205
  kind?: string | undefined;
140
206
  label?: string | undefined;
207
+ lastWorldSnapshotAt?: number | undefined;
208
+ ownerActorId?: string | undefined;
141
209
  revokedAt?: number | undefined;
142
210
  rotationPitch: number;
143
211
  rotationY: number;
@@ -150,6 +218,10 @@ export declare function jgengineTables(): {
150
218
  z: number;
151
219
  }, {
152
220
  serverId: import("convex/values").VString<string, "required">;
221
+ homeGameId: import("convex/values").VString<string | undefined, "optional">;
222
+ ownerActorId: import("convex/values").VString<string | undefined, "optional">;
223
+ lastWorldSnapshotAt: import("convex/values").VFloat64<number | undefined, "optional">;
224
+ chunkKey: import("convex/values").VString<string | undefined, "optional">;
153
225
  userId: import("convex/values").VString<string, "required">;
154
226
  /** One row per live session, so a second tab is a second row rather than a fight over one. */
155
227
  sessionId: import("convex/values").VString<string | undefined, "optional">;
@@ -165,13 +237,21 @@ export declare function jgengineTables(): {
165
237
  updatedAt: import("convex/values").VFloat64<number, "required">;
166
238
  /** Set when the session was displaced or left; the reaper deletes the row once it goes idle. */
167
239
  revokedAt: import("convex/values").VFloat64<number | undefined, "optional">;
168
- }, "required", "kind" | "label" | "revokedAt" | "rotationPitch" | "rotationY" | "serverId" | "sessionId" | "updatedAt" | "userId" | "x" | "y" | "z">, {
240
+ }, "required", "chunkKey" | "homeGameId" | "kind" | "label" | "lastWorldSnapshotAt" | "ownerActorId" | "revokedAt" | "rotationPitch" | "rotationY" | "serverId" | "sessionId" | "updatedAt" | "userId" | "x" | "y" | "z">, {
241
+ by_home_game: ["homeGameId", "_creationTime"];
242
+ by_home_game_revoked_updated: ["homeGameId", "revokedAt", "updatedAt", "_creationTime"];
243
+ by_revoked: ["revokedAt", "_creationTime"];
244
+ by_revoked_updated: ["revokedAt", "updatedAt", "_creationTime"];
169
245
  by_server: ["serverId", "_creationTime"];
246
+ by_server_and_chunk: ["serverId", "chunkKey", "_creationTime"];
170
247
  by_server_and_user: ["serverId", "userId", "_creationTime"];
248
+ by_server_user_revoked_updated: ["serverId", "userId", "revokedAt", "updatedAt", "_creationTime"];
171
249
  by_updated: ["updatedAt", "_creationTime"];
250
+ by_user: ["userId", "_creationTime"];
172
251
  }, {}, {}>;
173
252
  jgChatMessages: import("convex/server").TableDefinition<import("convex/values").VObject<{
174
253
  at: number;
254
+ authorName?: string | undefined;
175
255
  body: string;
176
256
  channelId: string;
177
257
  serverId: string;
@@ -181,9 +261,11 @@ export declare function jgengineTables(): {
181
261
  channelId: import("convex/values").VString<string, "required">;
182
262
  userId: import("convex/values").VString<string, "required">;
183
263
  body: import("convex/values").VString<string, "required">;
264
+ authorName: import("convex/values").VString<string | undefined, "optional">;
184
265
  at: import("convex/values").VFloat64<number, "required">;
185
- }, "required", "at" | "body" | "channelId" | "serverId" | "userId">, {
186
- by_server_channel: ["serverId", "channelId", "_creationTime"];
266
+ }, "required", "at" | "authorName" | "body" | "channelId" | "serverId" | "userId">, {
267
+ by_at: ["at", "_creationTime"];
268
+ by_server_channel: ["serverId", "channelId", "at", "_creationTime"];
187
269
  }, {}, {}>;
188
270
  };
189
271
  /**
@@ -225,6 +307,7 @@ declare const schemaForTypes: import("convex/server").SchemaDefinition<{
225
307
  gameId: string;
226
308
  joinCode?: string | undefined;
227
309
  lastSavedAt?: number | undefined;
310
+ memberCount?: number | undefined;
228
311
  memberUserIds: string[];
229
312
  mode?: string | undefined;
230
313
  modeConfig?: any;
@@ -238,6 +321,7 @@ declare const schemaForTypes: import("convex/server").SchemaDefinition<{
238
321
  slotsPerServer: number;
239
322
  status: "closed" | "open" | "running";
240
323
  tickAnchorMs: number;
324
+ topology?: "rooms" | "shared" | undefined;
241
325
  updatedAt: number;
242
326
  visibility?: "private" | "public" | undefined;
243
327
  }, {
@@ -247,6 +331,8 @@ declare const schemaForTypes: import("convex/server").SchemaDefinition<{
247
331
  modeConfig: import("convex/values").VAny<any, "optional", string>;
248
332
  visibility: import("convex/values").VUnion<"private" | "public" | undefined, [import("convex/values").VLiteral<"public", "required">, import("convex/values").VLiteral<"private", "required">], "optional", never>;
249
333
  joinCode: import("convex/values").VString<string | undefined, "optional">;
334
+ topology: import("convex/values").VUnion<"rooms" | "shared" | undefined, [import("convex/values").VLiteral<"shared", "required">, import("convex/values").VLiteral<"rooms", "required">], "optional", never>;
335
+ memberCount: import("convex/values").VFloat64<number | undefined, "optional">;
250
336
  memberUserIds: import("convex/values").VArray<string[], import("convex/values").VString<string, "required">, "required">;
251
337
  slotsPerServer: import("convex/values").VFloat64<number, "required">;
252
338
  save: import("convex/values").VUnion<"none" | {
@@ -267,29 +353,89 @@ declare const schemaForTypes: import("convex/server").SchemaDefinition<{
267
353
  dirtyAt: import("convex/values").VFloat64<number | undefined, "optional">;
268
354
  createdAt: import("convex/values").VFloat64<number, "required">;
269
355
  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}`>, {
356
+ }, "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
357
  by_dirty: ["dirtyAt", "_creationTime"];
272
358
  by_game_and_status: ["gameId", "status", "_creationTime"];
273
359
  by_game_status_tick: ["gameId", "status", "tickAnchorMs", "_creationTime"];
274
360
  by_status: ["status", "_creationTime"];
275
361
  }, {}, {}>;
362
+ jgServerCapacity: import("convex/server").TableDefinition<import("convex/values").VObject<{
363
+ gameId: string;
364
+ memberCount: number;
365
+ mode?: string | undefined;
366
+ serverId: GenericId<"jgGameServers">;
367
+ slotsPerServer: number;
368
+ status: "closed" | "open" | "running";
369
+ }, {
370
+ serverId: import("convex/values").VId<GenericId<"jgGameServers">, "required">;
371
+ gameId: import("convex/values").VString<string, "required">;
372
+ 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>;
373
+ mode: import("convex/values").VString<string | undefined, "optional">;
374
+ memberCount: import("convex/values").VFloat64<number, "required">;
375
+ slotsPerServer: import("convex/values").VFloat64<number, "required">;
376
+ }, "required", "gameId" | "memberCount" | "mode" | "serverId" | "slotsPerServer" | "status">, {
377
+ by_server: ["serverId", "_creationTime"];
378
+ }, {}, {}>;
379
+ jgServerMembers: import("convex/server").TableDefinition<import("convex/values").VObject<{
380
+ joinedAt: number;
381
+ serverId: GenericId<"jgGameServers">;
382
+ userId: string;
383
+ }, {
384
+ serverId: import("convex/values").VId<GenericId<"jgGameServers">, "required">;
385
+ userId: import("convex/values").VString<string, "required">;
386
+ joinedAt: import("convex/values").VFloat64<number, "required">;
387
+ }, "required", "joinedAt" | "serverId" | "userId">, {
388
+ by_server: ["serverId", "_creationTime"];
389
+ by_server_and_user: ["serverId", "userId", "_creationTime"];
390
+ }, {}, {}>;
391
+ jgRateLimits: import("convex/server").TableDefinition<import("convex/values").VObject<{
392
+ count: number;
393
+ expiresAt: number;
394
+ key: string;
395
+ startedAt: number;
396
+ }, {
397
+ key: import("convex/values").VString<string, "required">;
398
+ startedAt: import("convex/values").VFloat64<number, "required">;
399
+ count: import("convex/values").VFloat64<number, "required">;
400
+ expiresAt: import("convex/values").VFloat64<number, "required">;
401
+ }, "required", "count" | "expiresAt" | "key" | "startedAt">, {
402
+ by_expires: ["expiresAt", "_creationTime"];
403
+ by_key: ["key", "_creationTime"];
404
+ }, {}, {}>;
405
+ jgClientErrors: import("convex/server").TableDefinition<import("convex/values").VObject<{
406
+ createdAt: number;
407
+ details?: any;
408
+ message: string;
409
+ userId: string;
410
+ }, {
411
+ userId: import("convex/values").VString<string, "required">;
412
+ message: import("convex/values").VString<string, "required">;
413
+ details: import("convex/values").VAny<any, "optional", string>;
414
+ createdAt: import("convex/values").VFloat64<number, "required">;
415
+ }, "required", "createdAt" | "details" | "message" | "userId" | `details.${string}`>, {
416
+ by_created: ["createdAt", "_creationTime"];
417
+ }, {}, {}>;
276
418
  jgPlayerProfiles: import("convex/server").TableDefinition<import("convex/values").VObject<{
277
419
  createdAt: number;
278
420
  dirtyAt?: number | undefined;
279
421
  gameId: string;
280
422
  playerState: any;
281
423
  revision: number;
424
+ sessionServerId?: GenericId<"jgGameServers"> | undefined;
425
+ sessionState?: any;
282
426
  updatedAt: number;
283
427
  userId: string;
284
428
  }, {
285
429
  userId: import("convex/values").VString<string, "required">;
286
430
  gameId: import("convex/values").VString<string, "required">;
287
431
  playerState: import("convex/values").VAny<any, "required", string>;
432
+ sessionState: import("convex/values").VAny<any, "optional", string>;
433
+ sessionServerId: import("convex/values").VId<GenericId<"jgGameServers"> | undefined, "optional">;
288
434
  revision: import("convex/values").VFloat64<number, "required">;
289
435
  dirtyAt: import("convex/values").VFloat64<number | undefined, "optional">;
290
436
  createdAt: import("convex/values").VFloat64<number, "required">;
291
437
  updatedAt: import("convex/values").VFloat64<number, "required">;
292
- }, "required", "createdAt" | "dirtyAt" | "gameId" | "playerState" | "revision" | "updatedAt" | "userId" | `playerState.${string}`>, {
438
+ }, "required", "createdAt" | "dirtyAt" | "gameId" | "playerState" | "revision" | "sessionServerId" | "sessionState" | "updatedAt" | "userId" | `playerState.${string}` | `sessionState.${string}`>, {
293
439
  by_user_and_game: ["userId", "gameId", "_creationTime"];
294
440
  }, {}, {}>;
295
441
  jgWorldChunks: import("convex/server").TableDefinition<import("convex/values").VObject<{
@@ -342,8 +488,12 @@ declare const schemaForTypes: import("convex/server").SchemaDefinition<{
342
488
  by_server_and_action: ["serverId", "action", "_creationTime"];
343
489
  }, {}, {}>;
344
490
  jgPoses: import("convex/server").TableDefinition<import("convex/values").VObject<{
491
+ chunkKey?: string | undefined;
492
+ homeGameId?: string | undefined;
345
493
  kind?: string | undefined;
346
494
  label?: string | undefined;
495
+ lastWorldSnapshotAt?: number | undefined;
496
+ ownerActorId?: string | undefined;
347
497
  revokedAt?: number | undefined;
348
498
  rotationPitch: number;
349
499
  rotationY: number;
@@ -356,6 +506,10 @@ declare const schemaForTypes: import("convex/server").SchemaDefinition<{
356
506
  z: number;
357
507
  }, {
358
508
  serverId: import("convex/values").VString<string, "required">;
509
+ homeGameId: import("convex/values").VString<string | undefined, "optional">;
510
+ ownerActorId: import("convex/values").VString<string | undefined, "optional">;
511
+ lastWorldSnapshotAt: import("convex/values").VFloat64<number | undefined, "optional">;
512
+ chunkKey: import("convex/values").VString<string | undefined, "optional">;
359
513
  userId: import("convex/values").VString<string, "required">;
360
514
  /** One row per live session, so a second tab is a second row rather than a fight over one. */
361
515
  sessionId: import("convex/values").VString<string | undefined, "optional">;
@@ -371,13 +525,21 @@ declare const schemaForTypes: import("convex/server").SchemaDefinition<{
371
525
  updatedAt: import("convex/values").VFloat64<number, "required">;
372
526
  /** Set when the session was displaced or left; the reaper deletes the row once it goes idle. */
373
527
  revokedAt: import("convex/values").VFloat64<number | undefined, "optional">;
374
- }, "required", "kind" | "label" | "revokedAt" | "rotationPitch" | "rotationY" | "serverId" | "sessionId" | "updatedAt" | "userId" | "x" | "y" | "z">, {
528
+ }, "required", "chunkKey" | "homeGameId" | "kind" | "label" | "lastWorldSnapshotAt" | "ownerActorId" | "revokedAt" | "rotationPitch" | "rotationY" | "serverId" | "sessionId" | "updatedAt" | "userId" | "x" | "y" | "z">, {
529
+ by_home_game: ["homeGameId", "_creationTime"];
530
+ by_home_game_revoked_updated: ["homeGameId", "revokedAt", "updatedAt", "_creationTime"];
531
+ by_revoked: ["revokedAt", "_creationTime"];
532
+ by_revoked_updated: ["revokedAt", "updatedAt", "_creationTime"];
375
533
  by_server: ["serverId", "_creationTime"];
534
+ by_server_and_chunk: ["serverId", "chunkKey", "_creationTime"];
376
535
  by_server_and_user: ["serverId", "userId", "_creationTime"];
536
+ by_server_user_revoked_updated: ["serverId", "userId", "revokedAt", "updatedAt", "_creationTime"];
377
537
  by_updated: ["updatedAt", "_creationTime"];
538
+ by_user: ["userId", "_creationTime"];
378
539
  }, {}, {}>;
379
540
  jgChatMessages: import("convex/server").TableDefinition<import("convex/values").VObject<{
380
541
  at: number;
542
+ authorName?: string | undefined;
381
543
  body: string;
382
544
  channelId: string;
383
545
  serverId: string;
@@ -387,14 +549,18 @@ declare const schemaForTypes: import("convex/server").SchemaDefinition<{
387
549
  channelId: import("convex/values").VString<string, "required">;
388
550
  userId: import("convex/values").VString<string, "required">;
389
551
  body: import("convex/values").VString<string, "required">;
552
+ authorName: import("convex/values").VString<string | undefined, "optional">;
390
553
  at: import("convex/values").VFloat64<number, "required">;
391
- }, "required", "at" | "body" | "channelId" | "serverId" | "userId">, {
392
- by_server_channel: ["serverId", "channelId", "_creationTime"];
554
+ }, "required", "at" | "authorName" | "body" | "channelId" | "serverId" | "userId">, {
555
+ by_at: ["at", "_creationTime"];
556
+ by_server_channel: ["serverId", "channelId", "at", "_creationTime"];
393
557
  }, {}, {}>;
394
558
  }, true>;
395
559
  /** Data model of the {@link jgengineTables} schema — the shape a host's own Convex ctx must satisfy to
396
560
  * call the exported persistence helpers. */
397
561
  export type JGDataModel = DataModelFromSchemaDefinition<typeof schemaForTypes>;
562
+ /** Read-only context accepted by engine host queries. */
563
+ export type JGQueryCtx = GenericQueryCtx<JGDataModel>;
398
564
  /** Mutation ctx accepted by {@link loadServerSnapshot} / {@link persistServerSnapshot}. */
399
565
  export type JGMutationCtx = GenericMutationCtx<JGDataModel>;
400
566
  /** A `jgGameServers` row — the server handle the persistence helpers load from and write back to. */
@@ -460,6 +626,9 @@ export type RunCommandArgs = {
460
626
  * {@link CommandDef} declares through its own `scope`; omit both to hydrate the whole world.
461
627
  */
462
628
  scope?: LoadSnapshotScope;
629
+ /** Trusted host override after game-specific authorization; never forward a client-supplied actor. */
630
+ actorUserId?: string;
631
+ beforePersist?: (ctx: JGMutationCtx, loaded: LoadedServerSnapshot) => Promise<void>;
463
632
  };
464
633
  /** A server resolved for runtime work: its row, its registered runtime, and its hydrated snapshot. */
465
634
  export type LoadedServerSnapshot = {
@@ -473,6 +642,7 @@ export type LoadedServerSnapshot = {
473
642
  * table writes in one transaction, which a pre-registered mutation cannot do.
474
643
  */
475
644
  export type GameServerHelpers = {
645
+ ensureServer: (ctx: JGMutationCtx, gameId: string) => Promise<ServerDoc>;
476
646
  loadSnapshot: (ctx: JGMutationCtx, serverId: string, scope?: LoadSnapshotScope) => Promise<LoadedServerSnapshot | null>;
477
647
  applyCommand: (args: {
478
648
  gameId: string;
@@ -491,6 +661,7 @@ export type GameServerHelpers = {
491
661
  };
492
662
  persistSnapshot: (ctx: JGMutationCtx, server: ServerDoc, snapshot: GameRuntimeSnapshot, save?: SaveConfig) => Promise<boolean>;
493
663
  runCommand: (ctx: JGMutationCtx, args: RunCommandArgs) => Promise<RunCommandOutcome>;
664
+ resetPlayerProfile: (ctx: JGMutationCtx, serverId: string, userId: string) => Promise<RuntimePlayerRow>;
494
665
  };
495
666
  /** How many running servers one `tickActiveServers` transaction may hydrate, tick, and persist. */
496
667
  export declare const DEFAULT_MAX_SERVERS_PER_TICK = 32;
@@ -499,10 +670,9 @@ export declare const MAX_CHUNKS_PER_QUERY = 64;
499
670
  /**
500
671
  * Build the hosted-server Convex functions for a set of game runtimes.
501
672
  *
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.
673
+ * Room topology keeps its roster and session map in the server document and caps capacity at
674
+ * {@link JG_MAX_MEMBERS_PER_SERVER}. Shared topology stores membership, capacity, and per-player
675
+ * session state separately; player-only commands and roster changes do not rewrite the world row.
506
676
  * World geometry that outgrows one row belongs in `jgWorldChunks`, hydrated by key
507
677
  * ({@link LoadSnapshotScope}) and read by clients through `getChunks` — not in `serverState`.
508
678
  *
@@ -514,6 +684,7 @@ export declare function createGameServerFunctions(options?: {
514
684
  runtimes?: GameRuntime[];
515
685
  auth?: JgAuthMode;
516
686
  slotsPerServer?: number;
687
+ topology?: "shared" | "rooms";
517
688
  matchmaking?: MatchmakingMode;
518
689
  maxServersPerTick?: number;
519
690
  allowedFeedActions?: readonly string[];
@@ -527,6 +698,23 @@ export declare function createGameServerFunctions(options?: {
527
698
  serverId?: GenericId<"jgGameServers"> | undefined;
528
699
  visibility?: "private" | "public" | undefined;
529
700
  }, Promise<{
701
+ ok: false;
702
+ reason: "unauthorized";
703
+ serverId?: undefined;
704
+ isNew?: undefined;
705
+ } | {
706
+ ok: false;
707
+ reason: "closed";
708
+ serverId?: undefined;
709
+ isNew?: undefined;
710
+ } | {
711
+ ok: false;
712
+ reason: "full";
713
+ serverId?: undefined;
714
+ isNew?: undefined;
715
+ } | {
716
+ reason?: undefined;
717
+ ok: true;
530
718
  serverId: GenericId<"jgGameServers">;
531
719
  isNew: boolean;
532
720
  }>>;
@@ -579,8 +767,17 @@ export declare function createGameServerFunctions(options?: {
579
767
  mode: string | undefined;
580
768
  memberCount: number;
581
769
  slotsPerServer: number;
582
- revision: number;
583
- updatedAt: number;
770
+ } | null>>;
771
+ getServerCapacity: import("convex/server").RegisteredQuery<"public", {
772
+ externalId?: string | undefined;
773
+ serverId: GenericId<"jgGameServers">;
774
+ }, Promise<{
775
+ _id: GenericId<"jgGameServers">;
776
+ gameId: string;
777
+ status: "closed" | "open" | "running";
778
+ mode: string | undefined;
779
+ memberCount: number;
780
+ slotsPerServer: number;
584
781
  } | null>>;
585
782
  getChunks: import("convex/server").RegisteredQuery<"public", {
586
783
  chunkKeys: string[];
@@ -727,6 +924,7 @@ export declare function createPresenceFunctions(options?: {
727
924
  list: import("convex/server").RegisteredQuery<"public", {
728
925
  externalId?: string | undefined;
729
926
  serverId: string;
927
+ viewerChunkKey?: string | undefined;
730
928
  }, Promise<PresenceListRow[]>>;
731
929
  sync: import("convex/server").RegisteredMutation<"public", {
732
930
  externalId?: string | undefined;
@@ -751,6 +949,111 @@ export declare function createPresenceFunctions(options?: {
751
949
  reaped: number;
752
950
  }>>;
753
951
  };
952
+ /** One active player delivered to an online-system batch. */
953
+ export type OnlinePlayer = {
954
+ serverId: string;
955
+ userId: string;
956
+ homeGameId?: string;
957
+ };
958
+ /**
959
+ * Scan one bounded presence page and schedule its handler and continuation in separate transactions.
960
+ * @capability online-player-batches Schedule bounded active-player or home-game work in separate transactions.
961
+ */
962
+ export declare function forEachOnlinePlayer(ctx: JGMutationCtx, options: {
963
+ batchSize?: number;
964
+ handler: FunctionReference<"mutation", "internal", {
965
+ players: OnlinePlayer[];
966
+ nowMs: number;
967
+ }>;
968
+ continuation: FunctionReference<"mutation", "internal", {
969
+ cursor?: string | null;
970
+ nowMs?: number;
971
+ }>;
972
+ cursor?: string | null;
973
+ nowMs?: number;
974
+ freshWindowMs?: number;
975
+ /** Deliver one representative per home game when several agents share a simulation. */
976
+ groupBy?: "userId" | "homeGameId";
977
+ }): Promise<{
978
+ scheduled: number;
979
+ remaining: boolean;
980
+ }>;
981
+ /**
982
+ * Consume one request in a fixed rate window; concurrent callers serialize on the indexed key.
983
+ * @capability host-rate-limit Enforce indexed per-key request windows inside authoritative mutations.
984
+ */
985
+ export declare function rateLimit(ctx: JGMutationCtx, args: {
986
+ key: string;
987
+ windowMs: number;
988
+ max: number;
989
+ nowMs?: number;
990
+ }): Promise<{
991
+ ok: boolean;
992
+ retryAfterMs: number;
993
+ }>;
994
+ /**
995
+ * Write validated chat in a host transaction after the caller resolves its actor and world.
996
+ * @capability host-chat-send Validate, rate-limit and persist chat inside an authorized host mutation.
997
+ */
998
+ export declare function sendChatMessage(ctx: JGMutationCtx, args: {
999
+ serverId: string;
1000
+ userId: string;
1001
+ body: string;
1002
+ channelId?: string;
1003
+ authorName?: string;
1004
+ maxBodyLength?: number;
1005
+ minIntervalMs?: number;
1006
+ }): Promise<ChatSendOutcome>;
1007
+ /**
1008
+ * Bounded recent chat for a resolved server; authorization belongs to the calling query.
1009
+ * @capability host-chat-history Read bounded channel history inside an authorized host query.
1010
+ */
1011
+ export declare function recentChatMessages(ctx: {
1012
+ db: Pick<JGQueryCtx["db"], "query">;
1013
+ }, args: {
1014
+ serverId: string;
1015
+ channelId?: string;
1016
+ limit?: number;
1017
+ }): Promise<{
1018
+ _creationTime: number;
1019
+ _id: GenericId<"jgChatMessages">;
1020
+ at: number;
1021
+ authorName?: string | undefined;
1022
+ body: string;
1023
+ channelId: string;
1024
+ serverId: string;
1025
+ userId: string;
1026
+ }[]>;
1027
+ /**
1028
+ * Authenticated, bounded client diagnostics and bounded retention sweeps.
1029
+ * @capability client-error-reporting Accept authenticated bounded diagnostics with cooldown and retention sweeps.
1030
+ */
1031
+ export declare function createClientErrorFunctions(): {
1032
+ reportClientError: import("convex/server").RegisteredMutation<"public", {
1033
+ agentPrompt?: string | undefined;
1034
+ componentStack?: string | undefined;
1035
+ kind?: string | undefined;
1036
+ message: string;
1037
+ pathname?: string | undefined;
1038
+ stack?: string | undefined;
1039
+ url?: string | undefined;
1040
+ userAgent?: string | undefined;
1041
+ }, Promise<{
1042
+ ok: boolean;
1043
+ reason: string;
1044
+ } | {
1045
+ reason?: undefined;
1046
+ ok: boolean;
1047
+ }>>;
1048
+ pruneClientErrors: import("convex/server").RegisteredMutation<"internal", {}, Promise<{
1049
+ deleted: number;
1050
+ remaining: boolean;
1051
+ }>>;
1052
+ pruneRateLimits: import("convex/server").RegisteredMutation<"internal", {}, Promise<{
1053
+ deleted: number;
1054
+ remaining: boolean;
1055
+ }>>;
1056
+ };
754
1057
  /** @internal */
755
1058
  export declare function createChatFunctions(options?: {
756
1059
  auth?: JgAuthMode;
@@ -763,19 +1066,33 @@ export declare function createChatFunctions(options?: {
763
1066
  externalId?: string | undefined;
764
1067
  serverId: string;
765
1068
  }, Promise<ChatMessage[]>>;
1069
+ recent: import("convex/server").RegisteredQuery<"public", {
1070
+ channelId: string;
1071
+ externalId?: string | undefined;
1072
+ serverId: string;
1073
+ }, Promise<ChatMessage[]>>;
766
1074
  sendMessage: import("convex/server").RegisteredMutation<"public", {
767
1075
  body: string;
768
1076
  channelId: string;
769
1077
  externalId?: string | undefined;
770
1078
  serverId: string;
771
1079
  }, Promise<ChatSendOutcome>>;
1080
+ pruneChatMessages: import("convex/server").RegisteredMutation<"internal", {}, Promise<{
1081
+ deleted: number;
1082
+ remaining: boolean;
1083
+ }>>;
772
1084
  };
773
1085
  export type JgCronSpec = {
774
1086
  name: string;
775
1087
  intervalSeconds: number;
776
- functionKey: "tickActiveServers" | "flushDirtyServers" | "reapIdlePresence";
1088
+ functionKey: "tickActiveServers" | "flushDirtyServers" | "reapIdlePresence" | "pruneChatMessages" | "pruneClientErrors" | "pruneRateLimits";
777
1089
  /** Which factory's exports the function lives in, i.e. which `convex/*.ts` file to reach it through. */
778
- module: "runtime" | "presence";
1090
+ module: "runtime" | "presence" | "chat" | "clientErrors";
779
1091
  };
780
1092
  /** @internal */
781
- export declare function jgengineCronSpecs(): readonly JgCronSpec[];
1093
+ export declare function jgengineCronSpecs(options?: {
1094
+ runtimes?: readonly Pick<GameRuntime, "hasTick">[];
1095
+ intervalSeconds?: Partial<Record<JgCronSpec["functionKey"], number>>;
1096
+ chat?: boolean;
1097
+ clientErrors?: boolean;
1098
+ }): readonly JgCronSpec[];