@genex-ai/multiplayer 0.17.1 → 0.17.6
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/PROTOCOL.md +688 -7
- package/README.md +263 -0
- package/dist/authority/fast-udp.d.ts +13 -0
- package/dist/authority/fast-udp.js +52 -0
- package/dist/authority/fast-webtransport.d.ts +45 -0
- package/dist/authority/fast-webtransport.js +124 -0
- package/dist/authority/index.d.ts +2333 -0
- package/dist/authority/index.js +758 -0
- package/dist/authority/module.d.ts +1 -0
- package/dist/authority/module.js +12 -0
- package/dist/bridge-authority.d.ts +100 -0
- package/dist/bridge-authority.global.js +14 -0
- package/dist/bridge-authority.js +286 -0
- package/dist/bridge-core-Dhc73GC8.d.ts +27 -0
- package/dist/bridge.d.ts +26 -20
- package/dist/bridge.global.js +7 -7
- package/dist/bridge.js +17 -48
- package/dist/chunk-4R7C2TLH.js +68 -0
- package/dist/chunk-5JGRFT2N.js +3646 -0
- package/dist/{chunk-3ZUPU3GT.js → chunk-7QVOIHJF.js} +13 -122
- package/dist/chunk-K3SF5VDS.js +27 -0
- package/dist/chunk-UX3F6YTV.js +143 -0
- package/dist/chunk-Y6XHXDIK.js +6617 -0
- package/dist/{chunk-C52XFIB6.js → chunk-ZSWF4VCN.js} +141 -4
- package/dist/fast-client-CZ41v-oB.d.ts +465 -0
- package/dist/index.d.ts +3 -5
- package/dist/index.js +3 -2
- package/dist/join-CGoInvUM.d.ts +6 -0
- package/dist/module-DqtFb5wA.d.ts +1436 -0
- package/dist/physics-predict-49udN3a1.d.ts +1208 -0
- package/dist/protocol.d.ts +137 -2
- package/dist/protocol.js +33 -1
- package/package.json +33 -3
|
@@ -1,10 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
explainJoinRefusal,
|
|
3
|
+
isProtocolRefusal,
|
|
4
|
+
joinOptions,
|
|
5
|
+
relayClient,
|
|
6
|
+
seedsInterpolation,
|
|
7
|
+
useSdkReconnection,
|
|
8
|
+
waitForFirstState,
|
|
9
|
+
warnProtocolRefusal,
|
|
10
|
+
withoutReconnection
|
|
11
|
+
} from "./chunk-UX3F6YTV.js";
|
|
1
12
|
import {
|
|
2
13
|
ClientMessage,
|
|
3
14
|
ClientReconnection,
|
|
4
15
|
ClockSync,
|
|
5
16
|
CloseCode,
|
|
6
17
|
Interpolation,
|
|
7
|
-
JoinError,
|
|
8
18
|
Limits,
|
|
9
19
|
POSE_KEYS,
|
|
10
20
|
PROTOCOL_VERSION,
|
|
@@ -13,74 +23,10 @@ import {
|
|
|
13
23
|
SEND_TIME_KEY,
|
|
14
24
|
ServerMessage,
|
|
15
25
|
isReservedEventType
|
|
16
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-ZSWF4VCN.js";
|
|
17
27
|
|
|
18
28
|
// src/index.ts
|
|
19
|
-
import {
|
|
20
|
-
|
|
21
|
-
// src/relay-select.ts
|
|
22
|
-
import { Client } from "@colyseus/sdk";
|
|
23
|
-
var RELAY_SELECT_DEFAULTS = {
|
|
24
|
-
SAMPLES: 3,
|
|
25
|
-
// Well past Colyseus's 1.5 s, which a loading web export's main thread was measured exceeding.
|
|
26
|
-
TIMEOUT_MS: 4e3,
|
|
27
|
-
// A far relay's sample lands within a few hundred ms of a near one's; a dead relay must not hold
|
|
28
|
-
// a working join for the full timeout.
|
|
29
|
-
GRACE_MS: 1e3
|
|
30
|
-
};
|
|
31
|
-
async function selectRelayUrl(urls, options = {}) {
|
|
32
|
-
const first = urls[0];
|
|
33
|
-
if (first === void 0) throw new Error("genex multiplayer: `urls` must contain at least one relay URL");
|
|
34
|
-
if (urls.length === 1) return first;
|
|
35
|
-
const probe = options.probe ?? defaultProbe;
|
|
36
|
-
const samples = Math.max(1, options.samples ?? RELAY_SELECT_DEFAULTS.SAMPLES);
|
|
37
|
-
const timeoutMs = options.timeoutMs ?? RELAY_SELECT_DEFAULTS.TIMEOUT_MS;
|
|
38
|
-
const graceMs = options.graceMs ?? RELAY_SELECT_DEFAULTS.GRACE_MS;
|
|
39
|
-
const best = urls.map(() => void 0);
|
|
40
|
-
await new Promise((resolve) => {
|
|
41
|
-
let pending = urls.length * samples;
|
|
42
|
-
let grace;
|
|
43
|
-
const ceiling = setTimeout(done, timeoutMs);
|
|
44
|
-
function done() {
|
|
45
|
-
clearTimeout(ceiling);
|
|
46
|
-
clearTimeout(grace);
|
|
47
|
-
resolve();
|
|
48
|
-
}
|
|
49
|
-
const settle = () => {
|
|
50
|
-
if (--pending === 0) done();
|
|
51
|
-
};
|
|
52
|
-
urls.forEach((url, i) => {
|
|
53
|
-
for (let n = 0; n < samples; n++) {
|
|
54
|
-
let sample;
|
|
55
|
-
try {
|
|
56
|
-
sample = probe(url, timeoutMs);
|
|
57
|
-
} catch (err) {
|
|
58
|
-
sample = Promise.reject(err);
|
|
59
|
-
}
|
|
60
|
-
sample.then(
|
|
61
|
-
(ms) => {
|
|
62
|
-
if (Number.isFinite(ms) && ms >= 0) {
|
|
63
|
-
best[i] = Math.min(best[i] ?? Infinity, ms);
|
|
64
|
-
grace ??= setTimeout(done, graceMs);
|
|
65
|
-
}
|
|
66
|
-
settle();
|
|
67
|
-
},
|
|
68
|
-
settle
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
});
|
|
73
|
-
let pick = -1;
|
|
74
|
-
best.forEach((ms, i) => {
|
|
75
|
-
if (ms !== void 0 && (pick < 0 || ms < best[pick])) pick = i;
|
|
76
|
-
});
|
|
77
|
-
if (pick < 0) {
|
|
78
|
-
console.warn(`genex multiplayer: no relay answered the latency probe \u2014 connecting to ${first}`);
|
|
79
|
-
return first;
|
|
80
|
-
}
|
|
81
|
-
return urls[pick];
|
|
82
|
-
}
|
|
83
|
-
var defaultProbe = (url, timeoutMs) => new Client(url).getLatency({ pingCount: 1, timeout: timeoutMs });
|
|
29
|
+
import { getStateCallbacks } from "@colyseus/sdk";
|
|
84
30
|
|
|
85
31
|
// src/ice-request.ts
|
|
86
32
|
function createIceRequester(send, timeoutMs) {
|
|
@@ -305,40 +251,6 @@ function msUntil(epochMs, clock) {
|
|
|
305
251
|
return epochMs - clock.wallNow;
|
|
306
252
|
}
|
|
307
253
|
|
|
308
|
-
// src/room-lifecycle.ts
|
|
309
|
-
function useSdkReconnection(room) {
|
|
310
|
-
const r = room.reconnection;
|
|
311
|
-
r.enabled = ClientReconnection.enabled;
|
|
312
|
-
r.minUptime = ClientReconnection.minUptime;
|
|
313
|
-
r.delay = ClientReconnection.delay;
|
|
314
|
-
r.minDelay = ClientReconnection.minDelay;
|
|
315
|
-
r.maxDelay = ClientReconnection.maxDelay;
|
|
316
|
-
r.maxRetries = ClientReconnection.maxRetries;
|
|
317
|
-
r.maxEnqueuedMessages = ClientReconnection.maxEnqueuedMessages;
|
|
318
|
-
r.enqueuedMessages.length = 0;
|
|
319
|
-
return room;
|
|
320
|
-
}
|
|
321
|
-
function withoutReconnection(room) {
|
|
322
|
-
room.reconnection.enabled = false;
|
|
323
|
-
return room;
|
|
324
|
-
}
|
|
325
|
-
function waitForFirstState(room, timeoutMs) {
|
|
326
|
-
return new Promise((resolve) => {
|
|
327
|
-
const onState = () => {
|
|
328
|
-
clearTimeout(timer);
|
|
329
|
-
resolve(true);
|
|
330
|
-
};
|
|
331
|
-
const timer = setTimeout(() => {
|
|
332
|
-
room.onStateChange.remove(onState);
|
|
333
|
-
resolve(false);
|
|
334
|
-
}, timeoutMs);
|
|
335
|
-
room.onStateChange.once(onState);
|
|
336
|
-
});
|
|
337
|
-
}
|
|
338
|
-
function seedsInterpolation(id, selfId, sendTime) {
|
|
339
|
-
return id !== selfId && sendTime != null;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
254
|
// src/voice.ts
|
|
343
255
|
var NOT_DELEGATED = "The microphone was refused by the page that frames this game, not by the player: the frame was not given it, so the browser never asked. On its Genex page a build is given the microphone only when it declares it. An up-to-date genex CLI declares it at `genex preview` and `genex publish` when the game's own source calls voice.join(); to declare it by hand, add \"microphone\" to genex.capabilities in the game's package.json. Then preview or publish again.";
|
|
344
256
|
var VoiceNotDelegatedError = class extends Error {
|
|
@@ -744,11 +656,6 @@ var ICE_REQUEST_TIMEOUT_MS = 3e3;
|
|
|
744
656
|
var CONTROL_TIMEOUT_MS = 5e3;
|
|
745
657
|
var CONTROL_TIMEOUT_MAX_MS = 6e4;
|
|
746
658
|
var POSE_DATA_KEEPALIVE_MS = 2e3;
|
|
747
|
-
async function relayClient(opts) {
|
|
748
|
-
if (opts.url) return new Client2(opts.url);
|
|
749
|
-
return new Client2(await selectRelayUrl(opts.urls));
|
|
750
|
-
}
|
|
751
|
-
var resolveAuth = (auth) => typeof auth === "function" ? auth() : auth;
|
|
752
659
|
function predictSmoothing(opt) {
|
|
753
660
|
if (opt === "predict") return { mode: "lerp" };
|
|
754
661
|
if (opt && typeof opt === "object" && opt.engine === "predict") {
|
|
@@ -893,22 +800,6 @@ async function connect(opts) {
|
|
|
893
800
|
);
|
|
894
801
|
return attachSession(room, { pose: opts.pose, smoothing: opts.smoothing, predict });
|
|
895
802
|
}
|
|
896
|
-
function joinOptions(gameId, name, auth) {
|
|
897
|
-
return {
|
|
898
|
-
gameId,
|
|
899
|
-
name,
|
|
900
|
-
embedToken: resolveAuth(auth)?.embedToken,
|
|
901
|
-
protocol: PROTOCOL_VERSION
|
|
902
|
-
};
|
|
903
|
-
}
|
|
904
|
-
var isProtocolRefusal = (err) => !!err && typeof err === "object" && err.code === JoinError.PROTOCOL_UNSUPPORTED;
|
|
905
|
-
var warnProtocolRefusal = () => console.error(
|
|
906
|
-
`[multiplayer] the relay refused protocol ${PROTOCOL_VERSION} (code 426) \u2014 this game was built with a @genex-ai/multiplayer that this relay no longer serves. Update the package and republish.`
|
|
907
|
-
);
|
|
908
|
-
var explainJoinRefusal = (err) => {
|
|
909
|
-
if (isProtocolRefusal(err)) warnProtocolRefusal();
|
|
910
|
-
throw err;
|
|
911
|
-
};
|
|
912
803
|
var isSendableEventType = (type) => typeof type === "string" && type.length > 0 && type.length <= Limits.MAX_EVENT_TYPE_CHARS && !isReservedEventType(type);
|
|
913
804
|
var LOCAL_EVENTS = /* @__PURE__ */ new Set([
|
|
914
805
|
"join",
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// src/authority/module.ts
|
|
2
|
+
var MODULE_CONTRACT = 1;
|
|
3
|
+
var LeaveReason = {
|
|
4
|
+
/** The client left on purpose. */
|
|
5
|
+
LEFT: "left",
|
|
6
|
+
/** The connection dropped and the reconnection grace ran out. */
|
|
7
|
+
DROPPED: "dropped",
|
|
8
|
+
/** A newer session of the same player took the seat. */
|
|
9
|
+
SUPERSEDED: "superseded",
|
|
10
|
+
/** The player's session was revoked. */
|
|
11
|
+
REVOKED: "revoked",
|
|
12
|
+
/** The room is closing (drain, halt or fault). */
|
|
13
|
+
CLOSING: "closing"
|
|
14
|
+
};
|
|
15
|
+
var RemotePredict = {
|
|
16
|
+
EXTRAPOLATE: "extrapolate",
|
|
17
|
+
HOLD: "hold",
|
|
18
|
+
INPUT_DECAY: "inputDecay"
|
|
19
|
+
};
|
|
20
|
+
var defineRoom = (module) => module;
|
|
21
|
+
|
|
22
|
+
export {
|
|
23
|
+
MODULE_CONTRACT,
|
|
24
|
+
LeaveReason,
|
|
25
|
+
RemotePredict,
|
|
26
|
+
defineRoom
|
|
27
|
+
};
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ClientReconnection,
|
|
3
|
+
JoinError,
|
|
4
|
+
PROTOCOL_VERSION
|
|
5
|
+
} from "./chunk-ZSWF4VCN.js";
|
|
6
|
+
|
|
7
|
+
// src/join.ts
|
|
8
|
+
import { Client as Client2 } from "@colyseus/sdk";
|
|
9
|
+
|
|
10
|
+
// src/relay-select.ts
|
|
11
|
+
import { Client } from "@colyseus/sdk";
|
|
12
|
+
var RELAY_SELECT_DEFAULTS = {
|
|
13
|
+
SAMPLES: 3,
|
|
14
|
+
// Well past Colyseus's 1.5 s, which a loading web export's main thread was measured exceeding.
|
|
15
|
+
TIMEOUT_MS: 4e3,
|
|
16
|
+
// A far relay's sample lands within a few hundred ms of a near one's; a dead relay must not hold
|
|
17
|
+
// a working join for the full timeout.
|
|
18
|
+
GRACE_MS: 1e3
|
|
19
|
+
};
|
|
20
|
+
async function selectRelayUrl(urls, options = {}) {
|
|
21
|
+
const first = urls[0];
|
|
22
|
+
if (first === void 0) throw new Error("genex multiplayer: `urls` must contain at least one relay URL");
|
|
23
|
+
if (urls.length === 1) return first;
|
|
24
|
+
const probe = options.probe ?? defaultProbe;
|
|
25
|
+
const samples = Math.max(1, options.samples ?? RELAY_SELECT_DEFAULTS.SAMPLES);
|
|
26
|
+
const timeoutMs = options.timeoutMs ?? RELAY_SELECT_DEFAULTS.TIMEOUT_MS;
|
|
27
|
+
const graceMs = options.graceMs ?? RELAY_SELECT_DEFAULTS.GRACE_MS;
|
|
28
|
+
const best = urls.map(() => void 0);
|
|
29
|
+
await new Promise((resolve) => {
|
|
30
|
+
let pending = urls.length * samples;
|
|
31
|
+
let grace;
|
|
32
|
+
const ceiling = setTimeout(done, timeoutMs);
|
|
33
|
+
function done() {
|
|
34
|
+
clearTimeout(ceiling);
|
|
35
|
+
clearTimeout(grace);
|
|
36
|
+
resolve();
|
|
37
|
+
}
|
|
38
|
+
const settle = () => {
|
|
39
|
+
if (--pending === 0) done();
|
|
40
|
+
};
|
|
41
|
+
urls.forEach((url, i) => {
|
|
42
|
+
for (let n = 0; n < samples; n++) {
|
|
43
|
+
let sample;
|
|
44
|
+
try {
|
|
45
|
+
sample = probe(url, timeoutMs);
|
|
46
|
+
} catch (err) {
|
|
47
|
+
sample = Promise.reject(err);
|
|
48
|
+
}
|
|
49
|
+
sample.then(
|
|
50
|
+
(ms) => {
|
|
51
|
+
if (Number.isFinite(ms) && ms >= 0) {
|
|
52
|
+
best[i] = Math.min(best[i] ?? Infinity, ms);
|
|
53
|
+
grace ??= setTimeout(done, graceMs);
|
|
54
|
+
}
|
|
55
|
+
settle();
|
|
56
|
+
},
|
|
57
|
+
settle
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
let pick = -1;
|
|
63
|
+
best.forEach((ms, i) => {
|
|
64
|
+
if (ms !== void 0 && (pick < 0 || ms < best[pick])) pick = i;
|
|
65
|
+
});
|
|
66
|
+
if (pick < 0) {
|
|
67
|
+
console.warn(`genex multiplayer: no relay answered the latency probe \u2014 connecting to ${first}`);
|
|
68
|
+
return first;
|
|
69
|
+
}
|
|
70
|
+
return urls[pick];
|
|
71
|
+
}
|
|
72
|
+
var defaultProbe = (url, timeoutMs) => new Client(url).getLatency({ pingCount: 1, timeout: timeoutMs });
|
|
73
|
+
|
|
74
|
+
// src/join.ts
|
|
75
|
+
var resolveAuth = (auth) => typeof auth === "function" ? auth() : auth;
|
|
76
|
+
async function relayClient(opts) {
|
|
77
|
+
if (opts.url) return new Client2(opts.url);
|
|
78
|
+
return new Client2(await selectRelayUrl(opts.urls));
|
|
79
|
+
}
|
|
80
|
+
function joinOptions(gameId, name, auth) {
|
|
81
|
+
return {
|
|
82
|
+
gameId,
|
|
83
|
+
name,
|
|
84
|
+
embedToken: resolveAuth(auth)?.embedToken,
|
|
85
|
+
protocol: PROTOCOL_VERSION
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
var isProtocolRefusal = (err) => !!err && typeof err === "object" && err.code === JoinError.PROTOCOL_UNSUPPORTED;
|
|
89
|
+
var warnProtocolRefusal = () => console.error(
|
|
90
|
+
`[multiplayer] the relay refused protocol ${PROTOCOL_VERSION} (code 426) \u2014 this game was built with a @genex-ai/multiplayer that this relay no longer serves. Update the package and republish.`
|
|
91
|
+
);
|
|
92
|
+
var explainJoinRefusal = (err) => {
|
|
93
|
+
if (isProtocolRefusal(err)) warnProtocolRefusal();
|
|
94
|
+
throw err;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// src/room-lifecycle.ts
|
|
98
|
+
function useSdkReconnection(room) {
|
|
99
|
+
const r = room.reconnection;
|
|
100
|
+
r.enabled = ClientReconnection.enabled;
|
|
101
|
+
r.minUptime = ClientReconnection.minUptime;
|
|
102
|
+
r.delay = ClientReconnection.delay;
|
|
103
|
+
r.minDelay = ClientReconnection.minDelay;
|
|
104
|
+
r.maxDelay = ClientReconnection.maxDelay;
|
|
105
|
+
r.maxRetries = ClientReconnection.maxRetries;
|
|
106
|
+
r.maxEnqueuedMessages = ClientReconnection.maxEnqueuedMessages;
|
|
107
|
+
r.enqueuedMessages.length = 0;
|
|
108
|
+
return room;
|
|
109
|
+
}
|
|
110
|
+
function withoutReconnection(room) {
|
|
111
|
+
room.reconnection.enabled = false;
|
|
112
|
+
return room;
|
|
113
|
+
}
|
|
114
|
+
function waitForFirstState(room, timeoutMs) {
|
|
115
|
+
return new Promise((resolve) => {
|
|
116
|
+
const onState = () => {
|
|
117
|
+
clearTimeout(timer);
|
|
118
|
+
resolve(true);
|
|
119
|
+
};
|
|
120
|
+
const timer = setTimeout(() => {
|
|
121
|
+
room.onStateChange.remove(onState);
|
|
122
|
+
resolve(false);
|
|
123
|
+
}, timeoutMs);
|
|
124
|
+
room.onStateChange.once(onState);
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
function seedsInterpolation(id, selfId, sendTime) {
|
|
128
|
+
return id !== selfId && sendTime != null;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export {
|
|
132
|
+
RELAY_SELECT_DEFAULTS,
|
|
133
|
+
defaultProbe,
|
|
134
|
+
relayClient,
|
|
135
|
+
joinOptions,
|
|
136
|
+
isProtocolRefusal,
|
|
137
|
+
warnProtocolRefusal,
|
|
138
|
+
explainJoinRefusal,
|
|
139
|
+
useSdkReconnection,
|
|
140
|
+
withoutReconnection,
|
|
141
|
+
waitForFirstState,
|
|
142
|
+
seedsInterpolation
|
|
143
|
+
};
|