@colyseus/core 0.16.0-preview.9 → 0.16.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.
Files changed (146) hide show
  1. package/README.md +5 -5
  2. package/build/Debug.js +6 -2
  3. package/build/Debug.js.map +2 -2
  4. package/build/Debug.mjs +11 -10
  5. package/build/Debug.mjs.map +2 -2
  6. package/build/IPC.d.ts +1 -1
  7. package/build/IPC.js +3 -3
  8. package/build/IPC.js.map +2 -2
  9. package/build/IPC.mjs +4 -3
  10. package/build/IPC.mjs.map +2 -2
  11. package/build/Logger.mjs +4 -3
  12. package/build/Logger.mjs.map +1 -1
  13. package/build/MatchMaker.d.ts +35 -30
  14. package/build/MatchMaker.js +150 -100
  15. package/build/MatchMaker.js.map +2 -2
  16. package/build/MatchMaker.mjs +154 -107
  17. package/build/MatchMaker.mjs.map +2 -2
  18. package/build/Protocol.d.ts +3 -4
  19. package/build/Protocol.js +33 -19
  20. package/build/Protocol.js.map +2 -2
  21. package/build/Protocol.mjs +36 -21
  22. package/build/Protocol.mjs.map +2 -2
  23. package/build/Room.d.ts +64 -40
  24. package/build/Room.js +408 -151
  25. package/build/Room.js.map +2 -2
  26. package/build/Room.mjs +412 -158
  27. package/build/Room.mjs.map +2 -2
  28. package/build/Server.d.ts +8 -7
  29. package/build/Server.js +51 -18
  30. package/build/Server.js.map +2 -2
  31. package/build/Server.mjs +51 -21
  32. package/build/Server.mjs.map +3 -3
  33. package/build/Stats.d.ts +2 -0
  34. package/build/Stats.js +38 -3
  35. package/build/Stats.js.map +2 -2
  36. package/build/Stats.mjs +30 -6
  37. package/build/Stats.mjs.map +2 -2
  38. package/build/Transport.d.ts +8 -7
  39. package/build/Transport.js +1 -1
  40. package/build/Transport.js.map +2 -2
  41. package/build/Transport.mjs +6 -5
  42. package/build/Transport.mjs.map +2 -2
  43. package/build/discovery/index.d.ts +1 -1
  44. package/build/discovery/index.js.map +2 -2
  45. package/build/discovery/index.mjs +3 -2
  46. package/build/discovery/index.mjs.map +2 -2
  47. package/build/errors/RoomExceptions.d.ts +39 -0
  48. package/build/errors/RoomExceptions.js +100 -0
  49. package/build/errors/RoomExceptions.js.map +7 -0
  50. package/build/errors/RoomExceptions.mjs +71 -0
  51. package/build/errors/RoomExceptions.mjs.map +7 -0
  52. package/build/errors/SeatReservationError.mjs +3 -2
  53. package/build/errors/SeatReservationError.mjs.map +1 -1
  54. package/build/errors/ServerError.js +1 -1
  55. package/build/errors/ServerError.js.map +1 -1
  56. package/build/errors/ServerError.mjs +5 -4
  57. package/build/errors/ServerError.mjs.map +2 -2
  58. package/build/index.d.ts +21 -19
  59. package/build/index.js +47 -20
  60. package/build/index.js.map +2 -2
  61. package/build/index.mjs +41 -19
  62. package/build/index.mjs.map +2 -2
  63. package/build/matchmaker/Lobby.d.ts +3 -3
  64. package/build/matchmaker/Lobby.js +6 -3
  65. package/build/matchmaker/Lobby.js.map +2 -2
  66. package/build/matchmaker/Lobby.mjs +4 -4
  67. package/build/matchmaker/Lobby.mjs.map +2 -2
  68. package/build/matchmaker/RegisteredHandler.d.ts +6 -7
  69. package/build/matchmaker/RegisteredHandler.js +7 -10
  70. package/build/matchmaker/RegisteredHandler.js.map +2 -2
  71. package/build/matchmaker/RegisteredHandler.mjs +11 -13
  72. package/build/matchmaker/RegisteredHandler.mjs.map +2 -2
  73. package/build/matchmaker/controller.d.ts +4 -5
  74. package/build/matchmaker/controller.js +22 -15
  75. package/build/matchmaker/controller.js.map +2 -2
  76. package/build/matchmaker/controller.mjs +19 -13
  77. package/build/matchmaker/controller.mjs.map +2 -2
  78. package/build/matchmaker/driver/api.d.ts +104 -0
  79. package/build/matchmaker/driver/api.js +29 -0
  80. package/build/matchmaker/driver/api.js.map +7 -0
  81. package/build/matchmaker/driver/api.mjs +7 -0
  82. package/build/matchmaker/driver/api.mjs.map +7 -0
  83. package/build/matchmaker/driver/index.d.ts +2 -2
  84. package/build/matchmaker/driver/index.js +2 -2
  85. package/build/matchmaker/driver/index.js.map +2 -2
  86. package/build/matchmaker/driver/index.mjs +5 -4
  87. package/build/matchmaker/driver/index.mjs.map +2 -2
  88. package/build/matchmaker/driver/local/LocalDriver.d.ts +13 -0
  89. package/build/matchmaker/driver/local/LocalDriver.js +65 -0
  90. package/build/matchmaker/driver/local/LocalDriver.js.map +7 -0
  91. package/build/matchmaker/driver/local/LocalDriver.mjs +43 -0
  92. package/build/matchmaker/driver/local/LocalDriver.mjs.map +7 -0
  93. package/build/matchmaker/driver/local/Query.d.ts +9 -0
  94. package/build/matchmaker/driver/local/Query.js +78 -0
  95. package/build/matchmaker/driver/local/Query.js.map +7 -0
  96. package/build/matchmaker/driver/local/Query.mjs +56 -0
  97. package/build/matchmaker/driver/local/Query.mjs.map +7 -0
  98. package/build/matchmaker/driver/local/RoomData.d.ts +19 -0
  99. package/build/matchmaker/driver/local/RoomData.js +79 -0
  100. package/build/matchmaker/driver/local/RoomData.js.map +7 -0
  101. package/build/matchmaker/driver/local/RoomData.mjs +57 -0
  102. package/build/matchmaker/driver/local/RoomData.mjs.map +7 -0
  103. package/build/presence/LocalPresence.d.ts +10 -6
  104. package/build/presence/LocalPresence.js +85 -24
  105. package/build/presence/LocalPresence.js.map +3 -3
  106. package/build/presence/LocalPresence.mjs +85 -27
  107. package/build/presence/LocalPresence.mjs.map +3 -3
  108. package/build/presence/Presence.d.ts +38 -2
  109. package/build/presence/Presence.js.map +1 -1
  110. package/build/rooms/LobbyRoom.d.ts +6 -6
  111. package/build/rooms/LobbyRoom.js +8 -3
  112. package/build/rooms/LobbyRoom.js.map +2 -2
  113. package/build/rooms/LobbyRoom.mjs +7 -5
  114. package/build/rooms/LobbyRoom.mjs.map +2 -2
  115. package/build/rooms/RelayRoom.d.ts +3 -3
  116. package/build/rooms/RelayRoom.js +3 -1
  117. package/build/rooms/RelayRoom.js.map +2 -2
  118. package/build/rooms/RelayRoom.mjs +10 -7
  119. package/build/rooms/RelayRoom.mjs.map +2 -2
  120. package/build/serializer/NoneSerializer.d.ts +2 -2
  121. package/build/serializer/NoneSerializer.js.map +1 -1
  122. package/build/serializer/NoneSerializer.mjs +3 -2
  123. package/build/serializer/NoneSerializer.mjs.map +2 -2
  124. package/build/serializer/SchemaSerializer.d.ts +16 -15
  125. package/build/serializer/SchemaSerializer.js +12 -10
  126. package/build/serializer/SchemaSerializer.js.map +2 -2
  127. package/build/serializer/SchemaSerializer.mjs +16 -13
  128. package/build/serializer/SchemaSerializer.mjs.map +2 -2
  129. package/build/serializer/SchemaSerializerDebug.d.ts +7 -0
  130. package/build/serializer/SchemaSerializerDebug.js +0 -0
  131. package/build/serializer/SchemaSerializerDebug.js.map +7 -0
  132. package/build/serializer/SchemaSerializerDebug.mjs +0 -0
  133. package/build/serializer/SchemaSerializerDebug.mjs.map +7 -0
  134. package/build/serializer/Serializer.d.ts +1 -2
  135. package/build/serializer/Serializer.js.map +1 -1
  136. package/build/utils/DevMode.d.ts +2 -2
  137. package/build/utils/DevMode.js +8 -4
  138. package/build/utils/DevMode.js.map +2 -2
  139. package/build/utils/DevMode.mjs +7 -6
  140. package/build/utils/DevMode.mjs.map +2 -2
  141. package/build/utils/Utils.d.ts +8 -3
  142. package/build/utils/Utils.js +41 -17
  143. package/build/utils/Utils.js.map +2 -2
  144. package/build/utils/Utils.mjs +40 -21
  145. package/build/utils/Utils.mjs.map +2 -2
  146. package/package.json +17 -6
package/build/Room.js CHANGED
@@ -17,6 +17,10 @@ var __copyProps = (to, from, except, desc) => {
17
17
  return to;
18
18
  };
19
19
  var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
20
24
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
21
25
  mod
22
26
  ));
@@ -28,19 +32,20 @@ __export(Room_exports, {
28
32
  RoomInternalState: () => RoomInternalState
29
33
  });
30
34
  module.exports = __toCommonJS(Room_exports);
31
- var import_msgpackr = require("msgpackr");
35
+ var import_msgpackr = require("@colyseus/msgpackr");
32
36
  var import_schema = require("@colyseus/schema");
33
- var import_timer = __toESM(require("@gamestdio/timer"));
37
+ var import_timer = __toESM(require("@colyseus/timer"));
34
38
  var import_events = require("events");
35
- var import_Logger = require("./Logger");
36
- var import_NoneSerializer = require("./serializer/NoneSerializer");
37
- var import_SchemaSerializer = require("./serializer/SchemaSerializer");
39
+ var import_Logger = require("./Logger.js");
40
+ var import_NoneSerializer = require("./serializer/NoneSerializer.js");
41
+ var import_SchemaSerializer = require("./serializer/SchemaSerializer.js");
38
42
  var import_Protocol = require("./Protocol");
39
- var import_Utils = require("./utils/Utils");
40
- var import_DevMode = require("./utils/DevMode");
41
- var import_Debug = require("./Debug");
42
- var import_ServerError = require("./errors/ServerError");
43
+ var import_Utils = require("./utils/Utils.js");
44
+ var import_DevMode = require("./utils/DevMode.js");
45
+ var import_Debug = require("./Debug.js");
46
+ var import_ServerError = require("./errors/ServerError.js");
43
47
  var import_Transport = require("./Transport");
48
+ var import_RoomExceptions = require("./errors/RoomExceptions.js");
44
49
  const DEFAULT_PATCH_RATE = 1e3 / 20;
45
50
  const DEFAULT_SIMULATION_INTERVAL = 1e3 / 60;
46
51
  const noneSerializer = new import_NoneSerializer.NoneSerializer();
@@ -53,131 +58,308 @@ var RoomInternalState = /* @__PURE__ */ ((RoomInternalState2) => {
53
58
  })(RoomInternalState || {});
54
59
  class Room {
55
60
  constructor() {
61
+ /**
62
+ * Timing events tied to the room instance.
63
+ * Intervals and timeouts are cleared when the room is disposed.
64
+ */
56
65
  this.clock = new import_timer.default();
57
- this.#_autoDispose = true;
66
+ this.#_onLeaveConcurrent = 0;
67
+ // number of onLeave calls in progress
68
+ /**
69
+ * Maximum number of clients allowed to connect into the room. When room reaches this limit,
70
+ * it is locked automatically. Unless the room was explicitly locked by you via `lock()` method,
71
+ * the room will be unlocked as soon as a client disconnects from it.
72
+ */
58
73
  this.maxClients = Infinity;
74
+ this.#_maxClientsReached = false;
75
+ /**
76
+ * Automatically dispose the room when last client disconnects.
77
+ *
78
+ * @default true
79
+ */
80
+ this.autoDispose = true;
81
+ /**
82
+ * Frequency to send the room state to connected clients, in milliseconds.
83
+ *
84
+ * @default 50ms (20fps)
85
+ */
59
86
  this.patchRate = DEFAULT_PATCH_RATE;
87
+ /**
88
+ * The array of connected clients.
89
+ *
90
+ * @see {@link https://docs.colyseus.io/colyseus/server/room/#client|Client instance}
91
+ */
60
92
  this.clients = new import_Transport.ClientArray();
93
+ /** @internal */
61
94
  this._events = new import_events.EventEmitter();
95
+ // seat reservation & reconnection
62
96
  this.seatReservationTime = DEFAULT_SEAT_RESERVATION_TIME;
63
97
  this.reservedSeats = {};
64
98
  this.reservedSeatTimeouts = {};
65
99
  this._reconnections = {};
66
100
  this._reconnectingSessionId = /* @__PURE__ */ new Map();
67
- this.onMessageHandlers = {};
101
+ this.onMessageHandlers = {
102
+ "__no_message_handler": {
103
+ callback: (client, messageType, _) => {
104
+ const errorMessage = `room onMessage for "${messageType}" not registered.`;
105
+ (0, import_Debug.debugAndPrintError)(`${errorMessage} (roomId: ${this.roomId})`);
106
+ if (import_DevMode.isDevMode) {
107
+ client.error(import_Protocol.ErrorCode.INVALID_PAYLOAD, errorMessage);
108
+ } else {
109
+ client.leave(import_Protocol.Protocol.WS_CLOSE_WITH_ERROR, errorMessage);
110
+ }
111
+ }
112
+ }
113
+ };
68
114
  this._serializer = noneSerializer;
69
115
  this._afterNextPatchQueue = [];
70
116
  this._internalState = 0 /* CREATING */;
71
- this._locked = false;
72
117
  this._lockedExplicitly = false;
73
- this._maxClientsReached = false;
74
- this._events.once("dispose", async () => {
75
- try {
76
- await this._dispose();
77
- } catch (e) {
78
- (0, import_Debug.debugAndPrintError)(`onDispose error: ${e && e.message || e || "promise rejected"}`);
79
- }
80
- this._events.emit("disconnect");
118
+ this.#_locked = false;
119
+ this._events.once("dispose", () => {
120
+ this._dispose().catch((e) => (0, import_Debug.debugAndPrintError)(`onDispose error: ${e && e.stack || e.message || e || "promise rejected"} (roomId: ${this.roomId})`)).finally(() => this._events.emit("disconnect"));
81
121
  });
82
- this.setPatchRate(this.patchRate);
83
- this.resetAutoDisposeTimeout(this.seatReservationTime);
122
+ if (this.onUncaughtException !== void 0) {
123
+ this.#registerUncaughtExceptionHandlers();
124
+ }
84
125
  }
126
+ /**
127
+ * This property will change on these situations:
128
+ * - The maximum number of allowed clients has been reached (`maxClients`)
129
+ * - You manually locked, or unlocked the room using lock() or `unlock()`.
130
+ *
131
+ * @readonly
132
+ */
85
133
  get locked() {
86
- return this._locked;
134
+ return this.#_locked;
87
135
  }
88
136
  get metadata() {
89
137
  return this.listing.metadata;
90
138
  }
91
139
  #_roomId;
92
140
  #_roomName;
141
+ #_onLeaveConcurrent;
142
+ #_maxClientsReached;
143
+ #_maxClients;
93
144
  #_autoDispose;
94
- get autoDispose() {
95
- return this.#_autoDispose;
96
- }
97
- set autoDispose(value) {
98
- if (value !== this.#_autoDispose && this._internalState !== 2 /* DISPOSING */) {
99
- this.#_autoDispose = value;
100
- this.resetAutoDisposeTimeout();
145
+ #_patchRate;
146
+ #_patchInterval;
147
+ #_state;
148
+ #_locked;
149
+ /**
150
+ * This method is called by the MatchMaker before onCreate()
151
+ * @internal
152
+ */
153
+ __init() {
154
+ this.#_state = this.state;
155
+ this.#_autoDispose = this.autoDispose;
156
+ this.#_patchRate = this.patchRate;
157
+ this.#_maxClients = this.maxClients;
158
+ Object.defineProperties(this, {
159
+ state: {
160
+ enumerable: true,
161
+ get: () => this.#_state,
162
+ set: (newState) => {
163
+ if (newState[import_schema.$changes] !== void 0) {
164
+ this.setSerializer(new import_SchemaSerializer.SchemaSerializer());
165
+ } else if ("_definition" in newState) {
166
+ throw new Error("@colyseus/schema v2 compatibility currently missing (reach out if you need it)");
167
+ } else if (import_schema.$changes === void 0) {
168
+ throw new Error("Multiple @colyseus/schema versions detected. Please make sure you don't have multiple versions of @colyseus/schema installed.");
169
+ }
170
+ this._serializer.reset(newState);
171
+ this.#_state = newState;
172
+ }
173
+ },
174
+ maxClients: {
175
+ enumerable: true,
176
+ get: () => this.#_maxClients,
177
+ set: (value) => {
178
+ this.#_maxClients = value;
179
+ if (this._internalState === 1 /* CREATED */) {
180
+ const hasReachedMaxClients = this.hasReachedMaxClients();
181
+ if (!this._lockedExplicitly && this.#_maxClientsReached && !hasReachedMaxClients) {
182
+ this.#_maxClientsReached = false;
183
+ this.#_locked = false;
184
+ this.listing.locked = false;
185
+ }
186
+ if (hasReachedMaxClients) {
187
+ this.#_maxClientsReached = true;
188
+ this.#_locked = true;
189
+ this.listing.locked = true;
190
+ }
191
+ this.listing.maxClients = value;
192
+ this.listing.save();
193
+ }
194
+ }
195
+ },
196
+ autoDispose: {
197
+ enumerable: true,
198
+ get: () => this.#_autoDispose,
199
+ set: (value) => {
200
+ if (value !== this.#_autoDispose && this._internalState !== 2 /* DISPOSING */) {
201
+ this.#_autoDispose = value;
202
+ this.resetAutoDisposeTimeout();
203
+ }
204
+ }
205
+ },
206
+ patchRate: {
207
+ enumerable: true,
208
+ get: () => this.#_patchRate,
209
+ set: (milliseconds) => {
210
+ this.#_patchRate = milliseconds;
211
+ if (this.#_patchInterval) {
212
+ clearInterval(this.#_patchInterval);
213
+ this.#_patchInterval = void 0;
214
+ }
215
+ if (milliseconds !== null && milliseconds !== 0) {
216
+ this.#_patchInterval = setInterval(() => this.broadcastPatch(), milliseconds);
217
+ }
218
+ }
219
+ }
220
+ });
221
+ this.patchRate = this.#_patchRate;
222
+ if (this.#_state) {
223
+ this.state = this.#_state;
101
224
  }
225
+ this.resetAutoDisposeTimeout(this.seatReservationTime);
226
+ this.clock.start();
102
227
  }
228
+ /**
229
+ * The name of the room you provided as first argument for `gameServer.define()`.
230
+ *
231
+ * @returns roomName string
232
+ */
103
233
  get roomName() {
104
234
  return this.#_roomName;
105
235
  }
236
+ /**
237
+ * Setting the name of the room. Overwriting this property is restricted.
238
+ *
239
+ * @param roomName
240
+ */
106
241
  set roomName(roomName) {
107
242
  if (this.#_roomName) {
108
243
  throw new import_ServerError.ServerError(import_Protocol.ErrorCode.APPLICATION_ERROR, "'roomName' cannot be overwritten.");
109
244
  }
110
245
  this.#_roomName = roomName;
111
246
  }
247
+ /**
248
+ * A unique, auto-generated, 9-character-long id of the room.
249
+ * You may replace `this.roomId` during `onCreate()`.
250
+ *
251
+ * @returns roomId string
252
+ */
112
253
  get roomId() {
113
254
  return this.#_roomId;
114
255
  }
256
+ /**
257
+ * Setting the roomId, is restricted in room lifetime except upon room creation.
258
+ *
259
+ * @param roomId
260
+ * @returns roomId string
261
+ */
115
262
  set roomId(roomId) {
116
263
  if (this._internalState !== 0 /* CREATING */ && !import_DevMode.isDevMode) {
117
264
  throw new import_ServerError.ServerError(import_Protocol.ErrorCode.APPLICATION_ERROR, "'roomId' can only be overridden upon room creation.");
118
265
  }
119
266
  this.#_roomId = roomId;
120
267
  }
121
- onAuth(client, options, request) {
268
+ onAuth(client, options, context) {
122
269
  return true;
123
270
  }
124
- static async onAuth(token, req) {
271
+ static async onAuth(token, options, context) {
125
272
  return true;
126
273
  }
274
+ /**
275
+ * This method is called during graceful shutdown of the server process
276
+ * You may override this method to dispose the room in your own way.
277
+ *
278
+ * Once process reaches room count of 0, the room process will be terminated.
279
+ */
280
+ onBeforeShutdown() {
281
+ this.disconnect(
282
+ import_DevMode.isDevMode ? import_Protocol.Protocol.WS_CLOSE_DEVMODE_RESTART : import_Protocol.Protocol.WS_CLOSE_CONSENTED
283
+ );
284
+ }
285
+ /**
286
+ * Returns whether the sum of connected clients and reserved seats exceeds maximum number of clients.
287
+ *
288
+ * @returns boolean
289
+ */
127
290
  hasReachedMaxClients() {
128
291
  return this.clients.length + Object.keys(this.reservedSeats).length >= this.maxClients || this._internalState === 2 /* DISPOSING */;
129
292
  }
293
+ /**
294
+ * Set the number of seconds a room can wait for a client to effectively join the room.
295
+ * You should consider how long your `onAuth()` will have to wait for setting a different seat reservation time.
296
+ * The default value is 15 seconds. You may set the `COLYSEUS_SEAT_RESERVATION_TIME`
297
+ * environment variable if you'd like to change the seat reservation time globally.
298
+ *
299
+ * @default 15 seconds
300
+ *
301
+ * @param seconds - number of seconds.
302
+ * @returns The modified Room object.
303
+ */
130
304
  setSeatReservationTime(seconds) {
131
305
  this.seatReservationTime = seconds;
132
306
  return this;
133
307
  }
134
308
  hasReservedSeat(sessionId, reconnectionToken) {
135
- if (reconnectionToken) {
136
- const reconnection = this._reconnections[reconnectionToken];
137
- return reconnection && reconnection[0] === sessionId && this.reservedSeats[sessionId] !== void 0 && this._reconnectingSessionId.has(sessionId);
309
+ const reservedSeat = this.reservedSeats[sessionId];
310
+ if (reservedSeat === void 0) {
311
+ return false;
312
+ }
313
+ if (reservedSeat[3]) {
314
+ return reconnectionToken && this._reconnections[reconnectionToken]?.[0] === sessionId && this._reconnectingSessionId.has(sessionId);
138
315
  } else {
139
- return this.reservedSeats[sessionId] !== void 0 && (!this._reconnectingSessionId.has(sessionId) || this._reconnectingSessionId.get(sessionId) === sessionId);
316
+ return reservedSeat[2] === false;
140
317
  }
141
318
  }
142
319
  checkReconnectionToken(reconnectionToken) {
143
- const reconnection = this._reconnections[reconnectionToken];
144
- const sessionId = reconnection && reconnection[0];
145
- if (this.hasReservedSeat(sessionId)) {
320
+ const sessionId = this._reconnections[reconnectionToken]?.[0];
321
+ const reservedSeat = this.reservedSeats[sessionId];
322
+ if (reservedSeat && reservedSeat[3]) {
146
323
  this._reconnectingSessionId.set(sessionId, reconnectionToken);
147
324
  return sessionId;
148
325
  } else {
149
326
  return void 0;
150
327
  }
151
328
  }
329
+ /**
330
+ * (Optional) Set a simulation interval that can change the state of the game.
331
+ * The simulation interval is your game loop.
332
+ *
333
+ * @default 16.6ms (60fps)
334
+ *
335
+ * @param onTickCallback - You can implement your physics or world updates here!
336
+ * This is a good place to update the room state.
337
+ * @param delay - Interval delay on executing `onTickCallback` in milliseconds.
338
+ */
152
339
  setSimulationInterval(onTickCallback, delay = DEFAULT_SIMULATION_INTERVAL) {
153
340
  if (this._simulationInterval) {
154
341
  clearInterval(this._simulationInterval);
155
342
  }
156
343
  if (onTickCallback) {
344
+ if (this.onUncaughtException !== void 0) {
345
+ onTickCallback = (0, import_Utils.wrapTryCatch)(onTickCallback, this.onUncaughtException.bind(this), import_RoomExceptions.SimulationIntervalException, "setSimulationInterval");
346
+ }
157
347
  this._simulationInterval = setInterval(() => {
158
348
  this.clock.tick();
159
349
  onTickCallback(this.clock.deltaTime);
160
350
  }, delay);
161
351
  }
162
352
  }
353
+ /**
354
+ * @deprecated Use `.patchRate=` instead.
355
+ */
163
356
  setPatchRate(milliseconds) {
164
357
  this.patchRate = milliseconds;
165
- if (this._patchInterval) {
166
- clearInterval(this._patchInterval);
167
- this._patchInterval = void 0;
168
- }
169
- if (milliseconds !== null && milliseconds !== 0) {
170
- this._patchInterval = setInterval(() => this.broadcastPatch(), milliseconds);
171
- }
172
358
  }
359
+ /**
360
+ * @deprecated Use `.state =` instead.
361
+ */
173
362
  setState(newState) {
174
- this.clock.start();
175
- if (newState[import_schema.$changes] !== void 0) {
176
- this.setSerializer(new import_SchemaSerializer.SchemaSerializer());
177
- } else if (import_schema.$changes === void 0) {
178
- throw new Error("@colyseus/schema v2 compatibility currently missing (reach out if you need it)");
179
- }
180
- this._serializer.reset(newState);
181
363
  this.state = newState;
182
364
  }
183
365
  setSerializer(serializer) {
@@ -202,35 +384,40 @@ class Room {
202
384
  }
203
385
  }
204
386
  async setPrivate(bool = true) {
205
- if (this.listing.private === bool)
206
- return;
387
+ if (this.listing.private === bool) return;
207
388
  this.listing.private = bool;
208
389
  if (this._internalState === 1 /* CREATED */) {
209
390
  await this.listing.save();
210
391
  }
211
392
  this._events.emit("visibility-change", bool);
212
393
  }
394
+ /**
395
+ * Locking the room will remove it from the pool of available rooms for new clients to connect to.
396
+ */
213
397
  async lock() {
214
398
  this._lockedExplicitly = arguments[0] === void 0;
215
- if (this._locked) {
399
+ if (this.#_locked) {
216
400
  return;
217
401
  }
218
- this._locked = true;
402
+ this.#_locked = true;
219
403
  await this.listing.updateOne({
220
- $set: { locked: this._locked }
404
+ $set: { locked: this.#_locked }
221
405
  });
222
406
  this._events.emit("lock");
223
407
  }
408
+ /**
409
+ * Unlocking the room returns it to the pool of available rooms for new clients to connect to.
410
+ */
224
411
  async unlock() {
225
412
  if (arguments[0] === void 0) {
226
413
  this._lockedExplicitly = false;
227
414
  }
228
- if (!this._locked) {
415
+ if (!this.#_locked) {
229
416
  return;
230
417
  }
231
- this._locked = false;
418
+ this.#_locked = false;
232
419
  await this.listing.updateOne({
233
- $set: { locked: this._locked }
420
+ $set: { locked: this.#_locked }
234
421
  });
235
422
  this._events.emit("unlock");
236
423
  }
@@ -238,16 +425,28 @@ class Room {
238
425
  import_Logger.logger.warn("DEPRECATION WARNING: use client.send(...) instead of this.send(client, ...)");
239
426
  client.send(messageOrType, messageOrOptions, options);
240
427
  }
241
- broadcast(typeOrSchema, messageOrOptions, options) {
242
- const isSchema = typeof typeOrSchema === "object";
243
- const opts = isSchema ? messageOrOptions : options;
244
- if (opts && opts.afterNextPatch) {
245
- delete opts.afterNextPatch;
428
+ broadcast(type, message, options) {
429
+ if (options && options.afterNextPatch) {
430
+ delete options.afterNextPatch;
246
431
  this._afterNextPatchQueue.push(["broadcast", arguments]);
247
432
  return;
248
433
  }
249
- this.broadcastMessageType(typeOrSchema, messageOrOptions, opts);
434
+ this.broadcastMessageType(type, message, options);
250
435
  }
436
+ /**
437
+ * Broadcast bytes (UInt8Arrays) to a particular room
438
+ */
439
+ broadcastBytes(type, message, options) {
440
+ if (options && options.afterNextPatch) {
441
+ delete options.afterNextPatch;
442
+ this._afterNextPatchQueue.push(["broadcastBytes", arguments]);
443
+ return;
444
+ }
445
+ this.broadcastMessageType(type, message, options);
446
+ }
447
+ /**
448
+ * Checks whether mutations have occurred in the state, and broadcast them to all connected clients.
449
+ */
251
450
  broadcastPatch() {
252
451
  if (this.onBeforePatch) {
253
452
  this.onBeforePatch(this.state);
@@ -262,10 +461,16 @@ class Room {
262
461
  this._dequeueAfterPatchMessages();
263
462
  return hasChanges;
264
463
  }
265
- onMessage(messageType, callback) {
266
- this.onMessageHandlers[messageType] = callback;
464
+ onMessage(messageType, callback, validate) {
465
+ this.onMessageHandlers[messageType] = this.onUncaughtException !== void 0 ? { validate, callback: (0, import_Utils.wrapTryCatch)(callback, this.onUncaughtException.bind(this), import_RoomExceptions.OnMessageException, "onMessage", false, messageType) } : { validate, callback };
267
466
  return () => delete this.onMessageHandlers[messageType];
268
467
  }
468
+ /**
469
+ * Disconnect all connected clients, and then dispose the room.
470
+ *
471
+ * @param closeCode WebSocket close code (default = 4000, which is a "consented leave")
472
+ * @returns Promise<void>
473
+ */
269
474
  disconnect(closeCode = import_Protocol.Protocol.WS_CLOSE_CONSENTED) {
270
475
  if (this._internalState === 2 /* DISPOSING */) {
271
476
  return Promise.resolve(`disconnect() ignored: room (${this.roomId}) is already disposing.`);
@@ -277,7 +482,7 @@ class Room {
277
482
  this.#_autoDispose = true;
278
483
  const delayedDisconnection = new Promise((resolve) => this._events.once("disconnect", () => resolve()));
279
484
  for (const [_, reconnection] of Object.values(this._reconnections)) {
280
- reconnection.reject();
485
+ reconnection.reject(new Error("disconnecting"));
281
486
  }
282
487
  let numClients = this.clients.length;
283
488
  if (numClients > 0) {
@@ -289,7 +494,7 @@ class Room {
289
494
  }
290
495
  return delayedDisconnection;
291
496
  }
292
- async ["_onJoin"](client, req) {
497
+ async ["_onJoin"](client, authContext) {
293
498
  const sessionId = client.sessionId;
294
499
  client.reconnectionToken = (0, import_Utils.generateId)();
295
500
  if (this.reservedSeatTimeouts[sessionId]) {
@@ -300,26 +505,38 @@ class Room {
300
505
  clearTimeout(this._autoDisposeTimeout);
301
506
  this._autoDisposeTimeout = void 0;
302
507
  }
303
- const [joinOptions, authData] = this.reservedSeats[sessionId];
304
- if (this.reservedSeats[sessionId].length > 2) {
508
+ const [joinOptions, authData, isConsumed, isWaitingReconnection] = this.reservedSeats[sessionId];
509
+ if (isConsumed) {
305
510
  throw new import_ServerError.ServerError(import_Protocol.ErrorCode.MATCHMAKE_EXPIRED, "already consumed");
306
511
  }
307
- this.reservedSeats[sessionId].push(true);
512
+ this.reservedSeats[sessionId][2] = true;
513
+ (0, import_Debug.debugMatchMaking)("consuming seat reservation, sessionId: '%s' (roomId: %s)", client.sessionId, this.roomId);
308
514
  client._afterNextPatchQueue = this._afterNextPatchQueue;
309
515
  client.ref["onleave"] = (_) => client.state = import_Transport.ClientState.LEAVING;
310
516
  client.ref.once("close", client.ref["onleave"]);
311
- const previousReconnectionToken = this._reconnectingSessionId.get(sessionId);
312
- if (previousReconnectionToken) {
313
- this.clients.push(client);
314
- this._reconnections[previousReconnectionToken]?.[1].resolve(client);
517
+ if (isWaitingReconnection) {
518
+ const previousReconnectionToken = this._reconnectingSessionId.get(sessionId);
519
+ if (previousReconnectionToken) {
520
+ this.clients.push(client);
521
+ await this._reconnections[previousReconnectionToken]?.[1].resolve(client);
522
+ } else {
523
+ const errorMessage = process.env.NODE_ENV === "production" ? "already consumed" : "bad reconnection token";
524
+ throw new import_ServerError.ServerError(import_Protocol.ErrorCode.MATCHMAKE_EXPIRED, errorMessage);
525
+ }
315
526
  } else {
316
527
  try {
317
528
  if (authData) {
318
529
  client.auth = authData;
319
530
  } else if (this.onAuth !== Room.prototype.onAuth) {
320
- client.auth = await this.onAuth(client, joinOptions, req);
321
- if (!client.auth) {
322
- throw new import_ServerError.ServerError(import_Protocol.ErrorCode.AUTH_FAILED, "onAuth failed");
531
+ try {
532
+ client.auth = await this.onAuth(client, joinOptions, authContext);
533
+ if (!client.auth) {
534
+ throw new import_ServerError.ServerError(import_Protocol.ErrorCode.AUTH_FAILED, "onAuth failed");
535
+ }
536
+ } catch (e) {
537
+ delete this.reservedSeats[sessionId];
538
+ await this._decrementClientCount();
539
+ throw e;
323
540
  }
324
541
  }
325
542
  if (client.state === import_Transport.ClientState.LEAVING) {
@@ -333,15 +550,15 @@ class Room {
333
550
  if (this.onJoin) {
334
551
  await this.onJoin(client, joinOptions, client.auth);
335
552
  }
336
- this._events.emit("join", client);
337
- delete this.reservedSeats[sessionId];
338
553
  if (client.state === import_Transport.ClientState.LEAVING) {
339
- await this._onLeave(client, import_Protocol.Protocol.WS_CLOSE_GOING_AWAY);
554
+ throw new Error("early_leave");
555
+ } else {
556
+ delete this.reservedSeats[sessionId];
557
+ this._events.emit("join", client);
340
558
  }
341
559
  } catch (e) {
342
- this.clients.delete(client);
560
+ await this._onLeave(client, import_Protocol.Protocol.WS_CLOSE_GOING_AWAY);
343
561
  delete this.reservedSeats[sessionId];
344
- this._decrementClientCount();
345
562
  if (!e.code) {
346
563
  e.code = import_Protocol.ErrorCode.APPLICATION_ERROR;
347
564
  }
@@ -360,9 +577,19 @@ class Room {
360
577
  ));
361
578
  }
362
579
  }
580
+ /**
581
+ * Allow the specified client to reconnect into the room. Must be used inside `onLeave()` method.
582
+ * If seconds is provided, the reconnection is going to be cancelled after the provided amount of seconds.
583
+ *
584
+ * @param previousClient - The client which is to be waiting until re-connection happens.
585
+ * @param seconds - Timeout period on re-connection in seconds.
586
+ *
587
+ * @returns Deferred<Client> - The differed is a promise like type.
588
+ * This type can forcibly reject the promise by calling `.reject()`.
589
+ */
363
590
  allowReconnection(previousClient, seconds) {
364
591
  if (previousClient._enqueuedMessages !== void 0) {
365
- return;
592
+ return Promise.reject(new Error("not joined"));
366
593
  }
367
594
  if (seconds === void 0) {
368
595
  console.warn('DEPRECATED: allowReconnection() requires a second argument. Using "manual" mode.');
@@ -372,8 +599,7 @@ class Room {
372
599
  seconds = Infinity;
373
600
  }
374
601
  if (this._internalState === 2 /* DISPOSING */) {
375
- this._disposeIfEmpty();
376
- throw new Error("disconnecting");
602
+ return Promise.reject(new Error("disposing"));
377
603
  }
378
604
  const sessionId = previousClient.sessionId;
379
605
  const reconnectionToken = previousClient.reconnectionToken;
@@ -392,8 +618,9 @@ class Room {
392
618
  reconnection.then((newClient) => {
393
619
  newClient.auth = previousClient.auth;
394
620
  newClient.userData = previousClient.userData;
395
- previousClient.ref = newClient.ref;
396
621
  previousClient.state = import_Transport.ClientState.RECONNECTED;
622
+ previousClient.ref = newClient.ref;
623
+ previousClient.reconnectionToken = newClient.reconnectionToken;
397
624
  clearTimeout(this.reservedSeatTimeouts[sessionId]);
398
625
  cleanup();
399
626
  }).catch(() => {
@@ -413,8 +640,8 @@ class Room {
413
640
  }, timeoutInSeconds * 1e3);
414
641
  }
415
642
  broadcastMessageType(type, message, options = {}) {
416
- (0, import_Debug.debugMessage)("broadcast: %O", message);
417
- const encodedMessage = import_Protocol.getMessageBytes.raw(import_Protocol.Protocol.ROOM_DATA, type, message);
643
+ (0, import_Debug.debugMessage)("broadcast: %O (roomId: %s)", message, this.roomId);
644
+ const encodedMessage = message instanceof Uint8Array ? import_Protocol.getMessageBytes.raw(import_Protocol.Protocol.ROOM_DATA_BYTES, type, void 0, message) : import_Protocol.getMessageBytes.raw(import_Protocol.Protocol.ROOM_DATA, type, message);
418
645
  const except = typeof options.except !== "undefined" ? Array.isArray(options.except) ? options.except : [options.except] : void 0;
419
646
  let numClients = this.clients.length;
420
647
  while (numClients--) {
@@ -445,7 +672,7 @@ class Room {
445
672
  if (!allowReconnection && this.hasReachedMaxClients()) {
446
673
  return false;
447
674
  }
448
- this.reservedSeats[sessionId] = [joinOptions, authData];
675
+ this.reservedSeats[sessionId] = [joinOptions, authData, false, allowReconnection];
449
676
  if (!allowReconnection) {
450
677
  await this._incrementClientCount();
451
678
  this.reservedSeatTimeouts[sessionId] = setTimeout(async () => {
@@ -461,7 +688,8 @@ class Room {
461
688
  return true;
462
689
  }
463
690
  _disposeIfEmpty() {
464
- const willDispose = this.#_autoDispose && this._autoDisposeTimeout === void 0 && this.clients.length === 0 && Object.keys(this.reservedSeats).length === 0;
691
+ const willDispose = this.#_onLeaveConcurrent === 0 && // no "onLeave" calls in progress
692
+ this.#_autoDispose && this._autoDisposeTimeout === void 0 && this.clients.length === 0 && Object.keys(this.reservedSeats).length === 0;
465
693
  if (willDispose) {
466
694
  this._events.emit("dispose");
467
695
  }
@@ -469,14 +697,14 @@ class Room {
469
697
  }
470
698
  async _dispose() {
471
699
  this._internalState = 2 /* DISPOSING */;
472
- await this.listing.remove();
700
+ this.listing.remove();
473
701
  let userReturnData;
474
702
  if (this.onDispose) {
475
703
  userReturnData = this.onDispose();
476
704
  }
477
- if (this._patchInterval) {
478
- clearInterval(this._patchInterval);
479
- this._patchInterval = void 0;
705
+ if (this.#_patchInterval) {
706
+ clearInterval(this.#_patchInterval);
707
+ this.#_patchInterval = void 0;
480
708
  }
481
709
  if (this._simulationInterval) {
482
710
  clearInterval(this._simulationInterval);
@@ -490,56 +718,48 @@ class Room {
490
718
  this.clock.stop();
491
719
  return await (userReturnData || Promise.resolve());
492
720
  }
493
- _onMessage(client, bytes) {
721
+ _onMessage(client, buffer) {
494
722
  if (client.state === import_Transport.ClientState.LEAVING) {
495
723
  return;
496
724
  }
497
- const it = { offset: 0 };
498
- const code = import_schema.decode.uint8(bytes, it);
499
- if (!bytes) {
500
- (0, import_Debug.debugAndPrintError)(`${this.roomName} (${this.roomId}), couldn't decode message: ${bytes}`);
725
+ const it = { offset: 1 };
726
+ const code = buffer[0];
727
+ if (!buffer) {
728
+ (0, import_Debug.debugAndPrintError)(`${this.roomName} (roomId: ${this.roomId}), couldn't decode message: ${buffer}`);
501
729
  return;
502
730
  }
503
731
  if (code === import_Protocol.Protocol.ROOM_DATA) {
504
- const messageType = import_schema.decode.stringCheck(bytes, it) ? import_schema.decode.string(bytes, it) : import_schema.decode.number(bytes, it);
732
+ const messageType = import_schema.decode.stringCheck(buffer, it) ? import_schema.decode.string(buffer, it) : import_schema.decode.number(buffer, it);
733
+ const messageTypeHandler = this.onMessageHandlers[messageType];
505
734
  let message;
506
735
  try {
507
- message = bytes.length > it.offset ? (0, import_msgpackr.unpack)(new Uint8Array(bytes.slice(it.offset, bytes.length))) : void 0;
508
- (0, import_Debug.debugMessage)("received: '%s' -> %j", messageType, message);
736
+ message = buffer.byteLength > it.offset ? (0, import_msgpackr.unpack)(buffer.subarray(it.offset, buffer.byteLength)) : void 0;
737
+ (0, import_Debug.debugMessage)("received: '%s' -> %j (roomId: %s)", messageType, message, this.roomId);
738
+ if (messageTypeHandler?.validate !== void 0) {
739
+ message = messageTypeHandler.validate(message);
740
+ }
509
741
  } catch (e) {
510
742
  (0, import_Debug.debugAndPrintError)(e);
511
743
  client.leave(import_Protocol.Protocol.WS_CLOSE_WITH_ERROR);
512
744
  return;
513
745
  }
514
- if (this.onMessageHandlers[messageType]) {
515
- this.onMessageHandlers[messageType](client, message);
516
- } else if (this.onMessageHandlers["*"]) {
517
- this.onMessageHandlers["*"](client, messageType, message);
746
+ if (messageTypeHandler) {
747
+ messageTypeHandler.callback(client, message);
518
748
  } else {
519
- const errorMessage = `onMessage for "${messageType}" not registered.`;
520
- (0, import_Debug.debugAndPrintError)(errorMessage);
521
- if (import_DevMode.isDevMode) {
522
- client.error(import_Protocol.ErrorCode.INVALID_PAYLOAD, errorMessage);
523
- } else {
524
- client.leave(import_Protocol.Protocol.WS_CLOSE_WITH_ERROR, errorMessage);
525
- }
749
+ (this.onMessageHandlers["*"] || this.onMessageHandlers["__no_message_handler"]).callback(client, messageType, message);
526
750
  }
527
751
  } else if (code === import_Protocol.Protocol.ROOM_DATA_BYTES) {
528
- const messageType = import_schema.decode.stringCheck(bytes, it) ? import_schema.decode.string(bytes, it) : import_schema.decode.number(bytes, it);
529
- const message = bytes.slice(it.offset, bytes.length);
530
- (0, import_Debug.debugMessage)("received: '%s' -> %j", messageType, message);
531
- if (this.onMessageHandlers[messageType]) {
532
- this.onMessageHandlers[messageType](client, message);
533
- } else if (this.onMessageHandlers["*"]) {
534
- this.onMessageHandlers["*"](client, messageType, message);
752
+ const messageType = import_schema.decode.stringCheck(buffer, it) ? import_schema.decode.string(buffer, it) : import_schema.decode.number(buffer, it);
753
+ const messageTypeHandler = this.onMessageHandlers[messageType];
754
+ let message = buffer.subarray(it.offset, buffer.byteLength);
755
+ (0, import_Debug.debugMessage)("received: '%s' -> %j (roomId: %s)", messageType, message, this.roomId);
756
+ if (messageTypeHandler?.validate !== void 0) {
757
+ message = messageTypeHandler.validate(message);
758
+ }
759
+ if (messageTypeHandler) {
760
+ messageTypeHandler.callback(client, message);
535
761
  } else {
536
- const errorMessage = `onMessage for "${messageType}" not registered.`;
537
- (0, import_Debug.debugAndPrintError)(errorMessage);
538
- if (import_DevMode.isDevMode) {
539
- client.error(import_Protocol.ErrorCode.INVALID_PAYLOAD, errorMessage);
540
- } else {
541
- client.leave(import_Protocol.Protocol.WS_CLOSE_WITH_ERROR, errorMessage);
542
- }
762
+ (this.onMessageHandlers["*"] || this.onMessageHandlers["__no_message_handler"]).callback(client, messageType, message);
543
763
  }
544
764
  } else if (code === import_Protocol.Protocol.JOIN_ROOM && client.state === import_Transport.ClientState.JOINING) {
545
765
  client.state = import_Transport.ClientState.JOINED;
@@ -561,32 +781,43 @@ class Room {
561
781
  this._onLeave(client, closeCode).then(() => client.leave(closeCode));
562
782
  }
563
783
  async _onLeave(client, code) {
564
- const success = this.clients.delete(client);
565
- if (success) {
566
- client.state = import_Transport.ClientState.LEAVING;
567
- if (this.onLeave) {
568
- try {
569
- await this.onLeave(client, code === import_Protocol.Protocol.WS_CLOSE_CONSENTED);
570
- } catch (e) {
571
- (0, import_Debug.debugAndPrintError)(`onLeave error: ${e && e.message || e || "promise rejected"}`);
572
- }
573
- }
784
+ (0, import_Debug.debugMatchMaking)("onLeave, sessionId: '%s' (close code: %d, roomId: %s)", client.sessionId, code, this.roomId);
785
+ client.state = import_Transport.ClientState.LEAVING;
786
+ if (!this.clients.delete(client)) {
787
+ return;
574
788
  }
575
- if (client.state !== import_Transport.ClientState.RECONNECTED) {
576
- const willDispose = await this._decrementClientCount();
577
- if (this.reservedSeats[client.sessionId] === void 0) {
578
- this._events.emit("leave", client, willDispose);
789
+ if (this.onLeave) {
790
+ try {
791
+ this.#_onLeaveConcurrent++;
792
+ await this.onLeave(client, code === import_Protocol.Protocol.WS_CLOSE_CONSENTED);
793
+ } catch (e) {
794
+ (0, import_Debug.debugAndPrintError)(`onLeave error: ${e && e.message || e || "promise rejected"} (roomId: ${this.roomId})`);
795
+ } finally {
796
+ this.#_onLeaveConcurrent--;
579
797
  }
580
798
  }
799
+ if (this._reconnections[client.reconnectionToken]) {
800
+ this._reconnections[client.reconnectionToken][1].catch(async () => {
801
+ await this._onAfterLeave(client);
802
+ });
803
+ } else if (client.state !== import_Transport.ClientState.RECONNECTED) {
804
+ await this._onAfterLeave(client);
805
+ }
806
+ }
807
+ async _onAfterLeave(client) {
808
+ const willDispose = await this._decrementClientCount();
809
+ if (this.reservedSeats[client.sessionId] === void 0) {
810
+ this._events.emit("leave", client, willDispose);
811
+ }
581
812
  }
582
813
  async _incrementClientCount() {
583
- if (!this._locked && this.hasReachedMaxClients()) {
584
- this._maxClientsReached = true;
814
+ if (!this.#_locked && this.hasReachedMaxClients()) {
815
+ this.#_maxClientsReached = true;
585
816
  this.lock.call(this, true);
586
817
  }
587
818
  await this.listing.updateOne({
588
819
  $inc: { clients: 1 },
589
- $set: { locked: this._locked }
820
+ $set: { locked: this.#_locked }
590
821
  });
591
822
  }
592
823
  async _decrementClientCount() {
@@ -595,17 +826,43 @@ class Room {
595
826
  return true;
596
827
  }
597
828
  if (!willDispose) {
598
- if (this._maxClientsReached && !this._lockedExplicitly) {
599
- this._maxClientsReached = false;
829
+ if (this.#_maxClientsReached && !this._lockedExplicitly) {
830
+ this.#_maxClientsReached = false;
600
831
  this.unlock.call(this, true);
601
832
  }
602
833
  await this.listing.updateOne({
603
834
  $inc: { clients: -1 },
604
- $set: { locked: this._locked }
835
+ $set: { locked: this.#_locked }
605
836
  });
606
837
  }
607
838
  return willDispose;
608
839
  }
840
+ #registerUncaughtExceptionHandlers() {
841
+ const onUncaughtException = this.onUncaughtException.bind(this);
842
+ const originalSetTimeout = this.clock.setTimeout;
843
+ this.clock.setTimeout = (cb, timeout, ...args) => {
844
+ return originalSetTimeout.call(this.clock, (0, import_Utils.wrapTryCatch)(cb, onUncaughtException, import_RoomExceptions.TimedEventException, "setTimeout"), timeout, ...args);
845
+ };
846
+ const originalSetInterval = this.clock.setInterval;
847
+ this.clock.setInterval = (cb, timeout, ...args) => {
848
+ return originalSetInterval.call(this.clock, (0, import_Utils.wrapTryCatch)(cb, onUncaughtException, import_RoomExceptions.TimedEventException, "setInterval"), timeout, ...args);
849
+ };
850
+ if (this.onCreate !== void 0) {
851
+ this.onCreate = (0, import_Utils.wrapTryCatch)(this.onCreate.bind(this), onUncaughtException, import_RoomExceptions.OnCreateException, "onCreate", true);
852
+ }
853
+ if (this.onAuth !== void 0) {
854
+ this.onAuth = (0, import_Utils.wrapTryCatch)(this.onAuth.bind(this), onUncaughtException, import_RoomExceptions.OnAuthException, "onAuth", true);
855
+ }
856
+ if (this.onJoin !== void 0) {
857
+ this.onJoin = (0, import_Utils.wrapTryCatch)(this.onJoin.bind(this), onUncaughtException, import_RoomExceptions.OnJoinException, "onJoin", true);
858
+ }
859
+ if (this.onLeave !== void 0) {
860
+ this.onLeave = (0, import_Utils.wrapTryCatch)(this.onLeave.bind(this), onUncaughtException, import_RoomExceptions.OnLeaveException, "onLeave", true);
861
+ }
862
+ if (this.onDispose !== void 0) {
863
+ this.onDispose = (0, import_Utils.wrapTryCatch)(this.onDispose.bind(this), onUncaughtException, import_RoomExceptions.OnDisposeException, "onDispose");
864
+ }
865
+ }
609
866
  }
610
867
  // Annotate the CommonJS export names for ESM import in node:
611
868
  0 && (module.exports = {