@colyseus/core 0.16.0-preview.20 → 0.16.0-preview.27

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 (91) hide show
  1. package/build/MatchMaker.d.ts +23 -15
  2. package/build/MatchMaker.js +89 -73
  3. package/build/MatchMaker.js.map +2 -2
  4. package/build/MatchMaker.mjs +87 -71
  5. package/build/MatchMaker.mjs.map +2 -2
  6. package/build/Protocol.d.ts +3 -4
  7. package/build/Protocol.js +25 -20
  8. package/build/Protocol.js.map +2 -2
  9. package/build/Protocol.mjs +25 -20
  10. package/build/Protocol.mjs.map +2 -2
  11. package/build/Room.d.ts +30 -29
  12. package/build/Room.js +123 -84
  13. package/build/Room.js.map +2 -2
  14. package/build/Room.mjs +123 -84
  15. package/build/Room.mjs.map +2 -2
  16. package/build/Server.d.ts +0 -1
  17. package/build/Server.js +4 -4
  18. package/build/Server.js.map +2 -2
  19. package/build/Server.mjs +3 -3
  20. package/build/Server.mjs.map +2 -2
  21. package/build/Transport.d.ts +0 -5
  22. package/build/index.d.ts +1 -1
  23. package/build/index.js +1 -1
  24. package/build/index.js.map +2 -2
  25. package/build/index.mjs +1 -1
  26. package/build/index.mjs.map +1 -1
  27. package/build/matchmaker/Lobby.d.ts +2 -2
  28. package/build/matchmaker/Lobby.js.map +2 -2
  29. package/build/matchmaker/Lobby.mjs.map +2 -2
  30. package/build/matchmaker/RegisteredHandler.d.ts +4 -5
  31. package/build/matchmaker/RegisteredHandler.js +4 -3
  32. package/build/matchmaker/RegisteredHandler.js.map +2 -2
  33. package/build/matchmaker/RegisteredHandler.mjs +4 -3
  34. package/build/matchmaker/RegisteredHandler.mjs.map +2 -2
  35. package/build/matchmaker/controller.d.ts +1 -2
  36. package/build/matchmaker/driver/RoomData.d.ts +3 -3
  37. package/build/matchmaker/driver/RoomData.js +3 -3
  38. package/build/matchmaker/driver/RoomData.js.map +2 -2
  39. package/build/matchmaker/driver/RoomData.mjs +2 -2
  40. package/build/matchmaker/driver/RoomData.mjs.map +2 -2
  41. package/build/matchmaker/driver/api.d.ts +104 -0
  42. package/build/matchmaker/driver/api.js +29 -0
  43. package/build/matchmaker/driver/api.js.map +7 -0
  44. package/build/matchmaker/driver/api.mjs +6 -0
  45. package/build/matchmaker/driver/api.mjs.map +7 -0
  46. package/build/matchmaker/driver/index.d.ts +7 -7
  47. package/build/matchmaker/driver/index.js +1 -1
  48. package/build/matchmaker/driver/index.js.map +2 -2
  49. package/build/matchmaker/driver/index.mjs +2 -2
  50. package/build/matchmaker/driver/index.mjs.map +2 -2
  51. package/build/matchmaker/driver/interfaces.d.ts +7 -11
  52. package/build/matchmaker/driver/interfaces.js.map +1 -1
  53. package/build/matchmaker/driver/local/LocalDriver.d.ts +13 -0
  54. package/build/matchmaker/driver/local/LocalDriver.js +65 -0
  55. package/build/matchmaker/driver/local/LocalDriver.js.map +7 -0
  56. package/build/matchmaker/driver/local/LocalDriver.mjs +42 -0
  57. package/build/matchmaker/driver/local/LocalDriver.mjs.map +7 -0
  58. package/build/matchmaker/driver/local/Query.d.ts +9 -0
  59. package/build/matchmaker/driver/local/Query.js +78 -0
  60. package/build/matchmaker/driver/local/Query.js.map +7 -0
  61. package/build/matchmaker/driver/local/Query.mjs +55 -0
  62. package/build/matchmaker/driver/local/Query.mjs.map +7 -0
  63. package/build/matchmaker/driver/local/RoomData.d.ts +19 -0
  64. package/build/matchmaker/driver/local/RoomData.js +79 -0
  65. package/build/matchmaker/driver/local/RoomData.js.map +7 -0
  66. package/build/matchmaker/driver/local/RoomData.mjs +56 -0
  67. package/build/matchmaker/driver/local/RoomData.mjs.map +7 -0
  68. package/build/presence/LocalPresence.d.ts +9 -2
  69. package/build/presence/LocalPresence.js +77 -3
  70. package/build/presence/LocalPresence.js.map +3 -3
  71. package/build/presence/LocalPresence.mjs +77 -3
  72. package/build/presence/LocalPresence.mjs.map +3 -3
  73. package/build/presence/Presence.d.ts +38 -2
  74. package/build/presence/Presence.js.map +1 -1
  75. package/build/rooms/LobbyRoom.d.ts +4 -4
  76. package/build/rooms/LobbyRoom.js.map +2 -2
  77. package/build/rooms/LobbyRoom.mjs.map +2 -2
  78. package/build/serializer/SchemaSerializer.d.ts +11 -10
  79. package/build/serializer/SchemaSerializer.js.map +2 -2
  80. package/build/serializer/SchemaSerializer.mjs.map +2 -2
  81. package/build/serializer/Serializer.d.ts +0 -1
  82. package/build/utils/DevMode.js +1 -1
  83. package/build/utils/DevMode.js.map +2 -2
  84. package/build/utils/DevMode.mjs +2 -2
  85. package/build/utils/DevMode.mjs.map +2 -2
  86. package/build/utils/Utils.d.ts +4 -2
  87. package/build/utils/Utils.js +11 -2
  88. package/build/utils/Utils.js.map +2 -2
  89. package/build/utils/Utils.mjs +10 -2
  90. package/build/utils/Utils.mjs.map +2 -2
  91. package/package.json +2 -2
@@ -2,7 +2,7 @@ import { Deferred } from './utils/Utils';
2
2
  import { RegisteredHandler } from './matchmaker/RegisteredHandler';
3
3
  import { Room } from './Room';
4
4
  import { Presence } from './presence/Presence';
5
- import { IRoomListingData, RoomListingData, MatchMakerDriver } from './matchmaker/driver';
5
+ import { IRoomCache, MatchMakerDriver, SortOptions } from './matchmaker/driver/local/LocalDriver';
6
6
  import controller from './matchmaker/controller';
7
7
  import * as stats from "./Stats";
8
8
  import { Type } from './utils/types';
@@ -15,7 +15,7 @@ export type AuthOptions = {
15
15
  export type SelectProcessIdCallback = (roomName: string, clientOptions: ClientOptions) => Promise<string>;
16
16
  export interface SeatReservation {
17
17
  sessionId: string;
18
- room: RoomListingData;
18
+ room: IRoomCache;
19
19
  devMode?: boolean;
20
20
  }
21
21
  export declare let publicAddress: string;
@@ -23,7 +23,7 @@ export declare let processId: string;
23
23
  export declare let presence: Presence;
24
24
  export declare let driver: MatchMakerDriver;
25
25
  export declare let selectProcessIdToCreateRoom: SelectProcessIdCallback;
26
- export declare let isGracefullyShuttingDown: boolean;
26
+ export declare function setHealthChecksEnabled(value: boolean): void;
27
27
  export declare let onReady: Deferred;
28
28
  export declare enum MatchMakerState {
29
29
  INITIALIZING = 0,
@@ -60,7 +60,7 @@ export declare function join(roomName: string, clientOptions?: ClientOptions, au
60
60
  * Join a room by id and return seat reservation
61
61
  */
62
62
  export declare function reconnect(roomId: string, clientOptions?: ClientOptions): Promise<{
63
- room: RoomListingData<any>;
63
+ room: import("./matchmaker/driver/api").RoomCache<any>;
64
64
  sessionId: any;
65
65
  }>;
66
66
  /**
@@ -70,22 +70,23 @@ export declare function reconnect(roomId: string, clientOptions?: ClientOptions)
70
70
  * @param clientOptions - Options for the client seat reservation (for `onJoin`/`onAuth`)
71
71
  * @param authOptions - Optional authentication token
72
72
  *
73
- * @returns Promise<SeatReservation> - A promise which contains `sessionId` and `RoomListingData`.
73
+ * @returns Promise<SeatReservation> - A promise which contains `sessionId` and `IRoomCache`.
74
74
  */
75
75
  export declare function joinById(roomId: string, clientOptions?: ClientOptions, authOptions?: AuthOptions): Promise<SeatReservation>;
76
76
  /**
77
77
  * Perform a query for all cached rooms
78
78
  */
79
- export declare function query(conditions?: Partial<IRoomListingData>): Promise<RoomListingData<any>[]>;
79
+ export declare function query(conditions?: Partial<IRoomCache>, sortOptions?: SortOptions): Promise<IRoomCache[]>;
80
80
  /**
81
81
  * Find for a public and unlocked room available.
82
82
  *
83
83
  * @param roomName - The Id of the specific room.
84
- * @param clientOptions - Options for the client seat reservation (for `onJoin`/`onAuth`).
84
+ * @param filterOptions - Filter options.
85
+ * @param sortOptions - Sorting options.
85
86
  *
86
- * @returns Promise<RoomListingData> - A promise contaning an object which includes room metadata and configurations.
87
+ * @returns Promise<IRoomCache> - A promise contaning an object which includes room metadata and configurations.
87
88
  */
88
- export declare function findOneRoomAvailable(roomName: string, clientOptions: ClientOptions): Promise<RoomListingData>;
89
+ export declare function findOneRoomAvailable(roomName: string, filterOptions: ClientOptions, additionalSortOptions?: SortOptions): Promise<import("./matchmaker/driver/api").RoomCache<any>>;
89
90
  /**
90
91
  * Call a method or return a property on a remote room.
91
92
  *
@@ -107,11 +108,19 @@ export declare function getRoomClass(roomName: string): Type<Room>;
107
108
  * @param roomName - The identifier you defined on `gameServer.define()`
108
109
  * @param clientOptions - Options for `onCreate`
109
110
  *
110
- * @returns Promise<RoomListingData> - A promise contaning an object which includes room metadata and configurations.
111
+ * @returns Promise<IRoomCache> - A promise contaning an object which includes room metadata and configurations.
111
112
  */
112
- export declare function createRoom(roomName: string, clientOptions: ClientOptions): Promise<RoomListingData>;
113
- export declare function handleCreateRoom(roomName: string, clientOptions: ClientOptions, restoringRoomId?: string): Promise<RoomListingData>;
114
- export declare function getRoomById(roomId: string): Room<any, any, any, any>;
113
+ export declare function createRoom(roomName: string, clientOptions: ClientOptions): Promise<IRoomCache>;
114
+ export declare function handleCreateRoom(roomName: string, clientOptions: ClientOptions, restoringRoomId?: string): Promise<IRoomCache>;
115
+ /**
116
+ * Get room data by roomId.
117
+ * This method does not return the actual room instance, use `getLocalRoomById` for that.
118
+ */
119
+ export declare function getRoomById(roomId: string): Promise<import("./matchmaker/driver/api").RoomCache<any>>;
120
+ /**
121
+ * Get local room instance by roomId. (Can return "undefined" if the room is not available on this process)
122
+ */
123
+ export declare function getLocalRoomById(roomId: string): Room<any, any, any, any>;
115
124
  /**
116
125
  * Disconnects every client on every room in the current process.
117
126
  */
@@ -120,8 +129,7 @@ export declare function gracefullyShutdown(): Promise<any>;
120
129
  /**
121
130
  * Reserve a seat for a client in a room
122
131
  */
123
- export declare function reserveSeatFor(room: RoomListingData, options: ClientOptions, authData?: any): Promise<SeatReservation>;
124
- export declare function cleanupStaleRooms(roomName: string): Promise<void>;
132
+ export declare function reserveSeatFor(room: IRoomCache, options: ClientOptions, authData?: any): Promise<SeatReservation>;
125
133
  /**
126
134
  * Perform health check on all processes
127
135
  */
@@ -25,7 +25,6 @@ var MatchMaker_exports = {};
25
25
  __export(MatchMaker_exports, {
26
26
  MatchMakerState: () => MatchMakerState,
27
27
  accept: () => accept,
28
- cleanupStaleRooms: () => cleanupStaleRooms,
29
28
  controller: () => import_controller.default,
30
29
  create: () => create,
31
30
  createRoom: () => createRoom,
@@ -34,6 +33,7 @@ __export(MatchMaker_exports, {
34
33
  driver: () => driver,
35
34
  findOneRoomAvailable: () => findOneRoomAvailable,
36
35
  getHandler: () => getHandler,
36
+ getLocalRoomById: () => getLocalRoomById,
37
37
  getRoomById: () => getRoomById,
38
38
  getRoomClass: () => getRoomClass,
39
39
  gracefullyShutdown: () => gracefullyShutdown,
@@ -41,7 +41,6 @@ __export(MatchMaker_exports, {
41
41
  hasHandler: () => hasHandler,
42
42
  healthCheckAllProcesses: () => healthCheckAllProcesses,
43
43
  healthCheckProcessId: () => healthCheckProcessId,
44
- isGracefullyShuttingDown: () => isGracefullyShuttingDown,
45
44
  join: () => join,
46
45
  joinById: () => joinById,
47
46
  joinOrCreate: () => joinOrCreate,
@@ -55,6 +54,7 @@ __export(MatchMaker_exports, {
55
54
  removeRoomType: () => removeRoomType,
56
55
  reserveSeatFor: () => reserveSeatFor,
57
56
  selectProcessIdToCreateRoom: () => selectProcessIdToCreateRoom,
57
+ setHealthChecksEnabled: () => setHealthChecksEnabled,
58
58
  setup: () => setup,
59
59
  state: () => state,
60
60
  stats: () => stats
@@ -70,11 +70,12 @@ var import_LocalPresence = require("./presence/LocalPresence");
70
70
  var import_Debug = require("./Debug");
71
71
  var import_SeatReservationError = require("./errors/SeatReservationError");
72
72
  var import_ServerError = require("./errors/ServerError");
73
- var import_driver = require("./matchmaker/driver");
73
+ var import_LocalDriver = require("./matchmaker/driver/local/LocalDriver");
74
74
  var import_controller = __toESM(require("./matchmaker/controller"));
75
75
  var stats = __toESM(require("./Stats"));
76
76
  var import_Logger = require("./Logger");
77
77
  var import_discovery = require("./discovery");
78
+ var import_api = require("./matchmaker/driver/api");
78
79
  const handlers = {};
79
80
  const rooms = {};
80
81
  let publicAddress;
@@ -82,7 +83,10 @@ let processId;
82
83
  let presence;
83
84
  let driver;
84
85
  let selectProcessIdToCreateRoom;
85
- let isGracefullyShuttingDown;
86
+ let enableHealthChecks = true;
87
+ function setHealthChecksEnabled(value) {
88
+ enableHealthChecks = value;
89
+ }
86
90
  let onReady = new import_Utils.Deferred();
87
91
  var MatchMakerState = /* @__PURE__ */ ((MatchMakerState2) => {
88
92
  MatchMakerState2[MatchMakerState2["INITIALIZING"] = 0] = "INITIALIZING";
@@ -95,10 +99,9 @@ async function setup(_presence, _driver, _publicAddress, _selectProcessIdToCreat
95
99
  if (onReady === void 0) {
96
100
  onReady = new import_Utils.Deferred();
97
101
  }
98
- isGracefullyShuttingDown = false;
99
102
  state = 0 /* INITIALIZING */;
100
103
  presence = _presence || new import_LocalPresence.LocalPresence();
101
- driver = _driver || new import_driver.LocalDriver();
104
+ driver = _driver || new import_LocalDriver.LocalDriver();
102
105
  publicAddress = _publicAddress;
103
106
  stats.reset(false);
104
107
  if (import_DevMode.isDevMode) {
@@ -121,7 +124,9 @@ async function accept() {
121
124
  return handleCreateRoom.apply(void 0, args);
122
125
  }
123
126
  });
124
- await healthCheckAllProcesses();
127
+ if (enableHealthChecks) {
128
+ await healthCheckAllProcesses();
129
+ }
125
130
  state = 1 /* READY */;
126
131
  await stats.persist();
127
132
  if (import_DevMode.isDevMode) {
@@ -133,7 +138,23 @@ async function joinOrCreate(roomName, clientOptions = {}, authOptions) {
133
138
  const authData = await callOnAuth(roomName, authOptions);
134
139
  let room = await findOneRoomAvailable(roomName, clientOptions);
135
140
  if (!room) {
136
- room = await createRoom(roomName, clientOptions);
141
+ const handler = getHandler(roomName);
142
+ const filterOptions = handler.getFilterOptions(clientOptions);
143
+ const concurrencyKey = (0, import_api.getLockId)(filterOptions);
144
+ await concurrentJoinOrCreateRoomLock(handler, concurrencyKey, async (roomId) => {
145
+ if (roomId) {
146
+ room = await driver.findOne({ roomId });
147
+ }
148
+ if (!room) {
149
+ room = await findOneRoomAvailable(roomName, clientOptions);
150
+ }
151
+ if (!room) {
152
+ room = await createRoom(roomName, clientOptions);
153
+ presence.lpush(`l:${handler.name}:${concurrencyKey}`, room.roomId);
154
+ presence.expire(`l:${handler.name}:${concurrencyKey}`, import_Utils.MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME * 2);
155
+ }
156
+ return room;
157
+ });
137
158
  }
138
159
  return await reserveSeatFor(room, clientOptions, authData);
139
160
  }, 5, [import_SeatReservationError.SeatReservationError]);
@@ -187,23 +208,21 @@ async function joinById(roomId, clientOptions = {}, authOptions) {
187
208
  const authData = await callOnAuth(room.name, authOptions);
188
209
  return reserveSeatFor(room, clientOptions, authData);
189
210
  }
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
- });
211
+ async function query(conditions = {}, sortOptions) {
212
+ return await driver.query(conditions, sortOptions);
213
+ }
214
+ async function findOneRoomAvailable(roomName, filterOptions, additionalSortOptions) {
215
+ const handler = getHandler(roomName);
216
+ const sortOptions = Object.assign({}, handler.sortOptions ?? {});
217
+ if (additionalSortOptions) {
218
+ Object.assign(sortOptions, additionalSortOptions);
219
+ }
220
+ return await driver.findOne({
221
+ locked: false,
222
+ name: roomName,
223
+ private: false,
224
+ ...handler.getFilterOptions(filterOptions)
225
+ }, sortOptions);
207
226
  }
208
227
  async function remoteRoomCall(roomId, method, args, rejectionTimeout = import_Utils.REMOTE_ROOM_SHORT_TIMEOUT) {
209
228
  const room = rooms[roomId];
@@ -225,23 +244,17 @@ async function remoteRoomCall(roomId, method, args, rejectionTimeout = import_Ut
225
244
  }
226
245
  }
227
246
  function defineRoomType(roomName, klass, defaultOptions) {
228
- const registeredHandler = new import_RegisteredHandler.RegisteredHandler(klass, defaultOptions);
247
+ const registeredHandler = new import_RegisteredHandler.RegisteredHandler(roomName, klass, defaultOptions);
229
248
  handlers[roomName] = registeredHandler;
230
249
  if (klass.prototype["onAuth"] !== import_Room.Room.prototype["onAuth"]) {
231
250
  if (klass["onAuth"] !== import_Room.Room["onAuth"]) {
232
251
  import_Logger.logger.info(`\u274C "${roomName}"'s onAuth() defined at the instance level will be ignored.`);
233
252
  }
234
253
  }
235
- if (!import_DevMode.isDevMode) {
236
- cleanupStaleRooms(roomName);
237
- }
238
254
  return registeredHandler;
239
255
  }
240
256
  function removeRoomType(roomName) {
241
257
  delete handlers[roomName];
242
- if (!import_DevMode.isDevMode) {
243
- cleanupStaleRooms(roomName);
244
- }
245
258
  }
246
259
  function hasHandler(roomName) {
247
260
  import_Logger.logger.warn("hasHandler() is deprecated. Use getHandler() instead.");
@@ -276,7 +289,9 @@ async function createRoom(roomName, clientOptions) {
276
289
  } catch (e) {
277
290
  if (e.message === "ipc_timeout") {
278
291
  (0, import_Debug.debugAndPrintError)(`${e.message}: create room request timed out for ${roomName} on processId ${selectedProcessId}.`);
279
- await stats.excludeProcess(selectedProcessId);
292
+ if (enableHealthChecks) {
293
+ await stats.excludeProcess(selectedProcessId);
294
+ }
280
295
  room = await handleCreateRoom(roomName, clientOptions);
281
296
  } else {
282
297
  throw e;
@@ -300,9 +315,7 @@ async function handleCreateRoom(roomName, clientOptions, restoringRoomId) {
300
315
  } else {
301
316
  room.roomId = (0, import_Utils.generateId)();
302
317
  }
303
- if (room.state) {
304
- room.setState(room.state);
305
- }
318
+ room["__init"]();
306
319
  room.roomName = roomName;
307
320
  room.presence = presence;
308
321
  const additionalListingData = handler.getFilterOptions(clientOptions);
@@ -349,6 +362,9 @@ async function handleCreateRoom(roomName, clientOptions, restoringRoomId) {
349
362
  return room.listing;
350
363
  }
351
364
  function getRoomById(roomId) {
365
+ return driver.findOne({ roomId });
366
+ }
367
+ function getLocalRoomById(roomId) {
352
368
  return rooms[roomId];
353
369
  }
354
370
  function disconnectAll(closeCode) {
@@ -364,10 +380,9 @@ function disconnectAll(closeCode) {
364
380
  return promises;
365
381
  }
366
382
  async function gracefullyShutdown() {
367
- if (isGracefullyShuttingDown) {
383
+ if (state === 2 /* SHUTTING_DOWN */) {
368
384
  return Promise.reject("already_shutting_down");
369
385
  }
370
- isGracefullyShuttingDown = true;
371
386
  state = 2 /* SHUTTING_DOWN */;
372
387
  onReady = void 0;
373
388
  (0, import_Debug.debugMatchMaking)(`${processId} is shutting down!`);
@@ -399,7 +414,7 @@ async function reserveSeatFor(room, options, authData) {
399
414
  );
400
415
  } catch (e) {
401
416
  (0, import_Debug.debugMatchMaking)(e);
402
- if (e.message === "ipc_timeout" && !await healthCheckProcessId(room.processId)) {
417
+ if (e.message === "ipc_timeout" && !(enableHealthChecks && await healthCheckProcessId(room.processId))) {
403
418
  throw new import_SeatReservationError.SeatReservationError(`process ${room.processId} is not available.`);
404
419
  } else {
405
420
  successfulSeatReservation = false;
@@ -418,9 +433,6 @@ function callOnAuth(roomName, authOptions) {
418
433
  const roomClass = getRoomClass(roomName);
419
434
  return roomClass && roomClass["onAuth"] && roomClass["onAuth"] !== import_Room.Room["onAuth"] ? roomClass["onAuth"](authOptions.token, authOptions.request) : void 0;
420
435
  }
421
- async function cleanupStaleRooms(roomName) {
422
- await presence.del(getHandlerConcurrencyKey(roomName));
423
- }
424
436
  async function healthCheckAllProcesses() {
425
437
  const allStats = await stats.fetchAll();
426
438
  if (allStats.length > 0) {
@@ -461,13 +473,7 @@ function healthCheckProcessId(processId2) {
461
473
  return _healthCheckByProcessId[processId2];
462
474
  }
463
475
  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
- }
476
+ await driver.cleanup(processId2);
471
477
  }
472
478
  async function createRoomReferences(room, init = false) {
473
479
  rooms[room.roomId] = room;
@@ -483,29 +489,39 @@ async function createRoomReferences(room, init = false) {
483
489
  }
484
490
  return true;
485
491
  }
486
- async function awaitRoomAvailable(roomToJoin, callback) {
492
+ async function concurrentJoinOrCreateRoomLock(handler, concurrencyKey, callback) {
487
493
  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 () => {
494
+ const hkey = getConcurrencyHashKey(handler.name);
495
+ const concurrency = await presence.hincrbyex(
496
+ hkey,
497
+ concurrencyKey,
498
+ 1,
499
+ import_Utils.MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME * 2
500
+ ) - 1;
501
+ const fulfill = async (roomId) => {
500
502
  try {
501
- const result = await callback();
502
- resolve(result);
503
+ resolve(await callback(roomId));
503
504
  } catch (e) {
504
505
  reject(e);
505
506
  } finally {
506
- await presence.decr(concurrencyKey);
507
+ await presence.hincrby(hkey, concurrencyKey, -1);
507
508
  }
508
- }, concurrencyTimeout);
509
+ };
510
+ if (concurrency > 0) {
511
+ (0, import_Debug.debugMatchMaking)(
512
+ "receiving %d concurrent joinOrCreate for '%s' (%s)",
513
+ concurrency,
514
+ handler.name,
515
+ concurrencyKey
516
+ );
517
+ const result = await presence.brpop(
518
+ `l:${handler.name}:${concurrencyKey}`,
519
+ import_Utils.MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME + Math.min(concurrency, 3) * 0.2
520
+ );
521
+ return await fulfill(result && result[1]);
522
+ } else {
523
+ return await fulfill();
524
+ }
509
525
  });
510
526
  }
511
527
  function onClientJoinRoom(room, client) {
@@ -530,8 +546,9 @@ function onVisibilityChange(room, isInvisible) {
530
546
  handlers[room.roomName].emit("visibility-change", room, isInvisible);
531
547
  }
532
548
  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) {
549
+ (0, import_Debug.debugMatchMaking)("disposing '%s' (%s) on processId '%s' (graceful shutdown: %s)", roomName, room.roomId, processId, state === 2 /* SHUTTING_DOWN */);
550
+ room.listing.remove();
551
+ if (state !== 2 /* SHUTTING_DOWN */) {
535
552
  stats.local.roomCount--;
536
553
  stats.persist();
537
554
  if (import_DevMode.isDevMode) {
@@ -539,15 +556,14 @@ async function disposeRoom(roomName, room) {
539
556
  }
540
557
  }
541
558
  handlers[roomName].emit("dispose", room);
542
- presence.del(getHandlerConcurrencyKey(roomName));
543
559
  presence.unsubscribe(getRoomChannel(room.roomId));
544
560
  delete rooms[room.roomId];
545
561
  }
546
562
  function getRoomChannel(roomId) {
547
563
  return `$${roomId}`;
548
564
  }
549
- function getHandlerConcurrencyKey(name) {
550
- return `c:${name}`;
565
+ function getConcurrencyHashKey(roomName) {
566
+ return `ch:${roomName}`;
551
567
  }
552
568
  function getProcessChannel(id = processId) {
553
569
  return `p:${id}`;
@@ -556,7 +572,6 @@ function getProcessChannel(id = processId) {
556
572
  0 && (module.exports = {
557
573
  MatchMakerState,
558
574
  accept,
559
- cleanupStaleRooms,
560
575
  controller,
561
576
  create,
562
577
  createRoom,
@@ -565,6 +580,7 @@ function getProcessChannel(id = processId) {
565
580
  driver,
566
581
  findOneRoomAvailable,
567
582
  getHandler,
583
+ getLocalRoomById,
568
584
  getRoomById,
569
585
  getRoomClass,
570
586
  gracefullyShutdown,
@@ -572,7 +588,6 @@ function getProcessChannel(id = processId) {
572
588
  hasHandler,
573
589
  healthCheckAllProcesses,
574
590
  healthCheckProcessId,
575
- isGracefullyShuttingDown,
576
591
  join,
577
592
  joinById,
578
593
  joinOrCreate,
@@ -586,6 +601,7 @@ function getProcessChannel(id = processId) {
586
601
  removeRoomType,
587
602
  reserveSeatFor,
588
603
  selectProcessIdToCreateRoom,
604
+ setHealthChecksEnabled,
589
605
  setup,
590
606
  state,
591
607
  stats