@genex-ai/multiplayer 0.17.8 → 0.17.9

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.
@@ -9,10 +9,10 @@ import {
9
9
  } from "./chunk-4R7C2TLH.js";
10
10
  import {
11
11
  connectAuthority
12
- } from "./chunk-QZOCUQJT.js";
13
- import "./chunk-FPWP22Y7.js";
12
+ } from "./chunk-MQYARUGU.js";
13
+ import "./chunk-TEHGMFAG.js";
14
14
  import "./chunk-YWQ3ALYY.js";
15
- import "./chunk-NFMGIKFZ.js";
15
+ import "./chunk-ZDGSUR6E.js";
16
16
  import "./chunk-OD6NRSX2.js";
17
17
  import {
18
18
  firstpersonWorld
@@ -22,7 +22,7 @@ import {
22
22
  CloseCode,
23
23
  ServerMessage,
24
24
  isReservedEventType
25
- } from "./chunk-CSQEEANB.js";
25
+ } from "./chunk-WEMRRIHH.js";
26
26
 
27
27
  // src/bridge-authority.ts
28
28
  import { getAuthorityRegions, getAuthorityUrls, getColyseusAuth } from "@genex-ai/embed-sdk";
package/dist/bridge.js CHANGED
@@ -15,14 +15,14 @@ import {
15
15
  joinPrivate,
16
16
  matchmake,
17
17
  roomOf
18
- } from "./chunk-QY3QQNDJ.js";
19
- import "./chunk-FPWP22Y7.js";
18
+ } from "./chunk-DJ2XYETS.js";
19
+ import "./chunk-TEHGMFAG.js";
20
20
  import {
21
21
  Limits,
22
22
  RESERVED_SHARED_PREFIX,
23
23
  ServerMessage,
24
24
  isReservedEventType
25
- } from "./chunk-CSQEEANB.js";
25
+ } from "./chunk-WEMRRIHH.js";
26
26
 
27
27
  // src/bridge.ts
28
28
  var version = 2;
@@ -8,7 +8,7 @@ import {
8
8
  waitForFirstState,
9
9
  warnProtocolRefusal,
10
10
  withoutReconnection
11
- } from "./chunk-FPWP22Y7.js";
11
+ } from "./chunk-TEHGMFAG.js";
12
12
  import {
13
13
  ClientMessage,
14
14
  ClientReconnection,
@@ -23,7 +23,7 @@ import {
23
23
  SEND_TIME_KEY,
24
24
  ServerMessage,
25
25
  isReservedEventType
26
- } from "./chunk-CSQEEANB.js";
26
+ } from "./chunk-WEMRRIHH.js";
27
27
 
28
28
  // src/index.ts
29
29
  import { getStateCallbacks } from "@colyseus/sdk";
@@ -5,7 +5,7 @@ import {
5
5
  joinOptions,
6
6
  useSdkReconnection,
7
7
  waitForFirstState
8
- } from "./chunk-FPWP22Y7.js";
8
+ } from "./chunk-TEHGMFAG.js";
9
9
  import {
10
10
  udpFastOpener
11
11
  } from "./chunk-YWQ3ALYY.js";
@@ -60,7 +60,7 @@ import {
60
60
  serverTick,
61
61
  tickInstant,
62
62
  validRoomCode
63
- } from "./chunk-NFMGIKFZ.js";
63
+ } from "./chunk-ZDGSUR6E.js";
64
64
  import {
65
65
  RemotePredict
66
66
  } from "./chunk-OD6NRSX2.js";
@@ -83,11 +83,12 @@ import {
83
83
  AuthorityTiming,
84
84
  ClientReconnection,
85
85
  CloseCode,
86
+ FREE_QUOTA_REFUSAL,
86
87
  JoinError,
87
88
  RoomType,
88
89
  ServerMessage,
89
90
  authorityCloseCodeOf
90
- } from "./chunk-CSQEEANB.js";
91
+ } from "./chunk-WEMRRIHH.js";
91
92
 
92
93
  // src/authority/regions.ts
93
94
  var AUTHORITY_REFUSALS_PER_REGION = 2;
@@ -4654,6 +4655,32 @@ var ENTITY_MOTION_FIELDS = Object.keys(blankEntityMotion());
4654
4655
  var SEAT_MOTION_FIELDS = Object.keys(blankSeatEcho()).filter((f) => f !== "inAck");
4655
4656
  var motionFieldsOf = (kind) => kind === MotionRecordKind.Entity ? ENTITY_MOTION_FIELDS : SEAT_MOTION_FIELDS;
4656
4657
  var ROOT_TICK_FIELDS = ["tick", "keyframe"];
4658
+ var SETTLE_ACK_TRAIL = 64;
4659
+ function settleAckTrail() {
4660
+ let trail = [];
4661
+ let floor = 0;
4662
+ return {
4663
+ snapshot(tick, ack) {
4664
+ const last = trail.at(-1)?.ack ?? floor;
4665
+ if (ack <= last) return;
4666
+ trail.push({ tick, ack });
4667
+ if (trail.length > SETTLE_ACK_TRAIL) trail = trail.slice(-SETTLE_ACK_TRAIL);
4668
+ },
4669
+ through(tick) {
4670
+ let kept = 0;
4671
+ while (kept < trail.length && trail[kept].tick <= tick) {
4672
+ floor = Math.max(floor, trail[kept].ack);
4673
+ kept += 1;
4674
+ }
4675
+ if (kept > 0) trail = trail.slice(kept);
4676
+ return floor;
4677
+ },
4678
+ reset(at2) {
4679
+ trail = [];
4680
+ floor = at2;
4681
+ }
4682
+ };
4683
+ }
4657
4684
  function defaultFastLog(line) {
4658
4685
  const stderr = globalThis.process?.stderr;
4659
4686
  if (stderr?.write !== void 0) stderr.write(`${line}
@@ -4692,6 +4719,7 @@ function fastSeat(o) {
4692
4719
  let link = null;
4693
4720
  let fallback = null;
4694
4721
  let fastAck = 0;
4722
+ const settle2 = settleAckTrail();
4695
4723
  let early = [];
4696
4724
  let pendingBind = null;
4697
4725
  let offerAwaitedFrom = null;
@@ -4764,6 +4792,7 @@ function fastSeat(o) {
4764
4792
  const base = seqBase();
4765
4793
  const wsBefore = fallback.wsBefore + (handle.sentCount - fallback.at);
4766
4794
  fastAck = ack();
4795
+ settle2.reset(fastAck);
4767
4796
  link = { client: p.client, seqBase: base, wsBefore, stampAtBind: baselines?._lastStamp ?? 0 };
4768
4797
  fallback = null;
4769
4798
  retryAt = null;
@@ -4831,6 +4860,7 @@ function fastSeat(o) {
4831
4860
  if (at2 !== void 0) rtt = o.now() - at2;
4832
4861
  fastAck = acked;
4833
4862
  }
4863
+ settle2.snapshot(s.tick, fastAck);
4834
4864
  room.clock.sample(s.sent, rtt);
4835
4865
  guard?.write(s);
4836
4866
  };
@@ -4852,6 +4882,7 @@ function fastSeat(o) {
4852
4882
  askRoundTrip(client);
4853
4883
  link = { client, seqBase: seqBase(), wsBefore: handle.sentCount, stampAtBind: baselines?._lastStamp ?? 0 };
4854
4884
  fastAck = 0;
4885
+ settle2.reset(0);
4855
4886
  status.live = true;
4856
4887
  liveSince = o.now();
4857
4888
  for (const s of early) client.queue({ ...s, seq: link.seqBase + s.seq });
@@ -4890,6 +4921,10 @@ function fastSeat(o) {
4890
4921
  return {
4891
4922
  status,
4892
4923
  ack,
4924
+ settleAck() {
4925
+ if (link === null) return ack();
4926
+ return Math.min(ack(), settle2.through(guard?.webSocketTick() ?? Number(room.state.tick ?? 0)));
4927
+ },
4893
4928
  sent(seq, tick, input) {
4894
4929
  if (o.open === void 0 || fallback !== null) return;
4895
4930
  const render = renderStamp();
@@ -5065,6 +5100,9 @@ function motionGuard(room, live) {
5065
5100
  }
5066
5101
  });
5067
5102
  return {
5103
+ webSocketTick() {
5104
+ return Number(held.get(state)?.get("tick")?.ws ?? room.state.tick ?? 0);
5105
+ },
5068
5106
  write(s) {
5069
5107
  learn();
5070
5108
  const changes = [];
@@ -5233,12 +5271,16 @@ var SessionEvent = {
5233
5271
  var SESSION_EVENTS = new Set(Object.values(SessionEvent));
5234
5272
  var MESSAGE = {
5235
5273
  protocol: `[multiplayer] the authority server refused protocol ${AUTHORITY_PROTOCOL_VERSION} (code 426) \u2014 this game was built with a @genex-ai/multiplayer that the server no longer serves. Update the package and republish.`,
5236
- notEntitled: "[multiplayer] server-authoritative rooms are not available for this game (code 402): its owner is not entitled to them. This is final; the session does not retry."
5274
+ notEntitled: "[multiplayer] server-authoritative rooms are not available for this game (code 402): its owner is not entitled to them. This is final; the session does not retry.",
5275
+ freeQuota: "[multiplayer] no new server-authoritative room for this game (code 429): its owner is on Genex's free tier and has used the free included room time for this period. Rooms already running are not affected; new rooms open again next period, or with Genex Pro."
5237
5276
  };
5238
5277
  var errorCode = (err) => err && typeof err === "object" ? err.code : void 0;
5278
+ var errorMessage = (err) => err && typeof err === "object" ? err.message : void 0;
5279
+ var isFreeQuotaRefusal = (err) => errorCode(err) === JoinError.ROOM_QUOTA && errorMessage(err) === FREE_QUOTA_REFUSAL;
5239
5280
  function explainAuthorityRefusal(err) {
5240
5281
  if (isProtocolRefusal(err)) console.error(MESSAGE.protocol);
5241
5282
  else if (errorCode(err) === JoinError.NOT_ENTITLED) console.error(MESSAGE.notEntitled);
5283
+ else if (isFreeQuotaRefusal(err)) console.error(MESSAGE.freeQuota);
5242
5284
  throw err;
5243
5285
  }
5244
5286
  var predictsPhysics = (opts) => opts.physicsCore !== void 0 && opts.predictPhysics !== false;
@@ -5965,7 +6007,7 @@ function attachAuthoritySession(room, opts, url) {
5965
6007
  const unsettled = /* @__PURE__ */ new Map();
5966
6008
  const settleShots = () => {
5967
6009
  if (unsettled.size === 0) return;
5968
- const acked = fast.ack();
6010
+ const acked = fast.settleAck();
5969
6011
  for (const [shot, drop] of unsettled) {
5970
6012
  if (shot.epoch !== handle.epoch || shot.seq > acked || entityMap().has(shot.id)) continue;
5971
6013
  unsettled.delete(shot);
@@ -2,7 +2,7 @@ import {
2
2
  ClientReconnection,
3
3
  JoinError,
4
4
  PROTOCOL_VERSION
5
- } from "./chunk-CSQEEANB.js";
5
+ } from "./chunk-WEMRRIHH.js";
6
6
 
7
7
  // src/join.ts
8
8
  import { Client as Client2 } from "@colyseus/sdk";
@@ -55,7 +55,7 @@ var AuthorityJoinOption = {
55
55
  * refused with 400. */
56
56
  SUCCESSOR_CLOSED: "successorClosed"
57
57
  };
58
- var AUTHORITY_SDK_LABEL = "ts/0.17.8";
58
+ var AUTHORITY_SDK_LABEL = "ts/0.17.9";
59
59
  var AuthorityCaps = {
60
60
  /** Decodes and draws §17.10's physics bodies. A physics room refuses a join without it (426). An
61
61
  * SDK sends it only once its drawing passes `physics-draw.json` and, for an engine SDK, its own
@@ -149,6 +149,7 @@ var JoinError = {
149
149
  /** No authority server can take the room right now, or admission could not be checked. Retry later. */
150
150
  AUTHORITY_CAPACITY: 503
151
151
  };
152
+ var FREE_QUOTA_REFUSAL = "free_quota_exhausted";
152
153
  var STAGING_ROOM_PREFIX = "preview--";
153
154
  var roomKeyFor = (slug, channel) => channel === "staging" ? `${STAGING_ROOM_PREFIX}${slug}` : slug;
154
155
  var slugOfRoomKey = (key) => key.startsWith(STAGING_ROOM_PREFIX) ? key.slice(STAGING_ROOM_PREFIX.length) : key;
@@ -494,6 +495,7 @@ export {
494
495
  SERVER_OWNED_JOIN_OPTIONS,
495
496
  AUTHORITY_SERVER_OWNED_JOIN_OPTIONS,
496
497
  JoinError,
498
+ FREE_QUOTA_REFUSAL,
497
499
  STAGING_ROOM_PREFIX,
498
500
  roomKeyFor,
499
501
  slugOfRoomKey,
@@ -19,7 +19,7 @@ import {
19
19
  JoinError,
20
20
  Limits,
21
21
  isReservedEventType
22
- } from "./chunk-CSQEEANB.js";
22
+ } from "./chunk-WEMRRIHH.js";
23
23
 
24
24
  // src/authority/room-key.ts
25
25
  var AuthorityRoomLimits = {
package/dist/index.js CHANGED
@@ -3,9 +3,9 @@ import {
3
3
  createPrivate,
4
4
  joinPrivate,
5
5
  matchmake
6
- } from "./chunk-QY3QQNDJ.js";
7
- import "./chunk-FPWP22Y7.js";
8
- import "./chunk-CSQEEANB.js";
6
+ } from "./chunk-DJ2XYETS.js";
7
+ import "./chunk-TEHGMFAG.js";
8
+ import "./chunk-WEMRRIHH.js";
9
9
  export {
10
10
  connect,
11
11
  createPrivate,
@@ -76,7 +76,7 @@ interface AuthorityDrainNotice {
76
76
  drainMs: number;
77
77
  }
78
78
  /** The SDK label this TypeScript client sends. tests hold it to package.json. */
79
- declare const AUTHORITY_SDK_LABEL = "ts/0.17.8";
79
+ declare const AUTHORITY_SDK_LABEL = "ts/0.17.9";
80
80
  /** Client capabilities (§17.10 *Joining*). */
81
81
  declare const AuthorityCaps: {
82
82
  /** Decodes and draws §17.10's physics bodies. A physics room refuses a join without it (426). An
@@ -177,6 +177,9 @@ declare const JoinError: {
177
177
  /** No authority server can take the room right now, or admission could not be checked. Retry later. */
178
178
  readonly AUTHORITY_CAPACITY: 503;
179
179
  };
180
+ /** The ROOM_QUOTA refusal's reason when the game's owner is on the free tier and has used its
181
+ * included room time for this period (the API's `AdmitRefusal.FreeQuotaExhausted`). */
182
+ declare const FREE_QUOTA_REFUSAL = "free_quota_exhausted";
180
183
  /** A preview (staging) build's rooms: the relay keys them `preview--<slug>`, apart from the live
181
184
  * game's rooms, so testers on a preview link play together and never meet real players. The
182
185
  * client always sends the plain slug as `gameId`; the relay picks the room from the token's
@@ -636,4 +639,4 @@ declare const ClockSync: {
636
639
  readonly WARMUP_ACKS: 30;
637
640
  };
638
641
 
639
- export { AUTHORITY_ENCODER_BUFFER_BYTES, AUTHORITY_MAX_FRAME_BYTES, AUTHORITY_MAX_MESSAGES_PER_SECOND, AUTHORITY_MIN_PROTOCOL_VERSION, AUTHORITY_PHYSICS_TICK_WEIGHT, AUTHORITY_PHYSICS_UNITS, AUTHORITY_PROTOCOL_VERSION, AUTHORITY_ROOM_ENDED_TEXT, AUTHORITY_SDK_LABEL, AUTHORITY_SERVER_OWNED_JOIN_OPTIONS, AUTHORITY_WINDOW, AuthorityCaps, AuthorityCloseCode, type AuthorityDrainNotice, AuthorityJoinOption, AuthorityTiming, type ChatMessage, ClientMessage, type ClientMessageType, ClientReconnection, ClockSync, CloseCode, GLOBAL_RATE, type IceConfig, type IceServer, type InputEnvelope, Interpolation, JoinError, JoinOption, Limits, MAX_MESSAGES_PER_SECOND, MESSAGE_LANE, MIN_PROTOCOL_VERSION, type MatchEnded, type ObjectControlOperation, type ObjectControlReason, type ObjectControlRequest, type ObjectControlResult, type ObjectPublish, POSE_KEYS, PROTOCOL_VERSION, type PoseKey, type PosePublish, type ProtocolWindow, RATE_LANES, RELAY_WINDOW, RESERVED_EVENT_PREFIXES, RESERVED_EVENT_TYPES, RESERVED_SHARED_PREFIX, type RateLane, RelayTiming, RoomType, SEND_TIME_KEY, SERVER_OWNED_JOIN_OPTIONS, STAGING_ROOM_PREFIX, SeatCloseReason, ServerMessage, type ServerMessageType, type SharedWrite, type VoiceSignal, type VoiceSignalEnvelope, authorityCloseCodeOf, authorityPhysicsBudgetSixtieths, authorityRoomUnits, authorityTickBudgetMs, capsOf, inProtocolWindow, isReservedEventType, roomKeyFor, slugOfRoomKey };
642
+ export { AUTHORITY_ENCODER_BUFFER_BYTES, AUTHORITY_MAX_FRAME_BYTES, AUTHORITY_MAX_MESSAGES_PER_SECOND, AUTHORITY_MIN_PROTOCOL_VERSION, AUTHORITY_PHYSICS_TICK_WEIGHT, AUTHORITY_PHYSICS_UNITS, AUTHORITY_PROTOCOL_VERSION, AUTHORITY_ROOM_ENDED_TEXT, AUTHORITY_SDK_LABEL, AUTHORITY_SERVER_OWNED_JOIN_OPTIONS, AUTHORITY_WINDOW, AuthorityCaps, AuthorityCloseCode, type AuthorityDrainNotice, AuthorityJoinOption, AuthorityTiming, type ChatMessage, ClientMessage, type ClientMessageType, ClientReconnection, ClockSync, CloseCode, FREE_QUOTA_REFUSAL, GLOBAL_RATE, type IceConfig, type IceServer, type InputEnvelope, Interpolation, JoinError, JoinOption, Limits, MAX_MESSAGES_PER_SECOND, MESSAGE_LANE, MIN_PROTOCOL_VERSION, type MatchEnded, type ObjectControlOperation, type ObjectControlReason, type ObjectControlRequest, type ObjectControlResult, type ObjectPublish, POSE_KEYS, PROTOCOL_VERSION, type PoseKey, type PosePublish, type ProtocolWindow, RATE_LANES, RELAY_WINDOW, RESERVED_EVENT_PREFIXES, RESERVED_EVENT_TYPES, RESERVED_SHARED_PREFIX, type RateLane, RelayTiming, RoomType, SEND_TIME_KEY, SERVER_OWNED_JOIN_OPTIONS, STAGING_ROOM_PREFIX, SeatCloseReason, ServerMessage, type ServerMessageType, type SharedWrite, type VoiceSignal, type VoiceSignalEnvelope, authorityCloseCodeOf, authorityPhysicsBudgetSixtieths, authorityRoomUnits, authorityTickBudgetMs, capsOf, inProtocolWindow, isReservedEventType, roomKeyFor, slugOfRoomKey };
package/dist/protocol.js CHANGED
@@ -18,6 +18,7 @@ import {
18
18
  ClientReconnection,
19
19
  ClockSync,
20
20
  CloseCode,
21
+ FREE_QUOTA_REFUSAL,
21
22
  GLOBAL_RATE,
22
23
  Interpolation,
23
24
  JoinError,
@@ -49,7 +50,7 @@ import {
49
50
  isReservedEventType,
50
51
  roomKeyFor,
51
52
  slugOfRoomKey
52
- } from "./chunk-CSQEEANB.js";
53
+ } from "./chunk-WEMRRIHH.js";
53
54
  export {
54
55
  AUTHORITY_ENCODER_BUFFER_BYTES,
55
56
  AUTHORITY_MAX_FRAME_BYTES,
@@ -70,6 +71,7 @@ export {
70
71
  ClientReconnection,
71
72
  ClockSync,
72
73
  CloseCode,
74
+ FREE_QUOTA_REFUSAL,
73
75
  GLOBAL_RATE,
74
76
  Interpolation,
75
77
  JoinError,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genex-ai/multiplayer",
3
- "version": "0.17.8",
3
+ "version": "0.17.9",
4
4
  "description": "The multiplayer client wrapper + shared room schema for genex.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",