@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
@@ -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
  ));
@@ -25,7 +29,6 @@ var MatchMaker_exports = {};
25
29
  __export(MatchMaker_exports, {
26
30
  MatchMakerState: () => MatchMakerState,
27
31
  accept: () => accept,
28
- cleanupStaleRooms: () => cleanupStaleRooms,
29
32
  controller: () => import_controller.default,
30
33
  create: () => create,
31
34
  createRoom: () => createRoom,
@@ -34,6 +37,7 @@ __export(MatchMaker_exports, {
34
37
  driver: () => driver,
35
38
  findOneRoomAvailable: () => findOneRoomAvailable,
36
39
  getHandler: () => getHandler,
40
+ getLocalRoomById: () => getLocalRoomById,
37
41
  getRoomById: () => getRoomById,
38
42
  getRoomClass: () => getRoomClass,
39
43
  gracefullyShutdown: () => gracefullyShutdown,
@@ -41,7 +45,6 @@ __export(MatchMaker_exports, {
41
45
  hasHandler: () => hasHandler,
42
46
  healthCheckAllProcesses: () => healthCheckAllProcesses,
43
47
  healthCheckProcessId: () => healthCheckProcessId,
44
- isGracefullyShuttingDown: () => isGracefullyShuttingDown,
45
48
  join: () => join,
46
49
  joinById: () => joinById,
47
50
  joinOrCreate: () => joinOrCreate,
@@ -55,34 +58,41 @@ __export(MatchMaker_exports, {
55
58
  removeRoomType: () => removeRoomType,
56
59
  reserveSeatFor: () => reserveSeatFor,
57
60
  selectProcessIdToCreateRoom: () => selectProcessIdToCreateRoom,
61
+ setHealthChecksEnabled: () => setHealthChecksEnabled,
58
62
  setup: () => setup,
59
63
  state: () => state,
60
64
  stats: () => stats
61
65
  });
62
66
  module.exports = __toCommonJS(MatchMaker_exports);
63
- var import_Protocol = require("./Protocol");
64
- var import_IPC = require("./IPC");
65
- var import_Utils = require("./utils/Utils");
66
- var import_DevMode = require("./utils/DevMode");
67
- var import_RegisteredHandler = require("./matchmaker/RegisteredHandler");
68
- var import_Room = require("./Room");
69
- var import_LocalPresence = require("./presence/LocalPresence");
70
- var import_Debug = require("./Debug");
71
- var import_SeatReservationError = require("./errors/SeatReservationError");
72
- var import_ServerError = require("./errors/ServerError");
73
- var import_driver = require("./matchmaker/driver");
74
- var import_controller = __toESM(require("./matchmaker/controller"));
75
- var stats = __toESM(require("./Stats"));
76
- var import_Logger = require("./Logger");
77
- var import_discovery = require("./discovery");
67
+ var import_events = require("events");
68
+ var import_Protocol = require("./Protocol.js");
69
+ var import_IPC = require("./IPC.js");
70
+ var import_Utils = require("./utils/Utils.js");
71
+ var import_DevMode = require("./utils/DevMode.js");
72
+ var import_RegisteredHandler = require("./matchmaker/RegisteredHandler.js");
73
+ var import_Room = require("./Room.js");
74
+ var import_LocalPresence = require("./presence/LocalPresence.js");
75
+ var import_Debug = require("./Debug.js");
76
+ var import_SeatReservationError = require("./errors/SeatReservationError.js");
77
+ var import_ServerError = require("./errors/ServerError.js");
78
+ var import_LocalDriver = require("./matchmaker/driver/local/LocalDriver.js");
79
+ var import_controller = __toESM(require("./matchmaker/controller.js"));
80
+ var stats = __toESM(require("./Stats.js"));
81
+ var import_Logger = require("./Logger.js");
82
+ var import_discovery = require("./discovery/index.js");
83
+ var import_api = require("./matchmaker/driver/api.js");
78
84
  const handlers = {};
79
85
  const rooms = {};
86
+ const events = new import_events.EventEmitter();
80
87
  let publicAddress;
81
88
  let processId;
82
89
  let presence;
83
90
  let driver;
84
91
  let selectProcessIdToCreateRoom;
85
- let isGracefullyShuttingDown;
92
+ let enableHealthChecks = true;
93
+ function setHealthChecksEnabled(value) {
94
+ enableHealthChecks = value;
95
+ }
86
96
  let onReady = new import_Utils.Deferred();
87
97
  var MatchMakerState = /* @__PURE__ */ ((MatchMakerState2) => {
88
98
  MatchMakerState2[MatchMakerState2["INITIALIZING"] = 0] = "INITIALIZING";
@@ -95,10 +105,9 @@ async function setup(_presence, _driver, _publicAddress, _selectProcessIdToCreat
95
105
  if (onReady === void 0) {
96
106
  onReady = new import_Utils.Deferred();
97
107
  }
98
- isGracefullyShuttingDown = false;
99
108
  state = 0 /* INITIALIZING */;
100
109
  presence = _presence || new import_LocalPresence.LocalPresence();
101
- driver = _driver || new import_driver.LocalDriver();
110
+ driver = _driver || new import_LocalDriver.LocalDriver();
102
111
  publicAddress = _publicAddress;
103
112
  stats.reset(false);
104
113
  if (import_DevMode.isDevMode) {
@@ -121,25 +130,44 @@ async function accept() {
121
130
  return handleCreateRoom.apply(void 0, args);
122
131
  }
123
132
  });
124
- await healthCheckAllProcesses();
133
+ if (enableHealthChecks) {
134
+ await healthCheckAllProcesses();
135
+ stats.setAutoPersistInterval();
136
+ }
125
137
  state = 1 /* READY */;
126
138
  await stats.persist();
127
139
  if (import_DevMode.isDevMode) {
128
140
  await (0, import_DevMode.reloadFromCache)();
129
141
  }
130
142
  }
131
- async function joinOrCreate(roomName, clientOptions = {}, authOptions) {
143
+ async function joinOrCreate(roomName, clientOptions = {}, authContext) {
132
144
  return await (0, import_Utils.retry)(async () => {
133
- const authData = await callOnAuth(roomName, authOptions);
145
+ const authData = await callOnAuth(roomName, clientOptions, authContext);
134
146
  let room = await findOneRoomAvailable(roomName, clientOptions);
135
147
  if (!room) {
136
- room = await createRoom(roomName, clientOptions);
148
+ const handler = getHandler(roomName);
149
+ const filterOptions = handler.getFilterOptions(clientOptions);
150
+ const concurrencyKey = (0, import_api.getLockId)(filterOptions);
151
+ await concurrentJoinOrCreateRoomLock(handler, concurrencyKey, async (roomId) => {
152
+ if (roomId) {
153
+ room = await driver.findOne({ roomId });
154
+ }
155
+ if (!room) {
156
+ room = await findOneRoomAvailable(roomName, clientOptions);
157
+ }
158
+ if (!room) {
159
+ room = await createRoom(roomName, clientOptions);
160
+ presence.lpush(`l:${handler.name}:${concurrencyKey}`, room.roomId);
161
+ presence.expire(`l:${handler.name}:${concurrencyKey}`, import_Utils.MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME * 2);
162
+ }
163
+ return room;
164
+ });
137
165
  }
138
166
  return await reserveSeatFor(room, clientOptions, authData);
139
167
  }, 5, [import_SeatReservationError.SeatReservationError]);
140
168
  }
141
- async function create(roomName, clientOptions = {}, authOptions) {
142
- const authData = await callOnAuth(roomName, authOptions);
169
+ async function create(roomName, clientOptions = {}, authContext) {
170
+ const authData = await callOnAuth(roomName, clientOptions, authContext);
143
171
  const room = await createRoom(roomName, clientOptions);
144
172
  return reserveSeatFor(room, clientOptions, authData);
145
173
  }
@@ -187,23 +215,21 @@ async function joinById(roomId, clientOptions = {}, authOptions) {
187
215
  const authData = await callOnAuth(room.name, authOptions);
188
216
  return reserveSeatFor(room, clientOptions, authData);
189
217
  }
190
- async function query(conditions = {}) {
191
- return await driver.find(conditions);
192
- }
193
- async function findOneRoomAvailable(roomName, clientOptions) {
194
- return await awaitRoomAvailable(roomName, async () => {
195
- const handler = getHandler(roomName);
196
- const roomQuery = driver.findOne({
197
- locked: false,
198
- name: roomName,
199
- private: false,
200
- ...handler.getFilterOptions(clientOptions)
201
- });
202
- if (handler.sortOptions) {
203
- roomQuery.sort(handler.sortOptions);
204
- }
205
- return await roomQuery;
206
- });
218
+ async function query(conditions = {}, sortOptions) {
219
+ return await driver.query(conditions, sortOptions);
220
+ }
221
+ async function findOneRoomAvailable(roomName, filterOptions, additionalSortOptions) {
222
+ const handler = getHandler(roomName);
223
+ const sortOptions = Object.assign({}, handler.sortOptions ?? {});
224
+ if (additionalSortOptions) {
225
+ Object.assign(sortOptions, additionalSortOptions);
226
+ }
227
+ return await driver.findOne({
228
+ locked: false,
229
+ name: roomName,
230
+ private: false,
231
+ ...handler.getFilterOptions(filterOptions)
232
+ }, sortOptions);
207
233
  }
208
234
  async function remoteRoomCall(roomId, method, args, rejectionTimeout = import_Utils.REMOTE_ROOM_SHORT_TIMEOUT) {
209
235
  const room = rooms[roomId];
@@ -225,23 +251,17 @@ async function remoteRoomCall(roomId, method, args, rejectionTimeout = import_Ut
225
251
  }
226
252
  }
227
253
  function defineRoomType(roomName, klass, defaultOptions) {
228
- const registeredHandler = new import_RegisteredHandler.RegisteredHandler(klass, defaultOptions);
254
+ const registeredHandler = new import_RegisteredHandler.RegisteredHandler(roomName, klass, defaultOptions);
229
255
  handlers[roomName] = registeredHandler;
230
256
  if (klass.prototype["onAuth"] !== import_Room.Room.prototype["onAuth"]) {
231
257
  if (klass["onAuth"] !== import_Room.Room["onAuth"]) {
232
258
  import_Logger.logger.info(`\u274C "${roomName}"'s onAuth() defined at the instance level will be ignored.`);
233
259
  }
234
260
  }
235
- if (!import_DevMode.isDevMode) {
236
- cleanupStaleRooms(roomName);
237
- }
238
261
  return registeredHandler;
239
262
  }
240
263
  function removeRoomType(roomName) {
241
264
  delete handlers[roomName];
242
- if (!import_DevMode.isDevMode) {
243
- cleanupStaleRooms(roomName);
244
- }
245
265
  }
246
266
  function hasHandler(roomName) {
247
267
  import_Logger.logger.warn("hasHandler() is deprecated. Use getHandler() instead.");
@@ -276,7 +296,9 @@ async function createRoom(roomName, clientOptions) {
276
296
  } catch (e) {
277
297
  if (e.message === "ipc_timeout") {
278
298
  (0, import_Debug.debugAndPrintError)(`${e.message}: create room request timed out for ${roomName} on processId ${selectedProcessId}.`);
279
- await stats.excludeProcess(selectedProcessId);
299
+ if (enableHealthChecks) {
300
+ await stats.excludeProcess(selectedProcessId);
301
+ }
280
302
  room = await handleCreateRoom(roomName, clientOptions);
281
303
  } else {
282
304
  throw e;
@@ -300,9 +322,7 @@ async function handleCreateRoom(roomName, clientOptions, restoringRoomId) {
300
322
  } else {
301
323
  room.roomId = (0, import_Utils.generateId)();
302
324
  }
303
- if (room.state) {
304
- room.setState(room.state);
305
- }
325
+ room["__init"]();
306
326
  room.roomName = roomName;
307
327
  room.presence = presence;
308
328
  const additionalListingData = handler.getFilterOptions(clientOptions);
@@ -342,13 +362,21 @@ async function handleCreateRoom(roomName, clientOptions, restoringRoomId) {
342
362
  room._events.removeAllListeners("unlock");
343
363
  room._events.removeAllListeners("visibility-change");
344
364
  room._events.removeAllListeners("dispose");
365
+ if (stats.local.roomCount <= 0) {
366
+ events.emit("no-active-rooms");
367
+ }
345
368
  });
346
369
  await createRoomReferences(room, true);
347
- await room.listing.save();
370
+ if (state !== 2 /* SHUTTING_DOWN */) {
371
+ await room.listing.save();
372
+ }
348
373
  handler.emit("create", room);
349
374
  return room.listing;
350
375
  }
351
376
  function getRoomById(roomId) {
377
+ return driver.findOne({ roomId });
378
+ }
379
+ function getLocalRoomById(roomId) {
352
380
  return rooms[roomId];
353
381
  }
354
382
  function disconnectAll(closeCode) {
@@ -357,24 +385,42 @@ function disconnectAll(closeCode) {
357
385
  if (!rooms.hasOwnProperty(roomId)) {
358
386
  continue;
359
387
  }
360
- const room = rooms[roomId];
361
- room._events.removeAllListeners("leave");
362
- promises.push(room.disconnect(closeCode));
388
+ promises.push(rooms[roomId].disconnect(closeCode));
363
389
  }
364
390
  return promises;
365
391
  }
392
+ async function lockAndDisposeAll() {
393
+ await stats.excludeProcess(processId);
394
+ if (enableHealthChecks) {
395
+ stats.clearAutoPersistInterval();
396
+ }
397
+ const noActiveRooms = new import_Utils.Deferred();
398
+ if (stats.local.roomCount <= 0) {
399
+ noActiveRooms.resolve();
400
+ } else {
401
+ events.once("no-active-rooms", () => noActiveRooms.resolve());
402
+ }
403
+ for (const roomId in rooms) {
404
+ if (!rooms.hasOwnProperty(roomId)) {
405
+ continue;
406
+ }
407
+ const room = rooms[roomId];
408
+ room.lock();
409
+ room.onBeforeShutdown();
410
+ }
411
+ await noActiveRooms;
412
+ }
366
413
  async function gracefullyShutdown() {
367
- if (isGracefullyShuttingDown) {
414
+ if (state === 2 /* SHUTTING_DOWN */) {
368
415
  return Promise.reject("already_shutting_down");
369
416
  }
370
- isGracefullyShuttingDown = true;
417
+ (0, import_Debug.debugMatchMaking)(`${processId} is shutting down!`);
371
418
  state = 2 /* SHUTTING_DOWN */;
372
419
  onReady = void 0;
373
- (0, import_Debug.debugMatchMaking)(`${processId} is shutting down!`);
420
+ await lockAndDisposeAll();
374
421
  if (import_DevMode.isDevMode) {
375
422
  await (0, import_DevMode.cacheRoomHistory)(rooms);
376
423
  }
377
- await stats.excludeProcess(processId);
378
424
  await removeRoomsByProcessId(processId);
379
425
  presence.unsubscribe(getProcessChannel());
380
426
  return Promise.all(disconnectAll(
@@ -399,7 +445,7 @@ async function reserveSeatFor(room, options, authData) {
399
445
  );
400
446
  } catch (e) {
401
447
  (0, import_Debug.debugMatchMaking)(e);
402
- if (e.message === "ipc_timeout" && !await healthCheckProcessId(room.processId)) {
448
+ if (e.message === "ipc_timeout" && !(enableHealthChecks && await healthCheckProcessId(room.processId))) {
403
449
  throw new import_SeatReservationError.SeatReservationError(`process ${room.processId} is not available.`);
404
450
  } else {
405
451
  successfulSeatReservation = false;
@@ -414,12 +460,9 @@ async function reserveSeatFor(room, options, authData) {
414
460
  }
415
461
  return response;
416
462
  }
417
- function callOnAuth(roomName, authOptions) {
463
+ function callOnAuth(roomName, clientOptions, authContext) {
418
464
  const roomClass = getRoomClass(roomName);
419
- return roomClass && roomClass["onAuth"] && roomClass["onAuth"] !== import_Room.Room["onAuth"] ? roomClass["onAuth"](authOptions.token, authOptions.request) : void 0;
420
- }
421
- async function cleanupStaleRooms(roomName) {
422
- await presence.del(getHandlerConcurrencyKey(roomName));
465
+ return roomClass && roomClass["onAuth"] && roomClass["onAuth"] !== import_Room.Room["onAuth"] ? roomClass["onAuth"](authContext.token, clientOptions, authContext) : void 0;
423
466
  }
424
467
  async function healthCheckAllProcesses() {
425
468
  const allStats = await stats.fetchAll();
@@ -461,13 +504,7 @@ function healthCheckProcessId(processId2) {
461
504
  return _healthCheckByProcessId[processId2];
462
505
  }
463
506
  async function removeRoomsByProcessId(processId2) {
464
- if (typeof driver.cleanup === "function") {
465
- await driver.cleanup(processId2);
466
- } else {
467
- const cachedRooms = await driver.find({ processId: processId2 }, { _id: 1 });
468
- import_Logger.logger.debug("> Removing stale rooms by processId:", processId2, `(${cachedRooms.length} rooms found)`);
469
- cachedRooms.forEach((room) => room.remove());
470
- }
507
+ await driver.cleanup(processId2);
471
508
  }
472
509
  async function createRoomReferences(room, init = false) {
473
510
  rooms[room.roomId] = room;
@@ -483,29 +520,42 @@ async function createRoomReferences(room, init = false) {
483
520
  }
484
521
  return true;
485
522
  }
486
- async function awaitRoomAvailable(roomToJoin, callback) {
523
+ async function concurrentJoinOrCreateRoomLock(handler, concurrencyKey, callback) {
487
524
  return new Promise(async (resolve, reject) => {
488
- const concurrencyKey = getHandlerConcurrencyKey(roomToJoin);
489
- const concurrency = await presence.incr(concurrencyKey) - 1;
490
- const concurrencyTimeout = Math.min(concurrency * 100, 500);
491
- if (concurrency > 0) {
492
- (0, import_Debug.debugMatchMaking)(
493
- "receiving %d concurrent requests for joining '%s' (waiting %d ms)",
494
- concurrency,
495
- roomToJoin,
496
- concurrencyTimeout
497
- );
498
- }
499
- setTimeout(async () => {
525
+ const hkey = getConcurrencyHashKey(handler.name);
526
+ const concurrency = await presence.hincrbyex(
527
+ hkey,
528
+ concurrencyKey,
529
+ 1,
530
+ // increment by 1
531
+ import_Utils.MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME * 2
532
+ // expire in 2x the time of MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME
533
+ ) - 1;
534
+ const fulfill = async (roomId) => {
500
535
  try {
501
- const result = await callback();
502
- resolve(result);
536
+ resolve(await callback(roomId));
503
537
  } catch (e) {
504
538
  reject(e);
505
539
  } finally {
506
- await presence.decr(concurrencyKey);
540
+ await presence.hincrby(hkey, concurrencyKey, -1);
507
541
  }
508
- }, concurrencyTimeout);
542
+ };
543
+ if (concurrency > 0) {
544
+ (0, import_Debug.debugMatchMaking)(
545
+ "receiving %d concurrent joinOrCreate for '%s' (%s)",
546
+ concurrency,
547
+ handler.name,
548
+ concurrencyKey
549
+ );
550
+ const result = await presence.brpop(
551
+ `l:${handler.name}:${concurrencyKey}`,
552
+ import_Utils.MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME + Math.min(concurrency, 3) * 0.2
553
+ // add extra milliseconds for each concurrent request
554
+ );
555
+ return await fulfill(result && result[1]);
556
+ } else {
557
+ return await fulfill();
558
+ }
509
559
  });
510
560
  }
511
561
  function onClientJoinRoom(room, client) {
@@ -530,24 +580,24 @@ function onVisibilityChange(room, isInvisible) {
530
580
  handlers[room.roomName].emit("visibility-change", room, isInvisible);
531
581
  }
532
582
  async function disposeRoom(roomName, room) {
533
- (0, import_Debug.debugMatchMaking)("disposing '%s' (%s) on processId '%s' (graceful shutdown: %s)", roomName, room.roomId, processId, isGracefullyShuttingDown);
534
- if (!isGracefullyShuttingDown) {
535
- stats.local.roomCount--;
583
+ (0, import_Debug.debugMatchMaking)("disposing '%s' (%s) on processId '%s' (graceful shutdown: %s)", roomName, room.roomId, processId, state === 2 /* SHUTTING_DOWN */);
584
+ room.listing.remove();
585
+ stats.local.roomCount--;
586
+ if (state !== 2 /* SHUTTING_DOWN */) {
536
587
  stats.persist();
537
588
  if (import_DevMode.isDevMode) {
538
589
  await presence.hdel((0, import_DevMode.getRoomRestoreListKey)(), room.roomId);
539
590
  }
540
591
  }
541
592
  handlers[roomName].emit("dispose", room);
542
- presence.del(getHandlerConcurrencyKey(roomName));
543
593
  presence.unsubscribe(getRoomChannel(room.roomId));
544
594
  delete rooms[room.roomId];
545
595
  }
546
596
  function getRoomChannel(roomId) {
547
597
  return `$${roomId}`;
548
598
  }
549
- function getHandlerConcurrencyKey(name) {
550
- return `c:${name}`;
599
+ function getConcurrencyHashKey(roomName) {
600
+ return `ch:${roomName}`;
551
601
  }
552
602
  function getProcessChannel(id = processId) {
553
603
  return `p:${id}`;
@@ -556,7 +606,6 @@ function getProcessChannel(id = processId) {
556
606
  0 && (module.exports = {
557
607
  MatchMakerState,
558
608
  accept,
559
- cleanupStaleRooms,
560
609
  controller,
561
610
  create,
562
611
  createRoom,
@@ -565,6 +614,7 @@ function getProcessChannel(id = processId) {
565
614
  driver,
566
615
  findOneRoomAvailable,
567
616
  getHandler,
617
+ getLocalRoomById,
568
618
  getRoomById,
569
619
  getRoomClass,
570
620
  gracefullyShutdown,
@@ -572,7 +622,6 @@ function getProcessChannel(id = processId) {
572
622
  hasHandler,
573
623
  healthCheckAllProcesses,
574
624
  healthCheckProcessId,
575
- isGracefullyShuttingDown,
576
625
  join,
577
626
  joinById,
578
627
  joinOrCreate,
@@ -586,6 +635,7 @@ function getProcessChannel(id = processId) {
586
635
  removeRoomType,
587
636
  reserveSeatFor,
588
637
  selectProcessIdToCreateRoom,
638
+ setHealthChecksEnabled,
589
639
  setup,
590
640
  state,
591
641
  stats