@genex-ai/multiplayer 0.17.0 → 0.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bridge.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  joinPrivate,
5
5
  matchmake,
6
6
  roomOf
7
- } from "./chunk-CTDU4IJ5.js";
7
+ } from "./chunk-3ZUPU3GT.js";
8
8
  import {
9
9
  Limits,
10
10
  RESERVED_SHARED_PREFIX,
@@ -16,7 +16,71 @@ import {
16
16
  } from "./chunk-C52XFIB6.js";
17
17
 
18
18
  // src/index.ts
19
- import { Client, getStateCallbacks } from "@colyseus/sdk";
19
+ import { Client as Client2, getStateCallbacks } from "@colyseus/sdk";
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 });
20
84
 
21
85
  // src/ice-request.ts
22
86
  function createIceRequester(send, timeoutMs) {
@@ -681,11 +745,8 @@ var CONTROL_TIMEOUT_MS = 5e3;
681
745
  var CONTROL_TIMEOUT_MAX_MS = 6e4;
682
746
  var POSE_DATA_KEEPALIVE_MS = 2e3;
683
747
  async function relayClient(opts) {
684
- if (opts.url) return new Client(opts.url);
685
- if (opts.urls.length > 1) return Client.selectByLatency(opts.urls);
686
- const url = opts.urls[0];
687
- if (!url) throw new Error("genex multiplayer: `urls` must contain at least one relay URL");
688
- return new Client(url);
748
+ if (opts.url) return new Client2(opts.url);
749
+ return new Client2(await selectRelayUrl(opts.urls));
689
750
  }
690
751
  var resolveAuth = (auth) => typeof auth === "function" ? auth() : auth;
691
752
  function predictSmoothing(opt) {
package/dist/index.d.ts CHANGED
@@ -44,13 +44,14 @@ type ColyseusAuth = {
44
44
  };
45
45
  type AuthSource = ColyseusAuth | (() => ColyseusAuth | undefined);
46
46
  type ConnectOptions = {
47
- /** Regional relay urls (pass `getColyseusUrls()`) — the SDK pings each and connects to the
48
- * lowest-latency one; a single entry just connects. */
47
+ /** Regional relay urls (pass `getColyseusUrls()`) — the SDK probes each and connects to the
48
+ * lowest-latency one (a probe that times out falls back, it never fails the join); a single entry
49
+ * just connects. */
49
50
  urls: string[];
50
- /** Cross-region invite override: connect to THIS exact relay url, skipping latency selection. Two
51
- * players are only in the same room if they share one region's relay, so an invitee must follow the
52
- * inviter's region (carried in the invite) instead of picking its own nearest. Omit for normal
53
- * nearest-region play. */
51
+ /** Override: connect to THIS exact relay url, skipping latency selection. Relays that share
52
+ * presence (global matchmaking) seat players together whichever relay each enters through, so
53
+ * this is only needed against relays that do not — there two players meet only on one relay, and
54
+ * an invitee follows the inviter's region. Omit for normal nearest-region play. */
54
55
  url?: string;
55
56
  /** Game id — everyone who passes the same id shares a room; different ids are
56
57
  * fully isolated. Use your project slug (or any stable per-game string). */
@@ -518,8 +519,8 @@ type Matchmaking<TState extends Record<string, unknown>> = {
518
519
  type MatchmakeOptions = {
519
520
  /** Regional relay urls (pass `getColyseusUrls()`) — pings each, connects to the lowest-latency one. */
520
521
  urls: string[];
521
- /** Cross-region override: matchmake on THIS exact relay url, skipping latency selection — an invitee
522
- * follows the inviter's region so both land on the same relay. Omit for normal nearest-region play. */
522
+ /** Override: matchmake on THIS exact relay url, skipping latency selection — only needed against
523
+ * relays that do not share presence (see ConnectOptions.url). Omit for normal nearest-region play. */
523
524
  url?: string;
524
525
  /** Game id (your project slug/id) — isolates this game's matchmaking. */
525
526
  room: string;
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  createPrivate,
4
4
  joinPrivate,
5
5
  matchmake
6
- } from "./chunk-CTDU4IJ5.js";
6
+ } from "./chunk-3ZUPU3GT.js";
7
7
  import "./chunk-C52XFIB6.js";
8
8
  export {
9
9
  connect,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genex-ai/multiplayer",
3
- "version": "0.17.0",
3
+ "version": "0.17.1",
4
4
  "description": "The multiplayer client wrapper + shared room schema for genex.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",