@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/CHANGELOG.md +114 -2
- package/dist/convexPresenceTransport.d.ts +2 -6
- package/dist/convexPresenceTransport.js +2 -2
- package/dist/createConvexGameTransport.d.ts +13 -11
- package/dist/createConvexGameTransport.js +54 -3
- package/dist/hostedServer.d.ts +1 -1
- package/dist/hostedServer.js +12 -12
- package/dist/server.d.ts +339 -22
- package/dist/server.js +427 -118
- package/dist/territory.d.ts +20 -0
- package/dist/territory.js +47 -0
- package/dist/worldPresence.d.ts +68 -0
- package/dist/worldPresence.js +128 -0
- package/package.json +2 -2
- package/skills/ce-handoff/SKILL.md +1 -1
- package/skills/jgengine/SKILL.md +2 -0
- package/skills/jgengine/api.md +94 -19
- package/skills/jgengine/capabilities.md +28 -0
- package/skills/jgengine-multiplayer/SKILL.md +25 -0
- package/skills/jgengine-multiplayer/api.md +78 -36
- package/skills/jgengine-multiplayer/capabilities.md +37 -0
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
503
|
-
*
|
|
504
|
-
*
|
|
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
|
-
|
|
583
|
-
|
|
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(
|
|
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[];
|