@colyseus/core 0.15.33 → 0.15.35

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.
@@ -25,6 +25,15 @@ export declare let driver: MatchMakerDriver;
25
25
  export declare let selectProcessIdToCreateRoom: SelectProcessIdCallback;
26
26
  export declare let isGracefullyShuttingDown: boolean;
27
27
  export declare let onReady: Deferred;
28
+ export declare enum MatchMakerState {
29
+ INITIALIZING = 0,
30
+ READY = 1,
31
+ SHUTTING_DOWN = 2
32
+ }
33
+ /**
34
+ * Internal MatchMaker state
35
+ */
36
+ export declare let state: MatchMakerState;
28
37
  /**
29
38
  * @private
30
39
  */
@@ -113,3 +122,8 @@ export declare function gracefullyShutdown(): Promise<any>;
113
122
  */
114
123
  export declare function reserveSeatFor(room: RoomListingData, options: ClientOptions, authData?: any): Promise<SeatReservation>;
115
124
  export declare function cleanupStaleRooms(roomName: string): Promise<void>;
125
+ /**
126
+ * Perform health check on all processes
127
+ */
128
+ export declare function healthCheckAllProcesses(): Promise<void>;
129
+ export declare function healthCheckProcessId(processId: string): Promise<any>;
@@ -23,6 +23,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
23
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
24
24
  var MatchMaker_exports = {};
25
25
  __export(MatchMaker_exports, {
26
+ MatchMakerState: () => MatchMakerState,
26
27
  accept: () => accept,
27
28
  cleanupStaleRooms: () => cleanupStaleRooms,
28
29
  controller: () => import_controller.default,
@@ -38,6 +39,8 @@ __export(MatchMaker_exports, {
38
39
  gracefullyShutdown: () => gracefullyShutdown,
39
40
  handleCreateRoom: () => handleCreateRoom,
40
41
  hasHandler: () => hasHandler,
42
+ healthCheckAllProcesses: () => healthCheckAllProcesses,
43
+ healthCheckProcessId: () => healthCheckProcessId,
41
44
  isGracefullyShuttingDown: () => isGracefullyShuttingDown,
42
45
  join: () => join,
43
46
  joinById: () => joinById,
@@ -53,6 +56,7 @@ __export(MatchMaker_exports, {
53
56
  reserveSeatFor: () => reserveSeatFor,
54
57
  selectProcessIdToCreateRoom: () => selectProcessIdToCreateRoom,
55
58
  setup: () => setup,
59
+ state: () => state,
56
60
  stats: () => stats
57
61
  });
58
62
  module.exports = __toCommonJS(MatchMaker_exports);
@@ -80,10 +84,19 @@ let driver;
80
84
  let selectProcessIdToCreateRoom;
81
85
  let isGracefullyShuttingDown;
82
86
  let onReady = new import_Utils.Deferred();
87
+ var MatchMakerState = /* @__PURE__ */ ((MatchMakerState2) => {
88
+ MatchMakerState2[MatchMakerState2["INITIALIZING"] = 0] = "INITIALIZING";
89
+ MatchMakerState2[MatchMakerState2["READY"] = 1] = "READY";
90
+ MatchMakerState2[MatchMakerState2["SHUTTING_DOWN"] = 2] = "SHUTTING_DOWN";
91
+ return MatchMakerState2;
92
+ })(MatchMakerState || {});
93
+ let state;
83
94
  async function setup(_presence, _driver, _publicAddress, _selectProcessIdToCreateRoom) {
84
95
  if (onReady === void 0) {
85
96
  onReady = new import_Utils.Deferred();
86
97
  }
98
+ isGracefullyShuttingDown = false;
99
+ state = 0 /* INITIALIZING */;
87
100
  presence = _presence || new import_LocalPresence.LocalPresence();
88
101
  driver = _driver || new import_driver.LocalDriver();
89
102
  publicAddress = _publicAddress;
@@ -94,7 +107,6 @@ async function setup(_presence, _driver, _publicAddress, _selectProcessIdToCreat
94
107
  if (!processId) {
95
108
  processId = (0, import_Utils.generateId)();
96
109
  }
97
- isGracefullyShuttingDown = false;
98
110
  selectProcessIdToCreateRoom = _selectProcessIdToCreateRoom || async function() {
99
111
  return (await stats.fetchAll()).sort((p1, p2) => p1.roomCount > p2.roomCount ? 1 : -1)[0]?.processId || processId;
100
112
  };
@@ -109,27 +121,8 @@ async function accept() {
109
121
  return handleCreateRoom.apply(void 0, args);
110
122
  }
111
123
  });
112
- const previousStats = await stats.fetchAll();
113
- if (previousStats.length > 0) {
114
- import_Logger.logger.debug(`${previousStats.length} previous processId(s) found, health-checking...`);
115
- await Promise.all(previousStats.map(async (stat) => {
116
- try {
117
- await (0, import_IPC.requestFromIPC)(
118
- presence,
119
- getProcessChannel(stat.processId),
120
- "healthcheck",
121
- [],
122
- import_Utils.REMOTE_ROOM_SHORT_TIMEOUT
123
- );
124
- } catch (e) {
125
- import_Logger.logger.debug(`process ${stat.processId} failed to respond. excluding from stats`);
126
- const isProcessExcluded = await stats.excludeProcess(stat.processId);
127
- if (isProcessExcluded && !import_DevMode.isDevMode) {
128
- await removeRoomsByProcessId(stat.processId);
129
- }
130
- }
131
- }));
132
- }
124
+ await healthCheckAllProcesses();
125
+ state = 1 /* READY */;
133
126
  await stats.persist();
134
127
  if (import_DevMode.isDevMode) {
135
128
  await (0, import_DevMode.reloadFromCache)();
@@ -218,6 +211,9 @@ async function remoteRoomCall(roomId, method, args, rejectionTimeout = import_Ut
218
211
  try {
219
212
  return await (0, import_IPC.requestFromIPC)(presence, getRoomChannel(roomId), method, args, rejectionTimeout);
220
213
  } catch (e) {
214
+ if (method === "_reserveSeat" && e.message === "ipc_timeout") {
215
+ throw e;
216
+ }
221
217
  const request = `${method}${args && " with args " + JSON.stringify(args) || ""}`;
222
218
  throw new import_ServerError.ServerError(
223
219
  import_Protocol.ErrorCode.MATCHMAKE_UNHANDLED,
@@ -262,7 +258,7 @@ function getRoomClass(roomName) {
262
258
  return handlers[roomName]?.klass;
263
259
  }
264
260
  async function createRoom(roomName, clientOptions) {
265
- const selectedProcessId = selectProcessIdToCreateRoom !== void 0 ? await selectProcessIdToCreateRoom(roomName, clientOptions) : processId;
261
+ const selectedProcessId = state === 1 /* READY */ ? await selectProcessIdToCreateRoom(roomName, clientOptions) : processId;
266
262
  let room;
267
263
  if (selectedProcessId === void 0) {
268
264
  throw new import_ServerError.ServerError(import_Protocol.ErrorCode.MATCHMAKE_UNHANDLED, `no processId available to create room ${roomName}`);
@@ -369,6 +365,7 @@ async function gracefullyShutdown() {
369
365
  return Promise.reject("already_shutting_down");
370
366
  }
371
367
  isGracefullyShuttingDown = true;
368
+ state = 2 /* SHUTTING_DOWN */;
372
369
  onReady = void 0;
373
370
  (0, import_Debug.debugMatchMaking)(`${processId} is shutting down!`);
374
371
  if (import_DevMode.isDevMode) {
@@ -391,10 +388,19 @@ async function reserveSeatFor(room, options, authData) {
391
388
  );
392
389
  let successfulSeatReservation;
393
390
  try {
394
- successfulSeatReservation = await remoteRoomCall(room.roomId, "_reserveSeat", [sessionId, options, authData]);
391
+ successfulSeatReservation = await remoteRoomCall(
392
+ room.roomId,
393
+ "_reserveSeat",
394
+ [sessionId, options, authData],
395
+ import_Utils.REMOTE_ROOM_SHORT_TIMEOUT
396
+ );
395
397
  } catch (e) {
396
398
  (0, import_Debug.debugMatchMaking)(e);
397
- throw e;
399
+ if (e.message === "ipc_timeout" && !await healthCheckProcessId(room.processId)) {
400
+ throw new import_SeatReservationError.SeatReservationError(`process ${room.processId} is not available.`);
401
+ } else {
402
+ successfulSeatReservation = false;
403
+ }
398
404
  }
399
405
  if (!successfulSeatReservation) {
400
406
  throw new import_SeatReservationError.SeatReservationError(`${room.roomId} is already full.`);
@@ -412,12 +418,49 @@ function callOnAuth(roomName, authOptions) {
412
418
  async function cleanupStaleRooms(roomName) {
413
419
  await presence.del(getHandlerConcurrencyKey(roomName));
414
420
  }
421
+ async function healthCheckAllProcesses() {
422
+ const allStats = await stats.fetchAll();
423
+ if (allStats.length > 0) {
424
+ await Promise.all(allStats.map((stat) => healthCheckProcessId(stat.processId)));
425
+ }
426
+ }
427
+ const _healthCheckByProcessId = {};
428
+ function healthCheckProcessId(processId2) {
429
+ if (_healthCheckByProcessId[processId2] !== void 0) {
430
+ return _healthCheckByProcessId[processId2];
431
+ }
432
+ _healthCheckByProcessId[processId2] = new Promise(async (resolve, reject) => {
433
+ import_Logger.logger.debug(`> Performing health-check against processId: '${processId2}'...`);
434
+ try {
435
+ const requestTime = Date.now();
436
+ await (0, import_IPC.requestFromIPC)(
437
+ presence,
438
+ getProcessChannel(processId2),
439
+ "healthcheck",
440
+ [],
441
+ import_Utils.REMOTE_ROOM_SHORT_TIMEOUT
442
+ );
443
+ import_Logger.logger.debug(`\u2705 Process '${processId2}' successfully responded (${Date.now() - requestTime}ms)`);
444
+ resolve(true);
445
+ } catch (e) {
446
+ import_Logger.logger.debug(`\u274C Process '${processId2}' failed to respond. Cleaning it up.`);
447
+ const isProcessExcluded = await stats.excludeProcess(processId2);
448
+ if (isProcessExcluded && !import_DevMode.isDevMode) {
449
+ await removeRoomsByProcessId(processId2);
450
+ }
451
+ resolve(false);
452
+ } finally {
453
+ delete _healthCheckByProcessId[processId2];
454
+ }
455
+ });
456
+ return _healthCheckByProcessId[processId2];
457
+ }
415
458
  async function removeRoomsByProcessId(processId2) {
416
459
  if (typeof driver.cleanup === "function") {
417
460
  await driver.cleanup(processId2);
418
461
  } else {
419
462
  const cachedRooms = await driver.find({ processId: processId2 }, { _id: 1 });
420
- import_Logger.logger.debug("removing stale rooms by processId:", processId2, `(${cachedRooms.length} rooms found)`);
463
+ import_Logger.logger.debug("> Removing stale rooms by processId:", processId2, `(${cachedRooms.length} rooms found)`);
421
464
  cachedRooms.forEach((room) => room.remove());
422
465
  }
423
466
  }
@@ -506,6 +549,7 @@ function getProcessChannel(id = processId) {
506
549
  }
507
550
  // Annotate the CommonJS export names for ESM import in node:
508
551
  0 && (module.exports = {
552
+ MatchMakerState,
509
553
  accept,
510
554
  cleanupStaleRooms,
511
555
  controller,
@@ -521,6 +565,8 @@ function getProcessChannel(id = processId) {
521
565
  gracefullyShutdown,
522
566
  handleCreateRoom,
523
567
  hasHandler,
568
+ healthCheckAllProcesses,
569
+ healthCheckProcessId,
524
570
  isGracefullyShuttingDown,
525
571
  join,
526
572
  joinById,
@@ -536,5 +582,6 @@ function getProcessChannel(id = processId) {
536
582
  reserveSeatFor,
537
583
  selectProcessIdToCreateRoom,
538
584
  setup,
585
+ state,
539
586
  stats
540
587
  });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/MatchMaker.ts"],
4
- "sourcesContent": ["import { ErrorCode, Protocol } from './Protocol';\n\nimport { requestFromIPC, subscribeIPC } from './IPC';\n\nimport { Deferred, generateId, merge, REMOTE_ROOM_SHORT_TIMEOUT, retry } from './utils/Utils';\nimport { isDevMode, cacheRoomHistory, getPreviousProcessId, getRoomRestoreListKey, reloadFromCache } from './utils/DevMode';\n\nimport { RegisteredHandler } from './matchmaker/RegisteredHandler';\nimport { Room, RoomInternalState } from './Room';\n\nimport { LocalPresence } from './presence/LocalPresence';\nimport { Presence } from './presence/Presence';\n\nimport { debugAndPrintError, debugMatchMaking } from './Debug';\nimport { SeatReservationError } from './errors/SeatReservationError';\nimport { ServerError } from './errors/ServerError';\n\nimport { IRoomListingData, RoomListingData, LocalDriver, MatchMakerDriver } from './matchmaker/driver';\nimport controller from './matchmaker/controller';\nimport * as stats from \"./Stats\";\n\nimport { logger } from './Logger';\nimport { Client } from './Transport';\nimport { Type } from './utils/types';\nimport { getHostname } from \"./discovery\";\n\nexport { controller, stats, type MatchMakerDriver };\n\nexport type ClientOptions = any;\nexport type AuthOptions = { token?: string, request?: any };\nexport type SelectProcessIdCallback = (roomName: string, clientOptions: ClientOptions) => Promise<string>;\n\nexport interface SeatReservation {\n sessionId: string;\n room: RoomListingData;\n devMode?: boolean;\n}\n\nconst handlers: {[id: string]: RegisteredHandler} = {};\nconst rooms: {[roomId: string]: Room} = {};\n\nexport let publicAddress: string;\nexport let processId: string;\nexport let presence: Presence;\nexport let driver: MatchMakerDriver;\nexport let selectProcessIdToCreateRoom: SelectProcessIdCallback;\n\nexport let isGracefullyShuttingDown: boolean;\nexport let onReady: Deferred = new Deferred(); // onReady needs to be immediately available to @colyseus/auth integration.\n\n/**\n * @private\n */\nexport async function setup(\n _presence?: Presence,\n _driver?: MatchMakerDriver,\n _publicAddress?: string,\n _selectProcessIdToCreateRoom?: SelectProcessIdCallback,\n) {\n if (onReady === undefined) {\n //\n // for testing purposes only: onReady is turned into undefined on shutdown\n // (needs refactoring.)\n //\n onReady = new Deferred();\n }\n\n presence = _presence || new LocalPresence();\n\n driver = _driver || new LocalDriver();\n publicAddress = _publicAddress;\n\n stats.reset(false);\n\n // devMode: try to retrieve previous processId\n if (isDevMode) { processId = await getPreviousProcessId(await getHostname()); }\n\n // ensure processId is set\n if (!processId) { processId = generateId(); }\n\n isGracefullyShuttingDown = false;\n\n /**\n * Define default `assignRoomToProcessId` method.\n * By default, return the process with least amount of rooms created\n */\n selectProcessIdToCreateRoom = _selectProcessIdToCreateRoom || async function () {\n return (await stats.fetchAll())\n .sort((p1, p2) => p1.roomCount > p2.roomCount ? 1 : -1)[0]?.processId || processId;\n };\n\n onReady.resolve();\n}\n\n/**\n * - Accept receiving remote room creation requests\n * - Check for leftover/invalid processId's on startup\n * @private\n */\nexport async function accept() {\n await onReady; // make sure \"processId\" is available\n\n /**\n * Process-level subscription\n * - handle remote process healthcheck\n * - handle remote room creation\n */\n await subscribeIPC(presence, processId, getProcessChannel(), (method, args) => {\n if (method === 'healthcheck') {\n // health check for this processId\n return true;\n\n } else {\n // handle room creation\n return handleCreateRoom.apply(undefined, args);\n }\n });\n\n /**\n * Check for leftover/invalid processId's on startup\n */\n const previousStats = await stats.fetchAll();\n if (previousStats.length > 0) {\n logger.debug(`${previousStats.length} previous processId(s) found, health-checking...`);\n await Promise.all(previousStats.map(async (stat) => {\n try {\n await requestFromIPC<RoomListingData>(\n presence,\n getProcessChannel(stat.processId),\n 'healthcheck',\n [],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n // process succeeded to respond - nothing to do\n } catch (e) {\n // process failed to respond - remove it from stats\n logger.debug(`process ${stat.processId} failed to respond. excluding from stats`);\n const isProcessExcluded = await stats.excludeProcess(stat.processId);\n\n // clean-up possibly stale room ids\n if (isProcessExcluded && !isDevMode) {\n await removeRoomsByProcessId(stat.processId);\n }\n }\n }));\n }\n\n await stats.persist();\n\n if (isDevMode) {\n await reloadFromCache();\n }\n}\n\n/**\n * Join or create into a room and return seat reservation\n */\nexport async function joinOrCreate(roomName: string, clientOptions: ClientOptions = {}, authOptions?: AuthOptions) {\n return await retry<Promise<SeatReservation>>(async () => {\n const authData = await callOnAuth(roomName, authOptions);\n let room = await findOneRoomAvailable(roomName, clientOptions);\n\n //\n // TODO [?]\n // should we expose the \"creator\" auth data of the room during `onCreate()`?\n // it would be useful, though it could be accessed via `onJoin()` for now.\n //\n\n if (!room) {\n room = await createRoom(roomName, clientOptions);\n }\n\n return await reserveSeatFor(room, clientOptions, authData);\n }, 5, [SeatReservationError]);\n}\n\n/**\n * Create a room and return seat reservation\n */\nexport async function create(roomName: string, clientOptions: ClientOptions = {}, authOptions?: AuthOptions) {\n const authData = await callOnAuth(roomName, authOptions);\n const room = await createRoom(roomName, clientOptions);\n return reserveSeatFor(room, clientOptions, authData);\n}\n\n/**\n * Join a room and return seat reservation\n */\nexport async function join(roomName: string, clientOptions: ClientOptions = {}, authOptions?: AuthOptions) {\n return await retry<Promise<SeatReservation>>(async () => {\n const authData = await callOnAuth(roomName, authOptions);\n const room = await findOneRoomAvailable(roomName, clientOptions);\n\n if (!room) {\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_CRITERIA, `no rooms found with provided criteria`);\n }\n\n return reserveSeatFor(room, clientOptions, authData);\n });\n}\n\n/**\n * Join a room by id and return seat reservation\n */\nexport async function reconnect(roomId: string, clientOptions: ClientOptions = {}) {\n const room = await driver.findOne({ roomId });\n if (!room) {\n // TODO: support a \"logLevel\" out of the box?\n if (process.env.NODE_ENV !== 'production') {\n logger.info(`\u274C room \"${roomId}\" has been disposed. Did you missed .allowReconnection()?\\n\uD83D\uDC49 https://docs.colyseus.io/server/room/#allowreconnection-client-seconds`);\n }\n\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room \"${roomId}\" has been disposed.`);\n }\n\n // check for reconnection\n const reconnectionToken = clientOptions.reconnectionToken;\n if (!reconnectionToken) { throw new ServerError(ErrorCode.MATCHMAKE_UNHANDLED, `'reconnectionToken' must be provided for reconnection.`); }\n\n\n // respond to re-connection!\n const sessionId = await remoteRoomCall(room.roomId, 'checkReconnectionToken', [reconnectionToken]);\n if (sessionId) {\n return { room, sessionId };\n\n } else {\n // TODO: support a \"logLevel\" out of the box?\n if (process.env.NODE_ENV !== 'production') {\n logger.info(`\u274C reconnection token invalid or expired. Did you missed .allowReconnection()?\\n\uD83D\uDC49 https://docs.colyseus.io/server/room/#allowreconnection-client-seconds`);\n }\n throw new ServerError(ErrorCode.MATCHMAKE_EXPIRED, `reconnection token invalid or expired.`);\n }\n}\n\n/**\n * Join a room by id and return client seat reservation. An exception is thrown if a room is not found for roomId.\n *\n * @param roomId - The Id of the specific room instance.\n * @param clientOptions - Options for the client seat reservation (for `onJoin`/`onAuth`)\n * @param authOptions - Optional authentication token\n *\n * @returns Promise<SeatReservation> - A promise which contains `sessionId` and `RoomListingData`.\n */\nexport async function joinById(roomId: string, clientOptions: ClientOptions = {}, authOptions?: AuthOptions) {\n const room = await driver.findOne({ roomId });\n\n if (!room) {\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room \"${roomId}\" not found`);\n\n } else if (room.locked) {\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room \"${roomId}\" is locked`);\n }\n\n const authData = await callOnAuth(room.name, authOptions);\n\n return reserveSeatFor(room, clientOptions, authData);\n}\n\n/**\n * Perform a query for all cached rooms\n */\nexport async function query(conditions: Partial<IRoomListingData> = {}) {\n return await driver.find(conditions);\n}\n\n/**\n * Find for a public and unlocked room available.\n *\n * @param roomName - The Id of the specific room.\n * @param clientOptions - Options for the client seat reservation (for `onJoin`/`onAuth`).\n *\n * @returns Promise<RoomListingData> - A promise contaning an object which includes room metadata and configurations.\n */\nexport async function findOneRoomAvailable(roomName: string, clientOptions: ClientOptions): Promise<RoomListingData> {\n return await awaitRoomAvailable(roomName, async () => {\n const handler = getHandler(roomName);\n\n const roomQuery = driver.findOne({\n locked: false,\n name: roomName,\n private: false,\n ...handler.getFilterOptions(clientOptions),\n });\n\n if (handler.sortOptions) {\n roomQuery.sort(handler.sortOptions);\n }\n\n return await roomQuery;\n });\n}\n\n/**\n * Call a method or return a property on a remote room.\n *\n * @param roomId - The Id of the specific room instance.\n * @param method - Method or attribute to call or retrive.\n * @param args - Array of arguments for the method\n *\n * @returns Promise<any> - Returned value from the called or retrieved method/attribute.\n */\nexport async function remoteRoomCall<R= any>(\n roomId: string,\n method: string,\n args?: any[],\n rejectionTimeout = REMOTE_ROOM_SHORT_TIMEOUT,\n): Promise<R> {\n const room = rooms[roomId];\n\n if (!room) {\n try {\n return await requestFromIPC<R>(presence, getRoomChannel(roomId), method, args, rejectionTimeout);\n\n } catch (e) {\n const request = `${method}${args && ' with args ' + JSON.stringify(args) || ''}`;\n throw new ServerError(\n ErrorCode.MATCHMAKE_UNHANDLED,\n `remote room (${roomId}) timed out, requesting \"${request}\". (${rejectionTimeout}ms exceeded)`,\n );\n }\n\n } else {\n return (!args && typeof (room[method]) !== 'function')\n ? room[method]\n : (await room[method].apply(room, args && JSON.parse(JSON.stringify(args))));\n }\n}\n\nexport function defineRoomType<T extends Type<Room>>(\n roomName: string,\n klass: T,\n defaultOptions?: Parameters<NonNullable<InstanceType<T>['onCreate']>>[0],\n) {\n const registeredHandler = new RegisteredHandler(klass, defaultOptions);\n\n handlers[roomName] = registeredHandler;\n\n if (klass.prototype['onAuth'] !== Room.prototype['onAuth']) {\n // TODO: soft-deprecate instance level `onAuth` on 0.16\n // logger.warn(\"DEPRECATION WARNING: onAuth() at the instance level will be deprecated soon. Please use static onAuth() instead.\");\n\n if (klass['onAuth'] !== Room['onAuth']) {\n logger.info(`\u274C \"${roomName}\"'s onAuth() defined at the instance level will be ignored.`);\n }\n }\n\n if (!isDevMode) {\n cleanupStaleRooms(roomName);\n }\n\n return registeredHandler;\n}\n\nexport function removeRoomType(roomName: string) {\n delete handlers[roomName];\n\n if (!isDevMode) {\n cleanupStaleRooms(roomName);\n }\n}\n\n// TODO: legacy; remove me on 1.0\nexport function hasHandler(roomName: string) {\n logger.warn(\"hasHandler() is deprecated. Use getHandler() instead.\");\n return handlers[roomName] !== undefined;\n}\n\nexport function getHandler(roomName: string) {\n const handler = handlers[roomName];\n\n if (!handler) {\n throw new ServerError(ErrorCode.MATCHMAKE_NO_HANDLER, `provided room name \"${roomName}\" not defined`);\n }\n\n return handler;\n}\n\nexport function getRoomClass(roomName: string): Type<Room> {\n return handlers[roomName]?.klass;\n}\n\n\n/**\n * Creates a new room.\n *\n * @param roomName - The identifier you defined on `gameServer.define()`\n * @param clientOptions - Options for `onCreate`\n *\n * @returns Promise<RoomListingData> - A promise contaning an object which includes room metadata and configurations.\n */\nexport async function createRoom(roomName: string, clientOptions: ClientOptions): Promise<RoomListingData> {\n const selectedProcessId = (selectProcessIdToCreateRoom !== undefined)\n ? await selectProcessIdToCreateRoom(roomName, clientOptions)\n : processId;\n\n let room: RoomListingData;\n if (selectedProcessId === undefined) {\n throw new ServerError(ErrorCode.MATCHMAKE_UNHANDLED, `no processId available to create room ${roomName}`);\n\n } else if (selectedProcessId === processId) {\n // create the room on this process!\n room = await handleCreateRoom(roomName, clientOptions);\n\n } else {\n // ask other process to create the room!\n try {\n room = await requestFromIPC<RoomListingData>(\n presence,\n getProcessChannel(selectedProcessId),\n undefined,\n [roomName, clientOptions],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n\n } catch (e) {\n if (e.message === \"ipc_timeout\") {\n debugAndPrintError(`${e.message}: create room request timed out for ${roomName} on processId ${selectedProcessId}.`);\n\n //\n // clean-up possibly stale process from redis.\n // when a process disconnects ungracefully, it may leave its previous processId under \"roomcount\"\n // if the process is still alive, it will re-add itself shortly after the load-balancer selects it again.\n //\n await stats.excludeProcess(selectedProcessId);\n\n // if other process failed to respond, create the room on this process\n room = await handleCreateRoom(roomName, clientOptions);\n\n } else {\n // re-throw intentional exception thrown during remote onCreate()\n throw e;\n }\n }\n }\n\n if (isDevMode) {\n presence.hset(getRoomRestoreListKey(), room.roomId, JSON.stringify({\n \"clientOptions\": clientOptions,\n \"roomName\": roomName,\n \"processId\": processId\n }));\n }\n\n return room;\n}\n\nexport async function handleCreateRoom(roomName: string, clientOptions: ClientOptions, restoringRoomId?: string): Promise<RoomListingData> {\n const handler = getHandler(roomName);\n const room = new handler.klass();\n\n // set room public attributes\n if (restoringRoomId && isDevMode) {\n room.roomId = restoringRoomId;\n\n } else {\n room.roomId = generateId();\n }\n\n room.roomName = roomName;\n room.presence = presence;\n\n const additionalListingData: any = handler.getFilterOptions(clientOptions);\n\n // assign public host\n if (publicAddress) {\n additionalListingData.publicAddress = publicAddress;\n }\n\n // create a RoomCache reference.\n room.listing = driver.createInstance({\n name: roomName,\n processId,\n ...additionalListingData\n });\n\n if (room.onCreate) {\n try {\n await room.onCreate(merge({}, clientOptions, handler.options));\n\n } catch (e) {\n debugAndPrintError(e);\n throw new ServerError(\n e.code || ErrorCode.MATCHMAKE_UNHANDLED,\n e.message,\n );\n }\n }\n\n room['_internalState'] = RoomInternalState.CREATED;\n\n room.listing.roomId = room.roomId;\n room.listing.maxClients = room.maxClients;\n\n // imediatelly ask client to join the room\n debugMatchMaking('spawning \\'%s\\', roomId: %s, processId: %s', roomName, room.roomId, processId);\n\n // increment amount of rooms this process is handling\n stats.local.roomCount++;\n stats.persist();\n\n room._events.on('lock', lockRoom.bind(this, room));\n room._events.on('unlock', unlockRoom.bind(this, room));\n room._events.on('join', onClientJoinRoom.bind(this, room));\n room._events.on('leave', onClientLeaveRoom.bind(this, room));\n room._events.on('visibility-change', onVisibilityChange.bind(this, room));\n room._events.once('dispose', disposeRoom.bind(this, roomName, room));\n\n // when disconnect()'ing, keep only join/leave events for stat counting\n room._events.once('disconnect', () => {\n room._events.removeAllListeners('lock');\n room._events.removeAllListeners('unlock');\n room._events.removeAllListeners('visibility-change');\n room._events.removeAllListeners('dispose');\n });\n\n // room always start unlocked\n await createRoomReferences(room, true);\n await room.listing.save();\n\n handler.emit('create', room);\n\n return room.listing;\n}\n\nexport function getRoomById(roomId: string) {\n return rooms[roomId];\n}\n\n/**\n * Disconnects every client on every room in the current process.\n */\nexport function disconnectAll(closeCode?: number) {\n const promises: Array<Promise<any>> = [];\n\n for (const roomId in rooms) {\n if (!rooms.hasOwnProperty(roomId)) {\n continue;\n }\n\n const room = rooms[roomId];\n\n // prevent touching stats when process is shutting down\n room._events.removeAllListeners(\"leave\");\n\n promises.push(room.disconnect(closeCode));\n }\n\n return promises;\n}\n\nexport async function gracefullyShutdown(): Promise<any> {\n if (isGracefullyShuttingDown) {\n return Promise.reject('already_shutting_down');\n }\n\n isGracefullyShuttingDown = true;\n onReady = undefined;\n\n debugMatchMaking(`${processId} is shutting down!`);\n\n if (isDevMode) {\n await cacheRoomHistory(rooms);\n }\n\n // remove processId from room count key\n await stats.excludeProcess(processId);\n\n // remove cached rooms of this process\n await removeRoomsByProcessId(processId);\n\n // unsubscribe from process id channel\n presence.unsubscribe(getProcessChannel());\n\n return Promise.all(disconnectAll(\n (isDevMode)\n ? Protocol.WS_CLOSE_DEVMODE_RESTART\n : undefined\n ));\n}\n\n/**\n * Reserve a seat for a client in a room\n */\nexport async function reserveSeatFor(room: RoomListingData, options: ClientOptions, authData?: any) {\n const sessionId: string = generateId();\n\n debugMatchMaking(\n 'reserving seat. sessionId: \\'%s\\', roomId: \\'%s\\', processId: \\'%s\\'',\n sessionId, room.roomId, processId,\n );\n\n let successfulSeatReservation: boolean;\n\n try {\n successfulSeatReservation = await remoteRoomCall(room.roomId, '_reserveSeat', [sessionId, options, authData]);\n\n } catch (e) {\n debugMatchMaking(e);\n throw e;\n }\n\n if (!successfulSeatReservation) {\n throw new SeatReservationError(`${room.roomId} is already full.`);\n }\n\n const response: SeatReservation = { room, sessionId };\n\n if (isDevMode) {\n response.devMode = isDevMode;\n }\n\n return response;\n}\n\nfunction callOnAuth(roomName: string, authOptions?: AuthOptions) {\n const roomClass = getRoomClass(roomName);\n return (roomClass && roomClass['onAuth'] && roomClass['onAuth'] !== Room['onAuth'])\n ? roomClass['onAuth'](authOptions.token, authOptions.request)\n : undefined;\n}\n\nexport async function cleanupStaleRooms(roomName: string) {\n // remove connecting counts\n await presence.del(getHandlerConcurrencyKey(roomName));\n}\n\nasync function removeRoomsByProcessId(processId: string) {\n //\n // clean-up possibly stale room ids\n // (ungraceful shutdowns using Redis can result on stale room ids still on memory.)\n //\n if (typeof(driver.cleanup) === \"function\") {\n await driver.cleanup(processId);\n\n } else {\n //\n // TODO: remove this block on 1.0.\n //\n // driver.cleanup() has been added mid-way through 0.15\n // some users may still be using older versions of the driver.\n //\n const cachedRooms = await driver.find({ processId }, { _id: 1 });\n logger.debug(\"removing stale rooms by processId:\", processId, `(${cachedRooms.length} rooms found)`);\n cachedRooms.forEach((room) => room.remove());\n }\n}\n\nasync function createRoomReferences(room: Room, init: boolean = false): Promise<boolean> {\n rooms[room.roomId] = room;\n\n if (init) {\n await subscribeIPC(\n presence,\n processId,\n getRoomChannel(room.roomId),\n (method, args) => {\n return (!args && typeof (room[method]) !== 'function')\n ? room[method]\n : room[method].apply(room, args);\n },\n );\n }\n\n return true;\n}\n\nasync function awaitRoomAvailable(roomToJoin: string, callback: Function): Promise<RoomListingData> {\n return new Promise(async (resolve, reject) => {\n const concurrencyKey = getHandlerConcurrencyKey(roomToJoin);\n const concurrency = await presence.incr(concurrencyKey) - 1;\n\n //\n // avoid having too long timeout if 10+ clients ask to join at the same time\n //\n // TODO: we need a better solution here. either a lock or queue system should be implemented instead.\n // https://github.com/colyseus/colyseus/issues/466\n //\n const concurrencyTimeout = Math.min(concurrency * 100, 500);\n\n if (concurrency > 0) {\n debugMatchMaking(\n 'receiving %d concurrent requests for joining \\'%s\\' (waiting %d ms)',\n concurrency, roomToJoin, concurrencyTimeout,\n );\n }\n\n setTimeout(async () => {\n try {\n const result = await callback();\n resolve(result);\n\n } catch (e) {\n reject(e);\n\n } finally {\n await presence.decr(concurrencyKey);\n }\n }, concurrencyTimeout);\n });\n}\n\nfunction onClientJoinRoom(room: Room, client: Client) {\n // increment local CCU\n stats.local.ccu++;\n stats.persist();\n\n handlers[room.roomName].emit('join', room, client);\n}\n\nfunction onClientLeaveRoom(room: Room, client: Client, willDispose: boolean) {\n // decrement local CCU\n stats.local.ccu--;\n stats.persist();\n\n handlers[room.roomName].emit('leave', room, client, willDispose);\n}\n\nfunction lockRoom(room: Room): void {\n // emit public event on registered handler\n handlers[room.roomName].emit('lock', room);\n}\n\nasync function unlockRoom(room: Room) {\n if (await createRoomReferences(room)) {\n // emit public event on registered handler\n handlers[room.roomName].emit('unlock', room);\n }\n}\n\nfunction onVisibilityChange(room: Room, isInvisible: boolean): void {\n handlers[room.roomName].emit('visibility-change', room, isInvisible);\n}\n\nasync function disposeRoom(roomName: string, room: Room) {\n debugMatchMaking('disposing \\'%s\\' (%s) on processId \\'%s\\' (graceful shutdown: %s)', roomName, room.roomId, processId, isGracefullyShuttingDown);\n\n // decrease amount of rooms this process is handling\n if (!isGracefullyShuttingDown) {\n stats.local.roomCount--;\n stats.persist();\n\n // remove from devMode restore list\n if (isDevMode) {\n await presence.hdel(getRoomRestoreListKey(), room.roomId);\n }\n }\n\n // emit disposal on registered session handler\n handlers[roomName].emit('dispose', room);\n\n // remove concurrency key\n presence.del(getHandlerConcurrencyKey(roomName));\n\n // unsubscribe from remote connections\n presence.unsubscribe(getRoomChannel(room.roomId));\n\n // remove actual room reference\n delete rooms[room.roomId];\n}\n\n//\n// Presence keys\n//\nfunction getRoomChannel(roomId: string) {\n return `$${roomId}`;\n}\n\nfunction getHandlerConcurrencyKey(name: string) {\n return `c:${name}`;\n}\n\nfunction getProcessChannel(id: string = processId) {\n return `p:${id}`;\n}\n"],
5
- "mappings": "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",
6
- "names": ["controller", "processId"]
4
+ "sourcesContent": ["import { ErrorCode, Protocol } from './Protocol';\n\nimport { requestFromIPC, subscribeIPC } from './IPC';\n\nimport { Deferred, generateId, merge, REMOTE_ROOM_SHORT_TIMEOUT, retry } from './utils/Utils';\nimport { isDevMode, cacheRoomHistory, getPreviousProcessId, getRoomRestoreListKey, reloadFromCache } from './utils/DevMode';\n\nimport { RegisteredHandler } from './matchmaker/RegisteredHandler';\nimport { Room, RoomInternalState } from './Room';\n\nimport { LocalPresence } from './presence/LocalPresence';\nimport { Presence } from './presence/Presence';\n\nimport { debugAndPrintError, debugMatchMaking } from './Debug';\nimport { SeatReservationError } from './errors/SeatReservationError';\nimport { ServerError } from './errors/ServerError';\n\nimport { IRoomListingData, RoomListingData, LocalDriver, MatchMakerDriver } from './matchmaker/driver';\nimport controller from './matchmaker/controller';\nimport * as stats from \"./Stats\";\n\nimport { logger } from './Logger';\nimport { Client } from './Transport';\nimport { Type } from './utils/types';\nimport { getHostname } from \"./discovery\";\n\nexport { controller, stats, type MatchMakerDriver };\n\nexport type ClientOptions = any;\nexport type AuthOptions = { token?: string, request?: any };\nexport type SelectProcessIdCallback = (roomName: string, clientOptions: ClientOptions) => Promise<string>;\n\nexport interface SeatReservation {\n sessionId: string;\n room: RoomListingData;\n devMode?: boolean;\n}\n\nconst handlers: {[id: string]: RegisteredHandler} = {};\nconst rooms: {[roomId: string]: Room} = {};\n\nexport let publicAddress: string;\nexport let processId: string;\nexport let presence: Presence;\nexport let driver: MatchMakerDriver;\nexport let selectProcessIdToCreateRoom: SelectProcessIdCallback;\n\nexport let isGracefullyShuttingDown: boolean; // TODO: remove me on 1.0, use 'state' instead\nexport let onReady: Deferred = new Deferred(); // onReady needs to be immediately available to @colyseus/auth integration.\n\nexport enum MatchMakerState {\n INITIALIZING,\n READY,\n SHUTTING_DOWN\n}\n\n/**\n * Internal MatchMaker state\n */\nexport let state: MatchMakerState;\n\n/**\n * @private\n */\nexport async function setup(\n _presence?: Presence,\n _driver?: MatchMakerDriver,\n _publicAddress?: string,\n _selectProcessIdToCreateRoom?: SelectProcessIdCallback,\n) {\n if (onReady === undefined) {\n //\n // for testing purposes only: onReady is turned into undefined on shutdown\n // (needs refactoring.)\n //\n onReady = new Deferred();\n }\n\n isGracefullyShuttingDown = false;\n state = MatchMakerState.INITIALIZING;\n\n presence = _presence || new LocalPresence();\n\n driver = _driver || new LocalDriver();\n publicAddress = _publicAddress;\n\n stats.reset(false);\n\n // devMode: try to retrieve previous processId\n if (isDevMode) { processId = await getPreviousProcessId(await getHostname()); }\n\n // ensure processId is set\n if (!processId) { processId = generateId(); }\n\n /**\n * Define default `assignRoomToProcessId` method.\n * By default, return the process with least amount of rooms created\n */\n selectProcessIdToCreateRoom = _selectProcessIdToCreateRoom || async function () {\n return (await stats.fetchAll())\n .sort((p1, p2) => p1.roomCount > p2.roomCount ? 1 : -1)[0]?.processId || processId;\n };\n\n onReady.resolve();\n}\n\n/**\n * - Accept receiving remote room creation requests\n * - Check for leftover/invalid processId's on startup\n * @private\n */\nexport async function accept() {\n await onReady; // make sure \"processId\" is available\n\n /**\n * Process-level subscription\n * - handle remote process healthcheck\n * - handle remote room creation\n */\n await subscribeIPC(presence, processId, getProcessChannel(), (method, args) => {\n if (method === 'healthcheck') {\n // health check for this processId\n return true;\n\n } else {\n // handle room creation\n return handleCreateRoom.apply(undefined, args);\n }\n });\n\n /**\n * Check for leftover/invalid processId's on startup\n */\n await healthCheckAllProcesses();\n\n state = MatchMakerState.READY;\n\n await stats.persist();\n\n if (isDevMode) {\n await reloadFromCache();\n }\n}\n\n/**\n * Join or create into a room and return seat reservation\n */\nexport async function joinOrCreate(roomName: string, clientOptions: ClientOptions = {}, authOptions?: AuthOptions) {\n return await retry<Promise<SeatReservation>>(async () => {\n const authData = await callOnAuth(roomName, authOptions);\n let room = await findOneRoomAvailable(roomName, clientOptions);\n\n //\n // TODO [?]\n // should we expose the \"creator\" auth data of the room during `onCreate()`?\n // it would be useful, though it could be accessed via `onJoin()` for now.\n //\n\n if (!room) {\n room = await createRoom(roomName, clientOptions);\n }\n\n return await reserveSeatFor(room, clientOptions, authData);\n }, 5, [SeatReservationError]);\n}\n\n/**\n * Create a room and return seat reservation\n */\nexport async function create(roomName: string, clientOptions: ClientOptions = {}, authOptions?: AuthOptions) {\n const authData = await callOnAuth(roomName, authOptions);\n const room = await createRoom(roomName, clientOptions);\n return reserveSeatFor(room, clientOptions, authData);\n}\n\n/**\n * Join a room and return seat reservation\n */\nexport async function join(roomName: string, clientOptions: ClientOptions = {}, authOptions?: AuthOptions) {\n return await retry<Promise<SeatReservation>>(async () => {\n const authData = await callOnAuth(roomName, authOptions);\n const room = await findOneRoomAvailable(roomName, clientOptions);\n\n if (!room) {\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_CRITERIA, `no rooms found with provided criteria`);\n }\n\n return reserveSeatFor(room, clientOptions, authData);\n });\n}\n\n/**\n * Join a room by id and return seat reservation\n */\nexport async function reconnect(roomId: string, clientOptions: ClientOptions = {}) {\n const room = await driver.findOne({ roomId });\n if (!room) {\n // TODO: support a \"logLevel\" out of the box?\n if (process.env.NODE_ENV !== 'production') {\n logger.info(`\u274C room \"${roomId}\" has been disposed. Did you missed .allowReconnection()?\\n\uD83D\uDC49 https://docs.colyseus.io/server/room/#allowreconnection-client-seconds`);\n }\n\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room \"${roomId}\" has been disposed.`);\n }\n\n // check for reconnection\n const reconnectionToken = clientOptions.reconnectionToken;\n if (!reconnectionToken) { throw new ServerError(ErrorCode.MATCHMAKE_UNHANDLED, `'reconnectionToken' must be provided for reconnection.`); }\n\n\n // respond to re-connection!\n const sessionId = await remoteRoomCall(room.roomId, 'checkReconnectionToken', [reconnectionToken]);\n if (sessionId) {\n return { room, sessionId };\n\n } else {\n // TODO: support a \"logLevel\" out of the box?\n if (process.env.NODE_ENV !== 'production') {\n logger.info(`\u274C reconnection token invalid or expired. Did you missed .allowReconnection()?\\n\uD83D\uDC49 https://docs.colyseus.io/server/room/#allowreconnection-client-seconds`);\n }\n throw new ServerError(ErrorCode.MATCHMAKE_EXPIRED, `reconnection token invalid or expired.`);\n }\n}\n\n/**\n * Join a room by id and return client seat reservation. An exception is thrown if a room is not found for roomId.\n *\n * @param roomId - The Id of the specific room instance.\n * @param clientOptions - Options for the client seat reservation (for `onJoin`/`onAuth`)\n * @param authOptions - Optional authentication token\n *\n * @returns Promise<SeatReservation> - A promise which contains `sessionId` and `RoomListingData`.\n */\nexport async function joinById(roomId: string, clientOptions: ClientOptions = {}, authOptions?: AuthOptions) {\n const room = await driver.findOne({ roomId });\n\n if (!room) {\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room \"${roomId}\" not found`);\n\n } else if (room.locked) {\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room \"${roomId}\" is locked`);\n }\n\n const authData = await callOnAuth(room.name, authOptions);\n\n return reserveSeatFor(room, clientOptions, authData);\n}\n\n/**\n * Perform a query for all cached rooms\n */\nexport async function query(conditions: Partial<IRoomListingData> = {}) {\n return await driver.find(conditions);\n}\n\n/**\n * Find for a public and unlocked room available.\n *\n * @param roomName - The Id of the specific room.\n * @param clientOptions - Options for the client seat reservation (for `onJoin`/`onAuth`).\n *\n * @returns Promise<RoomListingData> - A promise contaning an object which includes room metadata and configurations.\n */\nexport async function findOneRoomAvailable(roomName: string, clientOptions: ClientOptions): Promise<RoomListingData> {\n return await awaitRoomAvailable(roomName, async () => {\n const handler = getHandler(roomName);\n\n const roomQuery = driver.findOne({\n locked: false,\n name: roomName,\n private: false,\n ...handler.getFilterOptions(clientOptions),\n });\n\n if (handler.sortOptions) {\n roomQuery.sort(handler.sortOptions);\n }\n\n return await roomQuery;\n });\n}\n\n/**\n * Call a method or return a property on a remote room.\n *\n * @param roomId - The Id of the specific room instance.\n * @param method - Method or attribute to call or retrive.\n * @param args - Array of arguments for the method\n *\n * @returns Promise<any> - Returned value from the called or retrieved method/attribute.\n */\nexport async function remoteRoomCall<R= any>(\n roomId: string,\n method: string,\n args?: any[],\n rejectionTimeout = REMOTE_ROOM_SHORT_TIMEOUT,\n): Promise<R> {\n const room = rooms[roomId];\n\n if (!room) {\n try {\n return await requestFromIPC<R>(presence, getRoomChannel(roomId), method, args, rejectionTimeout);\n\n } catch (e) {\n\n //\n // the room cache from an unavailable process might've been used here.\n // perform a health-check on the process before proceeding.\n // (this is a broken state when a process wasn't gracefully shut down)\n //\n if (method === '_reserveSeat' && e.message === \"ipc_timeout\") {\n throw e;\n }\n\n // TODO: for 1.0, consider always throwing previous error directly.\n\n const request = `${method}${args && ' with args ' + JSON.stringify(args) || ''}`;\n throw new ServerError(\n ErrorCode.MATCHMAKE_UNHANDLED,\n `remote room (${roomId}) timed out, requesting \"${request}\". (${rejectionTimeout}ms exceeded)`,\n );\n }\n\n } else {\n return (!args && typeof (room[method]) !== 'function')\n ? room[method]\n : (await room[method].apply(room, args && JSON.parse(JSON.stringify(args))));\n }\n}\n\nexport function defineRoomType<T extends Type<Room>>(\n roomName: string,\n klass: T,\n defaultOptions?: Parameters<NonNullable<InstanceType<T>['onCreate']>>[0],\n) {\n const registeredHandler = new RegisteredHandler(klass, defaultOptions);\n\n handlers[roomName] = registeredHandler;\n\n if (klass.prototype['onAuth'] !== Room.prototype['onAuth']) {\n // TODO: soft-deprecate instance level `onAuth` on 0.16\n // logger.warn(\"DEPRECATION WARNING: onAuth() at the instance level will be deprecated soon. Please use static onAuth() instead.\");\n\n if (klass['onAuth'] !== Room['onAuth']) {\n logger.info(`\u274C \"${roomName}\"'s onAuth() defined at the instance level will be ignored.`);\n }\n }\n\n if (!isDevMode) {\n cleanupStaleRooms(roomName);\n }\n\n return registeredHandler;\n}\n\nexport function removeRoomType(roomName: string) {\n delete handlers[roomName];\n\n if (!isDevMode) {\n cleanupStaleRooms(roomName);\n }\n}\n\n// TODO: legacy; remove me on 1.0\nexport function hasHandler(roomName: string) {\n logger.warn(\"hasHandler() is deprecated. Use getHandler() instead.\");\n return handlers[roomName] !== undefined;\n}\n\nexport function getHandler(roomName: string) {\n const handler = handlers[roomName];\n\n if (!handler) {\n throw new ServerError(ErrorCode.MATCHMAKE_NO_HANDLER, `provided room name \"${roomName}\" not defined`);\n }\n\n return handler;\n}\n\nexport function getRoomClass(roomName: string): Type<Room> {\n return handlers[roomName]?.klass;\n}\n\n\n/**\n * Creates a new room.\n *\n * @param roomName - The identifier you defined on `gameServer.define()`\n * @param clientOptions - Options for `onCreate`\n *\n * @returns Promise<RoomListingData> - A promise contaning an object which includes room metadata and configurations.\n */\nexport async function createRoom(roomName: string, clientOptions: ClientOptions): Promise<RoomListingData> {\n //\n // - select a process to create the room\n // - use local processId if MatchMaker is not ready yet\n //\n const selectedProcessId = (state === MatchMakerState.READY)\n ? await selectProcessIdToCreateRoom(roomName, clientOptions)\n : processId;\n\n let room: RoomListingData;\n if (selectedProcessId === undefined) {\n throw new ServerError(ErrorCode.MATCHMAKE_UNHANDLED, `no processId available to create room ${roomName}`);\n\n } else if (selectedProcessId === processId) {\n // create the room on this process!\n room = await handleCreateRoom(roomName, clientOptions);\n\n } else {\n // ask other process to create the room!\n try {\n room = await requestFromIPC<RoomListingData>(\n presence,\n getProcessChannel(selectedProcessId),\n undefined,\n [roomName, clientOptions],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n\n } catch (e) {\n if (e.message === \"ipc_timeout\") {\n debugAndPrintError(`${e.message}: create room request timed out for ${roomName} on processId ${selectedProcessId}.`);\n\n //\n // clean-up possibly stale process from redis.\n // when a process disconnects ungracefully, it may leave its previous processId under \"roomcount\"\n // if the process is still alive, it will re-add itself shortly after the load-balancer selects it again.\n //\n await stats.excludeProcess(selectedProcessId);\n\n // if other process failed to respond, create the room on this process\n room = await handleCreateRoom(roomName, clientOptions);\n\n } else {\n // re-throw intentional exception thrown during remote onCreate()\n throw e;\n }\n }\n }\n\n if (isDevMode) {\n presence.hset(getRoomRestoreListKey(), room.roomId, JSON.stringify({\n \"clientOptions\": clientOptions,\n \"roomName\": roomName,\n \"processId\": processId\n }));\n }\n\n return room;\n}\n\nexport async function handleCreateRoom(roomName: string, clientOptions: ClientOptions, restoringRoomId?: string): Promise<RoomListingData> {\n const handler = getHandler(roomName);\n const room = new handler.klass();\n\n // set room public attributes\n if (restoringRoomId && isDevMode) {\n room.roomId = restoringRoomId;\n\n } else {\n room.roomId = generateId();\n }\n\n room.roomName = roomName;\n room.presence = presence;\n\n const additionalListingData: any = handler.getFilterOptions(clientOptions);\n\n // assign public host\n if (publicAddress) {\n additionalListingData.publicAddress = publicAddress;\n }\n\n // create a RoomCache reference.\n room.listing = driver.createInstance({\n name: roomName,\n processId,\n ...additionalListingData\n });\n\n if (room.onCreate) {\n try {\n await room.onCreate(merge({}, clientOptions, handler.options));\n\n } catch (e) {\n debugAndPrintError(e);\n throw new ServerError(\n e.code || ErrorCode.MATCHMAKE_UNHANDLED,\n e.message,\n );\n }\n }\n\n room['_internalState'] = RoomInternalState.CREATED;\n\n room.listing.roomId = room.roomId;\n room.listing.maxClients = room.maxClients;\n\n // imediatelly ask client to join the room\n debugMatchMaking('spawning \\'%s\\', roomId: %s, processId: %s', roomName, room.roomId, processId);\n\n // increment amount of rooms this process is handling\n stats.local.roomCount++;\n stats.persist();\n\n room._events.on('lock', lockRoom.bind(this, room));\n room._events.on('unlock', unlockRoom.bind(this, room));\n room._events.on('join', onClientJoinRoom.bind(this, room));\n room._events.on('leave', onClientLeaveRoom.bind(this, room));\n room._events.on('visibility-change', onVisibilityChange.bind(this, room));\n room._events.once('dispose', disposeRoom.bind(this, roomName, room));\n\n // when disconnect()'ing, keep only join/leave events for stat counting\n room._events.once('disconnect', () => {\n room._events.removeAllListeners('lock');\n room._events.removeAllListeners('unlock');\n room._events.removeAllListeners('visibility-change');\n room._events.removeAllListeners('dispose');\n });\n\n // room always start unlocked\n await createRoomReferences(room, true);\n await room.listing.save();\n\n handler.emit('create', room);\n\n return room.listing;\n}\n\nexport function getRoomById(roomId: string) {\n return rooms[roomId];\n}\n\n/**\n * Disconnects every client on every room in the current process.\n */\nexport function disconnectAll(closeCode?: number) {\n const promises: Array<Promise<any>> = [];\n\n for (const roomId in rooms) {\n if (!rooms.hasOwnProperty(roomId)) {\n continue;\n }\n\n const room = rooms[roomId];\n\n // prevent touching stats when process is shutting down\n room._events.removeAllListeners(\"leave\");\n\n promises.push(room.disconnect(closeCode));\n }\n\n return promises;\n}\n\nexport async function gracefullyShutdown(): Promise<any> {\n if (isGracefullyShuttingDown) {\n return Promise.reject('already_shutting_down');\n }\n\n isGracefullyShuttingDown = true;\n state = MatchMakerState.SHUTTING_DOWN;\n\n onReady = undefined;\n\n debugMatchMaking(`${processId} is shutting down!`);\n\n if (isDevMode) {\n await cacheRoomHistory(rooms);\n }\n\n // remove processId from room count key\n await stats.excludeProcess(processId);\n\n // remove cached rooms of this process\n await removeRoomsByProcessId(processId);\n\n // unsubscribe from process id channel\n presence.unsubscribe(getProcessChannel());\n\n return Promise.all(disconnectAll(\n (isDevMode)\n ? Protocol.WS_CLOSE_DEVMODE_RESTART\n : undefined\n ));\n}\n\n/**\n * Reserve a seat for a client in a room\n */\nexport async function reserveSeatFor(room: RoomListingData, options: ClientOptions, authData?: any) {\n const sessionId: string = generateId();\n\n debugMatchMaking(\n 'reserving seat. sessionId: \\'%s\\', roomId: \\'%s\\', processId: \\'%s\\'',\n sessionId, room.roomId, processId,\n );\n\n let successfulSeatReservation: boolean;\n\n try {\n successfulSeatReservation = await remoteRoomCall(\n room.roomId,\n '_reserveSeat',\n [sessionId, options, authData],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n\n } catch (e) {\n debugMatchMaking(e);\n\n //\n // the room cache from an unavailable process might've been used here.\n // (this is a broken state when a process wasn't gracefully shut down)\n // perform a health-check on the process before proceeding.\n //\n if (e.message === \"ipc_timeout\" && !(await healthCheckProcessId(room.processId))) {\n throw new SeatReservationError(`process ${room.processId} is not available.`);\n\n } else {\n successfulSeatReservation = false;\n }\n }\n\n if (!successfulSeatReservation) {\n throw new SeatReservationError(`${room.roomId} is already full.`);\n }\n\n const response: SeatReservation = { room, sessionId };\n\n if (isDevMode) {\n response.devMode = isDevMode;\n }\n\n return response;\n}\n\nfunction callOnAuth(roomName: string, authOptions?: AuthOptions) {\n const roomClass = getRoomClass(roomName);\n return (roomClass && roomClass['onAuth'] && roomClass['onAuth'] !== Room['onAuth'])\n ? roomClass['onAuth'](authOptions.token, authOptions.request)\n : undefined;\n}\n\nexport async function cleanupStaleRooms(roomName: string) {\n // remove connecting counts\n await presence.del(getHandlerConcurrencyKey(roomName));\n}\n\n/**\n * Perform health check on all processes\n */\nexport async function healthCheckAllProcesses() {\n const allStats = await stats.fetchAll();\n if (allStats.length > 0) {\n await Promise.all(allStats.map((stat) => healthCheckProcessId(stat.processId)));\n }\n}\n\n/**\n * Perform health check on a remote process\n * @param processId\n */\nconst _healthCheckByProcessId: { [processId: string]: Promise<any> } = {};\nexport function healthCheckProcessId(processId: string) {\n //\n // re-use the same promise if health-check is already in progress\n // (may occur when _reserveSeat() fails multiple times for the same 'processId')\n //\n if (_healthCheckByProcessId[processId] !== undefined) {\n return _healthCheckByProcessId[processId];\n }\n\n _healthCheckByProcessId[processId] = new Promise<boolean>(async (resolve, reject) => {\n logger.debug(`> Performing health-check against processId: '${processId}'...`);\n\n try {\n const requestTime = Date.now();\n\n await requestFromIPC<RoomListingData>(\n presence,\n getProcessChannel(processId),\n 'healthcheck',\n [],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n\n logger.debug(`\u2705 Process '${processId}' successfully responded (${Date.now() - requestTime}ms)`);\n\n // succeeded to respond\n resolve(true)\n\n } catch (e) {\n // process failed to respond - remove it from stats\n logger.debug(`\u274C Process '${processId}' failed to respond. Cleaning it up.`);\n const isProcessExcluded = await stats.excludeProcess(processId);\n\n // clean-up possibly stale room ids\n if (isProcessExcluded && !isDevMode) {\n await removeRoomsByProcessId(processId);\n }\n\n resolve(false);\n } finally {\n delete _healthCheckByProcessId[processId];\n }\n });\n\n return _healthCheckByProcessId[processId];\n}\n\n/**\n * Remove cached rooms by processId\n * @param processId\n */\nasync function removeRoomsByProcessId(processId: string) {\n //\n // clean-up possibly stale room ids\n // (ungraceful shutdowns using Redis can result on stale room ids still on memory.)\n //\n if (typeof(driver.cleanup) === \"function\") {\n await driver.cleanup(processId);\n\n } else {\n //\n // TODO: remove this block on 1.0.\n //\n // driver.cleanup() has been added mid-way through 0.15\n // some users may still be using older versions of the driver.\n //\n const cachedRooms = await driver.find({ processId }, { _id: 1 });\n logger.debug(\"> Removing stale rooms by processId:\", processId, `(${cachedRooms.length} rooms found)`);\n cachedRooms.forEach((room) => room.remove());\n }\n}\n\nasync function createRoomReferences(room: Room, init: boolean = false): Promise<boolean> {\n rooms[room.roomId] = room;\n\n if (init) {\n await subscribeIPC(\n presence,\n processId,\n getRoomChannel(room.roomId),\n (method, args) => {\n return (!args && typeof (room[method]) !== 'function')\n ? room[method]\n : room[method].apply(room, args);\n },\n );\n }\n\n return true;\n}\n\nasync function awaitRoomAvailable(roomToJoin: string, callback: Function): Promise<RoomListingData> {\n return new Promise(async (resolve, reject) => {\n const concurrencyKey = getHandlerConcurrencyKey(roomToJoin);\n const concurrency = await presence.incr(concurrencyKey) - 1;\n\n //\n // avoid having too long timeout if 10+ clients ask to join at the same time\n //\n // TODO: we need a better solution here. either a lock or queue system should be implemented instead.\n // https://github.com/colyseus/colyseus/issues/466\n //\n const concurrencyTimeout = Math.min(concurrency * 100, 500);\n\n if (concurrency > 0) {\n debugMatchMaking(\n 'receiving %d concurrent requests for joining \\'%s\\' (waiting %d ms)',\n concurrency, roomToJoin, concurrencyTimeout,\n );\n }\n\n setTimeout(async () => {\n try {\n const result = await callback();\n resolve(result);\n\n } catch (e) {\n reject(e);\n\n } finally {\n await presence.decr(concurrencyKey);\n }\n }, concurrencyTimeout);\n });\n}\n\nfunction onClientJoinRoom(room: Room, client: Client) {\n // increment local CCU\n stats.local.ccu++;\n stats.persist();\n\n handlers[room.roomName].emit('join', room, client);\n}\n\nfunction onClientLeaveRoom(room: Room, client: Client, willDispose: boolean) {\n // decrement local CCU\n stats.local.ccu--;\n stats.persist();\n\n handlers[room.roomName].emit('leave', room, client, willDispose);\n}\n\nfunction lockRoom(room: Room): void {\n // emit public event on registered handler\n handlers[room.roomName].emit('lock', room);\n}\n\nasync function unlockRoom(room: Room) {\n if (await createRoomReferences(room)) {\n // emit public event on registered handler\n handlers[room.roomName].emit('unlock', room);\n }\n}\n\nfunction onVisibilityChange(room: Room, isInvisible: boolean): void {\n handlers[room.roomName].emit('visibility-change', room, isInvisible);\n}\n\nasync function disposeRoom(roomName: string, room: Room) {\n debugMatchMaking('disposing \\'%s\\' (%s) on processId \\'%s\\' (graceful shutdown: %s)', roomName, room.roomId, processId, isGracefullyShuttingDown);\n\n // decrease amount of rooms this process is handling\n if (!isGracefullyShuttingDown) {\n stats.local.roomCount--;\n stats.persist();\n\n // remove from devMode restore list\n if (isDevMode) {\n await presence.hdel(getRoomRestoreListKey(), room.roomId);\n }\n }\n\n // emit disposal on registered session handler\n handlers[roomName].emit('dispose', room);\n\n // remove concurrency key\n presence.del(getHandlerConcurrencyKey(roomName));\n\n // unsubscribe from remote connections\n presence.unsubscribe(getRoomChannel(room.roomId));\n\n // remove actual room reference\n delete rooms[room.roomId];\n}\n\n//\n// Presence keys\n//\nfunction getRoomChannel(roomId: string) {\n return `$${roomId}`;\n}\n\nfunction getHandlerConcurrencyKey(name: string) {\n return `c:${name}`;\n}\n\nfunction getProcessChannel(id: string = processId) {\n return `p:${id}`;\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["controller", "MatchMakerState", "processId"]
7
7
  }
@@ -22,10 +22,19 @@ let driver;
22
22
  let selectProcessIdToCreateRoom;
23
23
  let isGracefullyShuttingDown;
24
24
  let onReady = new Deferred();
25
+ var MatchMakerState = /* @__PURE__ */ ((MatchMakerState2) => {
26
+ MatchMakerState2[MatchMakerState2["INITIALIZING"] = 0] = "INITIALIZING";
27
+ MatchMakerState2[MatchMakerState2["READY"] = 1] = "READY";
28
+ MatchMakerState2[MatchMakerState2["SHUTTING_DOWN"] = 2] = "SHUTTING_DOWN";
29
+ return MatchMakerState2;
30
+ })(MatchMakerState || {});
31
+ let state;
25
32
  async function setup(_presence, _driver, _publicAddress, _selectProcessIdToCreateRoom) {
26
33
  if (onReady === void 0) {
27
34
  onReady = new Deferred();
28
35
  }
36
+ isGracefullyShuttingDown = false;
37
+ state = 0 /* INITIALIZING */;
29
38
  presence = _presence || new LocalPresence();
30
39
  driver = _driver || new LocalDriver();
31
40
  publicAddress = _publicAddress;
@@ -36,7 +45,6 @@ async function setup(_presence, _driver, _publicAddress, _selectProcessIdToCreat
36
45
  if (!processId) {
37
46
  processId = generateId();
38
47
  }
39
- isGracefullyShuttingDown = false;
40
48
  selectProcessIdToCreateRoom = _selectProcessIdToCreateRoom || async function() {
41
49
  return (await stats.fetchAll()).sort((p1, p2) => p1.roomCount > p2.roomCount ? 1 : -1)[0]?.processId || processId;
42
50
  };
@@ -51,27 +59,8 @@ async function accept() {
51
59
  return handleCreateRoom.apply(void 0, args);
52
60
  }
53
61
  });
54
- const previousStats = await stats.fetchAll();
55
- if (previousStats.length > 0) {
56
- logger.debug(`${previousStats.length} previous processId(s) found, health-checking...`);
57
- await Promise.all(previousStats.map(async (stat) => {
58
- try {
59
- await requestFromIPC(
60
- presence,
61
- getProcessChannel(stat.processId),
62
- "healthcheck",
63
- [],
64
- REMOTE_ROOM_SHORT_TIMEOUT
65
- );
66
- } catch (e) {
67
- logger.debug(`process ${stat.processId} failed to respond. excluding from stats`);
68
- const isProcessExcluded = await stats.excludeProcess(stat.processId);
69
- if (isProcessExcluded && !isDevMode) {
70
- await removeRoomsByProcessId(stat.processId);
71
- }
72
- }
73
- }));
74
- }
62
+ await healthCheckAllProcesses();
63
+ state = 1 /* READY */;
75
64
  await stats.persist();
76
65
  if (isDevMode) {
77
66
  await reloadFromCache();
@@ -160,6 +149,9 @@ async function remoteRoomCall(roomId, method, args, rejectionTimeout = REMOTE_RO
160
149
  try {
161
150
  return await requestFromIPC(presence, getRoomChannel(roomId), method, args, rejectionTimeout);
162
151
  } catch (e) {
152
+ if (method === "_reserveSeat" && e.message === "ipc_timeout") {
153
+ throw e;
154
+ }
163
155
  const request = `${method}${args && " with args " + JSON.stringify(args) || ""}`;
164
156
  throw new ServerError(
165
157
  ErrorCode.MATCHMAKE_UNHANDLED,
@@ -204,7 +196,7 @@ function getRoomClass(roomName) {
204
196
  return handlers[roomName]?.klass;
205
197
  }
206
198
  async function createRoom(roomName, clientOptions) {
207
- const selectedProcessId = selectProcessIdToCreateRoom !== void 0 ? await selectProcessIdToCreateRoom(roomName, clientOptions) : processId;
199
+ const selectedProcessId = state === 1 /* READY */ ? await selectProcessIdToCreateRoom(roomName, clientOptions) : processId;
208
200
  let room;
209
201
  if (selectedProcessId === void 0) {
210
202
  throw new ServerError(ErrorCode.MATCHMAKE_UNHANDLED, `no processId available to create room ${roomName}`);
@@ -311,6 +303,7 @@ async function gracefullyShutdown() {
311
303
  return Promise.reject("already_shutting_down");
312
304
  }
313
305
  isGracefullyShuttingDown = true;
306
+ state = 2 /* SHUTTING_DOWN */;
314
307
  onReady = void 0;
315
308
  debugMatchMaking(`${processId} is shutting down!`);
316
309
  if (isDevMode) {
@@ -333,10 +326,19 @@ async function reserveSeatFor(room, options, authData) {
333
326
  );
334
327
  let successfulSeatReservation;
335
328
  try {
336
- successfulSeatReservation = await remoteRoomCall(room.roomId, "_reserveSeat", [sessionId, options, authData]);
329
+ successfulSeatReservation = await remoteRoomCall(
330
+ room.roomId,
331
+ "_reserveSeat",
332
+ [sessionId, options, authData],
333
+ REMOTE_ROOM_SHORT_TIMEOUT
334
+ );
337
335
  } catch (e) {
338
336
  debugMatchMaking(e);
339
- throw e;
337
+ if (e.message === "ipc_timeout" && !await healthCheckProcessId(room.processId)) {
338
+ throw new SeatReservationError(`process ${room.processId} is not available.`);
339
+ } else {
340
+ successfulSeatReservation = false;
341
+ }
340
342
  }
341
343
  if (!successfulSeatReservation) {
342
344
  throw new SeatReservationError(`${room.roomId} is already full.`);
@@ -354,12 +356,49 @@ function callOnAuth(roomName, authOptions) {
354
356
  async function cleanupStaleRooms(roomName) {
355
357
  await presence.del(getHandlerConcurrencyKey(roomName));
356
358
  }
359
+ async function healthCheckAllProcesses() {
360
+ const allStats = await stats.fetchAll();
361
+ if (allStats.length > 0) {
362
+ await Promise.all(allStats.map((stat) => healthCheckProcessId(stat.processId)));
363
+ }
364
+ }
365
+ const _healthCheckByProcessId = {};
366
+ function healthCheckProcessId(processId2) {
367
+ if (_healthCheckByProcessId[processId2] !== void 0) {
368
+ return _healthCheckByProcessId[processId2];
369
+ }
370
+ _healthCheckByProcessId[processId2] = new Promise(async (resolve, reject) => {
371
+ logger.debug(`> Performing health-check against processId: '${processId2}'...`);
372
+ try {
373
+ const requestTime = Date.now();
374
+ await requestFromIPC(
375
+ presence,
376
+ getProcessChannel(processId2),
377
+ "healthcheck",
378
+ [],
379
+ REMOTE_ROOM_SHORT_TIMEOUT
380
+ );
381
+ logger.debug(`\u2705 Process '${processId2}' successfully responded (${Date.now() - requestTime}ms)`);
382
+ resolve(true);
383
+ } catch (e) {
384
+ logger.debug(`\u274C Process '${processId2}' failed to respond. Cleaning it up.`);
385
+ const isProcessExcluded = await stats.excludeProcess(processId2);
386
+ if (isProcessExcluded && !isDevMode) {
387
+ await removeRoomsByProcessId(processId2);
388
+ }
389
+ resolve(false);
390
+ } finally {
391
+ delete _healthCheckByProcessId[processId2];
392
+ }
393
+ });
394
+ return _healthCheckByProcessId[processId2];
395
+ }
357
396
  async function removeRoomsByProcessId(processId2) {
358
397
  if (typeof driver.cleanup === "function") {
359
398
  await driver.cleanup(processId2);
360
399
  } else {
361
400
  const cachedRooms = await driver.find({ processId: processId2 }, { _id: 1 });
362
- logger.debug("removing stale rooms by processId:", processId2, `(${cachedRooms.length} rooms found)`);
401
+ logger.debug("> Removing stale rooms by processId:", processId2, `(${cachedRooms.length} rooms found)`);
363
402
  cachedRooms.forEach((room) => room.remove());
364
403
  }
365
404
  }
@@ -447,6 +486,7 @@ function getProcessChannel(id = processId) {
447
486
  return `p:${id}`;
448
487
  }
449
488
  export {
489
+ MatchMakerState,
450
490
  accept,
451
491
  cleanupStaleRooms,
452
492
  controller,
@@ -462,6 +502,8 @@ export {
462
502
  gracefullyShutdown,
463
503
  handleCreateRoom,
464
504
  hasHandler,
505
+ healthCheckAllProcesses,
506
+ healthCheckProcessId,
465
507
  isGracefullyShuttingDown,
466
508
  join,
467
509
  joinById,
@@ -477,5 +519,6 @@ export {
477
519
  reserveSeatFor,
478
520
  selectProcessIdToCreateRoom,
479
521
  setup,
522
+ state,
480
523
  stats
481
524
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/MatchMaker.ts"],
4
- "sourcesContent": ["import { ErrorCode, Protocol } from './Protocol';\n\nimport { requestFromIPC, subscribeIPC } from './IPC';\n\nimport { Deferred, generateId, merge, REMOTE_ROOM_SHORT_TIMEOUT, retry } from './utils/Utils';\nimport { isDevMode, cacheRoomHistory, getPreviousProcessId, getRoomRestoreListKey, reloadFromCache } from './utils/DevMode';\n\nimport { RegisteredHandler } from './matchmaker/RegisteredHandler';\nimport { Room, RoomInternalState } from './Room';\n\nimport { LocalPresence } from './presence/LocalPresence';\nimport { Presence } from './presence/Presence';\n\nimport { debugAndPrintError, debugMatchMaking } from './Debug';\nimport { SeatReservationError } from './errors/SeatReservationError';\nimport { ServerError } from './errors/ServerError';\n\nimport { IRoomListingData, RoomListingData, LocalDriver, MatchMakerDriver } from './matchmaker/driver';\nimport controller from './matchmaker/controller';\nimport * as stats from \"./Stats\";\n\nimport { logger } from './Logger';\nimport { Client } from './Transport';\nimport { Type } from './utils/types';\nimport { getHostname } from \"./discovery\";\n\nexport { controller, stats, type MatchMakerDriver };\n\nexport type ClientOptions = any;\nexport type AuthOptions = { token?: string, request?: any };\nexport type SelectProcessIdCallback = (roomName: string, clientOptions: ClientOptions) => Promise<string>;\n\nexport interface SeatReservation {\n sessionId: string;\n room: RoomListingData;\n devMode?: boolean;\n}\n\nconst handlers: {[id: string]: RegisteredHandler} = {};\nconst rooms: {[roomId: string]: Room} = {};\n\nexport let publicAddress: string;\nexport let processId: string;\nexport let presence: Presence;\nexport let driver: MatchMakerDriver;\nexport let selectProcessIdToCreateRoom: SelectProcessIdCallback;\n\nexport let isGracefullyShuttingDown: boolean;\nexport let onReady: Deferred = new Deferred(); // onReady needs to be immediately available to @colyseus/auth integration.\n\n/**\n * @private\n */\nexport async function setup(\n _presence?: Presence,\n _driver?: MatchMakerDriver,\n _publicAddress?: string,\n _selectProcessIdToCreateRoom?: SelectProcessIdCallback,\n) {\n if (onReady === undefined) {\n //\n // for testing purposes only: onReady is turned into undefined on shutdown\n // (needs refactoring.)\n //\n onReady = new Deferred();\n }\n\n presence = _presence || new LocalPresence();\n\n driver = _driver || new LocalDriver();\n publicAddress = _publicAddress;\n\n stats.reset(false);\n\n // devMode: try to retrieve previous processId\n if (isDevMode) { processId = await getPreviousProcessId(await getHostname()); }\n\n // ensure processId is set\n if (!processId) { processId = generateId(); }\n\n isGracefullyShuttingDown = false;\n\n /**\n * Define default `assignRoomToProcessId` method.\n * By default, return the process with least amount of rooms created\n */\n selectProcessIdToCreateRoom = _selectProcessIdToCreateRoom || async function () {\n return (await stats.fetchAll())\n .sort((p1, p2) => p1.roomCount > p2.roomCount ? 1 : -1)[0]?.processId || processId;\n };\n\n onReady.resolve();\n}\n\n/**\n * - Accept receiving remote room creation requests\n * - Check for leftover/invalid processId's on startup\n * @private\n */\nexport async function accept() {\n await onReady; // make sure \"processId\" is available\n\n /**\n * Process-level subscription\n * - handle remote process healthcheck\n * - handle remote room creation\n */\n await subscribeIPC(presence, processId, getProcessChannel(), (method, args) => {\n if (method === 'healthcheck') {\n // health check for this processId\n return true;\n\n } else {\n // handle room creation\n return handleCreateRoom.apply(undefined, args);\n }\n });\n\n /**\n * Check for leftover/invalid processId's on startup\n */\n const previousStats = await stats.fetchAll();\n if (previousStats.length > 0) {\n logger.debug(`${previousStats.length} previous processId(s) found, health-checking...`);\n await Promise.all(previousStats.map(async (stat) => {\n try {\n await requestFromIPC<RoomListingData>(\n presence,\n getProcessChannel(stat.processId),\n 'healthcheck',\n [],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n // process succeeded to respond - nothing to do\n } catch (e) {\n // process failed to respond - remove it from stats\n logger.debug(`process ${stat.processId} failed to respond. excluding from stats`);\n const isProcessExcluded = await stats.excludeProcess(stat.processId);\n\n // clean-up possibly stale room ids\n if (isProcessExcluded && !isDevMode) {\n await removeRoomsByProcessId(stat.processId);\n }\n }\n }));\n }\n\n await stats.persist();\n\n if (isDevMode) {\n await reloadFromCache();\n }\n}\n\n/**\n * Join or create into a room and return seat reservation\n */\nexport async function joinOrCreate(roomName: string, clientOptions: ClientOptions = {}, authOptions?: AuthOptions) {\n return await retry<Promise<SeatReservation>>(async () => {\n const authData = await callOnAuth(roomName, authOptions);\n let room = await findOneRoomAvailable(roomName, clientOptions);\n\n //\n // TODO [?]\n // should we expose the \"creator\" auth data of the room during `onCreate()`?\n // it would be useful, though it could be accessed via `onJoin()` for now.\n //\n\n if (!room) {\n room = await createRoom(roomName, clientOptions);\n }\n\n return await reserveSeatFor(room, clientOptions, authData);\n }, 5, [SeatReservationError]);\n}\n\n/**\n * Create a room and return seat reservation\n */\nexport async function create(roomName: string, clientOptions: ClientOptions = {}, authOptions?: AuthOptions) {\n const authData = await callOnAuth(roomName, authOptions);\n const room = await createRoom(roomName, clientOptions);\n return reserveSeatFor(room, clientOptions, authData);\n}\n\n/**\n * Join a room and return seat reservation\n */\nexport async function join(roomName: string, clientOptions: ClientOptions = {}, authOptions?: AuthOptions) {\n return await retry<Promise<SeatReservation>>(async () => {\n const authData = await callOnAuth(roomName, authOptions);\n const room = await findOneRoomAvailable(roomName, clientOptions);\n\n if (!room) {\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_CRITERIA, `no rooms found with provided criteria`);\n }\n\n return reserveSeatFor(room, clientOptions, authData);\n });\n}\n\n/**\n * Join a room by id and return seat reservation\n */\nexport async function reconnect(roomId: string, clientOptions: ClientOptions = {}) {\n const room = await driver.findOne({ roomId });\n if (!room) {\n // TODO: support a \"logLevel\" out of the box?\n if (process.env.NODE_ENV !== 'production') {\n logger.info(`\u274C room \"${roomId}\" has been disposed. Did you missed .allowReconnection()?\\n\uD83D\uDC49 https://docs.colyseus.io/server/room/#allowreconnection-client-seconds`);\n }\n\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room \"${roomId}\" has been disposed.`);\n }\n\n // check for reconnection\n const reconnectionToken = clientOptions.reconnectionToken;\n if (!reconnectionToken) { throw new ServerError(ErrorCode.MATCHMAKE_UNHANDLED, `'reconnectionToken' must be provided for reconnection.`); }\n\n\n // respond to re-connection!\n const sessionId = await remoteRoomCall(room.roomId, 'checkReconnectionToken', [reconnectionToken]);\n if (sessionId) {\n return { room, sessionId };\n\n } else {\n // TODO: support a \"logLevel\" out of the box?\n if (process.env.NODE_ENV !== 'production') {\n logger.info(`\u274C reconnection token invalid or expired. Did you missed .allowReconnection()?\\n\uD83D\uDC49 https://docs.colyseus.io/server/room/#allowreconnection-client-seconds`);\n }\n throw new ServerError(ErrorCode.MATCHMAKE_EXPIRED, `reconnection token invalid or expired.`);\n }\n}\n\n/**\n * Join a room by id and return client seat reservation. An exception is thrown if a room is not found for roomId.\n *\n * @param roomId - The Id of the specific room instance.\n * @param clientOptions - Options for the client seat reservation (for `onJoin`/`onAuth`)\n * @param authOptions - Optional authentication token\n *\n * @returns Promise<SeatReservation> - A promise which contains `sessionId` and `RoomListingData`.\n */\nexport async function joinById(roomId: string, clientOptions: ClientOptions = {}, authOptions?: AuthOptions) {\n const room = await driver.findOne({ roomId });\n\n if (!room) {\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room \"${roomId}\" not found`);\n\n } else if (room.locked) {\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room \"${roomId}\" is locked`);\n }\n\n const authData = await callOnAuth(room.name, authOptions);\n\n return reserveSeatFor(room, clientOptions, authData);\n}\n\n/**\n * Perform a query for all cached rooms\n */\nexport async function query(conditions: Partial<IRoomListingData> = {}) {\n return await driver.find(conditions);\n}\n\n/**\n * Find for a public and unlocked room available.\n *\n * @param roomName - The Id of the specific room.\n * @param clientOptions - Options for the client seat reservation (for `onJoin`/`onAuth`).\n *\n * @returns Promise<RoomListingData> - A promise contaning an object which includes room metadata and configurations.\n */\nexport async function findOneRoomAvailable(roomName: string, clientOptions: ClientOptions): Promise<RoomListingData> {\n return await awaitRoomAvailable(roomName, async () => {\n const handler = getHandler(roomName);\n\n const roomQuery = driver.findOne({\n locked: false,\n name: roomName,\n private: false,\n ...handler.getFilterOptions(clientOptions),\n });\n\n if (handler.sortOptions) {\n roomQuery.sort(handler.sortOptions);\n }\n\n return await roomQuery;\n });\n}\n\n/**\n * Call a method or return a property on a remote room.\n *\n * @param roomId - The Id of the specific room instance.\n * @param method - Method or attribute to call or retrive.\n * @param args - Array of arguments for the method\n *\n * @returns Promise<any> - Returned value from the called or retrieved method/attribute.\n */\nexport async function remoteRoomCall<R= any>(\n roomId: string,\n method: string,\n args?: any[],\n rejectionTimeout = REMOTE_ROOM_SHORT_TIMEOUT,\n): Promise<R> {\n const room = rooms[roomId];\n\n if (!room) {\n try {\n return await requestFromIPC<R>(presence, getRoomChannel(roomId), method, args, rejectionTimeout);\n\n } catch (e) {\n const request = `${method}${args && ' with args ' + JSON.stringify(args) || ''}`;\n throw new ServerError(\n ErrorCode.MATCHMAKE_UNHANDLED,\n `remote room (${roomId}) timed out, requesting \"${request}\". (${rejectionTimeout}ms exceeded)`,\n );\n }\n\n } else {\n return (!args && typeof (room[method]) !== 'function')\n ? room[method]\n : (await room[method].apply(room, args && JSON.parse(JSON.stringify(args))));\n }\n}\n\nexport function defineRoomType<T extends Type<Room>>(\n roomName: string,\n klass: T,\n defaultOptions?: Parameters<NonNullable<InstanceType<T>['onCreate']>>[0],\n) {\n const registeredHandler = new RegisteredHandler(klass, defaultOptions);\n\n handlers[roomName] = registeredHandler;\n\n if (klass.prototype['onAuth'] !== Room.prototype['onAuth']) {\n // TODO: soft-deprecate instance level `onAuth` on 0.16\n // logger.warn(\"DEPRECATION WARNING: onAuth() at the instance level will be deprecated soon. Please use static onAuth() instead.\");\n\n if (klass['onAuth'] !== Room['onAuth']) {\n logger.info(`\u274C \"${roomName}\"'s onAuth() defined at the instance level will be ignored.`);\n }\n }\n\n if (!isDevMode) {\n cleanupStaleRooms(roomName);\n }\n\n return registeredHandler;\n}\n\nexport function removeRoomType(roomName: string) {\n delete handlers[roomName];\n\n if (!isDevMode) {\n cleanupStaleRooms(roomName);\n }\n}\n\n// TODO: legacy; remove me on 1.0\nexport function hasHandler(roomName: string) {\n logger.warn(\"hasHandler() is deprecated. Use getHandler() instead.\");\n return handlers[roomName] !== undefined;\n}\n\nexport function getHandler(roomName: string) {\n const handler = handlers[roomName];\n\n if (!handler) {\n throw new ServerError(ErrorCode.MATCHMAKE_NO_HANDLER, `provided room name \"${roomName}\" not defined`);\n }\n\n return handler;\n}\n\nexport function getRoomClass(roomName: string): Type<Room> {\n return handlers[roomName]?.klass;\n}\n\n\n/**\n * Creates a new room.\n *\n * @param roomName - The identifier you defined on `gameServer.define()`\n * @param clientOptions - Options for `onCreate`\n *\n * @returns Promise<RoomListingData> - A promise contaning an object which includes room metadata and configurations.\n */\nexport async function createRoom(roomName: string, clientOptions: ClientOptions): Promise<RoomListingData> {\n const selectedProcessId = (selectProcessIdToCreateRoom !== undefined)\n ? await selectProcessIdToCreateRoom(roomName, clientOptions)\n : processId;\n\n let room: RoomListingData;\n if (selectedProcessId === undefined) {\n throw new ServerError(ErrorCode.MATCHMAKE_UNHANDLED, `no processId available to create room ${roomName}`);\n\n } else if (selectedProcessId === processId) {\n // create the room on this process!\n room = await handleCreateRoom(roomName, clientOptions);\n\n } else {\n // ask other process to create the room!\n try {\n room = await requestFromIPC<RoomListingData>(\n presence,\n getProcessChannel(selectedProcessId),\n undefined,\n [roomName, clientOptions],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n\n } catch (e) {\n if (e.message === \"ipc_timeout\") {\n debugAndPrintError(`${e.message}: create room request timed out for ${roomName} on processId ${selectedProcessId}.`);\n\n //\n // clean-up possibly stale process from redis.\n // when a process disconnects ungracefully, it may leave its previous processId under \"roomcount\"\n // if the process is still alive, it will re-add itself shortly after the load-balancer selects it again.\n //\n await stats.excludeProcess(selectedProcessId);\n\n // if other process failed to respond, create the room on this process\n room = await handleCreateRoom(roomName, clientOptions);\n\n } else {\n // re-throw intentional exception thrown during remote onCreate()\n throw e;\n }\n }\n }\n\n if (isDevMode) {\n presence.hset(getRoomRestoreListKey(), room.roomId, JSON.stringify({\n \"clientOptions\": clientOptions,\n \"roomName\": roomName,\n \"processId\": processId\n }));\n }\n\n return room;\n}\n\nexport async function handleCreateRoom(roomName: string, clientOptions: ClientOptions, restoringRoomId?: string): Promise<RoomListingData> {\n const handler = getHandler(roomName);\n const room = new handler.klass();\n\n // set room public attributes\n if (restoringRoomId && isDevMode) {\n room.roomId = restoringRoomId;\n\n } else {\n room.roomId = generateId();\n }\n\n room.roomName = roomName;\n room.presence = presence;\n\n const additionalListingData: any = handler.getFilterOptions(clientOptions);\n\n // assign public host\n if (publicAddress) {\n additionalListingData.publicAddress = publicAddress;\n }\n\n // create a RoomCache reference.\n room.listing = driver.createInstance({\n name: roomName,\n processId,\n ...additionalListingData\n });\n\n if (room.onCreate) {\n try {\n await room.onCreate(merge({}, clientOptions, handler.options));\n\n } catch (e) {\n debugAndPrintError(e);\n throw new ServerError(\n e.code || ErrorCode.MATCHMAKE_UNHANDLED,\n e.message,\n );\n }\n }\n\n room['_internalState'] = RoomInternalState.CREATED;\n\n room.listing.roomId = room.roomId;\n room.listing.maxClients = room.maxClients;\n\n // imediatelly ask client to join the room\n debugMatchMaking('spawning \\'%s\\', roomId: %s, processId: %s', roomName, room.roomId, processId);\n\n // increment amount of rooms this process is handling\n stats.local.roomCount++;\n stats.persist();\n\n room._events.on('lock', lockRoom.bind(this, room));\n room._events.on('unlock', unlockRoom.bind(this, room));\n room._events.on('join', onClientJoinRoom.bind(this, room));\n room._events.on('leave', onClientLeaveRoom.bind(this, room));\n room._events.on('visibility-change', onVisibilityChange.bind(this, room));\n room._events.once('dispose', disposeRoom.bind(this, roomName, room));\n\n // when disconnect()'ing, keep only join/leave events for stat counting\n room._events.once('disconnect', () => {\n room._events.removeAllListeners('lock');\n room._events.removeAllListeners('unlock');\n room._events.removeAllListeners('visibility-change');\n room._events.removeAllListeners('dispose');\n });\n\n // room always start unlocked\n await createRoomReferences(room, true);\n await room.listing.save();\n\n handler.emit('create', room);\n\n return room.listing;\n}\n\nexport function getRoomById(roomId: string) {\n return rooms[roomId];\n}\n\n/**\n * Disconnects every client on every room in the current process.\n */\nexport function disconnectAll(closeCode?: number) {\n const promises: Array<Promise<any>> = [];\n\n for (const roomId in rooms) {\n if (!rooms.hasOwnProperty(roomId)) {\n continue;\n }\n\n const room = rooms[roomId];\n\n // prevent touching stats when process is shutting down\n room._events.removeAllListeners(\"leave\");\n\n promises.push(room.disconnect(closeCode));\n }\n\n return promises;\n}\n\nexport async function gracefullyShutdown(): Promise<any> {\n if (isGracefullyShuttingDown) {\n return Promise.reject('already_shutting_down');\n }\n\n isGracefullyShuttingDown = true;\n onReady = undefined;\n\n debugMatchMaking(`${processId} is shutting down!`);\n\n if (isDevMode) {\n await cacheRoomHistory(rooms);\n }\n\n // remove processId from room count key\n await stats.excludeProcess(processId);\n\n // remove cached rooms of this process\n await removeRoomsByProcessId(processId);\n\n // unsubscribe from process id channel\n presence.unsubscribe(getProcessChannel());\n\n return Promise.all(disconnectAll(\n (isDevMode)\n ? Protocol.WS_CLOSE_DEVMODE_RESTART\n : undefined\n ));\n}\n\n/**\n * Reserve a seat for a client in a room\n */\nexport async function reserveSeatFor(room: RoomListingData, options: ClientOptions, authData?: any) {\n const sessionId: string = generateId();\n\n debugMatchMaking(\n 'reserving seat. sessionId: \\'%s\\', roomId: \\'%s\\', processId: \\'%s\\'',\n sessionId, room.roomId, processId,\n );\n\n let successfulSeatReservation: boolean;\n\n try {\n successfulSeatReservation = await remoteRoomCall(room.roomId, '_reserveSeat', [sessionId, options, authData]);\n\n } catch (e) {\n debugMatchMaking(e);\n throw e;\n }\n\n if (!successfulSeatReservation) {\n throw new SeatReservationError(`${room.roomId} is already full.`);\n }\n\n const response: SeatReservation = { room, sessionId };\n\n if (isDevMode) {\n response.devMode = isDevMode;\n }\n\n return response;\n}\n\nfunction callOnAuth(roomName: string, authOptions?: AuthOptions) {\n const roomClass = getRoomClass(roomName);\n return (roomClass && roomClass['onAuth'] && roomClass['onAuth'] !== Room['onAuth'])\n ? roomClass['onAuth'](authOptions.token, authOptions.request)\n : undefined;\n}\n\nexport async function cleanupStaleRooms(roomName: string) {\n // remove connecting counts\n await presence.del(getHandlerConcurrencyKey(roomName));\n}\n\nasync function removeRoomsByProcessId(processId: string) {\n //\n // clean-up possibly stale room ids\n // (ungraceful shutdowns using Redis can result on stale room ids still on memory.)\n //\n if (typeof(driver.cleanup) === \"function\") {\n await driver.cleanup(processId);\n\n } else {\n //\n // TODO: remove this block on 1.0.\n //\n // driver.cleanup() has been added mid-way through 0.15\n // some users may still be using older versions of the driver.\n //\n const cachedRooms = await driver.find({ processId }, { _id: 1 });\n logger.debug(\"removing stale rooms by processId:\", processId, `(${cachedRooms.length} rooms found)`);\n cachedRooms.forEach((room) => room.remove());\n }\n}\n\nasync function createRoomReferences(room: Room, init: boolean = false): Promise<boolean> {\n rooms[room.roomId] = room;\n\n if (init) {\n await subscribeIPC(\n presence,\n processId,\n getRoomChannel(room.roomId),\n (method, args) => {\n return (!args && typeof (room[method]) !== 'function')\n ? room[method]\n : room[method].apply(room, args);\n },\n );\n }\n\n return true;\n}\n\nasync function awaitRoomAvailable(roomToJoin: string, callback: Function): Promise<RoomListingData> {\n return new Promise(async (resolve, reject) => {\n const concurrencyKey = getHandlerConcurrencyKey(roomToJoin);\n const concurrency = await presence.incr(concurrencyKey) - 1;\n\n //\n // avoid having too long timeout if 10+ clients ask to join at the same time\n //\n // TODO: we need a better solution here. either a lock or queue system should be implemented instead.\n // https://github.com/colyseus/colyseus/issues/466\n //\n const concurrencyTimeout = Math.min(concurrency * 100, 500);\n\n if (concurrency > 0) {\n debugMatchMaking(\n 'receiving %d concurrent requests for joining \\'%s\\' (waiting %d ms)',\n concurrency, roomToJoin, concurrencyTimeout,\n );\n }\n\n setTimeout(async () => {\n try {\n const result = await callback();\n resolve(result);\n\n } catch (e) {\n reject(e);\n\n } finally {\n await presence.decr(concurrencyKey);\n }\n }, concurrencyTimeout);\n });\n}\n\nfunction onClientJoinRoom(room: Room, client: Client) {\n // increment local CCU\n stats.local.ccu++;\n stats.persist();\n\n handlers[room.roomName].emit('join', room, client);\n}\n\nfunction onClientLeaveRoom(room: Room, client: Client, willDispose: boolean) {\n // decrement local CCU\n stats.local.ccu--;\n stats.persist();\n\n handlers[room.roomName].emit('leave', room, client, willDispose);\n}\n\nfunction lockRoom(room: Room): void {\n // emit public event on registered handler\n handlers[room.roomName].emit('lock', room);\n}\n\nasync function unlockRoom(room: Room) {\n if (await createRoomReferences(room)) {\n // emit public event on registered handler\n handlers[room.roomName].emit('unlock', room);\n }\n}\n\nfunction onVisibilityChange(room: Room, isInvisible: boolean): void {\n handlers[room.roomName].emit('visibility-change', room, isInvisible);\n}\n\nasync function disposeRoom(roomName: string, room: Room) {\n debugMatchMaking('disposing \\'%s\\' (%s) on processId \\'%s\\' (graceful shutdown: %s)', roomName, room.roomId, processId, isGracefullyShuttingDown);\n\n // decrease amount of rooms this process is handling\n if (!isGracefullyShuttingDown) {\n stats.local.roomCount--;\n stats.persist();\n\n // remove from devMode restore list\n if (isDevMode) {\n await presence.hdel(getRoomRestoreListKey(), room.roomId);\n }\n }\n\n // emit disposal on registered session handler\n handlers[roomName].emit('dispose', room);\n\n // remove concurrency key\n presence.del(getHandlerConcurrencyKey(roomName));\n\n // unsubscribe from remote connections\n presence.unsubscribe(getRoomChannel(room.roomId));\n\n // remove actual room reference\n delete rooms[room.roomId];\n}\n\n//\n// Presence keys\n//\nfunction getRoomChannel(roomId: string) {\n return `$${roomId}`;\n}\n\nfunction getHandlerConcurrencyKey(name: string) {\n return `c:${name}`;\n}\n\nfunction getProcessChannel(id: string = processId) {\n return `p:${id}`;\n}\n"],
5
- "mappings": "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",
6
- "names": ["processId"]
4
+ "sourcesContent": ["import { ErrorCode, Protocol } from './Protocol';\n\nimport { requestFromIPC, subscribeIPC } from './IPC';\n\nimport { Deferred, generateId, merge, REMOTE_ROOM_SHORT_TIMEOUT, retry } from './utils/Utils';\nimport { isDevMode, cacheRoomHistory, getPreviousProcessId, getRoomRestoreListKey, reloadFromCache } from './utils/DevMode';\n\nimport { RegisteredHandler } from './matchmaker/RegisteredHandler';\nimport { Room, RoomInternalState } from './Room';\n\nimport { LocalPresence } from './presence/LocalPresence';\nimport { Presence } from './presence/Presence';\n\nimport { debugAndPrintError, debugMatchMaking } from './Debug';\nimport { SeatReservationError } from './errors/SeatReservationError';\nimport { ServerError } from './errors/ServerError';\n\nimport { IRoomListingData, RoomListingData, LocalDriver, MatchMakerDriver } from './matchmaker/driver';\nimport controller from './matchmaker/controller';\nimport * as stats from \"./Stats\";\n\nimport { logger } from './Logger';\nimport { Client } from './Transport';\nimport { Type } from './utils/types';\nimport { getHostname } from \"./discovery\";\n\nexport { controller, stats, type MatchMakerDriver };\n\nexport type ClientOptions = any;\nexport type AuthOptions = { token?: string, request?: any };\nexport type SelectProcessIdCallback = (roomName: string, clientOptions: ClientOptions) => Promise<string>;\n\nexport interface SeatReservation {\n sessionId: string;\n room: RoomListingData;\n devMode?: boolean;\n}\n\nconst handlers: {[id: string]: RegisteredHandler} = {};\nconst rooms: {[roomId: string]: Room} = {};\n\nexport let publicAddress: string;\nexport let processId: string;\nexport let presence: Presence;\nexport let driver: MatchMakerDriver;\nexport let selectProcessIdToCreateRoom: SelectProcessIdCallback;\n\nexport let isGracefullyShuttingDown: boolean; // TODO: remove me on 1.0, use 'state' instead\nexport let onReady: Deferred = new Deferred(); // onReady needs to be immediately available to @colyseus/auth integration.\n\nexport enum MatchMakerState {\n INITIALIZING,\n READY,\n SHUTTING_DOWN\n}\n\n/**\n * Internal MatchMaker state\n */\nexport let state: MatchMakerState;\n\n/**\n * @private\n */\nexport async function setup(\n _presence?: Presence,\n _driver?: MatchMakerDriver,\n _publicAddress?: string,\n _selectProcessIdToCreateRoom?: SelectProcessIdCallback,\n) {\n if (onReady === undefined) {\n //\n // for testing purposes only: onReady is turned into undefined on shutdown\n // (needs refactoring.)\n //\n onReady = new Deferred();\n }\n\n isGracefullyShuttingDown = false;\n state = MatchMakerState.INITIALIZING;\n\n presence = _presence || new LocalPresence();\n\n driver = _driver || new LocalDriver();\n publicAddress = _publicAddress;\n\n stats.reset(false);\n\n // devMode: try to retrieve previous processId\n if (isDevMode) { processId = await getPreviousProcessId(await getHostname()); }\n\n // ensure processId is set\n if (!processId) { processId = generateId(); }\n\n /**\n * Define default `assignRoomToProcessId` method.\n * By default, return the process with least amount of rooms created\n */\n selectProcessIdToCreateRoom = _selectProcessIdToCreateRoom || async function () {\n return (await stats.fetchAll())\n .sort((p1, p2) => p1.roomCount > p2.roomCount ? 1 : -1)[0]?.processId || processId;\n };\n\n onReady.resolve();\n}\n\n/**\n * - Accept receiving remote room creation requests\n * - Check for leftover/invalid processId's on startup\n * @private\n */\nexport async function accept() {\n await onReady; // make sure \"processId\" is available\n\n /**\n * Process-level subscription\n * - handle remote process healthcheck\n * - handle remote room creation\n */\n await subscribeIPC(presence, processId, getProcessChannel(), (method, args) => {\n if (method === 'healthcheck') {\n // health check for this processId\n return true;\n\n } else {\n // handle room creation\n return handleCreateRoom.apply(undefined, args);\n }\n });\n\n /**\n * Check for leftover/invalid processId's on startup\n */\n await healthCheckAllProcesses();\n\n state = MatchMakerState.READY;\n\n await stats.persist();\n\n if (isDevMode) {\n await reloadFromCache();\n }\n}\n\n/**\n * Join or create into a room and return seat reservation\n */\nexport async function joinOrCreate(roomName: string, clientOptions: ClientOptions = {}, authOptions?: AuthOptions) {\n return await retry<Promise<SeatReservation>>(async () => {\n const authData = await callOnAuth(roomName, authOptions);\n let room = await findOneRoomAvailable(roomName, clientOptions);\n\n //\n // TODO [?]\n // should we expose the \"creator\" auth data of the room during `onCreate()`?\n // it would be useful, though it could be accessed via `onJoin()` for now.\n //\n\n if (!room) {\n room = await createRoom(roomName, clientOptions);\n }\n\n return await reserveSeatFor(room, clientOptions, authData);\n }, 5, [SeatReservationError]);\n}\n\n/**\n * Create a room and return seat reservation\n */\nexport async function create(roomName: string, clientOptions: ClientOptions = {}, authOptions?: AuthOptions) {\n const authData = await callOnAuth(roomName, authOptions);\n const room = await createRoom(roomName, clientOptions);\n return reserveSeatFor(room, clientOptions, authData);\n}\n\n/**\n * Join a room and return seat reservation\n */\nexport async function join(roomName: string, clientOptions: ClientOptions = {}, authOptions?: AuthOptions) {\n return await retry<Promise<SeatReservation>>(async () => {\n const authData = await callOnAuth(roomName, authOptions);\n const room = await findOneRoomAvailable(roomName, clientOptions);\n\n if (!room) {\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_CRITERIA, `no rooms found with provided criteria`);\n }\n\n return reserveSeatFor(room, clientOptions, authData);\n });\n}\n\n/**\n * Join a room by id and return seat reservation\n */\nexport async function reconnect(roomId: string, clientOptions: ClientOptions = {}) {\n const room = await driver.findOne({ roomId });\n if (!room) {\n // TODO: support a \"logLevel\" out of the box?\n if (process.env.NODE_ENV !== 'production') {\n logger.info(`\u274C room \"${roomId}\" has been disposed. Did you missed .allowReconnection()?\\n\uD83D\uDC49 https://docs.colyseus.io/server/room/#allowreconnection-client-seconds`);\n }\n\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room \"${roomId}\" has been disposed.`);\n }\n\n // check for reconnection\n const reconnectionToken = clientOptions.reconnectionToken;\n if (!reconnectionToken) { throw new ServerError(ErrorCode.MATCHMAKE_UNHANDLED, `'reconnectionToken' must be provided for reconnection.`); }\n\n\n // respond to re-connection!\n const sessionId = await remoteRoomCall(room.roomId, 'checkReconnectionToken', [reconnectionToken]);\n if (sessionId) {\n return { room, sessionId };\n\n } else {\n // TODO: support a \"logLevel\" out of the box?\n if (process.env.NODE_ENV !== 'production') {\n logger.info(`\u274C reconnection token invalid or expired. Did you missed .allowReconnection()?\\n\uD83D\uDC49 https://docs.colyseus.io/server/room/#allowreconnection-client-seconds`);\n }\n throw new ServerError(ErrorCode.MATCHMAKE_EXPIRED, `reconnection token invalid or expired.`);\n }\n}\n\n/**\n * Join a room by id and return client seat reservation. An exception is thrown if a room is not found for roomId.\n *\n * @param roomId - The Id of the specific room instance.\n * @param clientOptions - Options for the client seat reservation (for `onJoin`/`onAuth`)\n * @param authOptions - Optional authentication token\n *\n * @returns Promise<SeatReservation> - A promise which contains `sessionId` and `RoomListingData`.\n */\nexport async function joinById(roomId: string, clientOptions: ClientOptions = {}, authOptions?: AuthOptions) {\n const room = await driver.findOne({ roomId });\n\n if (!room) {\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room \"${roomId}\" not found`);\n\n } else if (room.locked) {\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room \"${roomId}\" is locked`);\n }\n\n const authData = await callOnAuth(room.name, authOptions);\n\n return reserveSeatFor(room, clientOptions, authData);\n}\n\n/**\n * Perform a query for all cached rooms\n */\nexport async function query(conditions: Partial<IRoomListingData> = {}) {\n return await driver.find(conditions);\n}\n\n/**\n * Find for a public and unlocked room available.\n *\n * @param roomName - The Id of the specific room.\n * @param clientOptions - Options for the client seat reservation (for `onJoin`/`onAuth`).\n *\n * @returns Promise<RoomListingData> - A promise contaning an object which includes room metadata and configurations.\n */\nexport async function findOneRoomAvailable(roomName: string, clientOptions: ClientOptions): Promise<RoomListingData> {\n return await awaitRoomAvailable(roomName, async () => {\n const handler = getHandler(roomName);\n\n const roomQuery = driver.findOne({\n locked: false,\n name: roomName,\n private: false,\n ...handler.getFilterOptions(clientOptions),\n });\n\n if (handler.sortOptions) {\n roomQuery.sort(handler.sortOptions);\n }\n\n return await roomQuery;\n });\n}\n\n/**\n * Call a method or return a property on a remote room.\n *\n * @param roomId - The Id of the specific room instance.\n * @param method - Method or attribute to call or retrive.\n * @param args - Array of arguments for the method\n *\n * @returns Promise<any> - Returned value from the called or retrieved method/attribute.\n */\nexport async function remoteRoomCall<R= any>(\n roomId: string,\n method: string,\n args?: any[],\n rejectionTimeout = REMOTE_ROOM_SHORT_TIMEOUT,\n): Promise<R> {\n const room = rooms[roomId];\n\n if (!room) {\n try {\n return await requestFromIPC<R>(presence, getRoomChannel(roomId), method, args, rejectionTimeout);\n\n } catch (e) {\n\n //\n // the room cache from an unavailable process might've been used here.\n // perform a health-check on the process before proceeding.\n // (this is a broken state when a process wasn't gracefully shut down)\n //\n if (method === '_reserveSeat' && e.message === \"ipc_timeout\") {\n throw e;\n }\n\n // TODO: for 1.0, consider always throwing previous error directly.\n\n const request = `${method}${args && ' with args ' + JSON.stringify(args) || ''}`;\n throw new ServerError(\n ErrorCode.MATCHMAKE_UNHANDLED,\n `remote room (${roomId}) timed out, requesting \"${request}\". (${rejectionTimeout}ms exceeded)`,\n );\n }\n\n } else {\n return (!args && typeof (room[method]) !== 'function')\n ? room[method]\n : (await room[method].apply(room, args && JSON.parse(JSON.stringify(args))));\n }\n}\n\nexport function defineRoomType<T extends Type<Room>>(\n roomName: string,\n klass: T,\n defaultOptions?: Parameters<NonNullable<InstanceType<T>['onCreate']>>[0],\n) {\n const registeredHandler = new RegisteredHandler(klass, defaultOptions);\n\n handlers[roomName] = registeredHandler;\n\n if (klass.prototype['onAuth'] !== Room.prototype['onAuth']) {\n // TODO: soft-deprecate instance level `onAuth` on 0.16\n // logger.warn(\"DEPRECATION WARNING: onAuth() at the instance level will be deprecated soon. Please use static onAuth() instead.\");\n\n if (klass['onAuth'] !== Room['onAuth']) {\n logger.info(`\u274C \"${roomName}\"'s onAuth() defined at the instance level will be ignored.`);\n }\n }\n\n if (!isDevMode) {\n cleanupStaleRooms(roomName);\n }\n\n return registeredHandler;\n}\n\nexport function removeRoomType(roomName: string) {\n delete handlers[roomName];\n\n if (!isDevMode) {\n cleanupStaleRooms(roomName);\n }\n}\n\n// TODO: legacy; remove me on 1.0\nexport function hasHandler(roomName: string) {\n logger.warn(\"hasHandler() is deprecated. Use getHandler() instead.\");\n return handlers[roomName] !== undefined;\n}\n\nexport function getHandler(roomName: string) {\n const handler = handlers[roomName];\n\n if (!handler) {\n throw new ServerError(ErrorCode.MATCHMAKE_NO_HANDLER, `provided room name \"${roomName}\" not defined`);\n }\n\n return handler;\n}\n\nexport function getRoomClass(roomName: string): Type<Room> {\n return handlers[roomName]?.klass;\n}\n\n\n/**\n * Creates a new room.\n *\n * @param roomName - The identifier you defined on `gameServer.define()`\n * @param clientOptions - Options for `onCreate`\n *\n * @returns Promise<RoomListingData> - A promise contaning an object which includes room metadata and configurations.\n */\nexport async function createRoom(roomName: string, clientOptions: ClientOptions): Promise<RoomListingData> {\n //\n // - select a process to create the room\n // - use local processId if MatchMaker is not ready yet\n //\n const selectedProcessId = (state === MatchMakerState.READY)\n ? await selectProcessIdToCreateRoom(roomName, clientOptions)\n : processId;\n\n let room: RoomListingData;\n if (selectedProcessId === undefined) {\n throw new ServerError(ErrorCode.MATCHMAKE_UNHANDLED, `no processId available to create room ${roomName}`);\n\n } else if (selectedProcessId === processId) {\n // create the room on this process!\n room = await handleCreateRoom(roomName, clientOptions);\n\n } else {\n // ask other process to create the room!\n try {\n room = await requestFromIPC<RoomListingData>(\n presence,\n getProcessChannel(selectedProcessId),\n undefined,\n [roomName, clientOptions],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n\n } catch (e) {\n if (e.message === \"ipc_timeout\") {\n debugAndPrintError(`${e.message}: create room request timed out for ${roomName} on processId ${selectedProcessId}.`);\n\n //\n // clean-up possibly stale process from redis.\n // when a process disconnects ungracefully, it may leave its previous processId under \"roomcount\"\n // if the process is still alive, it will re-add itself shortly after the load-balancer selects it again.\n //\n await stats.excludeProcess(selectedProcessId);\n\n // if other process failed to respond, create the room on this process\n room = await handleCreateRoom(roomName, clientOptions);\n\n } else {\n // re-throw intentional exception thrown during remote onCreate()\n throw e;\n }\n }\n }\n\n if (isDevMode) {\n presence.hset(getRoomRestoreListKey(), room.roomId, JSON.stringify({\n \"clientOptions\": clientOptions,\n \"roomName\": roomName,\n \"processId\": processId\n }));\n }\n\n return room;\n}\n\nexport async function handleCreateRoom(roomName: string, clientOptions: ClientOptions, restoringRoomId?: string): Promise<RoomListingData> {\n const handler = getHandler(roomName);\n const room = new handler.klass();\n\n // set room public attributes\n if (restoringRoomId && isDevMode) {\n room.roomId = restoringRoomId;\n\n } else {\n room.roomId = generateId();\n }\n\n room.roomName = roomName;\n room.presence = presence;\n\n const additionalListingData: any = handler.getFilterOptions(clientOptions);\n\n // assign public host\n if (publicAddress) {\n additionalListingData.publicAddress = publicAddress;\n }\n\n // create a RoomCache reference.\n room.listing = driver.createInstance({\n name: roomName,\n processId,\n ...additionalListingData\n });\n\n if (room.onCreate) {\n try {\n await room.onCreate(merge({}, clientOptions, handler.options));\n\n } catch (e) {\n debugAndPrintError(e);\n throw new ServerError(\n e.code || ErrorCode.MATCHMAKE_UNHANDLED,\n e.message,\n );\n }\n }\n\n room['_internalState'] = RoomInternalState.CREATED;\n\n room.listing.roomId = room.roomId;\n room.listing.maxClients = room.maxClients;\n\n // imediatelly ask client to join the room\n debugMatchMaking('spawning \\'%s\\', roomId: %s, processId: %s', roomName, room.roomId, processId);\n\n // increment amount of rooms this process is handling\n stats.local.roomCount++;\n stats.persist();\n\n room._events.on('lock', lockRoom.bind(this, room));\n room._events.on('unlock', unlockRoom.bind(this, room));\n room._events.on('join', onClientJoinRoom.bind(this, room));\n room._events.on('leave', onClientLeaveRoom.bind(this, room));\n room._events.on('visibility-change', onVisibilityChange.bind(this, room));\n room._events.once('dispose', disposeRoom.bind(this, roomName, room));\n\n // when disconnect()'ing, keep only join/leave events for stat counting\n room._events.once('disconnect', () => {\n room._events.removeAllListeners('lock');\n room._events.removeAllListeners('unlock');\n room._events.removeAllListeners('visibility-change');\n room._events.removeAllListeners('dispose');\n });\n\n // room always start unlocked\n await createRoomReferences(room, true);\n await room.listing.save();\n\n handler.emit('create', room);\n\n return room.listing;\n}\n\nexport function getRoomById(roomId: string) {\n return rooms[roomId];\n}\n\n/**\n * Disconnects every client on every room in the current process.\n */\nexport function disconnectAll(closeCode?: number) {\n const promises: Array<Promise<any>> = [];\n\n for (const roomId in rooms) {\n if (!rooms.hasOwnProperty(roomId)) {\n continue;\n }\n\n const room = rooms[roomId];\n\n // prevent touching stats when process is shutting down\n room._events.removeAllListeners(\"leave\");\n\n promises.push(room.disconnect(closeCode));\n }\n\n return promises;\n}\n\nexport async function gracefullyShutdown(): Promise<any> {\n if (isGracefullyShuttingDown) {\n return Promise.reject('already_shutting_down');\n }\n\n isGracefullyShuttingDown = true;\n state = MatchMakerState.SHUTTING_DOWN;\n\n onReady = undefined;\n\n debugMatchMaking(`${processId} is shutting down!`);\n\n if (isDevMode) {\n await cacheRoomHistory(rooms);\n }\n\n // remove processId from room count key\n await stats.excludeProcess(processId);\n\n // remove cached rooms of this process\n await removeRoomsByProcessId(processId);\n\n // unsubscribe from process id channel\n presence.unsubscribe(getProcessChannel());\n\n return Promise.all(disconnectAll(\n (isDevMode)\n ? Protocol.WS_CLOSE_DEVMODE_RESTART\n : undefined\n ));\n}\n\n/**\n * Reserve a seat for a client in a room\n */\nexport async function reserveSeatFor(room: RoomListingData, options: ClientOptions, authData?: any) {\n const sessionId: string = generateId();\n\n debugMatchMaking(\n 'reserving seat. sessionId: \\'%s\\', roomId: \\'%s\\', processId: \\'%s\\'',\n sessionId, room.roomId, processId,\n );\n\n let successfulSeatReservation: boolean;\n\n try {\n successfulSeatReservation = await remoteRoomCall(\n room.roomId,\n '_reserveSeat',\n [sessionId, options, authData],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n\n } catch (e) {\n debugMatchMaking(e);\n\n //\n // the room cache from an unavailable process might've been used here.\n // (this is a broken state when a process wasn't gracefully shut down)\n // perform a health-check on the process before proceeding.\n //\n if (e.message === \"ipc_timeout\" && !(await healthCheckProcessId(room.processId))) {\n throw new SeatReservationError(`process ${room.processId} is not available.`);\n\n } else {\n successfulSeatReservation = false;\n }\n }\n\n if (!successfulSeatReservation) {\n throw new SeatReservationError(`${room.roomId} is already full.`);\n }\n\n const response: SeatReservation = { room, sessionId };\n\n if (isDevMode) {\n response.devMode = isDevMode;\n }\n\n return response;\n}\n\nfunction callOnAuth(roomName: string, authOptions?: AuthOptions) {\n const roomClass = getRoomClass(roomName);\n return (roomClass && roomClass['onAuth'] && roomClass['onAuth'] !== Room['onAuth'])\n ? roomClass['onAuth'](authOptions.token, authOptions.request)\n : undefined;\n}\n\nexport async function cleanupStaleRooms(roomName: string) {\n // remove connecting counts\n await presence.del(getHandlerConcurrencyKey(roomName));\n}\n\n/**\n * Perform health check on all processes\n */\nexport async function healthCheckAllProcesses() {\n const allStats = await stats.fetchAll();\n if (allStats.length > 0) {\n await Promise.all(allStats.map((stat) => healthCheckProcessId(stat.processId)));\n }\n}\n\n/**\n * Perform health check on a remote process\n * @param processId\n */\nconst _healthCheckByProcessId: { [processId: string]: Promise<any> } = {};\nexport function healthCheckProcessId(processId: string) {\n //\n // re-use the same promise if health-check is already in progress\n // (may occur when _reserveSeat() fails multiple times for the same 'processId')\n //\n if (_healthCheckByProcessId[processId] !== undefined) {\n return _healthCheckByProcessId[processId];\n }\n\n _healthCheckByProcessId[processId] = new Promise<boolean>(async (resolve, reject) => {\n logger.debug(`> Performing health-check against processId: '${processId}'...`);\n\n try {\n const requestTime = Date.now();\n\n await requestFromIPC<RoomListingData>(\n presence,\n getProcessChannel(processId),\n 'healthcheck',\n [],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n\n logger.debug(`\u2705 Process '${processId}' successfully responded (${Date.now() - requestTime}ms)`);\n\n // succeeded to respond\n resolve(true)\n\n } catch (e) {\n // process failed to respond - remove it from stats\n logger.debug(`\u274C Process '${processId}' failed to respond. Cleaning it up.`);\n const isProcessExcluded = await stats.excludeProcess(processId);\n\n // clean-up possibly stale room ids\n if (isProcessExcluded && !isDevMode) {\n await removeRoomsByProcessId(processId);\n }\n\n resolve(false);\n } finally {\n delete _healthCheckByProcessId[processId];\n }\n });\n\n return _healthCheckByProcessId[processId];\n}\n\n/**\n * Remove cached rooms by processId\n * @param processId\n */\nasync function removeRoomsByProcessId(processId: string) {\n //\n // clean-up possibly stale room ids\n // (ungraceful shutdowns using Redis can result on stale room ids still on memory.)\n //\n if (typeof(driver.cleanup) === \"function\") {\n await driver.cleanup(processId);\n\n } else {\n //\n // TODO: remove this block on 1.0.\n //\n // driver.cleanup() has been added mid-way through 0.15\n // some users may still be using older versions of the driver.\n //\n const cachedRooms = await driver.find({ processId }, { _id: 1 });\n logger.debug(\"> Removing stale rooms by processId:\", processId, `(${cachedRooms.length} rooms found)`);\n cachedRooms.forEach((room) => room.remove());\n }\n}\n\nasync function createRoomReferences(room: Room, init: boolean = false): Promise<boolean> {\n rooms[room.roomId] = room;\n\n if (init) {\n await subscribeIPC(\n presence,\n processId,\n getRoomChannel(room.roomId),\n (method, args) => {\n return (!args && typeof (room[method]) !== 'function')\n ? room[method]\n : room[method].apply(room, args);\n },\n );\n }\n\n return true;\n}\n\nasync function awaitRoomAvailable(roomToJoin: string, callback: Function): Promise<RoomListingData> {\n return new Promise(async (resolve, reject) => {\n const concurrencyKey = getHandlerConcurrencyKey(roomToJoin);\n const concurrency = await presence.incr(concurrencyKey) - 1;\n\n //\n // avoid having too long timeout if 10+ clients ask to join at the same time\n //\n // TODO: we need a better solution here. either a lock or queue system should be implemented instead.\n // https://github.com/colyseus/colyseus/issues/466\n //\n const concurrencyTimeout = Math.min(concurrency * 100, 500);\n\n if (concurrency > 0) {\n debugMatchMaking(\n 'receiving %d concurrent requests for joining \\'%s\\' (waiting %d ms)',\n concurrency, roomToJoin, concurrencyTimeout,\n );\n }\n\n setTimeout(async () => {\n try {\n const result = await callback();\n resolve(result);\n\n } catch (e) {\n reject(e);\n\n } finally {\n await presence.decr(concurrencyKey);\n }\n }, concurrencyTimeout);\n });\n}\n\nfunction onClientJoinRoom(room: Room, client: Client) {\n // increment local CCU\n stats.local.ccu++;\n stats.persist();\n\n handlers[room.roomName].emit('join', room, client);\n}\n\nfunction onClientLeaveRoom(room: Room, client: Client, willDispose: boolean) {\n // decrement local CCU\n stats.local.ccu--;\n stats.persist();\n\n handlers[room.roomName].emit('leave', room, client, willDispose);\n}\n\nfunction lockRoom(room: Room): void {\n // emit public event on registered handler\n handlers[room.roomName].emit('lock', room);\n}\n\nasync function unlockRoom(room: Room) {\n if (await createRoomReferences(room)) {\n // emit public event on registered handler\n handlers[room.roomName].emit('unlock', room);\n }\n}\n\nfunction onVisibilityChange(room: Room, isInvisible: boolean): void {\n handlers[room.roomName].emit('visibility-change', room, isInvisible);\n}\n\nasync function disposeRoom(roomName: string, room: Room) {\n debugMatchMaking('disposing \\'%s\\' (%s) on processId \\'%s\\' (graceful shutdown: %s)', roomName, room.roomId, processId, isGracefullyShuttingDown);\n\n // decrease amount of rooms this process is handling\n if (!isGracefullyShuttingDown) {\n stats.local.roomCount--;\n stats.persist();\n\n // remove from devMode restore list\n if (isDevMode) {\n await presence.hdel(getRoomRestoreListKey(), room.roomId);\n }\n }\n\n // emit disposal on registered session handler\n handlers[roomName].emit('dispose', room);\n\n // remove concurrency key\n presence.del(getHandlerConcurrencyKey(roomName));\n\n // unsubscribe from remote connections\n presence.unsubscribe(getRoomChannel(room.roomId));\n\n // remove actual room reference\n delete rooms[room.roomId];\n}\n\n//\n// Presence keys\n//\nfunction getRoomChannel(roomId: string) {\n return `$${roomId}`;\n}\n\nfunction getHandlerConcurrencyKey(name: string) {\n return `c:${name}`;\n}\n\nfunction getProcessChannel(id: string = processId) {\n return `p:${id}`;\n}\n"],
5
+ "mappings": "AAAA,SAAS,WAAW,gBAAgB;AAEpC,SAAS,gBAAgB,oBAAoB;AAE7C,SAAS,UAAU,YAAY,OAAO,2BAA2B,aAAa;AAC9E,SAAS,WAAW,kBAAkB,sBAAsB,uBAAuB,uBAAuB;AAE1G,SAAS,yBAAyB;AAClC,SAAS,MAAM,yBAAyB;AAExC,SAAS,qBAAqB;AAG9B,SAAS,oBAAoB,wBAAwB;AACrD,SAAS,4BAA4B;AACrC,SAAS,mBAAmB;AAE5B,SAA4C,mBAAqC;AACjF,OAAO,gBAAgB;AACvB,YAAY,WAAW;AAEvB,SAAS,cAAc;AAGvB,SAAS,mBAAmB;AAc5B,MAAM,WAA8C,CAAC;AACrD,MAAM,QAAkC,CAAC;AAElC,IAAI;AACJ,IAAI;AACJ,IAAI;AACJ,IAAI;AACJ,IAAI;AAEJ,IAAI;AACJ,IAAI,UAAoB,IAAI,SAAS;AAErC,IAAK,kBAAL,kBAAKA,qBAAL;AACL,EAAAA,kCAAA;AACA,EAAAA,kCAAA;AACA,EAAAA,kCAAA;AAHU,SAAAA;AAAA,GAAA;AASL,IAAI;AAKX,eAAsB,MACpB,WACA,SACA,gBACA,8BACA;AACA,MAAI,YAAY,QAAW;AAKzB,cAAU,IAAI,SAAS;AAAA,EACzB;AAEA,6BAA2B;AAC3B,UAAQ;AAER,aAAW,aAAa,IAAI,cAAc;AAE1C,WAAS,WAAW,IAAI,YAAY;AACpC,kBAAgB;AAEhB,QAAM,MAAM,KAAK;AAGjB,MAAI,WAAW;AAAE,gBAAY,MAAM,qBAAqB,MAAM,YAAY,CAAC;AAAA,EAAG;AAG9E,MAAI,CAAC,WAAW;AAAE,gBAAY,WAAW;AAAA,EAAG;AAM5C,gCAA8B,gCAAgC,iBAAkB;AAC9E,YAAQ,MAAM,MAAM,SAAS,GAC1B,KAAK,CAAC,IAAI,OAAO,GAAG,YAAY,GAAG,YAAY,IAAI,EAAE,EAAE,IAAI,aAAa;AAAA,EAC7E;AAEA,UAAQ,QAAQ;AAClB;AAOA,eAAsB,SAAS;AAC7B,QAAM;AAON,QAAM,aAAa,UAAU,WAAW,kBAAkB,GAAG,CAAC,QAAQ,SAAS;AAC7E,QAAI,WAAW,eAAe;AAE5B,aAAO;AAAA,IAET,OAAO;AAEL,aAAO,iBAAiB,MAAM,QAAW,IAAI;AAAA,IAC/C;AAAA,EACF,CAAC;AAKD,QAAM,wBAAwB;AAE9B,UAAQ;AAER,QAAM,MAAM,QAAQ;AAEpB,MAAI,WAAW;AACb,UAAM,gBAAgB;AAAA,EACxB;AACF;AAKA,eAAsB,aAAa,UAAkB,gBAA+B,CAAC,GAAG,aAA2B;AACjH,SAAO,MAAM,MAAgC,YAAY;AACvD,UAAM,WAAW,MAAM,WAAW,UAAU,WAAW;AACvD,QAAI,OAAO,MAAM,qBAAqB,UAAU,aAAa;AAQ7D,QAAI,CAAC,MAAM;AACT,aAAO,MAAM,WAAW,UAAU,aAAa;AAAA,IACjD;AAEA,WAAO,MAAM,eAAe,MAAM,eAAe,QAAQ;AAAA,EAC3D,GAAG,GAAG,CAAC,oBAAoB,CAAC;AAC9B;AAKA,eAAsB,OAAO,UAAkB,gBAA+B,CAAC,GAAG,aAA2B;AAC3G,QAAM,WAAW,MAAM,WAAW,UAAU,WAAW;AACvD,QAAM,OAAO,MAAM,WAAW,UAAU,aAAa;AACrD,SAAO,eAAe,MAAM,eAAe,QAAQ;AACrD;AAKA,eAAsB,KAAK,UAAkB,gBAA+B,CAAC,GAAG,aAA2B;AACzG,SAAO,MAAM,MAAgC,YAAY;AACvD,UAAM,WAAW,MAAM,WAAW,UAAU,WAAW;AACvD,UAAM,OAAO,MAAM,qBAAqB,UAAU,aAAa;AAE/D,QAAI,CAAC,MAAM;AACT,YAAM,IAAI,YAAY,UAAU,4BAA4B,uCAAuC;AAAA,IACrG;AAEA,WAAO,eAAe,MAAM,eAAe,QAAQ;AAAA,EACrD,CAAC;AACH;AAKA,eAAsB,UAAU,QAAgB,gBAA+B,CAAC,GAAG;AACjF,QAAM,OAAO,MAAM,OAAO,QAAQ,EAAE,OAAO,CAAC;AAC5C,MAAI,CAAC,MAAM;AAET,QAAI,QAAQ,IAAI,aAAa,cAAc;AACzC,aAAO,KAAK,gBAAW;AAAA,iFAA4I;AAAA,IACrK;AAEA,UAAM,IAAI,YAAY,UAAU,2BAA2B,SAAS,4BAA4B;AAAA,EAClG;AAGA,QAAM,oBAAoB,cAAc;AACxC,MAAI,CAAC,mBAAmB;AAAE,UAAM,IAAI,YAAY,UAAU,qBAAqB,wDAAwD;AAAA,EAAG;AAI1I,QAAM,YAAY,MAAM,eAAe,KAAK,QAAQ,0BAA0B,CAAC,iBAAiB,CAAC;AACjG,MAAI,WAAW;AACb,WAAO,EAAE,MAAM,UAAU;AAAA,EAE3B,OAAO;AAEL,QAAI,QAAQ,IAAI,aAAa,cAAc;AACzC,aAAO,KAAK;AAAA,iFAA0J;AAAA,IACxK;AACA,UAAM,IAAI,YAAY,UAAU,mBAAmB,wCAAwC;AAAA,EAC7F;AACF;AAWA,eAAsB,SAAS,QAAgB,gBAA+B,CAAC,GAAG,aAA2B;AAC3G,QAAM,OAAO,MAAM,OAAO,QAAQ,EAAE,OAAO,CAAC;AAE5C,MAAI,CAAC,MAAM;AACT,UAAM,IAAI,YAAY,UAAU,2BAA2B,SAAS,mBAAmB;AAAA,EAEzF,WAAW,KAAK,QAAQ;AACtB,UAAM,IAAI,YAAY,UAAU,2BAA2B,SAAS,mBAAmB;AAAA,EACzF;AAEA,QAAM,WAAW,MAAM,WAAW,KAAK,MAAM,WAAW;AAExD,SAAO,eAAe,MAAM,eAAe,QAAQ;AACrD;AAKA,eAAsB,MAAM,aAAwC,CAAC,GAAG;AACtE,SAAO,MAAM,OAAO,KAAK,UAAU;AACrC;AAUA,eAAsB,qBAAqB,UAAkB,eAAwD;AACnH,SAAO,MAAM,mBAAmB,UAAU,YAAY;AACpD,UAAM,UAAU,WAAW,QAAQ;AAEnC,UAAM,YAAY,OAAO,QAAQ;AAAA,MAC/B,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,SAAS;AAAA,MACT,GAAG,QAAQ,iBAAiB,aAAa;AAAA,IAC3C,CAAC;AAED,QAAI,QAAQ,aAAa;AACvB,gBAAU,KAAK,QAAQ,WAAW;AAAA,IACpC;AAEA,WAAO,MAAM;AAAA,EACf,CAAC;AACH;AAWA,eAAsB,eACpB,QACA,QACA,MACA,mBAAmB,2BACP;AACZ,QAAM,OAAO,MAAM;AAEnB,MAAI,CAAC,MAAM;AACT,QAAI;AACF,aAAO,MAAM,eAAkB,UAAU,eAAe,MAAM,GAAG,QAAQ,MAAM,gBAAgB;AAAA,IAEjG,SAAS,GAAP;AAOA,UAAI,WAAW,kBAAkB,EAAE,YAAY,eAAe;AAC5D,cAAM;AAAA,MACR;AAIA,YAAM,UAAU,GAAG,SAAS,QAAQ,gBAAgB,KAAK,UAAU,IAAI,KAAK;AAC5E,YAAM,IAAI;AAAA,QACR,UAAU;AAAA,QACV,gBAAgB,kCAAkC,cAAc;AAAA,MAClE;AAAA,IACF;AAAA,EAEF,OAAO;AACL,WAAQ,CAAC,QAAQ,OAAQ,KAAK,YAAa,aACrC,KAAK,UACJ,MAAM,KAAK,QAAQ,MAAM,MAAM,QAAQ,KAAK,MAAM,KAAK,UAAU,IAAI,CAAC,CAAC;AAAA,EAChF;AACF;AAEO,SAAS,eACd,UACA,OACA,gBACA;AACA,QAAM,oBAAoB,IAAI,kBAAkB,OAAO,cAAc;AAErE,WAAS,YAAY;AAErB,MAAI,MAAM,UAAU,cAAc,KAAK,UAAU,WAAW;AAI1D,QAAI,MAAM,cAAc,KAAK,WAAW;AACtC,aAAO,KAAK,WAAM,qEAAqE;AAAA,IACzF;AAAA,EACF;AAEA,MAAI,CAAC,WAAW;AACd,sBAAkB,QAAQ;AAAA,EAC5B;AAEA,SAAO;AACT;AAEO,SAAS,eAAe,UAAkB;AAC/C,SAAO,SAAS;AAEhB,MAAI,CAAC,WAAW;AACd,sBAAkB,QAAQ;AAAA,EAC5B;AACF;AAGO,SAAS,WAAW,UAAkB;AAC3C,SAAO,KAAK,uDAAuD;AACnE,SAAO,SAAS,cAAc;AAChC;AAEO,SAAS,WAAW,UAAkB;AAC3C,QAAM,UAAU,SAAS;AAEzB,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,YAAY,UAAU,sBAAsB,uBAAuB,uBAAuB;AAAA,EACtG;AAEA,SAAO;AACT;AAEO,SAAS,aAAa,UAA8B;AACzD,SAAO,SAAS,WAAW;AAC7B;AAWA,eAAsB,WAAW,UAAkB,eAAwD;AAKzG,QAAM,oBAAqB,UAAU,gBACjC,MAAM,4BAA4B,UAAU,aAAa,IACzD;AAEJ,MAAI;AACJ,MAAI,sBAAsB,QAAW;AACnC,UAAM,IAAI,YAAY,UAAU,qBAAqB,yCAAyC,UAAU;AAAA,EAE1G,WAAW,sBAAsB,WAAW;AAE1C,WAAO,MAAM,iBAAiB,UAAU,aAAa;AAAA,EAEvD,OAAO;AAEL,QAAI;AACF,aAAO,MAAM;AAAA,QACX;AAAA,QACA,kBAAkB,iBAAiB;AAAA,QACnC;AAAA,QACA,CAAC,UAAU,aAAa;AAAA,QACxB;AAAA,MACF;AAAA,IAEF,SAAS,GAAP;AACA,UAAI,EAAE,YAAY,eAAe;AAC/B,2BAAmB,GAAG,EAAE,8CAA8C,yBAAyB,oBAAoB;AAOnH,cAAM,MAAM,eAAe,iBAAiB;AAG5C,eAAO,MAAM,iBAAiB,UAAU,aAAa;AAAA,MAEvD,OAAO;AAEL,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,MAAI,WAAW;AACb,aAAS,KAAK,sBAAsB,GAAG,KAAK,QAAQ,KAAK,UAAU;AAAA,MACjE,iBAAiB;AAAA,MACjB,YAAY;AAAA,MACZ,aAAa;AAAA,IACf,CAAC,CAAC;AAAA,EACJ;AAEA,SAAO;AACT;AAEA,eAAsB,iBAAiB,UAAkB,eAA8B,iBAAoD;AACzI,QAAM,UAAU,WAAW,QAAQ;AACnC,QAAM,OAAO,IAAI,QAAQ,MAAM;AAG/B,MAAI,mBAAmB,WAAW;AAChC,SAAK,SAAS;AAAA,EAEhB,OAAO;AACL,SAAK,SAAS,WAAW;AAAA,EAC3B;AAEA,OAAK,WAAW;AAChB,OAAK,WAAW;AAEhB,QAAM,wBAA6B,QAAQ,iBAAiB,aAAa;AAGzE,MAAI,eAAe;AACjB,0BAAsB,gBAAgB;AAAA,EACxC;AAGA,OAAK,UAAU,OAAO,eAAe;AAAA,IACnC,MAAM;AAAA,IACN;AAAA,IACA,GAAG;AAAA,EACL,CAAC;AAED,MAAI,KAAK,UAAU;AACjB,QAAI;AACF,YAAM,KAAK,SAAS,MAAM,CAAC,GAAG,eAAe,QAAQ,OAAO,CAAC;AAAA,IAE/D,SAAS,GAAP;AACA,yBAAmB,CAAC;AACpB,YAAM,IAAI;AAAA,QACR,EAAE,QAAQ,UAAU;AAAA,QACpB,EAAE;AAAA,MACJ;AAAA,IACF;AAAA,EACF;AAEA,OAAK,oBAAoB,kBAAkB;AAE3C,OAAK,QAAQ,SAAS,KAAK;AAC3B,OAAK,QAAQ,aAAa,KAAK;AAG/B,mBAAiB,4CAA8C,UAAU,KAAK,QAAQ,SAAS;AAG/F,QAAM,MAAM;AACZ,QAAM,QAAQ;AAEd,OAAK,QAAQ,GAAG,QAAQ,SAAS,KAAK,MAAM,IAAI,CAAC;AACjD,OAAK,QAAQ,GAAG,UAAU,WAAW,KAAK,MAAM,IAAI,CAAC;AACrD,OAAK,QAAQ,GAAG,QAAQ,iBAAiB,KAAK,MAAM,IAAI,CAAC;AACzD,OAAK,QAAQ,GAAG,SAAS,kBAAkB,KAAK,MAAM,IAAI,CAAC;AAC3D,OAAK,QAAQ,GAAG,qBAAqB,mBAAmB,KAAK,MAAM,IAAI,CAAC;AACxE,OAAK,QAAQ,KAAK,WAAW,YAAY,KAAK,MAAM,UAAU,IAAI,CAAC;AAGnE,OAAK,QAAQ,KAAK,cAAc,MAAM;AACpC,SAAK,QAAQ,mBAAmB,MAAM;AACtC,SAAK,QAAQ,mBAAmB,QAAQ;AACxC,SAAK,QAAQ,mBAAmB,mBAAmB;AACnD,SAAK,QAAQ,mBAAmB,SAAS;AAAA,EAC3C,CAAC;AAGD,QAAM,qBAAqB,MAAM,IAAI;AACrC,QAAM,KAAK,QAAQ,KAAK;AAExB,UAAQ,KAAK,UAAU,IAAI;AAE3B,SAAO,KAAK;AACd;AAEO,SAAS,YAAY,QAAgB;AAC1C,SAAO,MAAM;AACf;AAKO,SAAS,cAAc,WAAoB;AAChD,QAAM,WAAgC,CAAC;AAEvC,aAAW,UAAU,OAAO;AAC1B,QAAI,CAAC,MAAM,eAAe,MAAM,GAAG;AACjC;AAAA,IACF;AAEA,UAAM,OAAO,MAAM;AAGnB,SAAK,QAAQ,mBAAmB,OAAO;AAEvC,aAAS,KAAK,KAAK,WAAW,SAAS,CAAC;AAAA,EAC1C;AAEA,SAAO;AACT;AAEA,eAAsB,qBAAmC;AACvD,MAAI,0BAA0B;AAC5B,WAAO,QAAQ,OAAO,uBAAuB;AAAA,EAC/C;AAEA,6BAA2B;AAC3B,UAAQ;AAER,YAAU;AAEV,mBAAiB,GAAG,6BAA6B;AAEjD,MAAI,WAAW;AACb,UAAM,iBAAiB,KAAK;AAAA,EAC9B;AAGA,QAAM,MAAM,eAAe,SAAS;AAGpC,QAAM,uBAAuB,SAAS;AAGtC,WAAS,YAAY,kBAAkB,CAAC;AAExC,SAAO,QAAQ,IAAI;AAAA,IAChB,YACG,SAAS,2BACT;AAAA,EACN,CAAC;AACH;AAKA,eAAsB,eAAe,MAAuB,SAAwB,UAAgB;AAClG,QAAM,YAAoB,WAAW;AAErC;AAAA,IACE;AAAA,IACA;AAAA,IAAW,KAAK;AAAA,IAAQ;AAAA,EAC1B;AAEA,MAAI;AAEJ,MAAI;AACF,gCAA4B,MAAM;AAAA,MAChC,KAAK;AAAA,MACL;AAAA,MACA,CAAC,WAAW,SAAS,QAAQ;AAAA,MAC7B;AAAA,IACF;AAAA,EAEF,SAAS,GAAP;AACA,qBAAiB,CAAC;AAOlB,QAAI,EAAE,YAAY,iBAAiB,CAAE,MAAM,qBAAqB,KAAK,SAAS,GAAI;AAChF,YAAM,IAAI,qBAAqB,WAAW,KAAK,6BAA6B;AAAA,IAE9E,OAAO;AACL,kCAA4B;AAAA,IAC9B;AAAA,EACF;AAEA,MAAI,CAAC,2BAA2B;AAC9B,UAAM,IAAI,qBAAqB,GAAG,KAAK,yBAAyB;AAAA,EAClE;AAEA,QAAM,WAA4B,EAAE,MAAM,UAAU;AAEpD,MAAI,WAAW;AACb,aAAS,UAAU;AAAA,EACrB;AAEA,SAAO;AACT;AAEA,SAAS,WAAW,UAAkB,aAA2B;AAC/D,QAAM,YAAY,aAAa,QAAQ;AACvC,SAAQ,aAAa,UAAU,aAAa,UAAU,cAAc,KAAK,YACrE,UAAU,UAAU,YAAY,OAAO,YAAY,OAAO,IAC1D;AACN;AAEA,eAAsB,kBAAkB,UAAkB;AAExD,QAAM,SAAS,IAAI,yBAAyB,QAAQ,CAAC;AACvD;AAKA,eAAsB,0BAA0B;AAC9C,QAAM,WAAW,MAAM,MAAM,SAAS;AACtC,MAAI,SAAS,SAAS,GAAG;AACvB,UAAM,QAAQ,IAAI,SAAS,IAAI,CAAC,SAAS,qBAAqB,KAAK,SAAS,CAAC,CAAC;AAAA,EAChF;AACF;AAMA,MAAM,0BAAiE,CAAC;AACjE,SAAS,qBAAqBC,YAAmB;AAKtD,MAAI,wBAAwBA,gBAAe,QAAW;AACpD,WAAO,wBAAwBA;AAAA,EACjC;AAEA,0BAAwBA,cAAa,IAAI,QAAiB,OAAO,SAAS,WAAW;AACnF,WAAO,MAAM,iDAAiDA,gBAAe;AAE7E,QAAI;AACF,YAAM,cAAc,KAAK,IAAI;AAE7B,YAAM;AAAA,QACJ;AAAA,QACA,kBAAkBA,UAAS;AAAA,QAC3B;AAAA,QACA,CAAC;AAAA,QACD;AAAA,MACF;AAEA,aAAO,MAAM,mBAAcA,uCAAsC,KAAK,IAAI,IAAI,gBAAgB;AAG9F,cAAQ,IAAI;AAAA,IAEd,SAAS,GAAP;AAEA,aAAO,MAAM,mBAAcA,gDAA+C;AAC1E,YAAM,oBAAoB,MAAM,MAAM,eAAeA,UAAS;AAG9D,UAAI,qBAAqB,CAAC,WAAW;AACnC,cAAM,uBAAuBA,UAAS;AAAA,MACxC;AAEA,cAAQ,KAAK;AAAA,IACf,UAAE;AACA,aAAO,wBAAwBA;AAAA,IACjC;AAAA,EACF,CAAC;AAED,SAAO,wBAAwBA;AACjC;AAMA,eAAe,uBAAuBA,YAAmB;AAKvD,MAAI,OAAO,OAAO,YAAa,YAAY;AACzC,UAAM,OAAO,QAAQA,UAAS;AAAA,EAEhC,OAAO;AAOL,UAAM,cAAc,MAAM,OAAO,KAAK,EAAE,WAAAA,WAAU,GAAG,EAAE,KAAK,EAAE,CAAC;AAC/D,WAAO,MAAM,wCAAwCA,YAAW,IAAI,YAAY,qBAAqB;AACrG,gBAAY,QAAQ,CAAC,SAAS,KAAK,OAAO,CAAC;AAAA,EAC7C;AACF;AAEA,eAAe,qBAAqB,MAAY,OAAgB,OAAyB;AACvF,QAAM,KAAK,UAAU;AAErB,MAAI,MAAM;AACR,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA,eAAe,KAAK,MAAM;AAAA,MAC1B,CAAC,QAAQ,SAAS;AAChB,eAAQ,CAAC,QAAQ,OAAQ,KAAK,YAAa,aACvC,KAAK,UACL,KAAK,QAAQ,MAAM,MAAM,IAAI;AAAA,MACnC;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEA,eAAe,mBAAmB,YAAoB,UAA8C;AAClG,SAAO,IAAI,QAAQ,OAAO,SAAS,WAAW;AAC5C,UAAM,iBAAiB,yBAAyB,UAAU;AAC1D,UAAM,cAAc,MAAM,SAAS,KAAK,cAAc,IAAI;AAQ1D,UAAM,qBAAqB,KAAK,IAAI,cAAc,KAAK,GAAG;AAE1D,QAAI,cAAc,GAAG;AACnB;AAAA,QACE;AAAA,QACA;AAAA,QAAa;AAAA,QAAY;AAAA,MAC3B;AAAA,IACF;AAEA,eAAW,YAAY;AACrB,UAAI;AACF,cAAM,SAAS,MAAM,SAAS;AAC9B,gBAAQ,MAAM;AAAA,MAEhB,SAAS,GAAP;AACA,eAAO,CAAC;AAAA,MAEV,UAAE;AACA,cAAM,SAAS,KAAK,cAAc;AAAA,MACpC;AAAA,IACF,GAAG,kBAAkB;AAAA,EACvB,CAAC;AACH;AAEA,SAAS,iBAAiB,MAAY,QAAgB;AAEpD,QAAM,MAAM;AACZ,QAAM,QAAQ;AAEd,WAAS,KAAK,UAAU,KAAK,QAAQ,MAAM,MAAM;AACnD;AAEA,SAAS,kBAAkB,MAAY,QAAgB,aAAsB;AAE3E,QAAM,MAAM;AACZ,QAAM,QAAQ;AAEd,WAAS,KAAK,UAAU,KAAK,SAAS,MAAM,QAAQ,WAAW;AACjE;AAEA,SAAS,SAAS,MAAkB;AAElC,WAAS,KAAK,UAAU,KAAK,QAAQ,IAAI;AAC3C;AAEA,eAAe,WAAW,MAAY;AACpC,MAAI,MAAM,qBAAqB,IAAI,GAAG;AAEpC,aAAS,KAAK,UAAU,KAAK,UAAU,IAAI;AAAA,EAC7C;AACF;AAEA,SAAS,mBAAmB,MAAY,aAA4B;AAClE,WAAS,KAAK,UAAU,KAAK,qBAAqB,MAAM,WAAW;AACrE;AAEA,eAAe,YAAY,UAAkB,MAAY;AACvD,mBAAiB,iEAAqE,UAAU,KAAK,QAAQ,WAAW,wBAAwB;AAGhJ,MAAI,CAAC,0BAA0B;AAC7B,UAAM,MAAM;AACZ,UAAM,QAAQ;AAGd,QAAI,WAAW;AACb,YAAM,SAAS,KAAK,sBAAsB,GAAG,KAAK,MAAM;AAAA,IAC1D;AAAA,EACF;AAGA,WAAS,UAAU,KAAK,WAAW,IAAI;AAGvC,WAAS,IAAI,yBAAyB,QAAQ,CAAC;AAG/C,WAAS,YAAY,eAAe,KAAK,MAAM,CAAC;AAGhD,SAAO,MAAM,KAAK;AACpB;AAKA,SAAS,eAAe,QAAgB;AACtC,SAAO,IAAI;AACb;AAEA,SAAS,yBAAyB,MAAc;AAC9C,SAAO,KAAK;AACd;AAEA,SAAS,kBAAkB,KAAa,WAAW;AACjD,SAAO,KAAK;AACd;",
6
+ "names": ["MatchMakerState", "processId"]
7
7
  }
package/build/Stats.d.ts CHANGED
@@ -6,7 +6,7 @@ export declare let local: Stats;
6
6
  export declare function fetchAll(): Promise<(Stats & {
7
7
  processId: string;
8
8
  })[]>;
9
- export declare function persist(): any;
9
+ export declare function persist(forceNow?: boolean): any;
10
10
  export declare function reset(_persist?: boolean): void;
11
11
  export declare function excludeProcess(_processId: string): boolean | Promise<boolean>;
12
12
  export declare function getGlobalCCU(): Promise<number>;
package/build/Stats.js CHANGED
@@ -46,9 +46,9 @@ async function fetchAll() {
46
46
  let lastPersisted = 0;
47
47
  let persistTimeout = void 0;
48
48
  const persistInterval = 1e3;
49
- function persist() {
49
+ function persist(forceNow = false) {
50
50
  const now = Date.now();
51
- if (now - lastPersisted > persistInterval) {
51
+ if (forceNow || now - lastPersisted > persistInterval) {
52
52
  lastPersisted = now;
53
53
  return import_MatchMaker.presence.hset(getRoomCountKey(), import_MatchMaker.processId, `${local.roomCount},${local.ccu}`);
54
54
  } else {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/Stats.ts"],
4
- "sourcesContent": ["import { presence, processId } from \"./MatchMaker\";\n\nexport type Stats = {\n roomCount: number;\n ccu: number;\n}\n\nexport let local: Stats = {\n roomCount: 0,\n ccu: 0,\n};\n\nexport async function fetchAll() {\n // TODO: cache this value to avoid querying too often\n const allStats: Array<Stats & { processId: string }> = [];\n const allProcesses = await presence.hgetall(getRoomCountKey());\n for (let remoteProcessId in allProcesses) {\n if (remoteProcessId === processId) {\n allStats.push({ processId, roomCount: local.roomCount, ccu: local.ccu, });\n\n } else {\n const [roomCount, ccu] = allProcesses[remoteProcessId].split(',').map(Number);\n allStats.push({ processId: remoteProcessId, roomCount, ccu });\n }\n }\n return allStats;\n}\n\nlet lastPersisted = 0;\nlet persistTimeout = undefined;\nconst persistInterval = 1000;\n\nexport function persist() {\n /**\n * Avoid persisting too often.\n */\n const now = Date.now();\n\n if (now - lastPersisted > persistInterval) {\n lastPersisted = now;\n return presence.hset(getRoomCountKey(), processId, `${local.roomCount},${local.ccu}`);\n\n } else {\n clearTimeout(persistTimeout);\n persistTimeout = setTimeout(persist, persistInterval);\n }\n}\n\nexport function reset(_persist: boolean = true) {\n local.roomCount = 0;\n local.ccu = 0;\n\n if (_persist) {\n lastPersisted = 0;\n clearTimeout(persistTimeout);\n persist();\n }\n}\n\nexport function excludeProcess(_processId: string) {\n return presence.hdel(getRoomCountKey(), _processId);\n}\n\nexport async function getGlobalCCU() {\n const allStats = await fetchAll();\n return allStats.reduce((prev, next) => prev + next.ccu, 0);\n}\n\nfunction getRoomCountKey() {\n return 'roomcount';\n}"],
5
- "mappings": ";;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAAoC;AAO7B,IAAI,QAAe;AAAA,EACxB,WAAW;AAAA,EACX,KAAK;AACP;AAEA,eAAsB,WAAW;AAE/B,QAAM,WAAiD,CAAC;AACxD,QAAM,eAAe,MAAM,2BAAS,QAAQ,gBAAgB,CAAC;AAC7D,WAAS,mBAAmB,cAAc;AACxC,QAAI,oBAAoB,6BAAW;AACjC,eAAS,KAAK,EAAE,wCAAW,WAAW,MAAM,WAAW,KAAK,MAAM,IAAK,CAAC;AAAA,IAE1E,OAAO;AACL,YAAM,CAAC,WAAW,GAAG,IAAI,aAAa,iBAAiB,MAAM,GAAG,EAAE,IAAI,MAAM;AAC5E,eAAS,KAAK,EAAE,WAAW,iBAAiB,WAAW,IAAI,CAAC;AAAA,IAC9D;AAAA,EACF;AACA,SAAO;AACT;AAEA,IAAI,gBAAgB;AACpB,IAAI,iBAAiB;AACrB,MAAM,kBAAkB;AAEjB,SAAS,UAAU;AAIxB,QAAM,MAAM,KAAK,IAAI;AAErB,MAAI,MAAM,gBAAgB,iBAAiB;AACzC,oBAAgB;AAChB,WAAO,2BAAS,KAAK,gBAAgB,GAAG,6BAAW,GAAG,MAAM,aAAa,MAAM,KAAK;AAAA,EAEtF,OAAO;AACL,iBAAa,cAAc;AAC3B,qBAAiB,WAAW,SAAS,eAAe;AAAA,EACtD;AACF;AAEO,SAAS,MAAM,WAAoB,MAAM;AAC9C,QAAM,YAAY;AAClB,QAAM,MAAM;AAEZ,MAAI,UAAU;AACZ,oBAAgB;AAChB,iBAAa,cAAc;AAC3B,YAAQ;AAAA,EACV;AACF;AAEO,SAAS,eAAe,YAAoB;AACjD,SAAO,2BAAS,KAAK,gBAAgB,GAAG,UAAU;AACpD;AAEA,eAAsB,eAAe;AACnC,QAAM,WAAW,MAAM,SAAS;AAChC,SAAO,SAAS,OAAO,CAAC,MAAM,SAAS,OAAO,KAAK,KAAK,CAAC;AAC3D;AAEA,SAAS,kBAAkB;AACzB,SAAO;AACT;",
4
+ "sourcesContent": ["import { presence, processId } from \"./MatchMaker\";\n\nexport type Stats = {\n roomCount: number;\n ccu: number;\n}\n\nexport let local: Stats = {\n roomCount: 0,\n ccu: 0,\n};\n\nexport async function fetchAll() {\n // TODO: cache this value to avoid querying too often\n const allStats: Array<Stats & { processId: string }> = [];\n const allProcesses = await presence.hgetall(getRoomCountKey());\n for (let remoteProcessId in allProcesses) {\n if (remoteProcessId === processId) {\n allStats.push({ processId, roomCount: local.roomCount, ccu: local.ccu, });\n\n } else {\n const [roomCount, ccu] = allProcesses[remoteProcessId].split(',').map(Number);\n allStats.push({ processId: remoteProcessId, roomCount, ccu });\n }\n }\n return allStats;\n}\n\nlet lastPersisted = 0;\nlet persistTimeout = undefined;\nconst persistInterval = 1000;\n\nexport function persist(forceNow: boolean = false) {\n /**\n * Avoid persisting too often.\n */\n const now = Date.now();\n\n if (forceNow || (now - lastPersisted > persistInterval)) {\n lastPersisted = now;\n return presence.hset(getRoomCountKey(), processId, `${local.roomCount},${local.ccu}`);\n\n } else {\n clearTimeout(persistTimeout);\n persistTimeout = setTimeout(persist, persistInterval);\n }\n}\n\nexport function reset(_persist: boolean = true) {\n local.roomCount = 0;\n local.ccu = 0;\n\n if (_persist) {\n lastPersisted = 0;\n clearTimeout(persistTimeout);\n persist();\n }\n}\n\nexport function excludeProcess(_processId: string) {\n return presence.hdel(getRoomCountKey(), _processId);\n}\n\nexport async function getGlobalCCU() {\n const allStats = await fetchAll();\n return allStats.reduce((prev, next) => prev + next.ccu, 0);\n}\n\nfunction getRoomCountKey() {\n return 'roomcount';\n}"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAAoC;AAO7B,IAAI,QAAe;AAAA,EACxB,WAAW;AAAA,EACX,KAAK;AACP;AAEA,eAAsB,WAAW;AAE/B,QAAM,WAAiD,CAAC;AACxD,QAAM,eAAe,MAAM,2BAAS,QAAQ,gBAAgB,CAAC;AAC7D,WAAS,mBAAmB,cAAc;AACxC,QAAI,oBAAoB,6BAAW;AACjC,eAAS,KAAK,EAAE,wCAAW,WAAW,MAAM,WAAW,KAAK,MAAM,IAAK,CAAC;AAAA,IAE1E,OAAO;AACL,YAAM,CAAC,WAAW,GAAG,IAAI,aAAa,iBAAiB,MAAM,GAAG,EAAE,IAAI,MAAM;AAC5E,eAAS,KAAK,EAAE,WAAW,iBAAiB,WAAW,IAAI,CAAC;AAAA,IAC9D;AAAA,EACF;AACA,SAAO;AACT;AAEA,IAAI,gBAAgB;AACpB,IAAI,iBAAiB;AACrB,MAAM,kBAAkB;AAEjB,SAAS,QAAQ,WAAoB,OAAO;AAIjD,QAAM,MAAM,KAAK,IAAI;AAErB,MAAI,YAAa,MAAM,gBAAgB,iBAAkB;AACvD,oBAAgB;AAChB,WAAO,2BAAS,KAAK,gBAAgB,GAAG,6BAAW,GAAG,MAAM,aAAa,MAAM,KAAK;AAAA,EAEtF,OAAO;AACL,iBAAa,cAAc;AAC3B,qBAAiB,WAAW,SAAS,eAAe;AAAA,EACtD;AACF;AAEO,SAAS,MAAM,WAAoB,MAAM;AAC9C,QAAM,YAAY;AAClB,QAAM,MAAM;AAEZ,MAAI,UAAU;AACZ,oBAAgB;AAChB,iBAAa,cAAc;AAC3B,YAAQ;AAAA,EACV;AACF;AAEO,SAAS,eAAe,YAAoB;AACjD,SAAO,2BAAS,KAAK,gBAAgB,GAAG,UAAU;AACpD;AAEA,eAAsB,eAAe;AACnC,QAAM,WAAW,MAAM,SAAS;AAChC,SAAO,SAAS,OAAO,CAAC,MAAM,SAAS,OAAO,KAAK,KAAK,CAAC;AAC3D;AAEA,SAAS,kBAAkB;AACzB,SAAO;AACT;",
6
6
  "names": []
7
7
  }
package/build/Stats.mjs CHANGED
@@ -19,9 +19,9 @@ async function fetchAll() {
19
19
  let lastPersisted = 0;
20
20
  let persistTimeout = void 0;
21
21
  const persistInterval = 1e3;
22
- function persist() {
22
+ function persist(forceNow = false) {
23
23
  const now = Date.now();
24
- if (now - lastPersisted > persistInterval) {
24
+ if (forceNow || now - lastPersisted > persistInterval) {
25
25
  lastPersisted = now;
26
26
  return presence.hset(getRoomCountKey(), processId, `${local.roomCount},${local.ccu}`);
27
27
  } else {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/Stats.ts"],
4
- "sourcesContent": ["import { presence, processId } from \"./MatchMaker\";\n\nexport type Stats = {\n roomCount: number;\n ccu: number;\n}\n\nexport let local: Stats = {\n roomCount: 0,\n ccu: 0,\n};\n\nexport async function fetchAll() {\n // TODO: cache this value to avoid querying too often\n const allStats: Array<Stats & { processId: string }> = [];\n const allProcesses = await presence.hgetall(getRoomCountKey());\n for (let remoteProcessId in allProcesses) {\n if (remoteProcessId === processId) {\n allStats.push({ processId, roomCount: local.roomCount, ccu: local.ccu, });\n\n } else {\n const [roomCount, ccu] = allProcesses[remoteProcessId].split(',').map(Number);\n allStats.push({ processId: remoteProcessId, roomCount, ccu });\n }\n }\n return allStats;\n}\n\nlet lastPersisted = 0;\nlet persistTimeout = undefined;\nconst persistInterval = 1000;\n\nexport function persist() {\n /**\n * Avoid persisting too often.\n */\n const now = Date.now();\n\n if (now - lastPersisted > persistInterval) {\n lastPersisted = now;\n return presence.hset(getRoomCountKey(), processId, `${local.roomCount},${local.ccu}`);\n\n } else {\n clearTimeout(persistTimeout);\n persistTimeout = setTimeout(persist, persistInterval);\n }\n}\n\nexport function reset(_persist: boolean = true) {\n local.roomCount = 0;\n local.ccu = 0;\n\n if (_persist) {\n lastPersisted = 0;\n clearTimeout(persistTimeout);\n persist();\n }\n}\n\nexport function excludeProcess(_processId: string) {\n return presence.hdel(getRoomCountKey(), _processId);\n}\n\nexport async function getGlobalCCU() {\n const allStats = await fetchAll();\n return allStats.reduce((prev, next) => prev + next.ccu, 0);\n}\n\nfunction getRoomCountKey() {\n return 'roomcount';\n}"],
5
- "mappings": "AAAA,SAAS,UAAU,iBAAiB;AAO7B,IAAI,QAAe;AAAA,EACxB,WAAW;AAAA,EACX,KAAK;AACP;AAEA,eAAsB,WAAW;AAE/B,QAAM,WAAiD,CAAC;AACxD,QAAM,eAAe,MAAM,SAAS,QAAQ,gBAAgB,CAAC;AAC7D,WAAS,mBAAmB,cAAc;AACxC,QAAI,oBAAoB,WAAW;AACjC,eAAS,KAAK,EAAE,WAAW,WAAW,MAAM,WAAW,KAAK,MAAM,IAAK,CAAC;AAAA,IAE1E,OAAO;AACL,YAAM,CAAC,WAAW,GAAG,IAAI,aAAa,iBAAiB,MAAM,GAAG,EAAE,IAAI,MAAM;AAC5E,eAAS,KAAK,EAAE,WAAW,iBAAiB,WAAW,IAAI,CAAC;AAAA,IAC9D;AAAA,EACF;AACA,SAAO;AACT;AAEA,IAAI,gBAAgB;AACpB,IAAI,iBAAiB;AACrB,MAAM,kBAAkB;AAEjB,SAAS,UAAU;AAIxB,QAAM,MAAM,KAAK,IAAI;AAErB,MAAI,MAAM,gBAAgB,iBAAiB;AACzC,oBAAgB;AAChB,WAAO,SAAS,KAAK,gBAAgB,GAAG,WAAW,GAAG,MAAM,aAAa,MAAM,KAAK;AAAA,EAEtF,OAAO;AACL,iBAAa,cAAc;AAC3B,qBAAiB,WAAW,SAAS,eAAe;AAAA,EACtD;AACF;AAEO,SAAS,MAAM,WAAoB,MAAM;AAC9C,QAAM,YAAY;AAClB,QAAM,MAAM;AAEZ,MAAI,UAAU;AACZ,oBAAgB;AAChB,iBAAa,cAAc;AAC3B,YAAQ;AAAA,EACV;AACF;AAEO,SAAS,eAAe,YAAoB;AACjD,SAAO,SAAS,KAAK,gBAAgB,GAAG,UAAU;AACpD;AAEA,eAAsB,eAAe;AACnC,QAAM,WAAW,MAAM,SAAS;AAChC,SAAO,SAAS,OAAO,CAAC,MAAM,SAAS,OAAO,KAAK,KAAK,CAAC;AAC3D;AAEA,SAAS,kBAAkB;AACzB,SAAO;AACT;",
4
+ "sourcesContent": ["import { presence, processId } from \"./MatchMaker\";\n\nexport type Stats = {\n roomCount: number;\n ccu: number;\n}\n\nexport let local: Stats = {\n roomCount: 0,\n ccu: 0,\n};\n\nexport async function fetchAll() {\n // TODO: cache this value to avoid querying too often\n const allStats: Array<Stats & { processId: string }> = [];\n const allProcesses = await presence.hgetall(getRoomCountKey());\n for (let remoteProcessId in allProcesses) {\n if (remoteProcessId === processId) {\n allStats.push({ processId, roomCount: local.roomCount, ccu: local.ccu, });\n\n } else {\n const [roomCount, ccu] = allProcesses[remoteProcessId].split(',').map(Number);\n allStats.push({ processId: remoteProcessId, roomCount, ccu });\n }\n }\n return allStats;\n}\n\nlet lastPersisted = 0;\nlet persistTimeout = undefined;\nconst persistInterval = 1000;\n\nexport function persist(forceNow: boolean = false) {\n /**\n * Avoid persisting too often.\n */\n const now = Date.now();\n\n if (forceNow || (now - lastPersisted > persistInterval)) {\n lastPersisted = now;\n return presence.hset(getRoomCountKey(), processId, `${local.roomCount},${local.ccu}`);\n\n } else {\n clearTimeout(persistTimeout);\n persistTimeout = setTimeout(persist, persistInterval);\n }\n}\n\nexport function reset(_persist: boolean = true) {\n local.roomCount = 0;\n local.ccu = 0;\n\n if (_persist) {\n lastPersisted = 0;\n clearTimeout(persistTimeout);\n persist();\n }\n}\n\nexport function excludeProcess(_processId: string) {\n return presence.hdel(getRoomCountKey(), _processId);\n}\n\nexport async function getGlobalCCU() {\n const allStats = await fetchAll();\n return allStats.reduce((prev, next) => prev + next.ccu, 0);\n}\n\nfunction getRoomCountKey() {\n return 'roomcount';\n}"],
5
+ "mappings": "AAAA,SAAS,UAAU,iBAAiB;AAO7B,IAAI,QAAe;AAAA,EACxB,WAAW;AAAA,EACX,KAAK;AACP;AAEA,eAAsB,WAAW;AAE/B,QAAM,WAAiD,CAAC;AACxD,QAAM,eAAe,MAAM,SAAS,QAAQ,gBAAgB,CAAC;AAC7D,WAAS,mBAAmB,cAAc;AACxC,QAAI,oBAAoB,WAAW;AACjC,eAAS,KAAK,EAAE,WAAW,WAAW,MAAM,WAAW,KAAK,MAAM,IAAK,CAAC;AAAA,IAE1E,OAAO;AACL,YAAM,CAAC,WAAW,GAAG,IAAI,aAAa,iBAAiB,MAAM,GAAG,EAAE,IAAI,MAAM;AAC5E,eAAS,KAAK,EAAE,WAAW,iBAAiB,WAAW,IAAI,CAAC;AAAA,IAC9D;AAAA,EACF;AACA,SAAO;AACT;AAEA,IAAI,gBAAgB;AACpB,IAAI,iBAAiB;AACrB,MAAM,kBAAkB;AAEjB,SAAS,QAAQ,WAAoB,OAAO;AAIjD,QAAM,MAAM,KAAK,IAAI;AAErB,MAAI,YAAa,MAAM,gBAAgB,iBAAkB;AACvD,oBAAgB;AAChB,WAAO,SAAS,KAAK,gBAAgB,GAAG,WAAW,GAAG,MAAM,aAAa,MAAM,KAAK;AAAA,EAEtF,OAAO;AACL,iBAAa,cAAc;AAC3B,qBAAiB,WAAW,SAAS,eAAe;AAAA,EACtD;AACF;AAEO,SAAS,MAAM,WAAoB,MAAM;AAC9C,QAAM,YAAY;AAClB,QAAM,MAAM;AAEZ,MAAI,UAAU;AACZ,oBAAgB;AAChB,iBAAa,cAAc;AAC3B,YAAQ;AAAA,EACV;AACF;AAEO,SAAS,eAAe,YAAoB;AACjD,SAAO,SAAS,KAAK,gBAAgB,GAAG,UAAU;AACpD;AAEA,eAAsB,eAAe;AACnC,QAAM,WAAW,MAAM,SAAS;AAChC,SAAO,SAAS,OAAO,CAAC,MAAM,SAAS,OAAO,KAAK,KAAK,CAAC;AAC3D;AAEA,SAAS,kBAAkB;AACzB,SAAO;AACT;",
6
6
  "names": []
7
7
  }
@@ -45,7 +45,7 @@ class LocalDriver {
45
45
  }
46
46
  cleanup(processId) {
47
47
  const cachedRooms = this.find({ processId });
48
- import_Logger.logger.debug("removing stale rooms by processId:", processId, `(${cachedRooms.length} rooms found)`);
48
+ import_Logger.logger.debug("> Removing stale rooms by processId:", processId, `(${cachedRooms.length} rooms found)`);
49
49
  cachedRooms.forEach((room) => room.remove());
50
50
  return Promise.resolve();
51
51
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/matchmaker/driver/index.ts"],
4
- "sourcesContent": ["import { logger } from \"../../Logger\";\nimport { IRoomListingData, SortOptions, RoomListingData, QueryHelpers, MatchMakerDriver } from \"./interfaces\";\n\n// re-export\nexport type { IRoomListingData, SortOptions, RoomListingData, QueryHelpers, MatchMakerDriver };\n\nimport { Query } from './Query';\nimport { RoomCache } from './RoomData';\n\nexport class LocalDriver implements MatchMakerDriver {\n public rooms: RoomCache[] = [];\n\n public createInstance(initialValues: any = {}) {\n return new RoomCache(initialValues, this.rooms);\n }\n\n public has(roomId: string) {\n return this.rooms.some((room) => room.roomId === roomId);\n }\n\n public find(conditions: Partial<IRoomListingData>) {\n return this.rooms.filter(((room) => {\n for (const field in conditions) {\n if (\n conditions.hasOwnProperty(field) &&\n room[field] !== conditions[field]\n ) {\n return false;\n }\n }\n return true;\n }));\n }\n\n public cleanup(processId: string) {\n const cachedRooms = this.find({ processId });\n logger.debug(\"removing stale rooms by processId:\", processId, `(${cachedRooms.length} rooms found)`);\n\n cachedRooms.forEach((room) => room.remove());\n return Promise.resolve();\n }\n\n public findOne(conditions: Partial<IRoomListingData>) {\n return new Query<RoomListingData>(this.rooms, conditions) as any as QueryHelpers<RoomListingData>;\n }\n\n public clear() {\n this.rooms = [];\n }\n\n public shutdown() {\n }\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAAuB;AAMvB,mBAAsB;AACtB,sBAA0B;AAEnB,MAAM,YAAwC;AAAA,EAA9C;AACL,SAAO,QAAqB,CAAC;AAAA;AAAA,EAEtB,eAAe,gBAAqB,CAAC,GAAG;AAC7C,WAAO,IAAI,0BAAU,eAAe,KAAK,KAAK;AAAA,EAChD;AAAA,EAEO,IAAI,QAAgB;AACzB,WAAO,KAAK,MAAM,KAAK,CAAC,SAAS,KAAK,WAAW,MAAM;AAAA,EACzD;AAAA,EAEO,KAAK,YAAuC;AACjD,WAAO,KAAK,MAAM,OAAQ,CAAC,SAAS;AAClC,iBAAW,SAAS,YAAY;AAC9B,YACE,WAAW,eAAe,KAAK,KAC/B,KAAK,WAAW,WAAW,QAC3B;AACA,iBAAO;AAAA,QACT;AAAA,MACF;AACA,aAAO;AAAA,IACT,CAAE;AAAA,EACJ;AAAA,EAEO,QAAQ,WAAmB;AAChC,UAAM,cAAc,KAAK,KAAK,EAAE,UAAU,CAAC;AAC3C,yBAAO,MAAM,sCAAsC,WAAW,IAAI,YAAY,qBAAqB;AAEnG,gBAAY,QAAQ,CAAC,SAAS,KAAK,OAAO,CAAC;AAC3C,WAAO,QAAQ,QAAQ;AAAA,EACzB;AAAA,EAEO,QAAQ,YAAuC;AACpD,WAAO,IAAI,mBAAuB,KAAK,OAAO,UAAU;AAAA,EAC1D;AAAA,EAEO,QAAQ;AACb,SAAK,QAAQ,CAAC;AAAA,EAChB;AAAA,EAEO,WAAW;AAAA,EAClB;AACF;",
4
+ "sourcesContent": ["import { logger } from \"../../Logger\";\nimport { IRoomListingData, SortOptions, RoomListingData, QueryHelpers, MatchMakerDriver } from \"./interfaces\";\n\n// re-export\nexport type { IRoomListingData, SortOptions, RoomListingData, QueryHelpers, MatchMakerDriver };\n\nimport { Query } from './Query';\nimport { RoomCache } from './RoomData';\n\nexport class LocalDriver implements MatchMakerDriver {\n public rooms: RoomCache[] = [];\n\n public createInstance(initialValues: any = {}) {\n return new RoomCache(initialValues, this.rooms);\n }\n\n public has(roomId: string) {\n return this.rooms.some((room) => room.roomId === roomId);\n }\n\n public find(conditions: Partial<IRoomListingData>) {\n return this.rooms.filter(((room) => {\n for (const field in conditions) {\n if (\n conditions.hasOwnProperty(field) &&\n room[field] !== conditions[field]\n ) {\n return false;\n }\n }\n return true;\n }));\n }\n\n public cleanup(processId: string) {\n const cachedRooms = this.find({ processId });\n logger.debug(\"> Removing stale rooms by processId:\", processId, `(${cachedRooms.length} rooms found)`);\n\n cachedRooms.forEach((room) => room.remove());\n return Promise.resolve();\n }\n\n public findOne(conditions: Partial<IRoomListingData>) {\n return new Query<RoomListingData>(this.rooms, conditions) as any as QueryHelpers<RoomListingData>;\n }\n\n public clear() {\n this.rooms = [];\n }\n\n public shutdown() {\n }\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAAuB;AAMvB,mBAAsB;AACtB,sBAA0B;AAEnB,MAAM,YAAwC;AAAA,EAA9C;AACL,SAAO,QAAqB,CAAC;AAAA;AAAA,EAEtB,eAAe,gBAAqB,CAAC,GAAG;AAC7C,WAAO,IAAI,0BAAU,eAAe,KAAK,KAAK;AAAA,EAChD;AAAA,EAEO,IAAI,QAAgB;AACzB,WAAO,KAAK,MAAM,KAAK,CAAC,SAAS,KAAK,WAAW,MAAM;AAAA,EACzD;AAAA,EAEO,KAAK,YAAuC;AACjD,WAAO,KAAK,MAAM,OAAQ,CAAC,SAAS;AAClC,iBAAW,SAAS,YAAY;AAC9B,YACE,WAAW,eAAe,KAAK,KAC/B,KAAK,WAAW,WAAW,QAC3B;AACA,iBAAO;AAAA,QACT;AAAA,MACF;AACA,aAAO;AAAA,IACT,CAAE;AAAA,EACJ;AAAA,EAEO,QAAQ,WAAmB;AAChC,UAAM,cAAc,KAAK,KAAK,EAAE,UAAU,CAAC;AAC3C,yBAAO,MAAM,wCAAwC,WAAW,IAAI,YAAY,qBAAqB;AAErG,gBAAY,QAAQ,CAAC,SAAS,KAAK,OAAO,CAAC;AAC3C,WAAO,QAAQ,QAAQ;AAAA,EACzB;AAAA,EAEO,QAAQ,YAAuC;AACpD,WAAO,IAAI,mBAAuB,KAAK,OAAO,UAAU;AAAA,EAC1D;AAAA,EAEO,QAAQ;AACb,SAAK,QAAQ,CAAC;AAAA,EAChB;AAAA,EAEO,WAAW;AAAA,EAClB;AACF;",
6
6
  "names": []
7
7
  }
@@ -23,7 +23,7 @@ class LocalDriver {
23
23
  }
24
24
  cleanup(processId) {
25
25
  const cachedRooms = this.find({ processId });
26
- logger.debug("removing stale rooms by processId:", processId, `(${cachedRooms.length} rooms found)`);
26
+ logger.debug("> Removing stale rooms by processId:", processId, `(${cachedRooms.length} rooms found)`);
27
27
  cachedRooms.forEach((room) => room.remove());
28
28
  return Promise.resolve();
29
29
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/matchmaker/driver/index.ts"],
4
- "sourcesContent": ["import { logger } from \"../../Logger\";\nimport { IRoomListingData, SortOptions, RoomListingData, QueryHelpers, MatchMakerDriver } from \"./interfaces\";\n\n// re-export\nexport type { IRoomListingData, SortOptions, RoomListingData, QueryHelpers, MatchMakerDriver };\n\nimport { Query } from './Query';\nimport { RoomCache } from './RoomData';\n\nexport class LocalDriver implements MatchMakerDriver {\n public rooms: RoomCache[] = [];\n\n public createInstance(initialValues: any = {}) {\n return new RoomCache(initialValues, this.rooms);\n }\n\n public has(roomId: string) {\n return this.rooms.some((room) => room.roomId === roomId);\n }\n\n public find(conditions: Partial<IRoomListingData>) {\n return this.rooms.filter(((room) => {\n for (const field in conditions) {\n if (\n conditions.hasOwnProperty(field) &&\n room[field] !== conditions[field]\n ) {\n return false;\n }\n }\n return true;\n }));\n }\n\n public cleanup(processId: string) {\n const cachedRooms = this.find({ processId });\n logger.debug(\"removing stale rooms by processId:\", processId, `(${cachedRooms.length} rooms found)`);\n\n cachedRooms.forEach((room) => room.remove());\n return Promise.resolve();\n }\n\n public findOne(conditions: Partial<IRoomListingData>) {\n return new Query<RoomListingData>(this.rooms, conditions) as any as QueryHelpers<RoomListingData>;\n }\n\n public clear() {\n this.rooms = [];\n }\n\n public shutdown() {\n }\n}\n"],
5
- "mappings": "AAAA,SAAS,cAAc;AAMvB,SAAS,aAAa;AACtB,SAAS,iBAAiB;AAEnB,MAAM,YAAwC;AAAA,EAA9C;AACL,SAAO,QAAqB,CAAC;AAAA;AAAA,EAEtB,eAAe,gBAAqB,CAAC,GAAG;AAC7C,WAAO,IAAI,UAAU,eAAe,KAAK,KAAK;AAAA,EAChD;AAAA,EAEO,IAAI,QAAgB;AACzB,WAAO,KAAK,MAAM,KAAK,CAAC,SAAS,KAAK,WAAW,MAAM;AAAA,EACzD;AAAA,EAEO,KAAK,YAAuC;AACjD,WAAO,KAAK,MAAM,OAAQ,CAAC,SAAS;AAClC,iBAAW,SAAS,YAAY;AAC9B,YACE,WAAW,eAAe,KAAK,KAC/B,KAAK,WAAW,WAAW,QAC3B;AACA,iBAAO;AAAA,QACT;AAAA,MACF;AACA,aAAO;AAAA,IACT,CAAE;AAAA,EACJ;AAAA,EAEO,QAAQ,WAAmB;AAChC,UAAM,cAAc,KAAK,KAAK,EAAE,UAAU,CAAC;AAC3C,WAAO,MAAM,sCAAsC,WAAW,IAAI,YAAY,qBAAqB;AAEnG,gBAAY,QAAQ,CAAC,SAAS,KAAK,OAAO,CAAC;AAC3C,WAAO,QAAQ,QAAQ;AAAA,EACzB;AAAA,EAEO,QAAQ,YAAuC;AACpD,WAAO,IAAI,MAAuB,KAAK,OAAO,UAAU;AAAA,EAC1D;AAAA,EAEO,QAAQ;AACb,SAAK,QAAQ,CAAC;AAAA,EAChB;AAAA,EAEO,WAAW;AAAA,EAClB;AACF;",
4
+ "sourcesContent": ["import { logger } from \"../../Logger\";\nimport { IRoomListingData, SortOptions, RoomListingData, QueryHelpers, MatchMakerDriver } from \"./interfaces\";\n\n// re-export\nexport type { IRoomListingData, SortOptions, RoomListingData, QueryHelpers, MatchMakerDriver };\n\nimport { Query } from './Query';\nimport { RoomCache } from './RoomData';\n\nexport class LocalDriver implements MatchMakerDriver {\n public rooms: RoomCache[] = [];\n\n public createInstance(initialValues: any = {}) {\n return new RoomCache(initialValues, this.rooms);\n }\n\n public has(roomId: string) {\n return this.rooms.some((room) => room.roomId === roomId);\n }\n\n public find(conditions: Partial<IRoomListingData>) {\n return this.rooms.filter(((room) => {\n for (const field in conditions) {\n if (\n conditions.hasOwnProperty(field) &&\n room[field] !== conditions[field]\n ) {\n return false;\n }\n }\n return true;\n }));\n }\n\n public cleanup(processId: string) {\n const cachedRooms = this.find({ processId });\n logger.debug(\"> Removing stale rooms by processId:\", processId, `(${cachedRooms.length} rooms found)`);\n\n cachedRooms.forEach((room) => room.remove());\n return Promise.resolve();\n }\n\n public findOne(conditions: Partial<IRoomListingData>) {\n return new Query<RoomListingData>(this.rooms, conditions) as any as QueryHelpers<RoomListingData>;\n }\n\n public clear() {\n this.rooms = [];\n }\n\n public shutdown() {\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,cAAc;AAMvB,SAAS,aAAa;AACtB,SAAS,iBAAiB;AAEnB,MAAM,YAAwC;AAAA,EAA9C;AACL,SAAO,QAAqB,CAAC;AAAA;AAAA,EAEtB,eAAe,gBAAqB,CAAC,GAAG;AAC7C,WAAO,IAAI,UAAU,eAAe,KAAK,KAAK;AAAA,EAChD;AAAA,EAEO,IAAI,QAAgB;AACzB,WAAO,KAAK,MAAM,KAAK,CAAC,SAAS,KAAK,WAAW,MAAM;AAAA,EACzD;AAAA,EAEO,KAAK,YAAuC;AACjD,WAAO,KAAK,MAAM,OAAQ,CAAC,SAAS;AAClC,iBAAW,SAAS,YAAY;AAC9B,YACE,WAAW,eAAe,KAAK,KAC/B,KAAK,WAAW,WAAW,QAC3B;AACA,iBAAO;AAAA,QACT;AAAA,MACF;AACA,aAAO;AAAA,IACT,CAAE;AAAA,EACJ;AAAA,EAEO,QAAQ,WAAmB;AAChC,UAAM,cAAc,KAAK,KAAK,EAAE,UAAU,CAAC;AAC3C,WAAO,MAAM,wCAAwC,WAAW,IAAI,YAAY,qBAAqB;AAErG,gBAAY,QAAQ,CAAC,SAAS,KAAK,OAAO,CAAC;AAC3C,WAAO,QAAQ,QAAQ;AAAA,EACzB;AAAA,EAEO,QAAQ,YAAuC;AACpD,WAAO,IAAI,MAAuB,KAAK,OAAO,UAAU;AAAA,EAC1D;AAAA,EAEO,QAAQ;AACb,SAAK,QAAQ,CAAC;AAAA,EAChB;AAAA,EAEO,WAAW;AAAA,EAClB;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/presence/Presence.ts"],
4
- "sourcesContent": ["/**\n * When you need to scale your server on multiple processes and/or machines, you'd need to provide\n * the Presence option to the Server. The purpose of Presence is to allow communicating and\n * sharing data between different processes, specially during match-making.\n *\n * - Local presence - This is the default option. It's meant to be used when you're running Colyseus in\n * a single process.\n * - Redis presence - Use this option when you're running Colyseus on multiple processes and/or machines.\n *\n * @default Local presence\n */\nexport interface Presence {\n /**\n * Subscribes to the given topic. The callback will be triggered whenever a message is published on topic.\n *\n * @param topic - Topic name.\n * @param callback - Callback to trigger on subscribing.\n */\n subscribe(topic: string, callback: Function);\n /**\n * Unsubscribe from given topic.\n *\n * @param topic - Topic name.\n * @param callback - Callback to trigger on topic unsubscribing.\n */\n unsubscribe(topic: string, callback?: Function);\n\n /**\n * Posts a message to given topic.\n *\n * @param topic - Topic name.\n * @param data - Message body/object.\n */\n publish(topic: string, data: any);\n\n /**\n * Returns if key exists.\n *\n * @param roomId\n */\n exists(roomId: string): Promise<boolean>;\n\n /**\n * Set key to hold the string value.\n *\n * @param key - Identifier.\n * @param value - Message body/object.\n */\n set(key: string, value: string);\n\n /**\n * Set key to hold the string value and set key to timeout after a given number of seconds.\n *\n * @param key - Identifier.\n * @param value - Message body/object.\n * @param seconds - Timeout value.\n */\n setex(key: string, value: string, seconds: number);\n\n /**\n * Get the value of key.\n *\n * @param key - Identifier.\n */\n get(key: string);\n\n /**\n * Removes the specified key.\n *\n * @param key - Identifier of the object to removed.\n */\n del(key: string): void;\n\n /**\n * Add the specified members to the set stored at key. Specified members that are already\n * a member of this set are ignored. If key does not exist, a new set is created before\n * adding the specified members.\n *\n * @param key - Name/Identifier of the set.\n * @param value - Message body/object.\n */\n sadd(key: string, value: any);\n /**\n * Returns all the members of the set value stored at key.\n *\n * @param key - Name/Identifier of the set.\n */\n smembers(key: string): Promise<string[]>;\n /**\n * Returns if member is a member of the set stored at key.\n *\n * @param key - Name/Identifier of the set.\n * @param field - Key value within the set.\n * @returns `1` if the element is a member of the set else `0`.\n */\n sismember(key: string, field: string);\n /**\n * Remove the specified members from the set stored at key. Specified members that are not a\n * member of this set are ignored. If key does not exist, it is treated as an empty set\n * and this command returns 0.\n *\n * @param key - Name/Identifier of the set.\n * @param value - Key value within the set.\n */\n srem(key: string, value: any);\n /**\n * Returns the set cardinality (number of elements) of the set stored at key.\n *\n * @param key - Name/Identifier of the set.\n */\n scard(key: string);\n /**\n * Returns the members of the set resulting from the intersection of all the given sets.\n *\n * @param keys - Key values within the set.\n */\n sinter(...keys: string[]): Promise<string[]>;\n\n /**\n * Sets field in the hash stored at key to value. If key does not exist, a new key holding a hash is created.\n * If field already exists in the hash, it is overwritten.\n */\n hset(key: string, field: string, value: string);\n\n /**\n * Increments the number stored at field in the hash stored at key by increment. If key does not exist, a new key\n * holding a hash is created. If field does not exist the value is set to 0 before the operation is performed.\n */\n hincrby(key: string, field: string, value: number): number | Promise<number>;\n\n /**\n * Returns the value associated with field in the hash stored at key.\n */\n hget(key: string, field: string): Promise<string>;\n\n /**\n * Returns all fields and values of the hash stored at key.\n */\n hgetall(key: string): Promise<{ [key: string]: string }>;\n\n /**\n * Removes the specified fields from the hash stored at key. Specified fields that do not exist within\n * this hash are ignored. If key does not exist, it is treated as an empty hash and this command returns 0.\n */\n hdel(key: string, field: string): boolean | Promise<boolean>;\n\n /**\n * Returns the number of fields contained in the hash stored at key\n */\n hlen(key: string): Promise<number>;\n\n /**\n * Increments the number stored at key by one. If the key does not exist, it is set to 0 before performing\n * the operation. An error is returned if the key contains a value of the wrong type or\n * contains a string that can not be represented as integer. This operation is limited to 64-bit signed integers.\n */\n incr(key: string): Promise<number>;\n\n /**\n * Decrements the number stored at key by one. If the key does not exist, it is set to 0 before performing\n * the operation. An error is returned if the key contains a value of the wrong type or contains a string\n * that can not be represented as integer. This operation is limited to 64-bit signed integers.\n */\n decr(key: string): Promise<number>;\n\n shutdown(): void;\n}\n"],
4
+ "sourcesContent": ["/**\n * When you need to scale your server on multiple processes and/or machines, you'd need to provide\n * the Presence option to the Server. The purpose of Presence is to allow communicating and\n * sharing data between different processes, specially during match-making.\n *\n * - Local presence - This is the default option. It's meant to be used when you're running Colyseus in\n * a single process.\n * - Redis presence - Use this option when you're running Colyseus on multiple processes and/or machines.\n *\n * @default Local presence\n */\nexport interface Presence {\n /**\n * Subscribes to the given topic. The callback will be triggered whenever a message is published on topic.\n *\n * @param topic - Topic name.\n * @param callback - Callback to trigger on subscribing.\n */\n subscribe(topic: string, callback: Function);\n\n /**\n * Unsubscribe from given topic.\n *\n * @param topic - Topic name.\n * @param callback - Callback to trigger on topic unsubscribing.\n */\n unsubscribe(topic: string, callback?: Function);\n\n /**\n * Posts a message to given topic.\n *\n * @param topic - Topic name.\n * @param data - Message body/object.\n */\n publish(topic: string, data: any);\n\n /**\n * Returns if key exists.\n *\n * @param roomId\n */\n exists(roomId: string): Promise<boolean>;\n\n /**\n * Set key to hold the string value.\n *\n * @param key - Identifier.\n * @param value - Message body/object.\n */\n set(key: string, value: string);\n\n /**\n * Set key to hold the string value and set key to timeout after a given number of seconds.\n *\n * @param key - Identifier.\n * @param value - Message body/object.\n * @param seconds - Timeout value.\n */\n setex(key: string, value: string, seconds: number);\n\n /**\n * Get the value of key.\n *\n * @param key - Identifier.\n */\n get(key: string);\n\n /**\n * Removes the specified key.\n *\n * @param key - Identifier of the object to removed.\n */\n del(key: string): void;\n\n /**\n * Add the specified members to the set stored at key. Specified members that are already\n * a member of this set are ignored. If key does not exist, a new set is created before\n * adding the specified members.\n *\n * @param key - Name/Identifier of the set.\n * @param value - Message body/object.\n */\n sadd(key: string, value: any);\n /**\n * Returns all the members of the set value stored at key.\n *\n * @param key - Name/Identifier of the set.\n */\n smembers(key: string): Promise<string[]>;\n /**\n * Returns if member is a member of the set stored at key.\n *\n * @param key - Name/Identifier of the set.\n * @param field - Key value within the set.\n * @returns `1` if the element is a member of the set else `0`.\n */\n sismember(key: string, field: string);\n /**\n * Remove the specified members from the set stored at key. Specified members that are not a\n * member of this set are ignored. If key does not exist, it is treated as an empty set\n * and this command returns 0.\n *\n * @param key - Name/Identifier of the set.\n * @param value - Key value within the set.\n */\n srem(key: string, value: any);\n /**\n * Returns the set cardinality (number of elements) of the set stored at key.\n *\n * @param key - Name/Identifier of the set.\n */\n scard(key: string);\n /**\n * Returns the members of the set resulting from the intersection of all the given sets.\n *\n * @param keys - Key values within the set.\n */\n sinter(...keys: string[]): Promise<string[]>;\n\n /**\n * Sets field in the hash stored at key to value. If key does not exist, a new key holding a hash is created.\n * If field already exists in the hash, it is overwritten.\n */\n hset(key: string, field: string, value: string);\n\n /**\n * Increments the number stored at field in the hash stored at key by increment. If key does not exist, a new key\n * holding a hash is created. If field does not exist the value is set to 0 before the operation is performed.\n */\n hincrby(key: string, field: string, value: number): number | Promise<number>;\n\n /**\n * Returns the value associated with field in the hash stored at key.\n */\n hget(key: string, field: string): Promise<string>;\n\n /**\n * Returns all fields and values of the hash stored at key.\n */\n hgetall(key: string): Promise<{ [key: string]: string }>;\n\n /**\n * Removes the specified fields from the hash stored at key. Specified fields that do not exist within\n * this hash are ignored. If key does not exist, it is treated as an empty hash and this command returns 0.\n */\n hdel(key: string, field: string): boolean | Promise<boolean>;\n\n /**\n * Returns the number of fields contained in the hash stored at key\n */\n hlen(key: string): Promise<number>;\n\n /**\n * Increments the number stored at key by one. If the key does not exist, it is set to 0 before performing\n * the operation. An error is returned if the key contains a value of the wrong type or\n * contains a string that can not be represented as integer. This operation is limited to 64-bit signed integers.\n */\n incr(key: string): Promise<number>;\n\n /**\n * Decrements the number stored at key by one. If the key does not exist, it is set to 0 before performing\n * the operation. An error is returned if the key contains a value of the wrong type or contains a string\n * that can not be represented as integer. This operation is limited to 64-bit signed integers.\n */\n decr(key: string): Promise<number>;\n\n shutdown(): void;\n}\n"],
5
5
  "mappings": ";;;;;;;;;;;;;AAAA;AAAA;",
6
6
  "names": []
7
7
  }
@@ -57,12 +57,10 @@ async function reloadFromCache() {
57
57
  import_Logger.logger.debug(`\u{1F4CB} room '${roomId}' state =>`, recreatedRoom.state.toJSON());
58
58
  }
59
59
  recreatedRoom.onRestoreRoom?.(roomHistory["cache"]);
60
- for (const previousSessionId of roomHistory.clients) {
61
- await (0, import_MatchMaker.remoteRoomCall)(
62
- recreatedRoomListing.roomId,
63
- "_reserveSeat",
64
- [previousSessionId, {}, 20, false, true]
65
- );
60
+ if (roomHistory.clients) {
61
+ for (const previousSessionId of roomHistory.clients) {
62
+ await (0, import_MatchMaker.remoteRoomCall)(recreatedRoomListing.roomId, "_reserveSeat", [previousSessionId, {}, 20, false, true]);
63
+ }
66
64
  }
67
65
  }
68
66
  if (roomHistoryList.length > 0) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/utils/DevMode.ts"],
4
- "sourcesContent": ["import debug from \"debug\";\nimport { logger } from \"../Logger\";\nimport { debugAndPrintError } from \"../Debug\";\n\nimport { getRoomById, handleCreateRoom, presence, remoteRoomCall } from \"../MatchMaker\";\nimport type { Room } from \"../Room\";\n\nexport const debugDevMode = debug('colyseus:devmode');\n\nexport let isDevMode: boolean = false;\n\nexport function setDevMode(bool: boolean) {\n isDevMode = bool;\n}\n\nexport async function reloadFromCache() {\n const roomHistoryList = Object.entries(await presence.hgetall(getRoomRestoreListKey()));\n debugDevMode(\"rooms to restore: %i\", roomHistoryList.length);\n\n for (const [roomId, value] of roomHistoryList) {\n const roomHistory = JSON.parse(value);\n debugDevMode(\"restoring room %s (%s)\", roomHistory.roomName, roomId);\n\n const recreatedRoomListing = await handleCreateRoom(roomHistory.roomName, roomHistory.clientOptions, roomId);\n const recreatedRoom = getRoomById(recreatedRoomListing.roomId);\n logger.debug(`\uD83D\uDD04 room '${roomId}' has been restored.`);\n\n // Set previous state\n if (roomHistory.hasOwnProperty(\"state\")) {\n recreatedRoom.state.decode(roomHistory.state);\n\n //\n // WORKAROUND: @colyseus/schema is not capable of encoding a decoded\n // state. thus, we need a fresh clone immediately after decoding\n //\n recreatedRoom.setState(recreatedRoom.state.clone());\n logger.debug(`\uD83D\uDCCB room '${roomId}' state =>`, recreatedRoom.state.toJSON());\n }\n\n // call `onRestoreRoom` with custom 'cache'd property.\n recreatedRoom.onRestoreRoom?.(roomHistory[\"cache\"]);\n\n // Reserve seats for clients from cached history\n for (const previousSessionId of roomHistory.clients) {\n await remoteRoomCall(recreatedRoomListing.roomId, '_reserveSeat',\n [previousSessionId, {}, 20, false, true]); // reserve seat for 20 seconds\n }\n }\n\n if (roomHistoryList.length > 0) {\n logger.debug(\"\u2705\", roomHistoryList.length, \"room(s) have been restored.\");\n }\n}\n\nexport async function cacheRoomHistory(rooms: { [roomId: string]: Room }) {\n for (const room of Object.values(rooms)) {\n const roomHistoryResult = await presence.hget(getRoomRestoreListKey(), room.roomId);\n if (roomHistoryResult) {\n try {\n const roomHistory = JSON.parse(roomHistoryResult);\n\n // custom cache method\n roomHistory[\"cache\"] = room.onCacheRoom?.();\n\n // encode state\n debugDevMode(\"caching room %s (%s)\", room.roomName, room.roomId);\n\n if (room.state) {\n roomHistory[\"state\"] = room.state.encodeAll();\n }\n\n // cache active clients and reserved seats\n roomHistory[\"clients\"] = room.clients.map((client) => client.sessionId);\n\n for (const sessionId in room['reservedSeats']) {\n roomHistory[\"clients\"].push(sessionId);\n }\n\n await presence.hset(getRoomRestoreListKey(), room.roomId, JSON.stringify(roomHistory));\n\n // Rewrite updated room history\n logger.debug(`\uD83D\uDCBE caching room '${room.roomId}' (clients: ${room.clients.length}, state size: ${(roomHistory[\"state\"] || []).length} bytes)`);\n\n } catch (e) {\n debugAndPrintError(`\u274C couldn't cache room '${room.roomId}', due to:\\n${e.stack}`);\n }\n }\n }\n}\n\nexport async function getPreviousProcessId(hostname) {\n return await presence.hget(getProcessRestoreKey(), hostname);\n}\n\nexport function getRoomRestoreListKey() {\n return 'roomhistory';\n}\n\nexport function getProcessRestoreKey() {\n return 'processhistory';\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAkB;AAClB,oBAAuB;AACvB,mBAAmC;AAEnC,wBAAwE;AAGjE,MAAM,mBAAe,aAAAA,SAAM,kBAAkB;AAE7C,IAAI,YAAqB;AAEzB,SAAS,WAAW,MAAe;AACxC,cAAY;AACd;AAEA,eAAsB,kBAAkB;AACtC,QAAM,kBAAkB,OAAO,QAAQ,MAAM,2BAAS,QAAQ,sBAAsB,CAAC,CAAC;AACtF,eAAa,wBAAwB,gBAAgB,MAAM;AAE3D,aAAW,CAAC,QAAQ,KAAK,KAAK,iBAAiB;AAC7C,UAAM,cAAc,KAAK,MAAM,KAAK;AACpC,iBAAa,0BAA0B,YAAY,UAAU,MAAM;AAEnE,UAAM,uBAAuB,UAAM,oCAAiB,YAAY,UAAU,YAAY,eAAe,MAAM;AAC3G,UAAM,oBAAgB,+BAAY,qBAAqB,MAAM;AAC7D,yBAAO,MAAM,mBAAY,4BAA4B;AAGrD,QAAI,YAAY,eAAe,OAAO,GAAG;AACvC,oBAAc,MAAM,OAAO,YAAY,KAAK;AAM5C,oBAAc,SAAS,cAAc,MAAM,MAAM,CAAC;AAClD,2BAAO,MAAM,mBAAY,oBAAoB,cAAc,MAAM,OAAO,CAAC;AAAA,IAC3E;AAGA,kBAAc,gBAAgB,YAAY,QAAQ;AAGlD,eAAW,qBAAqB,YAAY,SAAS;AACnD,gBAAM;AAAA,QAAe,qBAAqB;AAAA,QAAQ;AAAA,QAChD,CAAC,mBAAmB,CAAC,GAAG,IAAI,OAAO,IAAI;AAAA,MAAC;AAAA,IAC5C;AAAA,EACF;AAEA,MAAI,gBAAgB,SAAS,GAAG;AAC9B,yBAAO,MAAM,UAAK,gBAAgB,QAAQ,6BAA6B;AAAA,EACzE;AACF;AAEA,eAAsB,iBAAiB,OAAmC;AACxE,aAAW,QAAQ,OAAO,OAAO,KAAK,GAAG;AACvC,UAAM,oBAAoB,MAAM,2BAAS,KAAK,sBAAsB,GAAG,KAAK,MAAM;AAClF,QAAI,mBAAmB;AACrB,UAAI;AACF,cAAM,cAAc,KAAK,MAAM,iBAAiB;AAGhD,oBAAY,WAAW,KAAK,cAAc;AAG1C,qBAAa,wBAAwB,KAAK,UAAU,KAAK,MAAM;AAE/D,YAAI,KAAK,OAAO;AACd,sBAAY,WAAW,KAAK,MAAM,UAAU;AAAA,QAC9C;AAGA,oBAAY,aAAa,KAAK,QAAQ,IAAI,CAAC,WAAW,OAAO,SAAS;AAEtE,mBAAW,aAAa,KAAK,kBAAkB;AAC7C,sBAAY,WAAW,KAAK,SAAS;AAAA,QACvC;AAEA,cAAM,2BAAS,KAAK,sBAAsB,GAAG,KAAK,QAAQ,KAAK,UAAU,WAAW,CAAC;AAGrF,6BAAO,MAAM,2BAAoB,KAAK,qBAAqB,KAAK,QAAQ,wBAAwB,YAAY,YAAY,CAAC,GAAG,eAAe;AAAA,MAE7I,SAAS,GAAP;AACA,6CAAmB,+BAA0B,KAAK;AAAA,EAAqB,EAAE,OAAO;AAAA,MAClF;AAAA,IACF;AAAA,EACF;AACF;AAEA,eAAsB,qBAAqB,UAAU;AACnD,SAAO,MAAM,2BAAS,KAAK,qBAAqB,GAAG,QAAQ;AAC7D;AAEO,SAAS,wBAAwB;AACtC,SAAO;AACT;AAEO,SAAS,uBAAuB;AACrC,SAAO;AACT;",
4
+ "sourcesContent": ["import debug from \"debug\";\nimport { logger } from \"../Logger\";\nimport { debugAndPrintError } from \"../Debug\";\n\nimport { getRoomById, handleCreateRoom, presence, remoteRoomCall } from \"../MatchMaker\";\nimport type { Room } from \"../Room\";\n\nexport const debugDevMode = debug('colyseus:devmode');\n\nexport let isDevMode: boolean = false;\n\nexport function setDevMode(bool: boolean) {\n isDevMode = bool;\n}\n\nexport async function reloadFromCache() {\n const roomHistoryList = Object.entries(await presence.hgetall(getRoomRestoreListKey()));\n debugDevMode(\"rooms to restore: %i\", roomHistoryList.length);\n\n for (const [roomId, value] of roomHistoryList) {\n const roomHistory = JSON.parse(value);\n debugDevMode(\"restoring room %s (%s)\", roomHistory.roomName, roomId);\n\n const recreatedRoomListing = await handleCreateRoom(roomHistory.roomName, roomHistory.clientOptions, roomId);\n const recreatedRoom = getRoomById(recreatedRoomListing.roomId);\n logger.debug(`\uD83D\uDD04 room '${roomId}' has been restored.`);\n\n // Set previous state\n if (roomHistory.hasOwnProperty(\"state\")) {\n recreatedRoom.state.decode(roomHistory.state);\n\n //\n // WORKAROUND: @colyseus/schema is not capable of encoding a decoded\n // state. thus, we need a fresh clone immediately after decoding\n //\n recreatedRoom.setState(recreatedRoom.state.clone());\n logger.debug(`\uD83D\uDCCB room '${roomId}' state =>`, recreatedRoom.state.toJSON());\n }\n\n // call `onRestoreRoom` with custom 'cache'd property.\n recreatedRoom.onRestoreRoom?.(roomHistory[\"cache\"]);\n\n // Reserve seats for clients from cached history\n if (roomHistory.clients) {\n for (const previousSessionId of roomHistory.clients) {\n // reserve seat for 20 seconds\n await remoteRoomCall(recreatedRoomListing.roomId, '_reserveSeat', [previousSessionId, {}, 20, false, true]);\n }\n }\n }\n\n if (roomHistoryList.length > 0) {\n logger.debug(\"\u2705\", roomHistoryList.length, \"room(s) have been restored.\");\n }\n}\n\nexport async function cacheRoomHistory(rooms: { [roomId: string]: Room }) {\n for (const room of Object.values(rooms)) {\n const roomHistoryResult = await presence.hget(getRoomRestoreListKey(), room.roomId);\n if (roomHistoryResult) {\n try {\n const roomHistory = JSON.parse(roomHistoryResult);\n\n // custom cache method\n roomHistory[\"cache\"] = room.onCacheRoom?.();\n\n // encode state\n debugDevMode(\"caching room %s (%s)\", room.roomName, room.roomId);\n\n if (room.state) {\n roomHistory[\"state\"] = room.state.encodeAll();\n }\n\n // cache active clients and reserved seats\n roomHistory[\"clients\"] = room.clients.map((client) => client.sessionId);\n\n for (const sessionId in room['reservedSeats']) {\n roomHistory[\"clients\"].push(sessionId);\n }\n\n await presence.hset(getRoomRestoreListKey(), room.roomId, JSON.stringify(roomHistory));\n\n // Rewrite updated room history\n logger.debug(`\uD83D\uDCBE caching room '${room.roomId}' (clients: ${room.clients.length}, state size: ${(roomHistory[\"state\"] || []).length} bytes)`);\n\n } catch (e) {\n debugAndPrintError(`\u274C couldn't cache room '${room.roomId}', due to:\\n${e.stack}`);\n }\n }\n }\n}\n\nexport async function getPreviousProcessId(hostname) {\n return await presence.hget(getProcessRestoreKey(), hostname);\n}\n\nexport function getRoomRestoreListKey() {\n return 'roomhistory';\n}\n\nexport function getProcessRestoreKey() {\n return 'processhistory';\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAkB;AAClB,oBAAuB;AACvB,mBAAmC;AAEnC,wBAAwE;AAGjE,MAAM,mBAAe,aAAAA,SAAM,kBAAkB;AAE7C,IAAI,YAAqB;AAEzB,SAAS,WAAW,MAAe;AACxC,cAAY;AACd;AAEA,eAAsB,kBAAkB;AACtC,QAAM,kBAAkB,OAAO,QAAQ,MAAM,2BAAS,QAAQ,sBAAsB,CAAC,CAAC;AACtF,eAAa,wBAAwB,gBAAgB,MAAM;AAE3D,aAAW,CAAC,QAAQ,KAAK,KAAK,iBAAiB;AAC7C,UAAM,cAAc,KAAK,MAAM,KAAK;AACpC,iBAAa,0BAA0B,YAAY,UAAU,MAAM;AAEnE,UAAM,uBAAuB,UAAM,oCAAiB,YAAY,UAAU,YAAY,eAAe,MAAM;AAC3G,UAAM,oBAAgB,+BAAY,qBAAqB,MAAM;AAC7D,yBAAO,MAAM,mBAAY,4BAA4B;AAGrD,QAAI,YAAY,eAAe,OAAO,GAAG;AACvC,oBAAc,MAAM,OAAO,YAAY,KAAK;AAM5C,oBAAc,SAAS,cAAc,MAAM,MAAM,CAAC;AAClD,2BAAO,MAAM,mBAAY,oBAAoB,cAAc,MAAM,OAAO,CAAC;AAAA,IAC3E;AAGA,kBAAc,gBAAgB,YAAY,QAAQ;AAGlD,QAAI,YAAY,SAAS;AACvB,iBAAW,qBAAqB,YAAY,SAAS;AAEnD,kBAAM,kCAAe,qBAAqB,QAAQ,gBAAgB,CAAC,mBAAmB,CAAC,GAAG,IAAI,OAAO,IAAI,CAAC;AAAA,MAC5G;AAAA,IACF;AAAA,EACF;AAEA,MAAI,gBAAgB,SAAS,GAAG;AAC9B,yBAAO,MAAM,UAAK,gBAAgB,QAAQ,6BAA6B;AAAA,EACzE;AACF;AAEA,eAAsB,iBAAiB,OAAmC;AACxE,aAAW,QAAQ,OAAO,OAAO,KAAK,GAAG;AACvC,UAAM,oBAAoB,MAAM,2BAAS,KAAK,sBAAsB,GAAG,KAAK,MAAM;AAClF,QAAI,mBAAmB;AACrB,UAAI;AACF,cAAM,cAAc,KAAK,MAAM,iBAAiB;AAGhD,oBAAY,WAAW,KAAK,cAAc;AAG1C,qBAAa,wBAAwB,KAAK,UAAU,KAAK,MAAM;AAE/D,YAAI,KAAK,OAAO;AACd,sBAAY,WAAW,KAAK,MAAM,UAAU;AAAA,QAC9C;AAGA,oBAAY,aAAa,KAAK,QAAQ,IAAI,CAAC,WAAW,OAAO,SAAS;AAEtE,mBAAW,aAAa,KAAK,kBAAkB;AAC7C,sBAAY,WAAW,KAAK,SAAS;AAAA,QACvC;AAEA,cAAM,2BAAS,KAAK,sBAAsB,GAAG,KAAK,QAAQ,KAAK,UAAU,WAAW,CAAC;AAGrF,6BAAO,MAAM,2BAAoB,KAAK,qBAAqB,KAAK,QAAQ,wBAAwB,YAAY,YAAY,CAAC,GAAG,eAAe;AAAA,MAE7I,SAAS,GAAP;AACA,6CAAmB,+BAA0B,KAAK;AAAA,EAAqB,EAAE,OAAO;AAAA,MAClF;AAAA,IACF;AAAA,EACF;AACF;AAEA,eAAsB,qBAAqB,UAAU;AACnD,SAAO,MAAM,2BAAS,KAAK,qBAAqB,GAAG,QAAQ;AAC7D;AAEO,SAAS,wBAAwB;AACtC,SAAO;AACT;AAEO,SAAS,uBAAuB;AACrC,SAAO;AACT;",
6
6
  "names": ["debug"]
7
7
  }
@@ -22,12 +22,10 @@ async function reloadFromCache() {
22
22
  logger.debug(`\u{1F4CB} room '${roomId}' state =>`, recreatedRoom.state.toJSON());
23
23
  }
24
24
  recreatedRoom.onRestoreRoom?.(roomHistory["cache"]);
25
- for (const previousSessionId of roomHistory.clients) {
26
- await remoteRoomCall(
27
- recreatedRoomListing.roomId,
28
- "_reserveSeat",
29
- [previousSessionId, {}, 20, false, true]
30
- );
25
+ if (roomHistory.clients) {
26
+ for (const previousSessionId of roomHistory.clients) {
27
+ await remoteRoomCall(recreatedRoomListing.roomId, "_reserveSeat", [previousSessionId, {}, 20, false, true]);
28
+ }
31
29
  }
32
30
  }
33
31
  if (roomHistoryList.length > 0) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/utils/DevMode.ts"],
4
- "sourcesContent": ["import debug from \"debug\";\nimport { logger } from \"../Logger\";\nimport { debugAndPrintError } from \"../Debug\";\n\nimport { getRoomById, handleCreateRoom, presence, remoteRoomCall } from \"../MatchMaker\";\nimport type { Room } from \"../Room\";\n\nexport const debugDevMode = debug('colyseus:devmode');\n\nexport let isDevMode: boolean = false;\n\nexport function setDevMode(bool: boolean) {\n isDevMode = bool;\n}\n\nexport async function reloadFromCache() {\n const roomHistoryList = Object.entries(await presence.hgetall(getRoomRestoreListKey()));\n debugDevMode(\"rooms to restore: %i\", roomHistoryList.length);\n\n for (const [roomId, value] of roomHistoryList) {\n const roomHistory = JSON.parse(value);\n debugDevMode(\"restoring room %s (%s)\", roomHistory.roomName, roomId);\n\n const recreatedRoomListing = await handleCreateRoom(roomHistory.roomName, roomHistory.clientOptions, roomId);\n const recreatedRoom = getRoomById(recreatedRoomListing.roomId);\n logger.debug(`\uD83D\uDD04 room '${roomId}' has been restored.`);\n\n // Set previous state\n if (roomHistory.hasOwnProperty(\"state\")) {\n recreatedRoom.state.decode(roomHistory.state);\n\n //\n // WORKAROUND: @colyseus/schema is not capable of encoding a decoded\n // state. thus, we need a fresh clone immediately after decoding\n //\n recreatedRoom.setState(recreatedRoom.state.clone());\n logger.debug(`\uD83D\uDCCB room '${roomId}' state =>`, recreatedRoom.state.toJSON());\n }\n\n // call `onRestoreRoom` with custom 'cache'd property.\n recreatedRoom.onRestoreRoom?.(roomHistory[\"cache\"]);\n\n // Reserve seats for clients from cached history\n for (const previousSessionId of roomHistory.clients) {\n await remoteRoomCall(recreatedRoomListing.roomId, '_reserveSeat',\n [previousSessionId, {}, 20, false, true]); // reserve seat for 20 seconds\n }\n }\n\n if (roomHistoryList.length > 0) {\n logger.debug(\"\u2705\", roomHistoryList.length, \"room(s) have been restored.\");\n }\n}\n\nexport async function cacheRoomHistory(rooms: { [roomId: string]: Room }) {\n for (const room of Object.values(rooms)) {\n const roomHistoryResult = await presence.hget(getRoomRestoreListKey(), room.roomId);\n if (roomHistoryResult) {\n try {\n const roomHistory = JSON.parse(roomHistoryResult);\n\n // custom cache method\n roomHistory[\"cache\"] = room.onCacheRoom?.();\n\n // encode state\n debugDevMode(\"caching room %s (%s)\", room.roomName, room.roomId);\n\n if (room.state) {\n roomHistory[\"state\"] = room.state.encodeAll();\n }\n\n // cache active clients and reserved seats\n roomHistory[\"clients\"] = room.clients.map((client) => client.sessionId);\n\n for (const sessionId in room['reservedSeats']) {\n roomHistory[\"clients\"].push(sessionId);\n }\n\n await presence.hset(getRoomRestoreListKey(), room.roomId, JSON.stringify(roomHistory));\n\n // Rewrite updated room history\n logger.debug(`\uD83D\uDCBE caching room '${room.roomId}' (clients: ${room.clients.length}, state size: ${(roomHistory[\"state\"] || []).length} bytes)`);\n\n } catch (e) {\n debugAndPrintError(`\u274C couldn't cache room '${room.roomId}', due to:\\n${e.stack}`);\n }\n }\n }\n}\n\nexport async function getPreviousProcessId(hostname) {\n return await presence.hget(getProcessRestoreKey(), hostname);\n}\n\nexport function getRoomRestoreListKey() {\n return 'roomhistory';\n}\n\nexport function getProcessRestoreKey() {\n return 'processhistory';\n}\n"],
5
- "mappings": "AAAA,OAAO,WAAW;AAClB,SAAS,cAAc;AACvB,SAAS,0BAA0B;AAEnC,SAAS,aAAa,kBAAkB,UAAU,sBAAsB;AAGjE,MAAM,eAAe,MAAM,kBAAkB;AAE7C,IAAI,YAAqB;AAEzB,SAAS,WAAW,MAAe;AACxC,cAAY;AACd;AAEA,eAAsB,kBAAkB;AACtC,QAAM,kBAAkB,OAAO,QAAQ,MAAM,SAAS,QAAQ,sBAAsB,CAAC,CAAC;AACtF,eAAa,wBAAwB,gBAAgB,MAAM;AAE3D,aAAW,CAAC,QAAQ,KAAK,KAAK,iBAAiB;AAC7C,UAAM,cAAc,KAAK,MAAM,KAAK;AACpC,iBAAa,0BAA0B,YAAY,UAAU,MAAM;AAEnE,UAAM,uBAAuB,MAAM,iBAAiB,YAAY,UAAU,YAAY,eAAe,MAAM;AAC3G,UAAM,gBAAgB,YAAY,qBAAqB,MAAM;AAC7D,WAAO,MAAM,mBAAY,4BAA4B;AAGrD,QAAI,YAAY,eAAe,OAAO,GAAG;AACvC,oBAAc,MAAM,OAAO,YAAY,KAAK;AAM5C,oBAAc,SAAS,cAAc,MAAM,MAAM,CAAC;AAClD,aAAO,MAAM,mBAAY,oBAAoB,cAAc,MAAM,OAAO,CAAC;AAAA,IAC3E;AAGA,kBAAc,gBAAgB,YAAY,QAAQ;AAGlD,eAAW,qBAAqB,YAAY,SAAS;AACnD,YAAM;AAAA,QAAe,qBAAqB;AAAA,QAAQ;AAAA,QAChD,CAAC,mBAAmB,CAAC,GAAG,IAAI,OAAO,IAAI;AAAA,MAAC;AAAA,IAC5C;AAAA,EACF;AAEA,MAAI,gBAAgB,SAAS,GAAG;AAC9B,WAAO,MAAM,UAAK,gBAAgB,QAAQ,6BAA6B;AAAA,EACzE;AACF;AAEA,eAAsB,iBAAiB,OAAmC;AACxE,aAAW,QAAQ,OAAO,OAAO,KAAK,GAAG;AACvC,UAAM,oBAAoB,MAAM,SAAS,KAAK,sBAAsB,GAAG,KAAK,MAAM;AAClF,QAAI,mBAAmB;AACrB,UAAI;AACF,cAAM,cAAc,KAAK,MAAM,iBAAiB;AAGhD,oBAAY,WAAW,KAAK,cAAc;AAG1C,qBAAa,wBAAwB,KAAK,UAAU,KAAK,MAAM;AAE/D,YAAI,KAAK,OAAO;AACd,sBAAY,WAAW,KAAK,MAAM,UAAU;AAAA,QAC9C;AAGA,oBAAY,aAAa,KAAK,QAAQ,IAAI,CAAC,WAAW,OAAO,SAAS;AAEtE,mBAAW,aAAa,KAAK,kBAAkB;AAC7C,sBAAY,WAAW,KAAK,SAAS;AAAA,QACvC;AAEA,cAAM,SAAS,KAAK,sBAAsB,GAAG,KAAK,QAAQ,KAAK,UAAU,WAAW,CAAC;AAGrF,eAAO,MAAM,2BAAoB,KAAK,qBAAqB,KAAK,QAAQ,wBAAwB,YAAY,YAAY,CAAC,GAAG,eAAe;AAAA,MAE7I,SAAS,GAAP;AACA,2BAAmB,+BAA0B,KAAK;AAAA,EAAqB,EAAE,OAAO;AAAA,MAClF;AAAA,IACF;AAAA,EACF;AACF;AAEA,eAAsB,qBAAqB,UAAU;AACnD,SAAO,MAAM,SAAS,KAAK,qBAAqB,GAAG,QAAQ;AAC7D;AAEO,SAAS,wBAAwB;AACtC,SAAO;AACT;AAEO,SAAS,uBAAuB;AACrC,SAAO;AACT;",
4
+ "sourcesContent": ["import debug from \"debug\";\nimport { logger } from \"../Logger\";\nimport { debugAndPrintError } from \"../Debug\";\n\nimport { getRoomById, handleCreateRoom, presence, remoteRoomCall } from \"../MatchMaker\";\nimport type { Room } from \"../Room\";\n\nexport const debugDevMode = debug('colyseus:devmode');\n\nexport let isDevMode: boolean = false;\n\nexport function setDevMode(bool: boolean) {\n isDevMode = bool;\n}\n\nexport async function reloadFromCache() {\n const roomHistoryList = Object.entries(await presence.hgetall(getRoomRestoreListKey()));\n debugDevMode(\"rooms to restore: %i\", roomHistoryList.length);\n\n for (const [roomId, value] of roomHistoryList) {\n const roomHistory = JSON.parse(value);\n debugDevMode(\"restoring room %s (%s)\", roomHistory.roomName, roomId);\n\n const recreatedRoomListing = await handleCreateRoom(roomHistory.roomName, roomHistory.clientOptions, roomId);\n const recreatedRoom = getRoomById(recreatedRoomListing.roomId);\n logger.debug(`\uD83D\uDD04 room '${roomId}' has been restored.`);\n\n // Set previous state\n if (roomHistory.hasOwnProperty(\"state\")) {\n recreatedRoom.state.decode(roomHistory.state);\n\n //\n // WORKAROUND: @colyseus/schema is not capable of encoding a decoded\n // state. thus, we need a fresh clone immediately after decoding\n //\n recreatedRoom.setState(recreatedRoom.state.clone());\n logger.debug(`\uD83D\uDCCB room '${roomId}' state =>`, recreatedRoom.state.toJSON());\n }\n\n // call `onRestoreRoom` with custom 'cache'd property.\n recreatedRoom.onRestoreRoom?.(roomHistory[\"cache\"]);\n\n // Reserve seats for clients from cached history\n if (roomHistory.clients) {\n for (const previousSessionId of roomHistory.clients) {\n // reserve seat for 20 seconds\n await remoteRoomCall(recreatedRoomListing.roomId, '_reserveSeat', [previousSessionId, {}, 20, false, true]);\n }\n }\n }\n\n if (roomHistoryList.length > 0) {\n logger.debug(\"\u2705\", roomHistoryList.length, \"room(s) have been restored.\");\n }\n}\n\nexport async function cacheRoomHistory(rooms: { [roomId: string]: Room }) {\n for (const room of Object.values(rooms)) {\n const roomHistoryResult = await presence.hget(getRoomRestoreListKey(), room.roomId);\n if (roomHistoryResult) {\n try {\n const roomHistory = JSON.parse(roomHistoryResult);\n\n // custom cache method\n roomHistory[\"cache\"] = room.onCacheRoom?.();\n\n // encode state\n debugDevMode(\"caching room %s (%s)\", room.roomName, room.roomId);\n\n if (room.state) {\n roomHistory[\"state\"] = room.state.encodeAll();\n }\n\n // cache active clients and reserved seats\n roomHistory[\"clients\"] = room.clients.map((client) => client.sessionId);\n\n for (const sessionId in room['reservedSeats']) {\n roomHistory[\"clients\"].push(sessionId);\n }\n\n await presence.hset(getRoomRestoreListKey(), room.roomId, JSON.stringify(roomHistory));\n\n // Rewrite updated room history\n logger.debug(`\uD83D\uDCBE caching room '${room.roomId}' (clients: ${room.clients.length}, state size: ${(roomHistory[\"state\"] || []).length} bytes)`);\n\n } catch (e) {\n debugAndPrintError(`\u274C couldn't cache room '${room.roomId}', due to:\\n${e.stack}`);\n }\n }\n }\n}\n\nexport async function getPreviousProcessId(hostname) {\n return await presence.hget(getProcessRestoreKey(), hostname);\n}\n\nexport function getRoomRestoreListKey() {\n return 'roomhistory';\n}\n\nexport function getProcessRestoreKey() {\n return 'processhistory';\n}\n"],
5
+ "mappings": "AAAA,OAAO,WAAW;AAClB,SAAS,cAAc;AACvB,SAAS,0BAA0B;AAEnC,SAAS,aAAa,kBAAkB,UAAU,sBAAsB;AAGjE,MAAM,eAAe,MAAM,kBAAkB;AAE7C,IAAI,YAAqB;AAEzB,SAAS,WAAW,MAAe;AACxC,cAAY;AACd;AAEA,eAAsB,kBAAkB;AACtC,QAAM,kBAAkB,OAAO,QAAQ,MAAM,SAAS,QAAQ,sBAAsB,CAAC,CAAC;AACtF,eAAa,wBAAwB,gBAAgB,MAAM;AAE3D,aAAW,CAAC,QAAQ,KAAK,KAAK,iBAAiB;AAC7C,UAAM,cAAc,KAAK,MAAM,KAAK;AACpC,iBAAa,0BAA0B,YAAY,UAAU,MAAM;AAEnE,UAAM,uBAAuB,MAAM,iBAAiB,YAAY,UAAU,YAAY,eAAe,MAAM;AAC3G,UAAM,gBAAgB,YAAY,qBAAqB,MAAM;AAC7D,WAAO,MAAM,mBAAY,4BAA4B;AAGrD,QAAI,YAAY,eAAe,OAAO,GAAG;AACvC,oBAAc,MAAM,OAAO,YAAY,KAAK;AAM5C,oBAAc,SAAS,cAAc,MAAM,MAAM,CAAC;AAClD,aAAO,MAAM,mBAAY,oBAAoB,cAAc,MAAM,OAAO,CAAC;AAAA,IAC3E;AAGA,kBAAc,gBAAgB,YAAY,QAAQ;AAGlD,QAAI,YAAY,SAAS;AACvB,iBAAW,qBAAqB,YAAY,SAAS;AAEnD,cAAM,eAAe,qBAAqB,QAAQ,gBAAgB,CAAC,mBAAmB,CAAC,GAAG,IAAI,OAAO,IAAI,CAAC;AAAA,MAC5G;AAAA,IACF;AAAA,EACF;AAEA,MAAI,gBAAgB,SAAS,GAAG;AAC9B,WAAO,MAAM,UAAK,gBAAgB,QAAQ,6BAA6B;AAAA,EACzE;AACF;AAEA,eAAsB,iBAAiB,OAAmC;AACxE,aAAW,QAAQ,OAAO,OAAO,KAAK,GAAG;AACvC,UAAM,oBAAoB,MAAM,SAAS,KAAK,sBAAsB,GAAG,KAAK,MAAM;AAClF,QAAI,mBAAmB;AACrB,UAAI;AACF,cAAM,cAAc,KAAK,MAAM,iBAAiB;AAGhD,oBAAY,WAAW,KAAK,cAAc;AAG1C,qBAAa,wBAAwB,KAAK,UAAU,KAAK,MAAM;AAE/D,YAAI,KAAK,OAAO;AACd,sBAAY,WAAW,KAAK,MAAM,UAAU;AAAA,QAC9C;AAGA,oBAAY,aAAa,KAAK,QAAQ,IAAI,CAAC,WAAW,OAAO,SAAS;AAEtE,mBAAW,aAAa,KAAK,kBAAkB;AAC7C,sBAAY,WAAW,KAAK,SAAS;AAAA,QACvC;AAEA,cAAM,SAAS,KAAK,sBAAsB,GAAG,KAAK,QAAQ,KAAK,UAAU,WAAW,CAAC;AAGrF,eAAO,MAAM,2BAAoB,KAAK,qBAAqB,KAAK,QAAQ,wBAAwB,YAAY,YAAY,CAAC,GAAG,eAAe;AAAA,MAE7I,SAAS,GAAP;AACA,2BAAmB,+BAA0B,KAAK;AAAA,EAAqB,EAAE,OAAO;AAAA,MAClF;AAAA,IACF;AAAA,EACF;AACF;AAEA,eAAsB,qBAAqB,UAAU;AACnD,SAAO,MAAM,SAAS,KAAK,qBAAqB,GAAG,QAAQ;AAC7D;AAEO,SAAS,wBAAwB;AACtC,SAAO;AACT;AAEO,SAAS,uBAAuB;AACrC,SAAO;AACT;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@colyseus/core",
3
- "version": "0.15.33",
3
+ "version": "0.15.35",
4
4
  "description": "Multiplayer Framework for Node.js.",
5
5
  "input": "./src/index.ts",
6
6
  "main": "./build/index.js",