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