@colyseus/core 0.15.17 → 0.15.18

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.
@@ -76,9 +76,11 @@ export declare function findOneRoomAvailable(roomName: string, clientOptions: Cl
76
76
  * @returns Promise<any> - Returned value from the called or retrieved method/attribute.
77
77
  */
78
78
  export declare function remoteRoomCall<R = any>(roomId: string, method: string, args?: any[], rejectionTimeout?: number): Promise<R>;
79
- export declare function defineRoomType<T extends Type<Room>>(name: string, klass: T, defaultOptions?: Parameters<NonNullable<InstanceType<T>['onCreate']>>[0]): RegisteredHandler;
80
- export declare function removeRoomType(name: string): void;
81
- export declare function hasHandler(name: string): boolean;
79
+ export declare function defineRoomType<T extends Type<Room>>(roomName: string, klass: T, defaultOptions?: Parameters<NonNullable<InstanceType<T>['onCreate']>>[0]): RegisteredHandler;
80
+ export declare function removeRoomType(roomName: string): void;
81
+ export declare function hasHandler(roomName: string): boolean;
82
+ export declare function getHandler(roomName: string): RegisteredHandler;
83
+ export declare function getRoomClass(roomName: string): Type<Room<any, any>>;
82
84
  /**
83
85
  * Creates a new room.
84
86
  *
@@ -30,7 +30,9 @@ __export(MatchMaker_exports, {
30
30
  disconnectAll: () => disconnectAll,
31
31
  driver: () => driver,
32
32
  findOneRoomAvailable: () => findOneRoomAvailable,
33
+ getHandler: () => getHandler,
33
34
  getRoomById: () => getRoomById,
35
+ getRoomClass: () => getRoomClass,
34
36
  gracefullyShutdown: () => gracefullyShutdown,
35
37
  handleCreateRoom: () => handleCreateRoom,
36
38
  hasHandler: () => hasHandler,
@@ -75,9 +77,11 @@ let presence;
75
77
  let driver;
76
78
  let selectProcessIdToCreateRoom;
77
79
  let isGracefullyShuttingDown;
78
- let onReady;
80
+ let onReady = new import_Utils.Deferred();
79
81
  async function setup(_presence, _driver, _publicAddress, _selectProcessIdToCreateRoom) {
80
- onReady = new import_Utils.Deferred();
82
+ if (onReady === void 0) {
83
+ onReady = new import_Utils.Deferred();
84
+ }
81
85
  presence = _presence || new import_LocalPresence.LocalPresence();
82
86
  driver = _driver || new import_driver.LocalDriver();
83
87
  publicAddress = _publicAddress;
@@ -182,10 +186,7 @@ async function query(conditions = {}) {
182
186
  }
183
187
  async function findOneRoomAvailable(roomName, clientOptions) {
184
188
  return await awaitRoomAvailable(roomName, async () => {
185
- const handler = handlers[roomName];
186
- if (!handler) {
187
- throw new import_ServerError.ServerError(import_Protocol.ErrorCode.MATCHMAKE_NO_HANDLER, `provided room name "${roomName}" not defined`);
188
- }
189
+ const handler = getHandler(roomName);
189
190
  const roomQuery = driver.findOne({
190
191
  locked: false,
191
192
  name: roomName,
@@ -214,22 +215,38 @@ async function remoteRoomCall(roomId, method, args, rejectionTimeout = import_Ut
214
215
  return !args && typeof room[method] !== "function" ? room[method] : await room[method].apply(room, args && args.map((arg) => JSON.parse(JSON.stringify(arg))));
215
216
  }
216
217
  }
217
- function defineRoomType(name, klass, defaultOptions) {
218
+ function defineRoomType(roomName, klass, defaultOptions) {
218
219
  const registeredHandler = new import_RegisteredHandler.RegisteredHandler(klass, defaultOptions);
219
- handlers[name] = registeredHandler;
220
+ handlers[roomName] = registeredHandler;
221
+ if (klass.prototype["onAuth"] !== import_Room.Room.prototype["onAuth"]) {
222
+ if (klass["onAuth"] !== import_Room.Room["onAuth"]) {
223
+ console.log(`\u274C "${roomName}"'s onAuth() defined at the instance level will be ignored.`);
224
+ }
225
+ }
220
226
  if (!import_DevMode.isDevMode) {
221
- cleanupStaleRooms(name);
227
+ cleanupStaleRooms(roomName);
222
228
  }
223
229
  return registeredHandler;
224
230
  }
225
- function removeRoomType(name) {
226
- delete handlers[name];
231
+ function removeRoomType(roomName) {
232
+ delete handlers[roomName];
227
233
  if (!import_DevMode.isDevMode) {
228
- cleanupStaleRooms(name);
234
+ cleanupStaleRooms(roomName);
229
235
  }
230
236
  }
231
- function hasHandler(name) {
232
- return handlers[name] !== void 0;
237
+ function hasHandler(roomName) {
238
+ console.warn("hasHandler() is deprecated. Use getHandler() instead.");
239
+ return handlers[roomName] !== void 0;
240
+ }
241
+ function getHandler(roomName) {
242
+ const handler = handlers[roomName];
243
+ if (!handler) {
244
+ throw new import_ServerError.ServerError(import_Protocol.ErrorCode.MATCHMAKE_NO_HANDLER, `provided room name "${roomName}" not defined`);
245
+ }
246
+ return handler;
247
+ }
248
+ function getRoomClass(roomName) {
249
+ return handlers[roomName]?.klass;
233
250
  }
234
251
  async function createRoom(roomName, clientOptions) {
235
252
  const selectedProcessId = await selectProcessIdToCreateRoom(roomName, clientOptions);
@@ -261,11 +278,8 @@ async function createRoom(roomName, clientOptions) {
261
278
  return room;
262
279
  }
263
280
  async function handleCreateRoom(roomName, clientOptions, restoringRoomId) {
264
- const registeredHandler = handlers[roomName];
265
- if (!registeredHandler) {
266
- throw new import_ServerError.ServerError(import_Protocol.ErrorCode.MATCHMAKE_NO_HANDLER, `provided room name "${roomName}" not defined`);
267
- }
268
- const room = new registeredHandler.klass();
281
+ const handler = getHandler(roomName);
282
+ const room = new handler.klass();
269
283
  if (restoringRoomId && import_DevMode.isDevMode) {
270
284
  room.roomId = restoringRoomId;
271
285
  } else {
@@ -273,7 +287,7 @@ async function handleCreateRoom(roomName, clientOptions, restoringRoomId) {
273
287
  }
274
288
  room.roomName = roomName;
275
289
  room.presence = presence;
276
- const additionalListingData = registeredHandler.getFilterOptions(clientOptions);
290
+ const additionalListingData = handler.getFilterOptions(clientOptions);
277
291
  if (publicAddress) {
278
292
  additionalListingData.publicAddress = publicAddress;
279
293
  }
@@ -284,7 +298,7 @@ async function handleCreateRoom(roomName, clientOptions, restoringRoomId) {
284
298
  });
285
299
  if (room.onCreate) {
286
300
  try {
287
- await room.onCreate((0, import_Utils.merge)({}, clientOptions, registeredHandler.options));
301
+ await room.onCreate((0, import_Utils.merge)({}, clientOptions, handler.options));
288
302
  } catch (e) {
289
303
  (0, import_Debug.debugAndPrintError)(e);
290
304
  throw new import_ServerError.ServerError(
@@ -303,11 +317,12 @@ async function handleCreateRoom(roomName, clientOptions, restoringRoomId) {
303
317
  room._events.on("unlock", unlockRoom.bind(this, room));
304
318
  room._events.on("join", onClientJoinRoom.bind(this, room));
305
319
  room._events.on("leave", onClientLeaveRoom.bind(this, room));
320
+ room._events.on("visibility-change", onVisibilityChange.bind(this, room));
306
321
  room._events.once("dispose", disposeRoom.bind(this, roomName, room));
307
322
  room._events.once("disconnect", () => room._events.removeAllListeners());
308
323
  await createRoomReferences(room, true);
309
324
  await room.listing.save();
310
- registeredHandler.emit("create", room);
325
+ handler.emit("create", room);
311
326
  return room.listing;
312
327
  }
313
328
  function getRoomById(roomId) {
@@ -328,6 +343,7 @@ async function gracefullyShutdown() {
328
343
  return Promise.reject("already_shutting_down");
329
344
  }
330
345
  isGracefullyShuttingDown = true;
346
+ onReady = void 0;
331
347
  (0, import_Debug.debugMatchMaking)(`${processId} is shutting down!`);
332
348
  if (import_DevMode.isDevMode) {
333
349
  await (0, import_DevMode.cacheRoomHistory)(rooms);
@@ -431,6 +447,9 @@ async function unlockRoom(room) {
431
447
  handlers[room.roomName].emit("unlock", room);
432
448
  }
433
449
  }
450
+ function onVisibilityChange(room, isInvisible) {
451
+ handlers[room.roomName].emit("visibility-change", room, isInvisible);
452
+ }
434
453
  async function disposeRoom(roomName, room) {
435
454
  (0, import_Debug.debugMatchMaking)("disposing '%s' (%s) on processId '%s'", roomName, room.roomId, processId);
436
455
  if (!isGracefullyShuttingDown) {
@@ -463,7 +482,9 @@ function getProcessChannel(id = processId) {
463
482
  disconnectAll,
464
483
  driver,
465
484
  findOneRoomAvailable,
485
+ getHandler,
466
486
  getRoomById,
487
+ getRoomClass,
467
488
  gracefullyShutdown,
468
489
  handleCreateRoom,
469
490
  hasHandler,
@@ -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 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;\n\n/**\n * @private\n */\nexport async function setup(\n _presence?: Presence,\n _driver?: MatchMakerDriver,\n _publicAddress?: string,\n _selectProcessIdToCreateRoom?: SelectProcessIdCallback,\n) {\n onReady = new Deferred();\n presence = _presence || new LocalPresence();\n driver = _driver || new LocalDriver();\n publicAddress = _publicAddress;\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]\n .processId;\n };\n\n /**\n * Process-level subscription\n * - handle remote process healthcheck\n * - handle remote room creation\n */\n 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 await stats.excludeProcess(stat.processId);\n }\n }));\n }\n\n await stats.reset();\n\n if (isDevMode) {\n await reloadFromCache();\n }\n\n onReady.resolve();\n}\n\n/**\n * Join or create into a room and return seat reservation\n */\nexport async function joinOrCreate(roomName: string, clientOptions: ClientOptions = {}) {\n return await retry<Promise<SeatReservation>>(async () => {\n let room = await findOneRoomAvailable(roomName, clientOptions);\n\n if (!room) {\n room = await createRoom(roomName, clientOptions);\n }\n\n return await reserveSeatFor(room, clientOptions);\n }, 5, [SeatReservationError]);\n}\n\n/**\n * Create a room and return seat reservation\n */\nexport async function create(roomName: string, clientOptions: ClientOptions = {}) {\n const room = await createRoom(roomName, clientOptions);\n return reserveSeatFor(room, clientOptions);\n}\n\n/**\n * Join a room and return seat reservation\n */\nexport async function join(roomName: string, clientOptions: ClientOptions = {}) {\n return await retry<Promise<SeatReservation>>(async () => {\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);\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 // 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 *\n * @returns Promise<SeatReservation> - A promise which contains `sessionId` and `RoomListingData`.\n */\nexport async function joinById(roomId: string, clientOptions: ClientOptions = {}) {\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 return reserveSeatFor(room, clientOptions);\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 = handlers[roomName];\n if (!handler) {\n throw new ServerError( ErrorCode.MATCHMAKE_NO_HANDLER, `provided room name \"${roomName}\" not defined`);\n }\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 && args.map((arg) => JSON.parse(JSON.stringify(arg)))));\n }\n}\n\nexport function defineRoomType<T extends Type<Room>>(\n name: string,\n klass: T,\n defaultOptions?: Parameters<NonNullable<InstanceType<T>['onCreate']>>[0],\n) {\n const registeredHandler = new RegisteredHandler(klass, defaultOptions);\n\n handlers[name] = registeredHandler;\n\n if (!isDevMode) {\n cleanupStaleRooms(name);\n }\n\n return registeredHandler;\n}\n\nexport function removeRoomType(name: string) {\n delete handlers[name];\n\n if (!isDevMode) {\n cleanupStaleRooms(name);\n }\n}\n\nexport function hasHandler(name: string) {\n return handlers[ name ] !== undefined;\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 = await selectProcessIdToCreateRoom(roomName, clientOptions);\n\n let room: RoomListingData;\n 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 debugAndPrintError(e);\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 }\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 registeredHandler = handlers[roomName];\n\n if (!registeredHandler) {\n throw new ServerError( ErrorCode.MATCHMAKE_NO_HANDLER, `provided room name \"${roomName}\" not defined`);\n }\n\n const room = new registeredHandler.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 = registeredHandler.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, registeredHandler.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.once('dispose', disposeRoom.bind(this, roomName, room));\n room._events.once('disconnect', () => room._events.removeAllListeners());\n\n // room always start unlocked\n await createRoomReferences(room, true);\n await room.listing.save();\n\n registeredHandler.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)) { continue; }\n promises.push(rooms[roomId].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\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 // 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: 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]);\n\n } catch (e) {\n debugMatchMaking(e);\n successfulSeatReservation = false;\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\nasync function cleanupStaleRooms(roomName: 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 const cachedRooms = await driver.find({ name: roomName }, { _id: 1 });\n\n // remove connecting counts\n await presence.del(getHandlerConcurrencyKey(roomName));\n\n await Promise.all(cachedRooms.map(async (room) => {\n try {\n // use hardcoded short timeout for cleaning up stale rooms.\n await remoteRoomCall(room.roomId, 'roomId');\n\n } catch (e) {\n debugMatchMaking(`cleaning up stale room '${roomName}', roomId: ${room.roomId}`);\n room.remove();\n }\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\nasync function disposeRoom(roomName: string, room: Room) {\n debugMatchMaking('disposing \\'%s\\' (%s) on processId \\'%s\\'', roomName, room.roomId, processId);\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}"],
5
- "mappings": "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",
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 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 driver = _driver || new LocalDriver();\n publicAddress = _publicAddress;\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]\n .processId;\n };\n\n /**\n * Process-level subscription\n * - handle remote process healthcheck\n * - handle remote room creation\n */\n 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 await stats.excludeProcess(stat.processId);\n }\n }));\n }\n\n await stats.reset();\n\n if (isDevMode) {\n await reloadFromCache();\n }\n\n onReady.resolve();\n}\n\n/**\n * Join or create into a room and return seat reservation\n */\nexport async function joinOrCreate(roomName: string, clientOptions: ClientOptions = {}) {\n return await retry<Promise<SeatReservation>>(async () => {\n let room = await findOneRoomAvailable(roomName, clientOptions);\n\n if (!room) {\n room = await createRoom(roomName, clientOptions);\n }\n\n return await reserveSeatFor(room, clientOptions);\n }, 5, [SeatReservationError]);\n}\n\n/**\n * Create a room and return seat reservation\n */\nexport async function create(roomName: string, clientOptions: ClientOptions = {}) {\n const room = await createRoom(roomName, clientOptions);\n return reserveSeatFor(room, clientOptions);\n}\n\n/**\n * Join a room and return seat reservation\n */\nexport async function join(roomName: string, clientOptions: ClientOptions = {}) {\n return await retry<Promise<SeatReservation>>(async () => {\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);\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 *\n * @returns Promise<SeatReservation> - A promise which contains `sessionId` and `RoomListingData`.\n */\nexport async function joinById(roomId: string, clientOptions: ClientOptions = {}) {\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 return reserveSeatFor(room, clientOptions);\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 && args.map((arg) => JSON.parse(JSON.stringify(arg)))));\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 // console.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 console.log(`\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 console.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) {\n return handlers[roomName]?.klass;\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 = await selectProcessIdToCreateRoom(roomName, clientOptions);\n\n let room: RoomListingData;\n 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 debugAndPrintError(e);\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 }\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 room._events.once('disconnect', () => room._events.removeAllListeners());\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)) { continue; }\n promises.push(rooms[roomId].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 // 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: 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]);\n\n } catch (e) {\n debugMatchMaking(e);\n successfulSeatReservation = false;\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\nasync function cleanupStaleRooms(roomName: 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 const cachedRooms = await driver.find({ name: roomName }, { _id: 1 });\n\n // remove connecting counts\n await presence.del(getHandlerConcurrencyKey(roomName));\n\n await Promise.all(cachedRooms.map(async (room) => {\n try {\n // use hardcoded short timeout for cleaning up stale rooms.\n await remoteRoomCall(room.roomId, 'roomId');\n\n } catch (e) {\n debugMatchMaking(`cleaning up stale room '${roomName}', roomId: ${room.roomId}`);\n room.remove();\n }\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\\'', roomName, room.roomId, processId);\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}"],
5
+ "mappings": "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",
6
6
  "names": ["controller"]
7
7
  }
@@ -3,7 +3,7 @@ import { requestFromIPC, subscribeIPC } from "./IPC";
3
3
  import { Deferred, generateId, merge, REMOTE_ROOM_SHORT_TIMEOUT, retry } from "./utils/Utils";
4
4
  import { isDevMode, cacheRoomHistory, getPreviousProcessId, getRoomRestoreListKey, reloadFromCache } from "./utils/DevMode";
5
5
  import { RegisteredHandler } from "./matchmaker/RegisteredHandler";
6
- import { RoomInternalState } from "./Room";
6
+ import { Room, RoomInternalState } from "./Room";
7
7
  import { LocalPresence } from "./presence/LocalPresence";
8
8
  import { debugAndPrintError, debugMatchMaking } from "./Debug";
9
9
  import { SeatReservationError } from "./errors/SeatReservationError";
@@ -21,9 +21,11 @@ let presence;
21
21
  let driver;
22
22
  let selectProcessIdToCreateRoom;
23
23
  let isGracefullyShuttingDown;
24
- let onReady;
24
+ let onReady = new Deferred();
25
25
  async function setup(_presence, _driver, _publicAddress, _selectProcessIdToCreateRoom) {
26
- onReady = new Deferred();
26
+ if (onReady === void 0) {
27
+ onReady = new Deferred();
28
+ }
27
29
  presence = _presence || new LocalPresence();
28
30
  driver = _driver || new LocalDriver();
29
31
  publicAddress = _publicAddress;
@@ -128,10 +130,7 @@ async function query(conditions = {}) {
128
130
  }
129
131
  async function findOneRoomAvailable(roomName, clientOptions) {
130
132
  return await awaitRoomAvailable(roomName, async () => {
131
- const handler = handlers[roomName];
132
- if (!handler) {
133
- throw new ServerError(ErrorCode.MATCHMAKE_NO_HANDLER, `provided room name "${roomName}" not defined`);
134
- }
133
+ const handler = getHandler(roomName);
135
134
  const roomQuery = driver.findOne({
136
135
  locked: false,
137
136
  name: roomName,
@@ -160,22 +159,38 @@ async function remoteRoomCall(roomId, method, args, rejectionTimeout = REMOTE_RO
160
159
  return !args && typeof room[method] !== "function" ? room[method] : await room[method].apply(room, args && args.map((arg) => JSON.parse(JSON.stringify(arg))));
161
160
  }
162
161
  }
163
- function defineRoomType(name, klass, defaultOptions) {
162
+ function defineRoomType(roomName, klass, defaultOptions) {
164
163
  const registeredHandler = new RegisteredHandler(klass, defaultOptions);
165
- handlers[name] = registeredHandler;
164
+ handlers[roomName] = registeredHandler;
165
+ if (klass.prototype["onAuth"] !== Room.prototype["onAuth"]) {
166
+ if (klass["onAuth"] !== Room["onAuth"]) {
167
+ console.log(`\u274C "${roomName}"'s onAuth() defined at the instance level will be ignored.`);
168
+ }
169
+ }
166
170
  if (!isDevMode) {
167
- cleanupStaleRooms(name);
171
+ cleanupStaleRooms(roomName);
168
172
  }
169
173
  return registeredHandler;
170
174
  }
171
- function removeRoomType(name) {
172
- delete handlers[name];
175
+ function removeRoomType(roomName) {
176
+ delete handlers[roomName];
173
177
  if (!isDevMode) {
174
- cleanupStaleRooms(name);
178
+ cleanupStaleRooms(roomName);
175
179
  }
176
180
  }
177
- function hasHandler(name) {
178
- return handlers[name] !== void 0;
181
+ function hasHandler(roomName) {
182
+ console.warn("hasHandler() is deprecated. Use getHandler() instead.");
183
+ return handlers[roomName] !== void 0;
184
+ }
185
+ function getHandler(roomName) {
186
+ const handler = handlers[roomName];
187
+ if (!handler) {
188
+ throw new ServerError(ErrorCode.MATCHMAKE_NO_HANDLER, `provided room name "${roomName}" not defined`);
189
+ }
190
+ return handler;
191
+ }
192
+ function getRoomClass(roomName) {
193
+ return handlers[roomName]?.klass;
179
194
  }
180
195
  async function createRoom(roomName, clientOptions) {
181
196
  const selectedProcessId = await selectProcessIdToCreateRoom(roomName, clientOptions);
@@ -207,11 +222,8 @@ async function createRoom(roomName, clientOptions) {
207
222
  return room;
208
223
  }
209
224
  async function handleCreateRoom(roomName, clientOptions, restoringRoomId) {
210
- const registeredHandler = handlers[roomName];
211
- if (!registeredHandler) {
212
- throw new ServerError(ErrorCode.MATCHMAKE_NO_HANDLER, `provided room name "${roomName}" not defined`);
213
- }
214
- const room = new registeredHandler.klass();
225
+ const handler = getHandler(roomName);
226
+ const room = new handler.klass();
215
227
  if (restoringRoomId && isDevMode) {
216
228
  room.roomId = restoringRoomId;
217
229
  } else {
@@ -219,7 +231,7 @@ async function handleCreateRoom(roomName, clientOptions, restoringRoomId) {
219
231
  }
220
232
  room.roomName = roomName;
221
233
  room.presence = presence;
222
- const additionalListingData = registeredHandler.getFilterOptions(clientOptions);
234
+ const additionalListingData = handler.getFilterOptions(clientOptions);
223
235
  if (publicAddress) {
224
236
  additionalListingData.publicAddress = publicAddress;
225
237
  }
@@ -230,7 +242,7 @@ async function handleCreateRoom(roomName, clientOptions, restoringRoomId) {
230
242
  });
231
243
  if (room.onCreate) {
232
244
  try {
233
- await room.onCreate(merge({}, clientOptions, registeredHandler.options));
245
+ await room.onCreate(merge({}, clientOptions, handler.options));
234
246
  } catch (e) {
235
247
  debugAndPrintError(e);
236
248
  throw new ServerError(
@@ -249,11 +261,12 @@ async function handleCreateRoom(roomName, clientOptions, restoringRoomId) {
249
261
  room._events.on("unlock", unlockRoom.bind(this, room));
250
262
  room._events.on("join", onClientJoinRoom.bind(this, room));
251
263
  room._events.on("leave", onClientLeaveRoom.bind(this, room));
264
+ room._events.on("visibility-change", onVisibilityChange.bind(this, room));
252
265
  room._events.once("dispose", disposeRoom.bind(this, roomName, room));
253
266
  room._events.once("disconnect", () => room._events.removeAllListeners());
254
267
  await createRoomReferences(room, true);
255
268
  await room.listing.save();
256
- registeredHandler.emit("create", room);
269
+ handler.emit("create", room);
257
270
  return room.listing;
258
271
  }
259
272
  function getRoomById(roomId) {
@@ -274,6 +287,7 @@ async function gracefullyShutdown() {
274
287
  return Promise.reject("already_shutting_down");
275
288
  }
276
289
  isGracefullyShuttingDown = true;
290
+ onReady = void 0;
277
291
  debugMatchMaking(`${processId} is shutting down!`);
278
292
  if (isDevMode) {
279
293
  await cacheRoomHistory(rooms);
@@ -377,6 +391,9 @@ async function unlockRoom(room) {
377
391
  handlers[room.roomName].emit("unlock", room);
378
392
  }
379
393
  }
394
+ function onVisibilityChange(room, isInvisible) {
395
+ handlers[room.roomName].emit("visibility-change", room, isInvisible);
396
+ }
380
397
  async function disposeRoom(roomName, room) {
381
398
  debugMatchMaking("disposing '%s' (%s) on processId '%s'", roomName, room.roomId, processId);
382
399
  if (!isGracefullyShuttingDown) {
@@ -408,7 +425,9 @@ export {
408
425
  disconnectAll,
409
426
  driver,
410
427
  findOneRoomAvailable,
428
+ getHandler,
411
429
  getRoomById,
430
+ getRoomClass,
412
431
  gracefullyShutdown,
413
432
  handleCreateRoom,
414
433
  hasHandler,