@jgengine/convex 0.17.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 +130 -2
- package/README.md +2 -2
- package/dist/convexPresenceTransport.d.ts +8 -6
- package/dist/convexPresenceTransport.js +2 -2
- package/dist/createConvexGameTransport.d.ts +29 -13
- package/dist/createConvexGameTransport.js +69 -5
- package/dist/hostedServer.d.ts +1 -1
- package/dist/hostedServer.js +12 -12
- package/dist/server.d.ts +447 -43
- package/dist/server.js +581 -151
- 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 +98 -22
- package/skills/jgengine/capabilities.md +28 -0
- package/skills/jgengine-multiplayer/SKILL.md +25 -0
- package/skills/jgengine-multiplayer/api.md +88 -42
- package/skills/jgengine-multiplayer/capabilities.md +37 -0
- package/skills/jgengine-multiplayer/reference.md +5 -1
package/dist/server.d.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
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
|
+
import type { CommandScope } from "@jgengine/core/runtime/commandRunner";
|
|
6
7
|
import type { GameRuntime } from "@jgengine/core/runtime/gameRuntime";
|
|
7
8
|
import type { LeaderboardIncrement } from "@jgengine/core/runtime/hostPersistence";
|
|
8
9
|
import { type MatchmakingMode } from "@jgengine/core/runtime/hostPolicy";
|
|
9
10
|
import type { SaveConfig } from "@jgengine/core/runtime/save";
|
|
10
|
-
import type { GameRuntimeSnapshot } from "@jgengine/core/runtime/snapshot";
|
|
11
|
+
import type { GameRuntimeSnapshot, RuntimePlayerRow } from "@jgengine/core/runtime/snapshot";
|
|
11
12
|
/** @internal Re-export shared host-policy pure helpers so existing convex imports keep working. */
|
|
12
13
|
export { JG_MAX_MEMBERS_PER_SERVER, canJoinPrivateServer, isListablePublicly, validateSlotsPerServer, type MatchmakingMode, } from "@jgengine/core/runtime/hostPolicy";
|
|
13
14
|
/** @internal */
|
|
@@ -18,6 +19,7 @@ export declare function jgengineTables(): {
|
|
|
18
19
|
gameId: string;
|
|
19
20
|
joinCode?: string | undefined;
|
|
20
21
|
lastSavedAt?: number | undefined;
|
|
22
|
+
memberCount?: number | undefined;
|
|
21
23
|
memberUserIds: string[];
|
|
22
24
|
mode?: string | undefined;
|
|
23
25
|
modeConfig?: any;
|
|
@@ -31,6 +33,7 @@ export declare function jgengineTables(): {
|
|
|
31
33
|
slotsPerServer: number;
|
|
32
34
|
status: "closed" | "open" | "running";
|
|
33
35
|
tickAnchorMs: number;
|
|
36
|
+
topology?: "rooms" | "shared" | undefined;
|
|
34
37
|
updatedAt: number;
|
|
35
38
|
visibility?: "private" | "public" | undefined;
|
|
36
39
|
}, {
|
|
@@ -40,6 +43,8 @@ export declare function jgengineTables(): {
|
|
|
40
43
|
modeConfig: import("convex/values").VAny<any, "optional", string>;
|
|
41
44
|
visibility: import("convex/values").VUnion<"private" | "public" | undefined, [import("convex/values").VLiteral<"public", "required">, import("convex/values").VLiteral<"private", "required">], "optional", never>;
|
|
42
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">;
|
|
43
48
|
memberUserIds: import("convex/values").VArray<string[], import("convex/values").VString<string, "required">, "required">;
|
|
44
49
|
slotsPerServer: import("convex/values").VFloat64<number, "required">;
|
|
45
50
|
save: import("convex/values").VUnion<"none" | {
|
|
@@ -60,29 +65,89 @@ export declare function jgengineTables(): {
|
|
|
60
65
|
dirtyAt: import("convex/values").VFloat64<number | undefined, "optional">;
|
|
61
66
|
createdAt: import("convex/values").VFloat64<number, "required">;
|
|
62
67
|
updatedAt: import("convex/values").VFloat64<number, "required">;
|
|
63
|
-
}, "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}`>, {
|
|
64
69
|
by_dirty: ["dirtyAt", "_creationTime"];
|
|
65
70
|
by_game_and_status: ["gameId", "status", "_creationTime"];
|
|
66
71
|
by_game_status_tick: ["gameId", "status", "tickAnchorMs", "_creationTime"];
|
|
67
72
|
by_status: ["status", "_creationTime"];
|
|
68
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
|
+
}, {}, {}>;
|
|
69
130
|
jgPlayerProfiles: import("convex/server").TableDefinition<import("convex/values").VObject<{
|
|
70
131
|
createdAt: number;
|
|
71
132
|
dirtyAt?: number | undefined;
|
|
72
133
|
gameId: string;
|
|
73
134
|
playerState: any;
|
|
74
135
|
revision: number;
|
|
136
|
+
sessionServerId?: GenericId<"jgGameServers"> | undefined;
|
|
137
|
+
sessionState?: any;
|
|
75
138
|
updatedAt: number;
|
|
76
139
|
userId: string;
|
|
77
140
|
}, {
|
|
78
141
|
userId: import("convex/values").VString<string, "required">;
|
|
79
142
|
gameId: import("convex/values").VString<string, "required">;
|
|
80
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">;
|
|
81
146
|
revision: import("convex/values").VFloat64<number, "required">;
|
|
82
147
|
dirtyAt: import("convex/values").VFloat64<number | undefined, "optional">;
|
|
83
148
|
createdAt: import("convex/values").VFloat64<number, "required">;
|
|
84
149
|
updatedAt: import("convex/values").VFloat64<number, "required">;
|
|
85
|
-
}, "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}`>, {
|
|
86
151
|
by_user_and_game: ["userId", "gameId", "_creationTime"];
|
|
87
152
|
}, {}, {}>;
|
|
88
153
|
jgWorldChunks: import("convex/server").TableDefinition<import("convex/values").VObject<{
|
|
@@ -135,9 +200,17 @@ export declare function jgengineTables(): {
|
|
|
135
200
|
by_server_and_action: ["serverId", "action", "_creationTime"];
|
|
136
201
|
}, {}, {}>;
|
|
137
202
|
jgPoses: import("convex/server").TableDefinition<import("convex/values").VObject<{
|
|
203
|
+
chunkKey?: string | undefined;
|
|
204
|
+
homeGameId?: string | undefined;
|
|
205
|
+
kind?: string | undefined;
|
|
206
|
+
label?: string | undefined;
|
|
207
|
+
lastWorldSnapshotAt?: number | undefined;
|
|
208
|
+
ownerActorId?: string | undefined;
|
|
209
|
+
revokedAt?: number | undefined;
|
|
138
210
|
rotationPitch: number;
|
|
139
211
|
rotationY: number;
|
|
140
212
|
serverId: string;
|
|
213
|
+
sessionId?: string | undefined;
|
|
141
214
|
updatedAt: number;
|
|
142
215
|
userId: string;
|
|
143
216
|
x: number;
|
|
@@ -145,19 +218,40 @@ export declare function jgengineTables(): {
|
|
|
145
218
|
z: number;
|
|
146
219
|
}, {
|
|
147
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">;
|
|
148
225
|
userId: import("convex/values").VString<string, "required">;
|
|
226
|
+
/** One row per live session, so a second tab is a second row rather than a fight over one. */
|
|
227
|
+
sessionId: import("convex/values").VString<string | undefined, "optional">;
|
|
228
|
+
/** Actor class, e.g. `"player"` / `"agent"`, which selects the pose rules applied to it. */
|
|
229
|
+
kind: import("convex/values").VString<string | undefined, "optional">;
|
|
230
|
+
/** Display name carried on the row, so nameplates need no per-frame join against users. */
|
|
231
|
+
label: import("convex/values").VString<string | undefined, "optional">;
|
|
149
232
|
x: import("convex/values").VFloat64<number, "required">;
|
|
150
233
|
y: import("convex/values").VFloat64<number, "required">;
|
|
151
234
|
z: import("convex/values").VFloat64<number, "required">;
|
|
152
235
|
rotationY: import("convex/values").VFloat64<number, "required">;
|
|
153
236
|
rotationPitch: import("convex/values").VFloat64<number, "required">;
|
|
154
237
|
updatedAt: import("convex/values").VFloat64<number, "required">;
|
|
155
|
-
|
|
238
|
+
/** Set when the session was displaced or left; the reaper deletes the row once it goes idle. */
|
|
239
|
+
revokedAt: import("convex/values").VFloat64<number | undefined, "optional">;
|
|
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"];
|
|
156
245
|
by_server: ["serverId", "_creationTime"];
|
|
246
|
+
by_server_and_chunk: ["serverId", "chunkKey", "_creationTime"];
|
|
157
247
|
by_server_and_user: ["serverId", "userId", "_creationTime"];
|
|
248
|
+
by_server_user_revoked_updated: ["serverId", "userId", "revokedAt", "updatedAt", "_creationTime"];
|
|
249
|
+
by_updated: ["updatedAt", "_creationTime"];
|
|
250
|
+
by_user: ["userId", "_creationTime"];
|
|
158
251
|
}, {}, {}>;
|
|
159
252
|
jgChatMessages: import("convex/server").TableDefinition<import("convex/values").VObject<{
|
|
160
253
|
at: number;
|
|
254
|
+
authorName?: string | undefined;
|
|
161
255
|
body: string;
|
|
162
256
|
channelId: string;
|
|
163
257
|
serverId: string;
|
|
@@ -167,9 +261,11 @@ export declare function jgengineTables(): {
|
|
|
167
261
|
channelId: import("convex/values").VString<string, "required">;
|
|
168
262
|
userId: import("convex/values").VString<string, "required">;
|
|
169
263
|
body: import("convex/values").VString<string, "required">;
|
|
264
|
+
authorName: import("convex/values").VString<string | undefined, "optional">;
|
|
170
265
|
at: import("convex/values").VFloat64<number, "required">;
|
|
171
|
-
}, "required", "at" | "body" | "channelId" | "serverId" | "userId">, {
|
|
172
|
-
|
|
266
|
+
}, "required", "at" | "authorName" | "body" | "channelId" | "serverId" | "userId">, {
|
|
267
|
+
by_at: ["at", "_creationTime"];
|
|
268
|
+
by_server_channel: ["serverId", "channelId", "at", "_creationTime"];
|
|
173
269
|
}, {}, {}>;
|
|
174
270
|
};
|
|
175
271
|
/**
|
|
@@ -211,6 +307,7 @@ declare const schemaForTypes: import("convex/server").SchemaDefinition<{
|
|
|
211
307
|
gameId: string;
|
|
212
308
|
joinCode?: string | undefined;
|
|
213
309
|
lastSavedAt?: number | undefined;
|
|
310
|
+
memberCount?: number | undefined;
|
|
214
311
|
memberUserIds: string[];
|
|
215
312
|
mode?: string | undefined;
|
|
216
313
|
modeConfig?: any;
|
|
@@ -224,6 +321,7 @@ declare const schemaForTypes: import("convex/server").SchemaDefinition<{
|
|
|
224
321
|
slotsPerServer: number;
|
|
225
322
|
status: "closed" | "open" | "running";
|
|
226
323
|
tickAnchorMs: number;
|
|
324
|
+
topology?: "rooms" | "shared" | undefined;
|
|
227
325
|
updatedAt: number;
|
|
228
326
|
visibility?: "private" | "public" | undefined;
|
|
229
327
|
}, {
|
|
@@ -233,6 +331,8 @@ declare const schemaForTypes: import("convex/server").SchemaDefinition<{
|
|
|
233
331
|
modeConfig: import("convex/values").VAny<any, "optional", string>;
|
|
234
332
|
visibility: import("convex/values").VUnion<"private" | "public" | undefined, [import("convex/values").VLiteral<"public", "required">, import("convex/values").VLiteral<"private", "required">], "optional", never>;
|
|
235
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">;
|
|
236
336
|
memberUserIds: import("convex/values").VArray<string[], import("convex/values").VString<string, "required">, "required">;
|
|
237
337
|
slotsPerServer: import("convex/values").VFloat64<number, "required">;
|
|
238
338
|
save: import("convex/values").VUnion<"none" | {
|
|
@@ -253,29 +353,89 @@ declare const schemaForTypes: import("convex/server").SchemaDefinition<{
|
|
|
253
353
|
dirtyAt: import("convex/values").VFloat64<number | undefined, "optional">;
|
|
254
354
|
createdAt: import("convex/values").VFloat64<number, "required">;
|
|
255
355
|
updatedAt: import("convex/values").VFloat64<number, "required">;
|
|
256
|
-
}, "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}`>, {
|
|
257
357
|
by_dirty: ["dirtyAt", "_creationTime"];
|
|
258
358
|
by_game_and_status: ["gameId", "status", "_creationTime"];
|
|
259
359
|
by_game_status_tick: ["gameId", "status", "tickAnchorMs", "_creationTime"];
|
|
260
360
|
by_status: ["status", "_creationTime"];
|
|
261
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
|
+
}, {}, {}>;
|
|
262
418
|
jgPlayerProfiles: import("convex/server").TableDefinition<import("convex/values").VObject<{
|
|
263
419
|
createdAt: number;
|
|
264
420
|
dirtyAt?: number | undefined;
|
|
265
421
|
gameId: string;
|
|
266
422
|
playerState: any;
|
|
267
423
|
revision: number;
|
|
424
|
+
sessionServerId?: GenericId<"jgGameServers"> | undefined;
|
|
425
|
+
sessionState?: any;
|
|
268
426
|
updatedAt: number;
|
|
269
427
|
userId: string;
|
|
270
428
|
}, {
|
|
271
429
|
userId: import("convex/values").VString<string, "required">;
|
|
272
430
|
gameId: import("convex/values").VString<string, "required">;
|
|
273
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">;
|
|
274
434
|
revision: import("convex/values").VFloat64<number, "required">;
|
|
275
435
|
dirtyAt: import("convex/values").VFloat64<number | undefined, "optional">;
|
|
276
436
|
createdAt: import("convex/values").VFloat64<number, "required">;
|
|
277
437
|
updatedAt: import("convex/values").VFloat64<number, "required">;
|
|
278
|
-
}, "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}`>, {
|
|
279
439
|
by_user_and_game: ["userId", "gameId", "_creationTime"];
|
|
280
440
|
}, {}, {}>;
|
|
281
441
|
jgWorldChunks: import("convex/server").TableDefinition<import("convex/values").VObject<{
|
|
@@ -328,9 +488,17 @@ declare const schemaForTypes: import("convex/server").SchemaDefinition<{
|
|
|
328
488
|
by_server_and_action: ["serverId", "action", "_creationTime"];
|
|
329
489
|
}, {}, {}>;
|
|
330
490
|
jgPoses: import("convex/server").TableDefinition<import("convex/values").VObject<{
|
|
491
|
+
chunkKey?: string | undefined;
|
|
492
|
+
homeGameId?: string | undefined;
|
|
493
|
+
kind?: string | undefined;
|
|
494
|
+
label?: string | undefined;
|
|
495
|
+
lastWorldSnapshotAt?: number | undefined;
|
|
496
|
+
ownerActorId?: string | undefined;
|
|
497
|
+
revokedAt?: number | undefined;
|
|
331
498
|
rotationPitch: number;
|
|
332
499
|
rotationY: number;
|
|
333
500
|
serverId: string;
|
|
501
|
+
sessionId?: string | undefined;
|
|
334
502
|
updatedAt: number;
|
|
335
503
|
userId: string;
|
|
336
504
|
x: number;
|
|
@@ -338,19 +506,40 @@ declare const schemaForTypes: import("convex/server").SchemaDefinition<{
|
|
|
338
506
|
z: number;
|
|
339
507
|
}, {
|
|
340
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">;
|
|
341
513
|
userId: import("convex/values").VString<string, "required">;
|
|
514
|
+
/** One row per live session, so a second tab is a second row rather than a fight over one. */
|
|
515
|
+
sessionId: import("convex/values").VString<string | undefined, "optional">;
|
|
516
|
+
/** Actor class, e.g. `"player"` / `"agent"`, which selects the pose rules applied to it. */
|
|
517
|
+
kind: import("convex/values").VString<string | undefined, "optional">;
|
|
518
|
+
/** Display name carried on the row, so nameplates need no per-frame join against users. */
|
|
519
|
+
label: import("convex/values").VString<string | undefined, "optional">;
|
|
342
520
|
x: import("convex/values").VFloat64<number, "required">;
|
|
343
521
|
y: import("convex/values").VFloat64<number, "required">;
|
|
344
522
|
z: import("convex/values").VFloat64<number, "required">;
|
|
345
523
|
rotationY: import("convex/values").VFloat64<number, "required">;
|
|
346
524
|
rotationPitch: import("convex/values").VFloat64<number, "required">;
|
|
347
525
|
updatedAt: import("convex/values").VFloat64<number, "required">;
|
|
348
|
-
|
|
526
|
+
/** Set when the session was displaced or left; the reaper deletes the row once it goes idle. */
|
|
527
|
+
revokedAt: import("convex/values").VFloat64<number | undefined, "optional">;
|
|
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"];
|
|
349
533
|
by_server: ["serverId", "_creationTime"];
|
|
534
|
+
by_server_and_chunk: ["serverId", "chunkKey", "_creationTime"];
|
|
350
535
|
by_server_and_user: ["serverId", "userId", "_creationTime"];
|
|
536
|
+
by_server_user_revoked_updated: ["serverId", "userId", "revokedAt", "updatedAt", "_creationTime"];
|
|
537
|
+
by_updated: ["updatedAt", "_creationTime"];
|
|
538
|
+
by_user: ["userId", "_creationTime"];
|
|
351
539
|
}, {}, {}>;
|
|
352
540
|
jgChatMessages: import("convex/server").TableDefinition<import("convex/values").VObject<{
|
|
353
541
|
at: number;
|
|
542
|
+
authorName?: string | undefined;
|
|
354
543
|
body: string;
|
|
355
544
|
channelId: string;
|
|
356
545
|
serverId: string;
|
|
@@ -360,14 +549,18 @@ declare const schemaForTypes: import("convex/server").SchemaDefinition<{
|
|
|
360
549
|
channelId: import("convex/values").VString<string, "required">;
|
|
361
550
|
userId: import("convex/values").VString<string, "required">;
|
|
362
551
|
body: import("convex/values").VString<string, "required">;
|
|
552
|
+
authorName: import("convex/values").VString<string | undefined, "optional">;
|
|
363
553
|
at: import("convex/values").VFloat64<number, "required">;
|
|
364
|
-
}, "required", "at" | "body" | "channelId" | "serverId" | "userId">, {
|
|
365
|
-
|
|
554
|
+
}, "required", "at" | "authorName" | "body" | "channelId" | "serverId" | "userId">, {
|
|
555
|
+
by_at: ["at", "_creationTime"];
|
|
556
|
+
by_server_channel: ["serverId", "channelId", "at", "_creationTime"];
|
|
366
557
|
}, {}, {}>;
|
|
367
558
|
}, true>;
|
|
368
559
|
/** Data model of the {@link jgengineTables} schema — the shape a host's own Convex ctx must satisfy to
|
|
369
560
|
* call the exported persistence helpers. */
|
|
370
561
|
export type JGDataModel = DataModelFromSchemaDefinition<typeof schemaForTypes>;
|
|
562
|
+
/** Read-only context accepted by engine host queries. */
|
|
563
|
+
export type JGQueryCtx = GenericQueryCtx<JGDataModel>;
|
|
371
564
|
/** Mutation ctx accepted by {@link loadServerSnapshot} / {@link persistServerSnapshot}. */
|
|
372
565
|
export type JGMutationCtx = GenericMutationCtx<JGDataModel>;
|
|
373
566
|
/** A `jgGameServers` row — the server handle the persistence helpers load from and write back to. */
|
|
@@ -389,13 +582,11 @@ export declare function resolveActor(ctx: {
|
|
|
389
582
|
*
|
|
390
583
|
* Persisting a narrowed snapshot is safe: the plan merges session state over the stored map and
|
|
391
584
|
* writes only dirty profiles and dirty chunks, so members and chunks left unhydrated are untouched.
|
|
585
|
+
*
|
|
586
|
+
* The registered `runCommand` mutation takes one, and a {@link CommandDef} can derive its own from
|
|
587
|
+
* its input via `scope`, so a game gets bounded hydration without writing its own mutation.
|
|
392
588
|
*/
|
|
393
|
-
export type LoadSnapshotScope =
|
|
394
|
-
/** Member ids to hydrate; omit for the whole roster. */
|
|
395
|
-
players?: readonly string[];
|
|
396
|
-
/** Chunk keys to hydrate; omit for every chunk of the server, `[]` for none. */
|
|
397
|
-
chunkKeys?: readonly string[];
|
|
398
|
-
};
|
|
589
|
+
export type LoadSnapshotScope = CommandScope;
|
|
399
590
|
/**
|
|
400
591
|
* Hydrate a server's `GameRuntimeSnapshot` from its row, its members' profiles, and its world chunks.
|
|
401
592
|
* Reach for it when writing your own mutation that must read runtime state before touching host tables.
|
|
@@ -430,6 +621,14 @@ export type RunCommandArgs = {
|
|
|
430
621
|
command: string;
|
|
431
622
|
input: unknown;
|
|
432
623
|
externalId?: string;
|
|
624
|
+
/**
|
|
625
|
+
* How much of the server to hydrate before applying the command. Overrides whatever the
|
|
626
|
+
* {@link CommandDef} declares through its own `scope`; omit both to hydrate the whole world.
|
|
627
|
+
*/
|
|
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>;
|
|
433
632
|
};
|
|
434
633
|
/** A server resolved for runtime work: its row, its registered runtime, and its hydrated snapshot. */
|
|
435
634
|
export type LoadedServerSnapshot = {
|
|
@@ -443,6 +642,7 @@ export type LoadedServerSnapshot = {
|
|
|
443
642
|
* table writes in one transaction, which a pre-registered mutation cannot do.
|
|
444
643
|
*/
|
|
445
644
|
export type GameServerHelpers = {
|
|
645
|
+
ensureServer: (ctx: JGMutationCtx, gameId: string) => Promise<ServerDoc>;
|
|
446
646
|
loadSnapshot: (ctx: JGMutationCtx, serverId: string, scope?: LoadSnapshotScope) => Promise<LoadedServerSnapshot | null>;
|
|
447
647
|
applyCommand: (args: {
|
|
448
648
|
gameId: string;
|
|
@@ -461,6 +661,7 @@ export type GameServerHelpers = {
|
|
|
461
661
|
};
|
|
462
662
|
persistSnapshot: (ctx: JGMutationCtx, server: ServerDoc, snapshot: GameRuntimeSnapshot, save?: SaveConfig) => Promise<boolean>;
|
|
463
663
|
runCommand: (ctx: JGMutationCtx, args: RunCommandArgs) => Promise<RunCommandOutcome>;
|
|
664
|
+
resetPlayerProfile: (ctx: JGMutationCtx, serverId: string, userId: string) => Promise<RuntimePlayerRow>;
|
|
464
665
|
};
|
|
465
666
|
/** How many running servers one `tickActiveServers` transaction may hydrate, tick, and persist. */
|
|
466
667
|
export declare const DEFAULT_MAX_SERVERS_PER_TICK = 32;
|
|
@@ -469,10 +670,9 @@ export declare const MAX_CHUNKS_PER_QUERY = 64;
|
|
|
469
670
|
/**
|
|
470
671
|
* Build the hosted-server Convex functions for a set of game runtimes.
|
|
471
672
|
*
|
|
472
|
-
*
|
|
473
|
-
*
|
|
474
|
-
*
|
|
475
|
-
* 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.
|
|
476
676
|
* World geometry that outgrows one row belongs in `jgWorldChunks`, hydrated by key
|
|
477
677
|
* ({@link LoadSnapshotScope}) and read by clients through `getChunks` — not in `serverState`.
|
|
478
678
|
*
|
|
@@ -484,6 +684,7 @@ export declare function createGameServerFunctions(options?: {
|
|
|
484
684
|
runtimes?: GameRuntime[];
|
|
485
685
|
auth?: JgAuthMode;
|
|
486
686
|
slotsPerServer?: number;
|
|
687
|
+
topology?: "shared" | "rooms";
|
|
487
688
|
matchmaking?: MatchmakingMode;
|
|
488
689
|
maxServersPerTick?: number;
|
|
489
690
|
allowedFeedActions?: readonly string[];
|
|
@@ -497,6 +698,23 @@ export declare function createGameServerFunctions(options?: {
|
|
|
497
698
|
serverId?: GenericId<"jgGameServers"> | undefined;
|
|
498
699
|
visibility?: "private" | "public" | undefined;
|
|
499
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;
|
|
500
718
|
serverId: GenericId<"jgGameServers">;
|
|
501
719
|
isNew: boolean;
|
|
502
720
|
}>>;
|
|
@@ -508,6 +726,10 @@ export declare function createGameServerFunctions(options?: {
|
|
|
508
726
|
command: string;
|
|
509
727
|
externalId?: string | undefined;
|
|
510
728
|
input: any;
|
|
729
|
+
scope?: {
|
|
730
|
+
chunkKeys?: string[] | undefined;
|
|
731
|
+
players?: string[] | undefined;
|
|
732
|
+
} | undefined;
|
|
511
733
|
serverId: GenericId<"jgGameServers">;
|
|
512
734
|
}, Promise<RunCommandOutcome>>;
|
|
513
735
|
flushSave: import("convex/server").RegisteredMutation<"public", {
|
|
@@ -545,8 +767,17 @@ export declare function createGameServerFunctions(options?: {
|
|
|
545
767
|
mode: string | undefined;
|
|
546
768
|
memberCount: number;
|
|
547
769
|
slotsPerServer: number;
|
|
548
|
-
|
|
549
|
-
|
|
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;
|
|
550
781
|
} | null>>;
|
|
551
782
|
getChunks: import("convex/server").RegisteredQuery<"public", {
|
|
552
783
|
chunkKeys: string[];
|
|
@@ -630,41 +861,198 @@ export declare function createLeaderboardFunctions(options?: {
|
|
|
630
861
|
gameId: string;
|
|
631
862
|
}, Promise<null>>;
|
|
632
863
|
};
|
|
633
|
-
/**
|
|
864
|
+
/** One member's pose as `list` reports it. */
|
|
865
|
+
export type PresenceListRow = {
|
|
866
|
+
userId: string;
|
|
867
|
+
sessionId?: string;
|
|
868
|
+
kind?: string;
|
|
869
|
+
label?: string;
|
|
870
|
+
position: {
|
|
871
|
+
x: number;
|
|
872
|
+
y: number;
|
|
873
|
+
z: number;
|
|
874
|
+
};
|
|
875
|
+
rotationY: number;
|
|
876
|
+
rotationPitch: number;
|
|
877
|
+
lastSeenAt: number;
|
|
878
|
+
};
|
|
879
|
+
/** What `sync` hands back: the pose the server holds after clamping, and whether this session had been displaced. */
|
|
880
|
+
export type PresenceSyncResult = {
|
|
881
|
+
pose: {
|
|
882
|
+
x: number;
|
|
883
|
+
y: number;
|
|
884
|
+
z: number;
|
|
885
|
+
rotationY: number;
|
|
886
|
+
rotationPitch: number;
|
|
887
|
+
};
|
|
888
|
+
lastSeenAt: number;
|
|
889
|
+
displaced: boolean;
|
|
890
|
+
};
|
|
891
|
+
/**
|
|
892
|
+
* The pose lane of a hosted server: the backend behind `PresenceSync`, which replicates where each
|
|
893
|
+
* member of one `jgGameServers` server is standing. It is not the backend for `PresenceTransport` —
|
|
894
|
+
* that contract (`ConvexPresenceFunctions` in `./convexPresenceTransport`) is a game-owned world's
|
|
895
|
+
* own presence, keyed by the game's own ids, with its own notion of who counts as a resident. A game
|
|
896
|
+
* on `createConvexPresenceTransport` supplies those functions itself; these do not substitute for them.
|
|
897
|
+
* @internal
|
|
898
|
+
*/
|
|
634
899
|
export declare function createPresenceFunctions(options?: {
|
|
635
900
|
auth?: JgAuthMode;
|
|
901
|
+
/** How recently a row must have been written to appear in `list`. */
|
|
636
902
|
freshWindowMs?: number;
|
|
637
|
-
|
|
903
|
+
/** How long a row may go unwritten before `reapIdlePresence` deletes it. */
|
|
904
|
+
idleCutoffMs?: number;
|
|
905
|
+
/** Rows deleted per reaper run, so one sweep is a bounded transaction. */
|
|
906
|
+
maxReapPerRun?: number;
|
|
907
|
+
/** Movement clamping, optionally per actor `kind` — an agent need not obey a human's speed cap. */
|
|
908
|
+
poseRules?: PoseSyncRules | ((kind: string | undefined) => PoseSyncRules);
|
|
909
|
+
/** Where a session with no row starts. Async and ctx-bearing, so a spawn can read the world. */
|
|
910
|
+
resolveSpawn?: (ctx: JGMutationCtx, args: {
|
|
911
|
+
serverId: string;
|
|
912
|
+
userId: string;
|
|
913
|
+
kind?: string;
|
|
914
|
+
}) => Promise<{
|
|
915
|
+
x: number;
|
|
916
|
+
y: number;
|
|
917
|
+
z: number;
|
|
918
|
+
} | null> | {
|
|
919
|
+
x: number;
|
|
920
|
+
y: number;
|
|
921
|
+
z: number;
|
|
922
|
+
} | null;
|
|
638
923
|
}): {
|
|
639
924
|
list: import("convex/server").RegisteredQuery<"public", {
|
|
640
925
|
externalId?: string | undefined;
|
|
641
926
|
serverId: string;
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
position: {
|
|
645
|
-
x: number;
|
|
646
|
-
y: number;
|
|
647
|
-
z: number;
|
|
648
|
-
};
|
|
649
|
-
rotationY: number;
|
|
650
|
-
rotationPitch: number;
|
|
651
|
-
lastSeenAt: number;
|
|
652
|
-
}[]>>;
|
|
927
|
+
viewerChunkKey?: string | undefined;
|
|
928
|
+
}, Promise<PresenceListRow[]>>;
|
|
653
929
|
sync: import("convex/server").RegisteredMutation<"public", {
|
|
654
930
|
externalId?: string | undefined;
|
|
655
|
-
|
|
931
|
+
kind?: string | undefined;
|
|
932
|
+
label?: string | undefined;
|
|
933
|
+
pose?: {
|
|
656
934
|
rotationPitch: number;
|
|
657
935
|
rotationY: number;
|
|
658
936
|
x: number;
|
|
659
937
|
y: number;
|
|
660
938
|
z: number;
|
|
661
|
-
};
|
|
939
|
+
} | undefined;
|
|
662
940
|
serverId: string;
|
|
663
|
-
|
|
941
|
+
sessionId?: string | undefined;
|
|
942
|
+
}, Promise<PresenceSyncResult | null>>;
|
|
664
943
|
leave: import("convex/server").RegisteredMutation<"public", {
|
|
665
944
|
externalId?: string | undefined;
|
|
666
945
|
serverId: string;
|
|
946
|
+
sessionId?: string | undefined;
|
|
667
947
|
}, Promise<null>>;
|
|
948
|
+
reapIdlePresence: import("convex/server").RegisteredMutation<"internal", {}, Promise<{
|
|
949
|
+
reaped: number;
|
|
950
|
+
}>>;
|
|
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
|
+
}>>;
|
|
668
1056
|
};
|
|
669
1057
|
/** @internal */
|
|
670
1058
|
export declare function createChatFunctions(options?: {
|
|
@@ -678,17 +1066,33 @@ export declare function createChatFunctions(options?: {
|
|
|
678
1066
|
externalId?: string | undefined;
|
|
679
1067
|
serverId: string;
|
|
680
1068
|
}, Promise<ChatMessage[]>>;
|
|
1069
|
+
recent: import("convex/server").RegisteredQuery<"public", {
|
|
1070
|
+
channelId: string;
|
|
1071
|
+
externalId?: string | undefined;
|
|
1072
|
+
serverId: string;
|
|
1073
|
+
}, Promise<ChatMessage[]>>;
|
|
681
1074
|
sendMessage: import("convex/server").RegisteredMutation<"public", {
|
|
682
1075
|
body: string;
|
|
683
1076
|
channelId: string;
|
|
684
1077
|
externalId?: string | undefined;
|
|
685
1078
|
serverId: string;
|
|
686
1079
|
}, Promise<ChatSendOutcome>>;
|
|
1080
|
+
pruneChatMessages: import("convex/server").RegisteredMutation<"internal", {}, Promise<{
|
|
1081
|
+
deleted: number;
|
|
1082
|
+
remaining: boolean;
|
|
1083
|
+
}>>;
|
|
687
1084
|
};
|
|
688
1085
|
export type JgCronSpec = {
|
|
689
1086
|
name: string;
|
|
690
1087
|
intervalSeconds: number;
|
|
691
|
-
functionKey: "tickActiveServers" | "flushDirtyServers";
|
|
1088
|
+
functionKey: "tickActiveServers" | "flushDirtyServers" | "reapIdlePresence" | "pruneChatMessages" | "pruneClientErrors" | "pruneRateLimits";
|
|
1089
|
+
/** Which factory's exports the function lives in, i.e. which `convex/*.ts` file to reach it through. */
|
|
1090
|
+
module: "runtime" | "presence" | "chat" | "clientErrors";
|
|
692
1091
|
};
|
|
693
1092
|
/** @internal */
|
|
694
|
-
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[];
|