@jgengine/convex 0.49.1 → 0.51.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1401 -0
- package/dist/createConvexBackend.d.ts +2 -2
- package/dist/createConvexGameTransport.d.ts +10 -10
- package/dist/createConvexGameTransport.js +13 -0
- package/dist/hostedServer.d.ts +11 -11
- package/dist/hostedServer.js +3 -2
- package/dist/resolveConvexMultiplayer.d.ts +1 -1
- package/dist/server.d.ts +126 -102
- package/dist/server.js +49 -24
- package/dist/worldPresence.d.ts +1 -1
- package/dist/worldPresence.js +2 -1
- package/package.json +2 -2
- package/skills/jgengine/SKILL.md +43 -63
- package/skills/jgengine/api.md +29 -14
- package/skills/jgengine/capabilities.md +8 -0
- package/skills/jgengine/recipes/cookbook.md +752 -0
- package/skills/jgengine-multiplayer/SKILL.md +9 -0
- package/skills/jgengine-multiplayer/api.md +49 -32
- package/skills/jgengine-multiplayer/references/peer-worlds.md +1 -1
|
@@ -13,8 +13,8 @@ export type ConvexBackendOptions<TRawPresenceRow extends {
|
|
|
13
13
|
client: ConvexReactClient;
|
|
14
14
|
gameId: string;
|
|
15
15
|
userId: string;
|
|
16
|
-
api?: ConvexGameApi;
|
|
17
|
-
poseTuning?: PoseSyncTuning;
|
|
16
|
+
api?: ConvexGameApi | undefined;
|
|
17
|
+
poseTuning?: PoseSyncTuning | undefined;
|
|
18
18
|
presence?: {
|
|
19
19
|
functions: ConvexPresenceFunctions;
|
|
20
20
|
mapRow: (row: TRawPresenceRow) => TPresenceRow;
|
|
@@ -15,23 +15,23 @@ export type ConvexGameApi = {
|
|
|
15
15
|
runtime: {
|
|
16
16
|
joinServer: FunctionReference<"mutation", "public", {
|
|
17
17
|
gameId: string;
|
|
18
|
-
serverId?: string;
|
|
18
|
+
serverId?: string | undefined;
|
|
19
19
|
mode?: string;
|
|
20
20
|
visibility?: "public" | "private";
|
|
21
21
|
joinCode?: string;
|
|
22
|
-
externalId?: string;
|
|
22
|
+
externalId?: string | undefined;
|
|
23
23
|
sessionId?: string;
|
|
24
24
|
}, JoinServerOutcome>;
|
|
25
25
|
leaveServer: FunctionReference<"mutation", "public", {
|
|
26
26
|
serverId: string;
|
|
27
|
-
externalId?: string;
|
|
27
|
+
externalId?: string | undefined;
|
|
28
28
|
sessionId?: string;
|
|
29
29
|
}, null>;
|
|
30
30
|
runCommand: FunctionReference<"mutation", "public", {
|
|
31
31
|
serverId: string;
|
|
32
32
|
command: string;
|
|
33
33
|
input: unknown;
|
|
34
|
-
externalId?: string;
|
|
34
|
+
externalId?: string | undefined;
|
|
35
35
|
}, {
|
|
36
36
|
ok: true;
|
|
37
37
|
} | {
|
|
@@ -42,7 +42,7 @@ export type ConvexGameApi = {
|
|
|
42
42
|
serverId: string;
|
|
43
43
|
action: string;
|
|
44
44
|
entry: unknown;
|
|
45
|
-
externalId?: string;
|
|
45
|
+
externalId?: string | undefined;
|
|
46
46
|
}, null>;
|
|
47
47
|
getServer: FunctionReference<"query", "public", {
|
|
48
48
|
serverId: string;
|
|
@@ -55,13 +55,13 @@ export type ConvexGameApi = {
|
|
|
55
55
|
getFeed: FunctionReference<"query", "public", {
|
|
56
56
|
serverId: string;
|
|
57
57
|
action: string;
|
|
58
|
-
externalId?: string;
|
|
58
|
+
externalId?: string | undefined;
|
|
59
59
|
}, unknown[]>;
|
|
60
60
|
};
|
|
61
61
|
presence: {
|
|
62
62
|
list: FunctionReference<"query", "public", {
|
|
63
63
|
serverId: string;
|
|
64
|
-
externalId?: string;
|
|
64
|
+
externalId?: string | undefined;
|
|
65
65
|
viewerChunkKey?: string;
|
|
66
66
|
}, PresencePoseRow[]>;
|
|
67
67
|
sync: FunctionReference<"mutation", "public", {
|
|
@@ -76,7 +76,7 @@ export type ConvexGameApi = {
|
|
|
76
76
|
sessionId?: string;
|
|
77
77
|
kind?: string;
|
|
78
78
|
label?: string;
|
|
79
|
-
externalId?: string;
|
|
79
|
+
externalId?: string | undefined;
|
|
80
80
|
}, {
|
|
81
81
|
pose: {
|
|
82
82
|
x: number;
|
|
@@ -98,13 +98,13 @@ export type ConvexGameApi = {
|
|
|
98
98
|
messages: FunctionReference<"query", "public", {
|
|
99
99
|
serverId: string;
|
|
100
100
|
channelId: string;
|
|
101
|
-
externalId?: string;
|
|
101
|
+
externalId?: string | undefined;
|
|
102
102
|
}, ChatMessage[]>;
|
|
103
103
|
sendMessage: FunctionReference<"mutation", "public", {
|
|
104
104
|
serverId: string;
|
|
105
105
|
channelId: string;
|
|
106
106
|
body: string;
|
|
107
|
-
externalId?: string;
|
|
107
|
+
externalId?: string | undefined;
|
|
108
108
|
}, ChatSendOutcome>;
|
|
109
109
|
};
|
|
110
110
|
leaderboard: {
|
|
@@ -104,6 +104,14 @@ export function createConvexPresenceSync(client, api, config, tuning) {
|
|
|
104
104
|
const viewerChunks = new Map();
|
|
105
105
|
const refreshers = new Map();
|
|
106
106
|
const lastSentAt = new Map();
|
|
107
|
+
// A pose row outlives its tab unless something ends it; the last unsubscribe and an unload both do.
|
|
108
|
+
const leave = (serverId) => {
|
|
109
|
+
void client.mutation(api.presence.leave, { serverId, ...(config.userId === undefined ? {} : { externalId: config.userId }) }).catch(() => undefined);
|
|
110
|
+
};
|
|
111
|
+
const onPageHide = () => {
|
|
112
|
+
for (const serverId of refreshers.keys())
|
|
113
|
+
leave(serverId);
|
|
114
|
+
};
|
|
107
115
|
return {
|
|
108
116
|
subscribe(serverId, onChange) {
|
|
109
117
|
let unsubscribe;
|
|
@@ -115,6 +123,8 @@ export function createConvexPresenceSync(client, api, config, tuning) {
|
|
|
115
123
|
let listeners = refreshers.get(serverId);
|
|
116
124
|
if (listeners === undefined) {
|
|
117
125
|
listeners = new Set();
|
|
126
|
+
if (refreshers.size === 0)
|
|
127
|
+
globalThis.addEventListener?.("pagehide", onPageHide);
|
|
118
128
|
refreshers.set(serverId, listeners);
|
|
119
129
|
}
|
|
120
130
|
listeners.add(refresh);
|
|
@@ -127,6 +137,9 @@ export function createConvexPresenceSync(client, api, config, tuning) {
|
|
|
127
137
|
viewerChunks.delete(serverId);
|
|
128
138
|
gates.delete(serverId);
|
|
129
139
|
lastSentAt.delete(serverId);
|
|
140
|
+
leave(serverId);
|
|
141
|
+
if (refreshers.size === 0)
|
|
142
|
+
globalThis.removeEventListener?.("pagehide", onPageHide);
|
|
130
143
|
}
|
|
131
144
|
};
|
|
132
145
|
},
|
package/dist/hostedServer.d.ts
CHANGED
|
@@ -39,7 +39,7 @@ export interface HostedWorldOutcome<T> {
|
|
|
39
39
|
*/
|
|
40
40
|
export declare function invokeHostedWorld<T>(invocation: HostedWorldInvocation<T>): Promise<HostedWorldOutcome<T>>;
|
|
41
41
|
/** Default minimum elapsed ms before `tickHostedWorlds` advances a world — override via the factory's `tickMs`. */
|
|
42
|
-
export declare const JG_HOSTED_TICK_MS =
|
|
42
|
+
export declare const JG_HOSTED_TICK_MS = 50;
|
|
43
43
|
/**
|
|
44
44
|
* The Convex counterpart of ws's `createWorldGameHost`: serves GameContext worlds statelessly by
|
|
45
45
|
* reconstructing a hosted session from the persisted `jgHostedWorlds` row on every invocation via
|
|
@@ -53,9 +53,9 @@ export declare function createHostedGameServerFunctions(options: {
|
|
|
53
53
|
tickMs?: number;
|
|
54
54
|
}): {
|
|
55
55
|
getHostedServer: import("convex/server").RegisteredQuery<"public", {
|
|
56
|
-
externalId?: string
|
|
56
|
+
externalId?: string;
|
|
57
57
|
gameId: string;
|
|
58
|
-
serverId?: string
|
|
58
|
+
serverId?: string;
|
|
59
59
|
}, Promise<{
|
|
60
60
|
serverId: string;
|
|
61
61
|
gameId: string;
|
|
@@ -64,24 +64,24 @@ export declare function createHostedGameServerFunctions(options: {
|
|
|
64
64
|
memberUserIds: string[];
|
|
65
65
|
} | null>>;
|
|
66
66
|
joinHostedServer: import("convex/server").RegisteredMutation<"public", {
|
|
67
|
-
externalId?: string
|
|
67
|
+
externalId?: string;
|
|
68
68
|
gameId: string;
|
|
69
|
-
serverId?: string
|
|
69
|
+
serverId?: string;
|
|
70
70
|
}, Promise<{
|
|
71
71
|
serverId: string;
|
|
72
72
|
isNew: boolean;
|
|
73
73
|
}>>;
|
|
74
74
|
leaveHostedServer: import("convex/server").RegisteredMutation<"public", {
|
|
75
|
-
externalId?: string
|
|
75
|
+
externalId?: string;
|
|
76
76
|
gameId: string;
|
|
77
|
-
serverId?: string
|
|
77
|
+
serverId?: string;
|
|
78
78
|
}, Promise<null>>;
|
|
79
79
|
runHostedCommand: import("convex/server").RegisteredMutation<"public", {
|
|
80
80
|
command: string;
|
|
81
|
-
externalId?: string
|
|
81
|
+
externalId?: string;
|
|
82
82
|
gameId: string;
|
|
83
83
|
input: any;
|
|
84
|
-
serverId?: string
|
|
84
|
+
serverId?: string;
|
|
85
85
|
}, Promise<{
|
|
86
86
|
ok: false;
|
|
87
87
|
reason: string;
|
|
@@ -89,10 +89,10 @@ export declare function createHostedGameServerFunctions(options: {
|
|
|
89
89
|
ok: true;
|
|
90
90
|
}>>;
|
|
91
91
|
submitHostedInput: import("convex/server").RegisteredMutation<"public", {
|
|
92
|
-
externalId?: string
|
|
92
|
+
externalId?: string;
|
|
93
93
|
frame: any;
|
|
94
94
|
gameId: string;
|
|
95
|
-
serverId?: string
|
|
95
|
+
serverId?: string;
|
|
96
96
|
}, Promise<null>>;
|
|
97
97
|
tickHostedWorlds: import("convex/server").RegisteredMutation<"internal", {}, Promise<{
|
|
98
98
|
ticked: number;
|
package/dist/hostedServer.js
CHANGED
|
@@ -2,6 +2,7 @@ import { defineSchema, internalMutationGeneric, mutationGeneric, queryGeneric, }
|
|
|
2
2
|
import { ConvexError, v } from "convex/values";
|
|
3
3
|
import { createHostedGameRunner, INPUT_COMMAND } from "@jgengine/core/runtime/hostedGameRunner";
|
|
4
4
|
import { createHostedWorldSessionAsync, } from "@jgengine/core/runtime/hostedWorldSession";
|
|
5
|
+
import { DEFAULT_HOST_TICK_MS, resolveHostTickMs } from "@jgengine/core/runtime/hostPolicy";
|
|
5
6
|
import { commitIfRevisionMatch } from "./occ.js";
|
|
6
7
|
import { jgengineHostedTables, resolveActor } from "./server.js";
|
|
7
8
|
/**
|
|
@@ -40,7 +41,7 @@ const query = queryGeneric;
|
|
|
40
41
|
const mutation = mutationGeneric;
|
|
41
42
|
const internalMutation = internalMutationGeneric;
|
|
42
43
|
/** Default minimum elapsed ms before `tickHostedWorlds` advances a world — override via the factory's `tickMs`. */
|
|
43
|
-
export const JG_HOSTED_TICK_MS =
|
|
44
|
+
export const JG_HOSTED_TICK_MS = DEFAULT_HOST_TICK_MS;
|
|
44
45
|
async function getWorldRow(ctx, gameId, serverId) {
|
|
45
46
|
return await ctx.db
|
|
46
47
|
.query("jgHostedWorlds")
|
|
@@ -100,7 +101,7 @@ async function persistWorldRow(ctx, args) {
|
|
|
100
101
|
*/
|
|
101
102
|
export function createHostedGameServerFunctions(options) {
|
|
102
103
|
const mode = options.auth ?? "anonymous";
|
|
103
|
-
const tickMs = options.tickMs ?? JG_HOSTED_TICK_MS;
|
|
104
|
+
const tickMs = resolveHostTickMs(options.tickMs ?? JG_HOSTED_TICK_MS);
|
|
104
105
|
function resolveGame(gameId) {
|
|
105
106
|
const game = options.games[gameId];
|
|
106
107
|
if (game === undefined)
|
|
@@ -7,7 +7,7 @@ export declare function randomConvexPlayerId(): string;
|
|
|
7
7
|
export declare function resolveConvexMultiplayer(args: {
|
|
8
8
|
game: GameDefinition;
|
|
9
9
|
gameId: string;
|
|
10
|
-
url?: string;
|
|
10
|
+
url?: string | undefined;
|
|
11
11
|
client?: ConvexReactClient;
|
|
12
12
|
api?: ConvexGameApi;
|
|
13
13
|
userId?: string;
|