@colyseus/core 0.15.28 → 0.15.30

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.
@@ -331,7 +331,12 @@ async function handleCreateRoom(roomName, clientOptions, restoringRoomId) {
331
331
  room._events.on("leave", onClientLeaveRoom.bind(this, room));
332
332
  room._events.on("visibility-change", onVisibilityChange.bind(this, room));
333
333
  room._events.once("dispose", disposeRoom.bind(this, roomName, room));
334
- room._events.once("disconnect", () => room._events.removeAllListeners());
334
+ room._events.once("disconnect", () => {
335
+ room._events.removeAllListeners("lock");
336
+ room._events.removeAllListeners("unlock");
337
+ room._events.removeAllListeners("visibility-change");
338
+ room._events.removeAllListeners("dispose");
339
+ });
335
340
  await createRoomReferences(room, true);
336
341
  await room.listing.save();
337
342
  handler.emit("create", room);
@@ -467,7 +472,7 @@ function onVisibilityChange(room, isInvisible) {
467
472
  handlers[room.roomName].emit("visibility-change", room, isInvisible);
468
473
  }
469
474
  async function disposeRoom(roomName, room) {
470
- (0, import_Debug.debugMatchMaking)("disposing '%s' (%s) on processId '%s'", roomName, room.roomId, processId);
475
+ (0, import_Debug.debugMatchMaking)("disposing '%s' (%s) on processId '%s' (graceful shutdown: %s)", roomName, room.roomId, processId, isGracefullyShuttingDown);
471
476
  if (!isGracefullyShuttingDown) {
472
477
  stats.local.roomCount--;
473
478
  stats.persist();
@@ -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 // 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 await stats.excludeProcess(stat.processId);\n }\n }));\n }\n\n await stats.reset();\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 // 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): 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 = 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 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 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: 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 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\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\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}\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 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 // 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 await stats.excludeProcess(stat.processId);\n }\n }));\n }\n\n await stats.reset();\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 // 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): 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 = 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 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)) { 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: 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 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\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\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\\' (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
6
  "names": ["controller"]
7
7
  }
@@ -274,7 +274,12 @@ async function handleCreateRoom(roomName, clientOptions, restoringRoomId) {
274
274
  room._events.on("leave", onClientLeaveRoom.bind(this, room));
275
275
  room._events.on("visibility-change", onVisibilityChange.bind(this, room));
276
276
  room._events.once("dispose", disposeRoom.bind(this, roomName, room));
277
- room._events.once("disconnect", () => room._events.removeAllListeners());
277
+ room._events.once("disconnect", () => {
278
+ room._events.removeAllListeners("lock");
279
+ room._events.removeAllListeners("unlock");
280
+ room._events.removeAllListeners("visibility-change");
281
+ room._events.removeAllListeners("dispose");
282
+ });
278
283
  await createRoomReferences(room, true);
279
284
  await room.listing.save();
280
285
  handler.emit("create", room);
@@ -410,7 +415,7 @@ function onVisibilityChange(room, isInvisible) {
410
415
  handlers[room.roomName].emit("visibility-change", room, isInvisible);
411
416
  }
412
417
  async function disposeRoom(roomName, room) {
413
- debugMatchMaking("disposing '%s' (%s) on processId '%s'", roomName, room.roomId, processId);
418
+ debugMatchMaking("disposing '%s' (%s) on processId '%s' (graceful shutdown: %s)", roomName, room.roomId, processId, isGracefullyShuttingDown);
414
419
  if (!isGracefullyShuttingDown) {
415
420
  stats.local.roomCount--;
416
421
  stats.persist();
@@ -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 // 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 await stats.excludeProcess(stat.processId);\n }\n }));\n }\n\n await stats.reset();\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 // 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): 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 = 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 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 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: 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 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\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\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}\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 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 // 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 await stats.excludeProcess(stat.processId);\n }\n }));\n }\n\n await stats.reset();\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 // 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): 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 = 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 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)) { 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: 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 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\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\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\\' (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
6
  "names": []
7
7
  }
package/build/Room.js CHANGED
@@ -98,8 +98,8 @@ class Room {
98
98
  }
99
99
  set autoDispose(value) {
100
100
  if (value !== this.#_autoDispose && this._internalState !== 2 /* DISPOSING */) {
101
- this.resetAutoDisposeTimeout();
102
101
  this.#_autoDispose = value;
102
+ this.resetAutoDisposeTimeout();
103
103
  }
104
104
  }
105
105
  get roomName() {
@@ -572,7 +572,9 @@ class Room {
572
572
  }
573
573
  if (client.state !== import_Transport.ClientState.RECONNECTED) {
574
574
  const willDispose = await this._decrementClientCount();
575
- this._events.emit("leave", client, willDispose);
575
+ if (this.reservedSeats[client.sessionId] === void 0) {
576
+ this._events.emit("leave", client, willDispose);
577
+ }
576
578
  }
577
579
  }
578
580
  async _incrementClientCount() {
package/build/Room.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/Room.ts"],
4
- "sourcesContent": ["import http, { IncomingMessage } from 'http';\nimport WebSocket from 'ws'; // TODO: move this to Transport\n\nimport { unpack } from 'msgpackr';\nimport { decode, Iterator, Schema } from '@colyseus/schema';\n\nimport Clock from '@gamestdio/timer';\nimport { EventEmitter } from 'events';\nimport { logger } from './Logger';\n\nimport { Presence } from './presence/Presence';\n\nimport { NoneSerializer } from './serializer/NoneSerializer';\nimport { SchemaSerializer } from './serializer/SchemaSerializer';\nimport { Serializer } from './serializer/Serializer';\n\nimport { ErrorCode, getMessageBytes, Protocol } from './Protocol';\nimport { Deferred, generateId } from './utils/Utils';\nimport { isDevMode } from './utils/DevMode';\n\nimport { debugAndPrintError, debugMessage } from './Debug';\nimport { ServerError } from './errors/ServerError';\nimport { RoomListingData } from './matchmaker/driver';\nimport { Client, ClientArray, ClientState, ISendOptions } from './Transport';\n\nconst DEFAULT_PATCH_RATE = 1000 / 20; // 20fps (50ms)\nconst DEFAULT_SIMULATION_INTERVAL = 1000 / 60; // 60fps (16.66ms)\nconst noneSerializer = new NoneSerializer();\n\nexport const DEFAULT_SEAT_RESERVATION_TIME = Number(process.env.COLYSEUS_SEAT_RESERVATION_TIME || 15);\n\nexport type SimulationCallback = (deltaTime: number) => void;\n\nexport interface IBroadcastOptions extends ISendOptions {\n except?: Client | Client[];\n}\n\nexport enum RoomInternalState {\n CREATING = 0,\n CREATED = 1,\n DISPOSING = 2,\n}\n\ntype ExtractUserData<T> = T extends ClientArray<infer U> ? U : never;\ntype ExtractAuthData<T> = T extends ClientArray<infer _, infer U> ? U : never;\n\n/**\n * A Room class is meant to implement a game session, and/or serve as the communication channel\n * between a group of clients.\n *\n * - Rooms are created on demand during matchmaking by default\n * - Room classes must be exposed using `.define()`\n */\nexport abstract class Room<State extends object= any, Metadata= any> {\n\n /**\n * This property will change on these situations:\n * - The maximum number of allowed clients has been reached (`maxClients`)\n * - You manually locked, or unlocked the room using lock() or `unlock()`.\n *\n * @readonly\n */\n public get locked() {\n return this._locked;\n }\n\n public get metadata() {\n return this.listing.metadata;\n }\n\n public listing: RoomListingData<Metadata>;\n\n /**\n * A ClockTimer instance, used for timing events.\n */\n public clock: Clock = new Clock();\n\n #_roomId: string;\n #_roomName: string;\n #_autoDispose: boolean = true;\n\n /**\n * Maximum number of clients allowed to connect into the room. When room reaches this limit,\n * it is locked automatically. Unless the room was explicitly locked by you via `lock()` method,\n * the room will be unlocked as soon as a client disconnects from it.\n */\n public maxClients: number = Infinity;\n /**\n * Frequency to send the room state to connected clients, in milliseconds.\n *\n * @default 50ms (20fps)\n */\n public patchRate: number = DEFAULT_PATCH_RATE;\n\n /**\n * The state instance you provided to `setState()`.\n */\n public state: State;\n /**\n * The presence instance. Check Presence API for more details.\n *\n * @see {@link https://docs.colyseus.io/colyseus/server/presence/|Presence API}\n */\n public presence: Presence;\n\n /**\n * The array of connected clients.\n *\n * @see {@link https://docs.colyseus.io/colyseus/server/room/#client|Client instance}\n */\n public clients: ClientArray = new ClientArray();\n\n /** @internal */\n public _events = new EventEmitter();\n\n // seat reservation & reconnection\n protected seatReservationTime: number = DEFAULT_SEAT_RESERVATION_TIME;\n protected reservedSeats: { [sessionId: string]: [any, any] } = {};\n protected reservedSeatTimeouts: { [sessionId: string]: NodeJS.Timer } = {};\n\n protected _reconnections: { [reconnectionToken: string]: [string, Deferred] } = {};\n private _reconnectingSessionId = new Map<string, string>();\n\n private onMessageHandlers: {[id: string]: (client: Client, message: any) => void} = {};\n\n private _serializer: Serializer<State> = noneSerializer;\n private _afterNextPatchQueue: Array<[string | Client, IArguments]> = [];\n\n private _simulationInterval: NodeJS.Timer;\n private _patchInterval: NodeJS.Timer;\n\n private _internalState: RoomInternalState = RoomInternalState.CREATING;\n private _locked: boolean = false;\n private _lockedExplicitly: boolean = false;\n private _maxClientsReached: boolean = false;\n\n // this timeout prevents rooms that are created by one process, but no client\n // ever had success joining into it on the specified interval.\n private _autoDisposeTimeout: NodeJS.Timer;\n\n // TODO: remove \"presence\" from constructor on 0.16.0\n constructor(presence?: Presence) {\n this.presence = presence;\n\n this._events.once('dispose', async () => {\n try {\n await this._dispose();\n\n } catch (e) {\n debugAndPrintError(`onDispose error: ${(e && e.message || e || 'promise rejected')}`);\n }\n this._events.emit('disconnect');\n });\n\n this.setPatchRate(this.patchRate);\n // set default _autoDisposeTimeout\n this.resetAutoDisposeTimeout(this.seatReservationTime);\n }\n\n /**\n * Automatically dispose the room when last client disconnects.\n *\n * @default true\n */\n public get autoDispose() { return this.#_autoDispose; }\n public set autoDispose(value: boolean) {\n if (\n value !== this.#_autoDispose &&\n this._internalState !== RoomInternalState.DISPOSING\n ) {\n this.resetAutoDisposeTimeout();\n this.#_autoDispose = value;\n }\n }\n\n /**\n * The name of the room you provided as first argument for `gameServer.define()`.\n *\n * @returns roomName string\n */\n public get roomName() { return this.#_roomName; }\n /**\n * Setting the name of the room. Overwriting this property is restricted.\n *\n * @param roomName\n */\n public set roomName(roomName: string) {\n if (this.#_roomName) {\n // prevent user from setting roomName after it has been defined.\n throw new ServerError(ErrorCode.APPLICATION_ERROR, \"'roomName' cannot be overwritten.\");\n }\n this.#_roomName = roomName;\n }\n\n /**\n * A unique, auto-generated, 9-character-long id of the room.\n * You may replace `this.roomId` during `onCreate()`.\n *\n * @returns roomId string\n */\n public get roomId() { return this.#_roomId; }\n /**\n * Setting the roomId, is restricted in room lifetime except upon room creation.\n *\n * @param roomId\n * @returns roomId string\n */\n public set roomId(roomId: string) {\n if (this._internalState !== RoomInternalState.CREATING && !isDevMode) {\n // prevent user from setting roomId after room has been created.\n throw new ServerError(ErrorCode.APPLICATION_ERROR, \"'roomId' can only be overridden upon room creation.\");\n }\n this.#_roomId = roomId;\n }\n\n // Optional abstract methods\n public onBeforePatch?(state: State): void | Promise<any>;\n public onCreate?(options: any): void | Promise<any>;\n public onJoin?(\n client: Client<ExtractUserData<typeof this['clients']>, ExtractAuthData<typeof this['clients']>>,\n options?: any,\n auth?: ExtractAuthData<typeof this['clients']>,\n ): void | Promise<any>;\n public onLeave?(\n client: Client<ExtractUserData<typeof this['clients']>, ExtractAuthData<typeof this['clients']>>,\n consented?: boolean,\n ): void | Promise<any>;\n public onDispose?(): void | Promise<any>;\n\n // TODO: flag as @deprecated on v0.16\n // TOOD: remove instance level `onAuth` on 1.0\n /**\n * onAuth at the instance level will be deprecated in the future.\n * Please use \"static onAuth(token, req) instead\n */\n public onAuth(\n client: Client<ExtractUserData<typeof this['clients']>, ExtractAuthData<typeof this['clients']>>,\n options: any,\n request?: http.IncomingMessage\n ): any | Promise<any> {\n return true;\n }\n\n static async onAuth(token: string, req: IncomingMessage): Promise<unknown> {\n return true;\n }\n\n /**\n * devMode: When `devMode` is enabled, `onCacheRoom` method is called during\n * graceful shutdown.\n *\n * Implement this method to return custom data to be cached. `onRestoreRoom`\n * will be called with the data returned by `onCacheRoom`\n */\n public onCacheRoom?(): any;\n\n /**\n * devMode: When `devMode` is enabled, `onRestoreRoom` method is called during\n * process startup, with the data returned by the `onCacheRoom` method.\n */\n public onRestoreRoom?(cached?: any): void;\n\n /**\n * Returns whether the sum of connected clients and reserved seats exceeds maximum number of clients.\n *\n * @returns boolean\n */\n public hasReachedMaxClients(): boolean {\n return (\n (this.clients.length + Object.keys(this.reservedSeats).length) >= this.maxClients ||\n this._internalState === RoomInternalState.DISPOSING\n );\n }\n\n /**\n * Set the number of seconds a room can wait for a client to effectively join the room.\n * You should consider how long your `onAuth()` will have to wait for setting a different seat reservation time.\n * The default value is 15 seconds. You may set the `COLYSEUS_SEAT_RESERVATION_TIME`\n * environment variable if you'd like to change the seat reservation time globally.\n *\n * @default 15 seconds\n *\n * @param seconds - number of seconds.\n * @returns The modified Room object.\n */\n public setSeatReservationTime(seconds: number) {\n this.seatReservationTime = seconds;\n return this;\n }\n\n public hasReservedSeat(sessionId: string, reconnectionToken?: string): boolean {\n if (reconnectionToken) {\n const reconnection = this._reconnections[reconnectionToken];\n return (\n reconnection &&\n reconnection[0] === sessionId &&\n this.reservedSeats[sessionId] !== undefined &&\n this._reconnectingSessionId.has(sessionId)\n );\n\n } else {\n return (\n this.reservedSeats[sessionId] !== undefined &&\n (\n !this._reconnectingSessionId.has(sessionId) || // prevent possible \"reconnect\" requests without a reconnection token\n (this._reconnectingSessionId.get(sessionId) === sessionId) // devMode reconnection\n )\n );\n }\n }\n\n public checkReconnectionToken(reconnectionToken: string) {\n const reconnection = this._reconnections[reconnectionToken];\n const sessionId = (reconnection && reconnection[0]);\n\n if (this.hasReservedSeat(sessionId)) {\n this._reconnectingSessionId.set(sessionId, reconnectionToken);\n return sessionId;\n\n } else {\n return undefined;\n }\n }\n\n /**\n * (Optional) Set a simulation interval that can change the state of the game.\n * The simulation interval is your game loop.\n *\n * @default 16.6ms (60fps)\n *\n * @param onTickCallback - You can implement your physics or world updates here!\n * This is a good place to update the room state.\n * @param delay - Interval delay on executing `onTickCallback` in milliseconds.\n */\n public setSimulationInterval(onTickCallback?: SimulationCallback, delay: number = DEFAULT_SIMULATION_INTERVAL): void {\n // clear previous interval in case called setSimulationInterval more than once\n if (this._simulationInterval) { clearInterval(this._simulationInterval); }\n\n if (onTickCallback) {\n this._simulationInterval = setInterval(() => {\n this.clock.tick();\n onTickCallback(this.clock.deltaTime);\n }, delay);\n }\n }\n\n public setPatchRate(milliseconds: number | null): void {\n this.patchRate = milliseconds;\n\n // clear previous interval in case called setPatchRate more than once\n if (this._patchInterval) {\n clearInterval(this._patchInterval);\n this._patchInterval = undefined;\n }\n\n if (milliseconds !== null && milliseconds !== 0) {\n this._patchInterval = setInterval(() => this.broadcastPatch(), milliseconds);\n }\n }\n\n public setState(newState: State) {\n this.clock.start();\n\n if ('_definition' in newState) {\n this.setSerializer(new SchemaSerializer());\n }\n\n this._serializer.reset(newState);\n\n this.state = newState;\n }\n\n public setSerializer(serializer: Serializer<State>) {\n this._serializer = serializer;\n }\n\n public async setMetadata(meta: Partial<Metadata>) {\n if (!this.listing.metadata) {\n this.listing.metadata = meta as Metadata;\n\n } else {\n for (const field in meta) {\n if (!meta.hasOwnProperty(field)) { continue; }\n this.listing.metadata[field] = meta[field];\n }\n\n // `MongooseDriver` workaround: persit metadata mutations\n if ('markModified' in this.listing) {\n (this.listing as any).markModified('metadata');\n }\n }\n\n if (this._internalState === RoomInternalState.CREATED) {\n await this.listing.save();\n }\n }\n\n public async setPrivate(bool: boolean = true) {\n if (this.listing.private === bool) return;\n\n this.listing.private = bool;\n\n if (this._internalState === RoomInternalState.CREATED) {\n await this.listing.save();\n }\n\n this._events.emit('visibility-change', bool);\n }\n\n /**\n * Locking the room will remove it from the pool of available rooms for new clients to connect to.\n */\n public async lock() {\n // rooms locked internally aren't explicit locks.\n this._lockedExplicitly = (arguments[0] === undefined);\n\n // skip if already locked.\n if (this._locked) { return; }\n\n this._locked = true;\n\n await this.listing.updateOne({\n $set: { locked: this._locked },\n });\n\n this._events.emit('lock');\n }\n\n /**\n * Unlocking the room returns it to the pool of available rooms for new clients to connect to.\n */\n public async unlock() {\n // only internal usage passes arguments to this function.\n if (arguments[0] === undefined) {\n this._lockedExplicitly = false;\n }\n\n // skip if already locked\n if (!this._locked) { return; }\n\n this._locked = false;\n\n await this.listing.updateOne({\n $set: { locked: this._locked },\n });\n\n this._events.emit('unlock');\n }\n\n public send(client: Client, type: string | number, message: any, options?: ISendOptions): void;\n public send(client: Client, message: Schema, options?: ISendOptions): void;\n public send(client: Client, messageOrType: any, messageOrOptions?: any | ISendOptions, options?: ISendOptions): void {\n logger.warn('DEPRECATION WARNING: use client.send(...) instead of this.send(client, ...)');\n client.send(messageOrType, messageOrOptions, options);\n }\n\n public broadcast(type: string | number, message?: any, options?: IBroadcastOptions);\n public broadcast<T extends Schema>(message: T, options?: IBroadcastOptions);\n public broadcast(\n typeOrSchema: string | number | Schema,\n messageOrOptions?: any | IBroadcastOptions,\n options?: IBroadcastOptions,\n ) {\n const isSchema = (typeof(typeOrSchema) === 'object');\n const opts: IBroadcastOptions = ((isSchema) ? messageOrOptions : options);\n\n if (opts && opts.afterNextPatch) {\n delete opts.afterNextPatch;\n this._afterNextPatchQueue.push(['broadcast', arguments]);\n return;\n }\n\n if (isSchema) {\n this.broadcastMessageSchema(typeOrSchema as Schema, opts);\n\n } else {\n\n this.broadcastMessageType(typeOrSchema as string, messageOrOptions, opts);\n }\n }\n\n /**\n * Checks whether mutations have occurred in the state, and broadcast them to all connected clients.\n */\n public broadcastPatch() {\n if (this.onBeforePatch) {\n this.onBeforePatch(this.state);\n }\n\n if (!this._simulationInterval) {\n this.clock.tick();\n }\n\n if (!this.state) {\n return false;\n }\n\n const hasChanges = this._serializer.applyPatches(this.clients, this.state);\n\n // broadcast messages enqueued for \"after patch\"\n this._dequeueAfterPatchMessages();\n\n return hasChanges;\n }\n\n public onMessage<T = any>(\n messageType: '*',\n callback: (client: Client<ExtractUserData<typeof this['clients']>, ExtractAuthData<typeof this['clients']>>, type: string | number, message: T) => void\n );\n public onMessage<T = any>(\n messageType: string | number,\n callback: (client: Client<ExtractUserData<typeof this['clients']>, ExtractAuthData<typeof this['clients']>>, message: T) => void\n );\n public onMessage<T = any>(messageType: '*' | string | number, callback: (...args: any[]) => void) {\n this.onMessageHandlers[messageType] = callback;\n // returns a method to unbind the callback\n return () => delete this.onMessageHandlers[messageType];\n }\n\n /**\n * Disconnect all connected clients, and then dispose the room.\n *\n * @param closeCode WebSocket close code (default = 4000, which is a \"consented leave\")\n * @returns Promise<void>\n */\n public disconnect(closeCode: number = Protocol.WS_CLOSE_CONSENTED): Promise<any> {\n // skip if already disposing\n if (this._internalState === RoomInternalState.DISPOSING) {\n return;\n\n } else if (this._internalState === RoomInternalState.CREATING) {\n throw new Error(\"cannot disconnect during onCreate()\");\n }\n\n this._internalState = RoomInternalState.DISPOSING;\n this.listing.remove();\n\n this.#_autoDispose = true;\n\n const delayedDisconnection = new Promise<void>((resolve) =>\n this._events.once('disconnect', () => resolve()));\n\n for (const [_, reconnection] of Object.values(this._reconnections)) {\n reconnection.reject();\n }\n\n let numClients = this.clients.length;\n if (numClients > 0) {\n // clients may have `async onLeave`, room will be disposed after they're fulfilled\n while (numClients--) {\n this._forciblyCloseClient(this.clients[numClients], closeCode);\n }\n } else {\n // no clients connected, dispose immediately.\n this._events.emit('dispose');\n }\n\n return delayedDisconnection;\n }\n\n public async ['_onJoin'](client: Client, req?: http.IncomingMessage) {\n const sessionId = client.sessionId;\n\n // generate unique private reconnection token\n client._reconnectionToken = generateId();\n\n if (this.reservedSeatTimeouts[sessionId]) {\n clearTimeout(this.reservedSeatTimeouts[sessionId]);\n delete this.reservedSeatTimeouts[sessionId];\n }\n\n // clear auto-dispose timeout.\n if (this._autoDisposeTimeout) {\n clearTimeout(this._autoDisposeTimeout);\n this._autoDisposeTimeout = undefined;\n }\n\n // get seat reservation options and clear it\n const [joinOptions, authData] = this.reservedSeats[sessionId];\n\n //\n // TODO: remove this check on 1.0.0\n // - the seat reservation is used to keep track of number of clients and their pending seats (see `hasReachedMaxClients`)\n // - when we fully migrate to static onAuth(), the seat reservation can be removed immediately here\n // - if async onAuth() is in use, the seat reservation is removed after onAuth() is fulfilled.\n // - mark reservation as \"consumed\"\n //\n if (this.reservedSeats[sessionId].length > 2) {\n throw new ServerError(ErrorCode.MATCHMAKE_EXPIRED, \"already consumed\");\n }\n this.reservedSeats[sessionId].push(true);\n\n // share \"after next patch queue\" reference with every client.\n client._afterNextPatchQueue = this._afterNextPatchQueue;\n\n // add temporary callback to keep track of disconnections during `onJoin`.\n client.ref['onleave'] = (_) => client.state = ClientState.LEAVING;\n client.ref.once('close', client.ref['onleave']);\n\n const previousReconnectionToken = this._reconnectingSessionId.get(sessionId);\n if (previousReconnectionToken) {\n this.clients.push(client);\n this._reconnections[previousReconnectionToken]?.[1].resolve(client);\n\n } else {\n try {\n if (authData) {\n client.auth = authData;\n\n } else if (this.onAuth !== Room.prototype.onAuth) {\n client.auth = await this.onAuth(client, joinOptions, req);\n\n if (!client.auth) {\n throw new ServerError(ErrorCode.AUTH_FAILED, 'onAuth failed');\n }\n }\n\n //\n // On async onAuth, client may have been disconnected.\n //\n if (client.readyState !== WebSocket.OPEN) {\n throw new ServerError(Protocol.WS_CLOSE_GOING_AWAY, 'already disconnected');\n }\n\n this.clients.push(client);\n\n if (this.onJoin) {\n await this.onJoin(client, joinOptions, client.auth);\n }\n\n // emit 'join' to room handler (if not reconnecting)\n this._events.emit('join', client);\n\n // remove seat reservation\n delete this.reservedSeats[sessionId];\n\n // client left during `onJoin`, call _onLeave immediately.\n if (client.state === ClientState.LEAVING) {\n await this._onLeave(client, Protocol.WS_CLOSE_GOING_AWAY);\n }\n\n } catch (e) {\n this.clients.delete(client);\n\n // remove seat reservation\n delete this.reservedSeats[sessionId];\n\n this._decrementClientCount();\n\n // make sure an error code is provided.\n if (!e.code) {\n e.code = ErrorCode.APPLICATION_ERROR;\n }\n\n throw e;\n }\n }\n\n // state might already be ClientState.LEAVING here\n if (client.state === ClientState.JOINING) {\n client.ref.removeListener('close', client.ref['onleave']);\n\n // only bind _onLeave after onJoin has been successful\n client.ref['onleave'] = this._onLeave.bind(this, client);\n client.ref.once('close', client.ref['onleave']);\n\n // allow client to send messages after onJoin has succeeded.\n client.ref.on('message', this._onMessage.bind(this, client));\n\n // confirm room id that matches the room name requested to join\n client.raw(getMessageBytes[Protocol.JOIN_ROOM](\n client._reconnectionToken,\n this._serializer.id,\n this._serializer.handshake && this._serializer.handshake(),\n ));\n }\n }\n\n /**\n * Allow the specified client to reconnect into the room. Must be used inside `onLeave()` method.\n * If seconds is provided, the reconnection is going to be cancelled after the provided amount of seconds.\n *\n * @param previousClient - The client which is to be waiting until re-connection happens.\n * @param seconds - Timeout period on re-connection in seconds.\n *\n * @returns Deferred<Client> - The differed is a promise like type.\n * This type can forcibly reject the promise by calling `.reject()`.\n */\n public allowReconnection(previousClient: Client, seconds: number | \"manual\"): Deferred<Client> {\n //\n // skip reconnection if client has never fully JOINED.\n //\n // (having `_enqueuedMessages !== undefined` means that the client has never\n // been at \"ClientState.JOINED\" state)\n //\n if (previousClient._enqueuedMessages !== undefined) {\n return;\n }\n\n if (seconds === undefined) { // TODO: remove this check\n console.warn(\"DEPRECATED: allowReconnection() requires a second argument. Using \\\"manual\\\" mode.\");\n seconds = \"manual\";\n }\n\n if (seconds === \"manual\") {\n seconds = Infinity;\n }\n\n if (this._internalState === RoomInternalState.DISPOSING) {\n this._disposeIfEmpty(); // gracefully shutting down\n throw new Error('disconnecting');\n }\n\n const sessionId = previousClient.sessionId;\n const reconnectionToken = previousClient._reconnectionToken;\n\n this._reserveSeat(sessionId, true, previousClient.auth, seconds, true);\n\n // keep reconnection reference in case the user reconnects into this room.\n const reconnection = new Deferred<Client>();\n this._reconnections[reconnectionToken] = [sessionId, reconnection];\n\n if (seconds !== Infinity) {\n // expire seat reservation after timeout\n this.reservedSeatTimeouts[sessionId] = setTimeout(() =>\n reconnection.reject(false), seconds * 1000);\n }\n\n const cleanup = () => {\n delete this._reconnections[reconnectionToken];\n delete this.reservedSeats[sessionId];\n delete this.reservedSeatTimeouts[sessionId];\n this._reconnectingSessionId.delete(sessionId);\n };\n\n reconnection.\n then((newClient) => {\n newClient.auth = previousClient.auth;\n newClient.userData = previousClient.userData;\n previousClient.ref = newClient.ref; // swap \"ref\" for convenience\n previousClient.state = ClientState.RECONNECTED;\n clearTimeout(this.reservedSeatTimeouts[sessionId]);\n cleanup();\n }).\n catch(() => {\n cleanup();\n this.resetAutoDisposeTimeout();\n });\n\n return reconnection;\n }\n\n protected resetAutoDisposeTimeout(timeoutInSeconds: number = 1) {\n clearTimeout(this._autoDisposeTimeout);\n\n if (!this.#_autoDispose) {\n return;\n }\n\n this._autoDisposeTimeout = setTimeout(() => {\n this._autoDisposeTimeout = undefined;\n this._disposeIfEmpty();\n }, timeoutInSeconds * 1000);\n }\n\n private broadcastMessageSchema<T extends Schema>(message: T, options: IBroadcastOptions = {}) {\n debugMessage(\"broadcast: %O\", message);\n const encodedMessage = getMessageBytes[Protocol.ROOM_DATA_SCHEMA](message);\n const except = (typeof (options.except) !== \"undefined\")\n ? Array.isArray(options.except)\n ? options.except\n : [options.except]\n : undefined;\n\n let numClients = this.clients.length;\n while (numClients--) {\n const client = this.clients[numClients];\n\n if (!except || !except.includes(client)) {\n client.enqueueRaw(encodedMessage);\n }\n }\n }\n\n private broadcastMessageType(type: string, message?: any, options: IBroadcastOptions = {}) {\n debugMessage(\"broadcast: %O\", message);\n const encodedMessage = getMessageBytes.raw(Protocol.ROOM_DATA, type, message);\n const except = (typeof (options.except) !== \"undefined\")\n ? Array.isArray(options.except)\n ? options.except\n : [options.except]\n : undefined;\n\n let numClients = this.clients.length;\n while (numClients--) {\n const client = this.clients[numClients];\n\n if (!except || !except.includes(client)) {\n client.enqueueRaw(encodedMessage);\n }\n }\n }\n\n private sendFullState(client: Client): void {\n client.enqueueRaw(getMessageBytes[Protocol.ROOM_STATE](this._serializer.getFullState(client)));\n }\n\n private _dequeueAfterPatchMessages() {\n const length = this._afterNextPatchQueue.length;\n\n if (length > 0) {\n for (let i = 0; i < length; i++) {\n const [target, args] = this._afterNextPatchQueue[i];\n\n if (target === \"broadcast\") {\n this.broadcast.apply(this, args);\n\n } else {\n (target as Client).raw.apply(target, args);\n }\n }\n\n // new messages may have been added in the meantime,\n // let's splice the ones that have been processed\n this._afterNextPatchQueue.splice(0, length);\n }\n }\n\n private async _reserveSeat(\n sessionId: string,\n joinOptions: any = true,\n authData: any = undefined,\n seconds: number = this.seatReservationTime,\n allowReconnection: boolean = false,\n devModeReconnection?: boolean,\n ) {\n if (!allowReconnection && this.hasReachedMaxClients()) {\n return false;\n }\n\n this.reservedSeats[sessionId] = [joinOptions, authData];\n\n if (!allowReconnection) {\n await this._incrementClientCount();\n\n this.reservedSeatTimeouts[sessionId] = setTimeout(async () => {\n delete this.reservedSeats[sessionId];\n delete this.reservedSeatTimeouts[sessionId];\n await this._decrementClientCount();\n }, seconds * 1000);\n\n this.resetAutoDisposeTimeout(seconds);\n }\n\n //\n // isDevMode workaround to allow players to reconnect on devMode\n //\n if (devModeReconnection) {\n this._reconnectingSessionId.set(sessionId, sessionId);\n }\n\n return true;\n }\n\n private _disposeIfEmpty() {\n const willDispose = (\n this.#_autoDispose &&\n this._autoDisposeTimeout === undefined &&\n this.clients.length === 0 &&\n Object.keys(this.reservedSeats).length === 0\n );\n\n if (willDispose) {\n this._events.emit('dispose');\n }\n\n return willDispose;\n }\n\n private async _dispose(): Promise<any> {\n this._internalState = RoomInternalState.DISPOSING;\n\n await this.listing.remove();\n\n let userReturnData;\n if (this.onDispose) {\n userReturnData = this.onDispose();\n }\n\n if (this._patchInterval) {\n clearInterval(this._patchInterval);\n this._patchInterval = undefined;\n }\n\n if (this._simulationInterval) {\n clearInterval(this._simulationInterval);\n this._simulationInterval = undefined;\n }\n\n if (this._autoDisposeTimeout) {\n clearInterval(this._autoDisposeTimeout);\n this._autoDisposeTimeout = undefined;\n }\n\n // clear all timeouts/intervals + force to stop ticking\n this.clock.clear();\n this.clock.stop();\n\n return await (userReturnData || Promise.resolve());\n }\n\n private _onMessage(client: Client, bytes: number[]) {\n // skip if client is on LEAVING state.\n if (client.state === ClientState.LEAVING) { return; }\n\n const it: Iterator = { offset: 0 };\n const code = decode.uint8(bytes, it);\n\n if (!bytes) {\n debugAndPrintError(`${this.roomName} (${this.roomId}), couldn't decode message: ${bytes}`);\n return;\n }\n\n if (code === Protocol.ROOM_DATA) {\n const messageType = (decode.stringCheck(bytes, it))\n ? decode.string(bytes, it)\n : decode.number(bytes, it);\n\n let message;\n try {\n message = (bytes.length > it.offset)\n ? unpack(new Uint8Array(bytes.slice(it.offset, bytes.length)))\n : undefined;\n debugMessage(\"received: '%s' -> %j\", messageType, message);\n } catch (e) {\n debugAndPrintError(e);\n return;\n }\n\n if (this.onMessageHandlers[messageType]) {\n this.onMessageHandlers[messageType](client, message);\n\n } else if (this.onMessageHandlers['*']) {\n (this.onMessageHandlers['*'] as any)(client, messageType, message);\n\n } else {\n debugAndPrintError(`onMessage for \"${messageType}\" not registered.`);\n }\n\n } else if (code === Protocol.ROOM_DATA_BYTES) {\n const messageType = (decode.stringCheck(bytes, it))\n ? decode.string(bytes, it)\n : decode.number(bytes, it);\n\n const message = bytes.slice(it.offset, bytes.length);\n debugMessage(\"received: '%s' -> %j\", messageType, message);\n\n if (this.onMessageHandlers[messageType]) {\n this.onMessageHandlers[messageType](client, message);\n\n } else if (this.onMessageHandlers['*']) {\n (this.onMessageHandlers['*'] as any)(client, messageType, message);\n\n } else {\n debugAndPrintError(`onMessage for \"${messageType}\" not registered.`);\n }\n\n } else if (code === Protocol.JOIN_ROOM && client.state === ClientState.JOINING) {\n // join room has been acknowledged by the client\n client.state = ClientState.JOINED;\n\n // send current state when new client joins the room\n if (this.state) {\n this.sendFullState(client);\n }\n\n // dequeue messages sent before client has joined effectively (on user-defined `onJoin`)\n if (client._enqueuedMessages.length > 0) {\n client._enqueuedMessages.forEach((enqueued) => client.raw(enqueued));\n }\n delete client._enqueuedMessages;\n\n } else if (code === Protocol.LEAVE_ROOM) {\n this._forciblyCloseClient(client, Protocol.WS_CLOSE_CONSENTED);\n }\n\n }\n\n private _forciblyCloseClient(client: Client, closeCode: number) {\n // stop receiving messages from this client\n client.ref.removeAllListeners('message');\n\n // prevent \"onLeave\" from being called twice if player asks to leave\n client.ref.removeListener('close', client.ref['onleave']);\n\n // only effectively close connection when \"onLeave\" is fulfilled\n this._onLeave(client, closeCode).then(() => client.leave(closeCode));\n }\n\n private async _onLeave(client: Client, code?: number): Promise<any> {\n const success = this.clients.delete(client);\n\n // call 'onLeave' method only if the client has been successfully accepted.\n if (success) {\n client.state = ClientState.LEAVING;\n\n if (this.onLeave) {\n try {\n await this.onLeave(client, (code === Protocol.WS_CLOSE_CONSENTED));\n\n } catch (e) {\n debugAndPrintError(`onLeave error: ${(e && e.message || e || 'promise rejected')}`);\n }\n }\n }\n\n if (client.state !== ClientState.RECONNECTED) {\n // try to dispose immediately if client reconnection isn't set up.\n const willDispose = await this._decrementClientCount();\n\n this._events.emit('leave', client, willDispose);\n }\n }\n\n private async _incrementClientCount() {\n // lock automatically when maxClients is reached\n if (!this._locked && this.hasReachedMaxClients()) {\n this._maxClientsReached = true;\n this.lock.call(this, true);\n }\n\n await this.listing.updateOne({\n $inc: { clients: 1 },\n $set: { locked: this._locked },\n });\n }\n\n private async _decrementClientCount() {\n const willDispose = this._disposeIfEmpty();\n\n if (this._internalState === RoomInternalState.DISPOSING) {\n return true;\n }\n\n // unlock if room is available for new connections\n if (!willDispose) {\n if (this._maxClientsReached && !this._lockedExplicitly) {\n this._maxClientsReached = false;\n this.unlock.call(this, true);\n }\n\n // update room listing cache\n await this.listing.updateOne({\n $inc: { clients: -1 },\n $set: { locked: this._locked },\n });\n }\n\n return willDispose;\n }\n\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import http, { IncomingMessage } from 'http';\nimport WebSocket from 'ws'; // TODO: move this to Transport\n\nimport { unpack } from 'msgpackr';\nimport { decode, Iterator, Schema } from '@colyseus/schema';\n\nimport Clock from '@gamestdio/timer';\nimport { EventEmitter } from 'events';\nimport { logger } from './Logger';\n\nimport { Presence } from './presence/Presence';\n\nimport { NoneSerializer } from './serializer/NoneSerializer';\nimport { SchemaSerializer } from './serializer/SchemaSerializer';\nimport { Serializer } from './serializer/Serializer';\n\nimport { ErrorCode, getMessageBytes, Protocol } from './Protocol';\nimport { Deferred, generateId } from './utils/Utils';\nimport { isDevMode } from './utils/DevMode';\n\nimport { debugAndPrintError, debugMessage } from './Debug';\nimport { ServerError } from './errors/ServerError';\nimport { RoomListingData } from './matchmaker/driver';\nimport { Client, ClientArray, ClientState, ISendOptions } from './Transport';\n\nconst DEFAULT_PATCH_RATE = 1000 / 20; // 20fps (50ms)\nconst DEFAULT_SIMULATION_INTERVAL = 1000 / 60; // 60fps (16.66ms)\nconst noneSerializer = new NoneSerializer();\n\nexport const DEFAULT_SEAT_RESERVATION_TIME = Number(process.env.COLYSEUS_SEAT_RESERVATION_TIME || 15);\n\nexport type SimulationCallback = (deltaTime: number) => void;\n\nexport interface IBroadcastOptions extends ISendOptions {\n except?: Client | Client[];\n}\n\nexport enum RoomInternalState {\n CREATING = 0,\n CREATED = 1,\n DISPOSING = 2,\n}\n\ntype ExtractUserData<T> = T extends ClientArray<infer U> ? U : never;\ntype ExtractAuthData<T> = T extends ClientArray<infer _, infer U> ? U : never;\n\n/**\n * A Room class is meant to implement a game session, and/or serve as the communication channel\n * between a group of clients.\n *\n * - Rooms are created on demand during matchmaking by default\n * - Room classes must be exposed using `.define()`\n */\nexport abstract class Room<State extends object= any, Metadata= any> {\n\n /**\n * This property will change on these situations:\n * - The maximum number of allowed clients has been reached (`maxClients`)\n * - You manually locked, or unlocked the room using lock() or `unlock()`.\n *\n * @readonly\n */\n public get locked() {\n return this._locked;\n }\n\n public get metadata() {\n return this.listing.metadata;\n }\n\n public listing: RoomListingData<Metadata>;\n\n /**\n * A ClockTimer instance, used for timing events.\n */\n public clock: Clock = new Clock();\n\n #_roomId: string;\n #_roomName: string;\n #_autoDispose: boolean = true;\n\n /**\n * Maximum number of clients allowed to connect into the room. When room reaches this limit,\n * it is locked automatically. Unless the room was explicitly locked by you via `lock()` method,\n * the room will be unlocked as soon as a client disconnects from it.\n */\n public maxClients: number = Infinity;\n /**\n * Frequency to send the room state to connected clients, in milliseconds.\n *\n * @default 50ms (20fps)\n */\n public patchRate: number = DEFAULT_PATCH_RATE;\n\n /**\n * The state instance you provided to `setState()`.\n */\n public state: State;\n /**\n * The presence instance. Check Presence API for more details.\n *\n * @see {@link https://docs.colyseus.io/colyseus/server/presence/|Presence API}\n */\n public presence: Presence;\n\n /**\n * The array of connected clients.\n *\n * @see {@link https://docs.colyseus.io/colyseus/server/room/#client|Client instance}\n */\n public clients: ClientArray = new ClientArray();\n\n /** @internal */\n public _events = new EventEmitter();\n\n // seat reservation & reconnection\n protected seatReservationTime: number = DEFAULT_SEAT_RESERVATION_TIME;\n protected reservedSeats: { [sessionId: string]: [any, any] } = {};\n protected reservedSeatTimeouts: { [sessionId: string]: NodeJS.Timer } = {};\n\n protected _reconnections: { [reconnectionToken: string]: [string, Deferred] } = {};\n private _reconnectingSessionId = new Map<string, string>();\n\n private onMessageHandlers: {[id: string]: (client: Client, message: any) => void} = {};\n\n private _serializer: Serializer<State> = noneSerializer;\n private _afterNextPatchQueue: Array<[string | Client, IArguments]> = [];\n\n private _simulationInterval: NodeJS.Timer;\n private _patchInterval: NodeJS.Timer;\n\n private _internalState: RoomInternalState = RoomInternalState.CREATING;\n private _locked: boolean = false;\n private _lockedExplicitly: boolean = false;\n private _maxClientsReached: boolean = false;\n\n // this timeout prevents rooms that are created by one process, but no client\n // ever had success joining into it on the specified interval.\n private _autoDisposeTimeout: NodeJS.Timer;\n\n // TODO: remove \"presence\" from constructor on 0.16.0\n constructor(presence?: Presence) {\n this.presence = presence;\n\n this._events.once('dispose', async () => {\n try {\n await this._dispose();\n\n } catch (e) {\n debugAndPrintError(`onDispose error: ${(e && e.message || e || 'promise rejected')}`);\n }\n this._events.emit('disconnect');\n });\n\n this.setPatchRate(this.patchRate);\n // set default _autoDisposeTimeout\n this.resetAutoDisposeTimeout(this.seatReservationTime);\n }\n\n /**\n * Automatically dispose the room when last client disconnects.\n *\n * @default true\n */\n public get autoDispose() { return this.#_autoDispose; }\n public set autoDispose(value: boolean) {\n if (\n value !== this.#_autoDispose &&\n this._internalState !== RoomInternalState.DISPOSING\n ) {\n this.#_autoDispose = value;\n this.resetAutoDisposeTimeout();\n }\n }\n\n /**\n * The name of the room you provided as first argument for `gameServer.define()`.\n *\n * @returns roomName string\n */\n public get roomName() { return this.#_roomName; }\n /**\n * Setting the name of the room. Overwriting this property is restricted.\n *\n * @param roomName\n */\n public set roomName(roomName: string) {\n if (this.#_roomName) {\n // prevent user from setting roomName after it has been defined.\n throw new ServerError(ErrorCode.APPLICATION_ERROR, \"'roomName' cannot be overwritten.\");\n }\n this.#_roomName = roomName;\n }\n\n /**\n * A unique, auto-generated, 9-character-long id of the room.\n * You may replace `this.roomId` during `onCreate()`.\n *\n * @returns roomId string\n */\n public get roomId() { return this.#_roomId; }\n /**\n * Setting the roomId, is restricted in room lifetime except upon room creation.\n *\n * @param roomId\n * @returns roomId string\n */\n public set roomId(roomId: string) {\n if (this._internalState !== RoomInternalState.CREATING && !isDevMode) {\n // prevent user from setting roomId after room has been created.\n throw new ServerError(ErrorCode.APPLICATION_ERROR, \"'roomId' can only be overridden upon room creation.\");\n }\n this.#_roomId = roomId;\n }\n\n // Optional abstract methods\n public onBeforePatch?(state: State): void | Promise<any>;\n public onCreate?(options: any): void | Promise<any>;\n public onJoin?(\n client: Client<ExtractUserData<typeof this['clients']>, ExtractAuthData<typeof this['clients']>>,\n options?: any,\n auth?: ExtractAuthData<typeof this['clients']>,\n ): void | Promise<any>;\n public onLeave?(\n client: Client<ExtractUserData<typeof this['clients']>, ExtractAuthData<typeof this['clients']>>,\n consented?: boolean,\n ): void | Promise<any>;\n public onDispose?(): void | Promise<any>;\n\n // TODO: flag as @deprecated on v0.16\n // TOOD: remove instance level `onAuth` on 1.0\n /**\n * onAuth at the instance level will be deprecated in the future.\n * Please use \"static onAuth(token, req) instead\n */\n public onAuth(\n client: Client<ExtractUserData<typeof this['clients']>, ExtractAuthData<typeof this['clients']>>,\n options: any,\n request?: http.IncomingMessage\n ): any | Promise<any> {\n return true;\n }\n\n static async onAuth(token: string, req: IncomingMessage): Promise<unknown> {\n return true;\n }\n\n /**\n * devMode: When `devMode` is enabled, `onCacheRoom` method is called during\n * graceful shutdown.\n *\n * Implement this method to return custom data to be cached. `onRestoreRoom`\n * will be called with the data returned by `onCacheRoom`\n */\n public onCacheRoom?(): any;\n\n /**\n * devMode: When `devMode` is enabled, `onRestoreRoom` method is called during\n * process startup, with the data returned by the `onCacheRoom` method.\n */\n public onRestoreRoom?(cached?: any): void;\n\n /**\n * Returns whether the sum of connected clients and reserved seats exceeds maximum number of clients.\n *\n * @returns boolean\n */\n public hasReachedMaxClients(): boolean {\n return (\n (this.clients.length + Object.keys(this.reservedSeats).length) >= this.maxClients ||\n this._internalState === RoomInternalState.DISPOSING\n );\n }\n\n /**\n * Set the number of seconds a room can wait for a client to effectively join the room.\n * You should consider how long your `onAuth()` will have to wait for setting a different seat reservation time.\n * The default value is 15 seconds. You may set the `COLYSEUS_SEAT_RESERVATION_TIME`\n * environment variable if you'd like to change the seat reservation time globally.\n *\n * @default 15 seconds\n *\n * @param seconds - number of seconds.\n * @returns The modified Room object.\n */\n public setSeatReservationTime(seconds: number) {\n this.seatReservationTime = seconds;\n return this;\n }\n\n public hasReservedSeat(sessionId: string, reconnectionToken?: string): boolean {\n if (reconnectionToken) {\n const reconnection = this._reconnections[reconnectionToken];\n return (\n reconnection &&\n reconnection[0] === sessionId &&\n this.reservedSeats[sessionId] !== undefined &&\n this._reconnectingSessionId.has(sessionId)\n );\n\n } else {\n return (\n this.reservedSeats[sessionId] !== undefined &&\n (\n !this._reconnectingSessionId.has(sessionId) || // prevent possible \"reconnect\" requests without a reconnection token\n (this._reconnectingSessionId.get(sessionId) === sessionId) // devMode reconnection\n )\n );\n }\n }\n\n public checkReconnectionToken(reconnectionToken: string) {\n const reconnection = this._reconnections[reconnectionToken];\n const sessionId = (reconnection && reconnection[0]);\n\n if (this.hasReservedSeat(sessionId)) {\n this._reconnectingSessionId.set(sessionId, reconnectionToken);\n return sessionId;\n\n } else {\n return undefined;\n }\n }\n\n /**\n * (Optional) Set a simulation interval that can change the state of the game.\n * The simulation interval is your game loop.\n *\n * @default 16.6ms (60fps)\n *\n * @param onTickCallback - You can implement your physics or world updates here!\n * This is a good place to update the room state.\n * @param delay - Interval delay on executing `onTickCallback` in milliseconds.\n */\n public setSimulationInterval(onTickCallback?: SimulationCallback, delay: number = DEFAULT_SIMULATION_INTERVAL): void {\n // clear previous interval in case called setSimulationInterval more than once\n if (this._simulationInterval) { clearInterval(this._simulationInterval); }\n\n if (onTickCallback) {\n this._simulationInterval = setInterval(() => {\n this.clock.tick();\n onTickCallback(this.clock.deltaTime);\n }, delay);\n }\n }\n\n public setPatchRate(milliseconds: number | null): void {\n this.patchRate = milliseconds;\n\n // clear previous interval in case called setPatchRate more than once\n if (this._patchInterval) {\n clearInterval(this._patchInterval);\n this._patchInterval = undefined;\n }\n\n if (milliseconds !== null && milliseconds !== 0) {\n this._patchInterval = setInterval(() => this.broadcastPatch(), milliseconds);\n }\n }\n\n public setState(newState: State) {\n this.clock.start();\n\n if ('_definition' in newState) {\n this.setSerializer(new SchemaSerializer());\n }\n\n this._serializer.reset(newState);\n\n this.state = newState;\n }\n\n public setSerializer(serializer: Serializer<State>) {\n this._serializer = serializer;\n }\n\n public async setMetadata(meta: Partial<Metadata>) {\n if (!this.listing.metadata) {\n this.listing.metadata = meta as Metadata;\n\n } else {\n for (const field in meta) {\n if (!meta.hasOwnProperty(field)) { continue; }\n this.listing.metadata[field] = meta[field];\n }\n\n // `MongooseDriver` workaround: persit metadata mutations\n if ('markModified' in this.listing) {\n (this.listing as any).markModified('metadata');\n }\n }\n\n if (this._internalState === RoomInternalState.CREATED) {\n await this.listing.save();\n }\n }\n\n public async setPrivate(bool: boolean = true) {\n if (this.listing.private === bool) return;\n\n this.listing.private = bool;\n\n if (this._internalState === RoomInternalState.CREATED) {\n await this.listing.save();\n }\n\n this._events.emit('visibility-change', bool);\n }\n\n /**\n * Locking the room will remove it from the pool of available rooms for new clients to connect to.\n */\n public async lock() {\n // rooms locked internally aren't explicit locks.\n this._lockedExplicitly = (arguments[0] === undefined);\n\n // skip if already locked.\n if (this._locked) { return; }\n\n this._locked = true;\n\n await this.listing.updateOne({\n $set: { locked: this._locked },\n });\n\n this._events.emit('lock');\n }\n\n /**\n * Unlocking the room returns it to the pool of available rooms for new clients to connect to.\n */\n public async unlock() {\n // only internal usage passes arguments to this function.\n if (arguments[0] === undefined) {\n this._lockedExplicitly = false;\n }\n\n // skip if already locked\n if (!this._locked) { return; }\n\n this._locked = false;\n\n await this.listing.updateOne({\n $set: { locked: this._locked },\n });\n\n this._events.emit('unlock');\n }\n\n public send(client: Client, type: string | number, message: any, options?: ISendOptions): void;\n public send(client: Client, message: Schema, options?: ISendOptions): void;\n public send(client: Client, messageOrType: any, messageOrOptions?: any | ISendOptions, options?: ISendOptions): void {\n logger.warn('DEPRECATION WARNING: use client.send(...) instead of this.send(client, ...)');\n client.send(messageOrType, messageOrOptions, options);\n }\n\n public broadcast(type: string | number, message?: any, options?: IBroadcastOptions);\n public broadcast<T extends Schema>(message: T, options?: IBroadcastOptions);\n public broadcast(\n typeOrSchema: string | number | Schema,\n messageOrOptions?: any | IBroadcastOptions,\n options?: IBroadcastOptions,\n ) {\n const isSchema = (typeof(typeOrSchema) === 'object');\n const opts: IBroadcastOptions = ((isSchema) ? messageOrOptions : options);\n\n if (opts && opts.afterNextPatch) {\n delete opts.afterNextPatch;\n this._afterNextPatchQueue.push(['broadcast', arguments]);\n return;\n }\n\n if (isSchema) {\n this.broadcastMessageSchema(typeOrSchema as Schema, opts);\n\n } else {\n\n this.broadcastMessageType(typeOrSchema as string, messageOrOptions, opts);\n }\n }\n\n /**\n * Checks whether mutations have occurred in the state, and broadcast them to all connected clients.\n */\n public broadcastPatch() {\n if (this.onBeforePatch) {\n this.onBeforePatch(this.state);\n }\n\n if (!this._simulationInterval) {\n this.clock.tick();\n }\n\n if (!this.state) {\n return false;\n }\n\n const hasChanges = this._serializer.applyPatches(this.clients, this.state);\n\n // broadcast messages enqueued for \"after patch\"\n this._dequeueAfterPatchMessages();\n\n return hasChanges;\n }\n\n public onMessage<T = any>(\n messageType: '*',\n callback: (client: Client<ExtractUserData<typeof this['clients']>, ExtractAuthData<typeof this['clients']>>, type: string | number, message: T) => void\n );\n public onMessage<T = any>(\n messageType: string | number,\n callback: (client: Client<ExtractUserData<typeof this['clients']>, ExtractAuthData<typeof this['clients']>>, message: T) => void\n );\n public onMessage<T = any>(messageType: '*' | string | number, callback: (...args: any[]) => void) {\n this.onMessageHandlers[messageType] = callback;\n // returns a method to unbind the callback\n return () => delete this.onMessageHandlers[messageType];\n }\n\n /**\n * Disconnect all connected clients, and then dispose the room.\n *\n * @param closeCode WebSocket close code (default = 4000, which is a \"consented leave\")\n * @returns Promise<void>\n */\n public disconnect(closeCode: number = Protocol.WS_CLOSE_CONSENTED): Promise<any> {\n // skip if already disposing\n if (this._internalState === RoomInternalState.DISPOSING) {\n return;\n\n } else if (this._internalState === RoomInternalState.CREATING) {\n throw new Error(\"cannot disconnect during onCreate()\");\n }\n\n this._internalState = RoomInternalState.DISPOSING;\n this.listing.remove();\n\n this.#_autoDispose = true;\n\n const delayedDisconnection = new Promise<void>((resolve) =>\n this._events.once('disconnect', () => resolve()));\n\n // reject pending reconnections\n for (const [_, reconnection] of Object.values(this._reconnections)) {\n reconnection.reject();\n }\n\n let numClients = this.clients.length;\n if (numClients > 0) {\n // clients may have `async onLeave`, room will be disposed after they're fulfilled\n while (numClients--) {\n this._forciblyCloseClient(this.clients[numClients], closeCode);\n }\n\n } else {\n // no clients connected, dispose immediately.\n this._events.emit('dispose');\n }\n\n return delayedDisconnection;\n }\n\n public async ['_onJoin'](client: Client, req?: http.IncomingMessage) {\n const sessionId = client.sessionId;\n\n // generate unique private reconnection token\n client._reconnectionToken = generateId();\n\n if (this.reservedSeatTimeouts[sessionId]) {\n clearTimeout(this.reservedSeatTimeouts[sessionId]);\n delete this.reservedSeatTimeouts[sessionId];\n }\n\n // clear auto-dispose timeout.\n if (this._autoDisposeTimeout) {\n clearTimeout(this._autoDisposeTimeout);\n this._autoDisposeTimeout = undefined;\n }\n\n // get seat reservation options and clear it\n const [joinOptions, authData] = this.reservedSeats[sessionId];\n\n //\n // TODO: remove this check on 1.0.0\n // - the seat reservation is used to keep track of number of clients and their pending seats (see `hasReachedMaxClients`)\n // - when we fully migrate to static onAuth(), the seat reservation can be removed immediately here\n // - if async onAuth() is in use, the seat reservation is removed after onAuth() is fulfilled.\n // - mark reservation as \"consumed\"\n //\n if (this.reservedSeats[sessionId].length > 2) {\n throw new ServerError(ErrorCode.MATCHMAKE_EXPIRED, \"already consumed\");\n }\n this.reservedSeats[sessionId].push(true);\n\n // share \"after next patch queue\" reference with every client.\n client._afterNextPatchQueue = this._afterNextPatchQueue;\n\n // add temporary callback to keep track of disconnections during `onJoin`.\n client.ref['onleave'] = (_) => client.state = ClientState.LEAVING;\n client.ref.once('close', client.ref['onleave']);\n\n const previousReconnectionToken = this._reconnectingSessionId.get(sessionId);\n if (previousReconnectionToken) {\n this.clients.push(client);\n this._reconnections[previousReconnectionToken]?.[1].resolve(client);\n\n } else {\n try {\n if (authData) {\n client.auth = authData;\n\n } else if (this.onAuth !== Room.prototype.onAuth) {\n client.auth = await this.onAuth(client, joinOptions, req);\n\n if (!client.auth) {\n throw new ServerError(ErrorCode.AUTH_FAILED, 'onAuth failed');\n }\n }\n\n //\n // On async onAuth, client may have been disconnected.\n //\n if (client.readyState !== WebSocket.OPEN) {\n throw new ServerError(Protocol.WS_CLOSE_GOING_AWAY, 'already disconnected');\n }\n\n this.clients.push(client);\n\n if (this.onJoin) {\n await this.onJoin(client, joinOptions, client.auth);\n }\n\n // emit 'join' to room handler\n this._events.emit('join', client);\n\n // remove seat reservation\n delete this.reservedSeats[sessionId];\n\n // client left during `onJoin`, call _onLeave immediately.\n if (client.state === ClientState.LEAVING) {\n await this._onLeave(client, Protocol.WS_CLOSE_GOING_AWAY);\n }\n\n } catch (e) {\n this.clients.delete(client);\n\n // remove seat reservation\n delete this.reservedSeats[sessionId];\n\n this._decrementClientCount();\n\n // make sure an error code is provided.\n if (!e.code) {\n e.code = ErrorCode.APPLICATION_ERROR;\n }\n\n throw e;\n }\n }\n\n // state might already be ClientState.LEAVING here\n if (client.state === ClientState.JOINING) {\n client.ref.removeListener('close', client.ref['onleave']);\n\n // only bind _onLeave after onJoin has been successful\n client.ref['onleave'] = this._onLeave.bind(this, client);\n client.ref.once('close', client.ref['onleave']);\n\n // allow client to send messages after onJoin has succeeded.\n client.ref.on('message', this._onMessage.bind(this, client));\n\n // confirm room id that matches the room name requested to join\n client.raw(getMessageBytes[Protocol.JOIN_ROOM](\n client._reconnectionToken,\n this._serializer.id,\n this._serializer.handshake && this._serializer.handshake(),\n ));\n }\n }\n\n /**\n * Allow the specified client to reconnect into the room. Must be used inside `onLeave()` method.\n * If seconds is provided, the reconnection is going to be cancelled after the provided amount of seconds.\n *\n * @param previousClient - The client which is to be waiting until re-connection happens.\n * @param seconds - Timeout period on re-connection in seconds.\n *\n * @returns Deferred<Client> - The differed is a promise like type.\n * This type can forcibly reject the promise by calling `.reject()`.\n */\n public allowReconnection(previousClient: Client, seconds: number | \"manual\"): Deferred<Client> {\n //\n // skip reconnection if client has never fully JOINED.\n //\n // (having `_enqueuedMessages !== undefined` means that the client has never\n // been at \"ClientState.JOINED\" state)\n //\n if (previousClient._enqueuedMessages !== undefined) {\n return;\n }\n\n if (seconds === undefined) { // TODO: remove this check\n console.warn(\"DEPRECATED: allowReconnection() requires a second argument. Using \\\"manual\\\" mode.\");\n seconds = \"manual\";\n }\n\n if (seconds === \"manual\") {\n seconds = Infinity;\n }\n\n if (this._internalState === RoomInternalState.DISPOSING) {\n this._disposeIfEmpty(); // gracefully shutting down\n throw new Error('disconnecting');\n }\n\n const sessionId = previousClient.sessionId;\n const reconnectionToken = previousClient._reconnectionToken;\n\n this._reserveSeat(sessionId, true, previousClient.auth, seconds, true);\n\n // keep reconnection reference in case the user reconnects into this room.\n const reconnection = new Deferred<Client>();\n this._reconnections[reconnectionToken] = [sessionId, reconnection];\n\n if (seconds !== Infinity) {\n // expire seat reservation after timeout\n this.reservedSeatTimeouts[sessionId] = setTimeout(() =>\n reconnection.reject(false), seconds * 1000);\n }\n\n const cleanup = () => {\n delete this._reconnections[reconnectionToken];\n delete this.reservedSeats[sessionId];\n delete this.reservedSeatTimeouts[sessionId];\n this._reconnectingSessionId.delete(sessionId);\n };\n\n reconnection.\n then((newClient) => {\n newClient.auth = previousClient.auth;\n newClient.userData = previousClient.userData;\n previousClient.ref = newClient.ref; // swap \"ref\" for convenience\n previousClient.state = ClientState.RECONNECTED;\n clearTimeout(this.reservedSeatTimeouts[sessionId]);\n cleanup();\n }).\n catch(() => {\n cleanup();\n this.resetAutoDisposeTimeout();\n });\n\n return reconnection;\n }\n\n protected resetAutoDisposeTimeout(timeoutInSeconds: number = 1) {\n clearTimeout(this._autoDisposeTimeout);\n\n if (!this.#_autoDispose) {\n return;\n }\n\n this._autoDisposeTimeout = setTimeout(() => {\n this._autoDisposeTimeout = undefined;\n this._disposeIfEmpty();\n }, timeoutInSeconds * 1000);\n }\n\n private broadcastMessageSchema<T extends Schema>(message: T, options: IBroadcastOptions = {}) {\n debugMessage(\"broadcast: %O\", message);\n const encodedMessage = getMessageBytes[Protocol.ROOM_DATA_SCHEMA](message);\n const except = (typeof (options.except) !== \"undefined\")\n ? Array.isArray(options.except)\n ? options.except\n : [options.except]\n : undefined;\n\n let numClients = this.clients.length;\n while (numClients--) {\n const client = this.clients[numClients];\n\n if (!except || !except.includes(client)) {\n client.enqueueRaw(encodedMessage);\n }\n }\n }\n\n private broadcastMessageType(type: string, message?: any, options: IBroadcastOptions = {}) {\n debugMessage(\"broadcast: %O\", message);\n const encodedMessage = getMessageBytes.raw(Protocol.ROOM_DATA, type, message);\n const except = (typeof (options.except) !== \"undefined\")\n ? Array.isArray(options.except)\n ? options.except\n : [options.except]\n : undefined;\n\n let numClients = this.clients.length;\n while (numClients--) {\n const client = this.clients[numClients];\n\n if (!except || !except.includes(client)) {\n client.enqueueRaw(encodedMessage);\n }\n }\n }\n\n private sendFullState(client: Client): void {\n client.enqueueRaw(getMessageBytes[Protocol.ROOM_STATE](this._serializer.getFullState(client)));\n }\n\n private _dequeueAfterPatchMessages() {\n const length = this._afterNextPatchQueue.length;\n\n if (length > 0) {\n for (let i = 0; i < length; i++) {\n const [target, args] = this._afterNextPatchQueue[i];\n\n if (target === \"broadcast\") {\n this.broadcast.apply(this, args);\n\n } else {\n (target as Client).raw.apply(target, args);\n }\n }\n\n // new messages may have been added in the meantime,\n // let's splice the ones that have been processed\n this._afterNextPatchQueue.splice(0, length);\n }\n }\n\n private async _reserveSeat(\n sessionId: string,\n joinOptions: any = true,\n authData: any = undefined,\n seconds: number = this.seatReservationTime,\n allowReconnection: boolean = false,\n devModeReconnection?: boolean,\n ) {\n if (!allowReconnection && this.hasReachedMaxClients()) {\n return false;\n }\n\n this.reservedSeats[sessionId] = [joinOptions, authData];\n\n if (!allowReconnection) {\n await this._incrementClientCount();\n\n this.reservedSeatTimeouts[sessionId] = setTimeout(async () => {\n delete this.reservedSeats[sessionId];\n delete this.reservedSeatTimeouts[sessionId];\n await this._decrementClientCount();\n }, seconds * 1000);\n\n this.resetAutoDisposeTimeout(seconds);\n }\n\n //\n // isDevMode workaround to allow players to reconnect on devMode\n //\n if (devModeReconnection) {\n this._reconnectingSessionId.set(sessionId, sessionId);\n }\n\n return true;\n }\n\n private _disposeIfEmpty() {\n const willDispose = (\n this.#_autoDispose &&\n this._autoDisposeTimeout === undefined &&\n this.clients.length === 0 &&\n Object.keys(this.reservedSeats).length === 0\n );\n\n if (willDispose) {\n this._events.emit('dispose');\n }\n\n return willDispose;\n }\n\n private async _dispose(): Promise<any> {\n this._internalState = RoomInternalState.DISPOSING;\n\n await this.listing.remove();\n\n let userReturnData;\n if (this.onDispose) {\n userReturnData = this.onDispose();\n }\n\n if (this._patchInterval) {\n clearInterval(this._patchInterval);\n this._patchInterval = undefined;\n }\n\n if (this._simulationInterval) {\n clearInterval(this._simulationInterval);\n this._simulationInterval = undefined;\n }\n\n if (this._autoDisposeTimeout) {\n clearInterval(this._autoDisposeTimeout);\n this._autoDisposeTimeout = undefined;\n }\n\n // clear all timeouts/intervals + force to stop ticking\n this.clock.clear();\n this.clock.stop();\n\n return await (userReturnData || Promise.resolve());\n }\n\n private _onMessage(client: Client, bytes: number[]) {\n // skip if client is on LEAVING state.\n if (client.state === ClientState.LEAVING) { return; }\n\n const it: Iterator = { offset: 0 };\n const code = decode.uint8(bytes, it);\n\n if (!bytes) {\n debugAndPrintError(`${this.roomName} (${this.roomId}), couldn't decode message: ${bytes}`);\n return;\n }\n\n if (code === Protocol.ROOM_DATA) {\n const messageType = (decode.stringCheck(bytes, it))\n ? decode.string(bytes, it)\n : decode.number(bytes, it);\n\n let message;\n try {\n message = (bytes.length > it.offset)\n ? unpack(new Uint8Array(bytes.slice(it.offset, bytes.length)))\n : undefined;\n debugMessage(\"received: '%s' -> %j\", messageType, message);\n } catch (e) {\n debugAndPrintError(e);\n return;\n }\n\n if (this.onMessageHandlers[messageType]) {\n this.onMessageHandlers[messageType](client, message);\n\n } else if (this.onMessageHandlers['*']) {\n (this.onMessageHandlers['*'] as any)(client, messageType, message);\n\n } else {\n debugAndPrintError(`onMessage for \"${messageType}\" not registered.`);\n }\n\n } else if (code === Protocol.ROOM_DATA_BYTES) {\n const messageType = (decode.stringCheck(bytes, it))\n ? decode.string(bytes, it)\n : decode.number(bytes, it);\n\n const message = bytes.slice(it.offset, bytes.length);\n debugMessage(\"received: '%s' -> %j\", messageType, message);\n\n if (this.onMessageHandlers[messageType]) {\n this.onMessageHandlers[messageType](client, message);\n\n } else if (this.onMessageHandlers['*']) {\n (this.onMessageHandlers['*'] as any)(client, messageType, message);\n\n } else {\n debugAndPrintError(`onMessage for \"${messageType}\" not registered.`);\n }\n\n } else if (code === Protocol.JOIN_ROOM && client.state === ClientState.JOINING) {\n // join room has been acknowledged by the client\n client.state = ClientState.JOINED;\n\n // send current state when new client joins the room\n if (this.state) {\n this.sendFullState(client);\n }\n\n // dequeue messages sent before client has joined effectively (on user-defined `onJoin`)\n if (client._enqueuedMessages.length > 0) {\n client._enqueuedMessages.forEach((enqueued) => client.raw(enqueued));\n }\n delete client._enqueuedMessages;\n\n } else if (code === Protocol.LEAVE_ROOM) {\n this._forciblyCloseClient(client, Protocol.WS_CLOSE_CONSENTED);\n }\n\n }\n\n private _forciblyCloseClient(client: Client, closeCode: number) {\n // stop receiving messages from this client\n client.ref.removeAllListeners('message');\n\n // prevent \"onLeave\" from being called twice if player asks to leave\n client.ref.removeListener('close', client.ref['onleave']);\n\n // only effectively close connection when \"onLeave\" is fulfilled\n this._onLeave(client, closeCode).then(() => client.leave(closeCode));\n }\n\n private async _onLeave(client: Client, code?: number): Promise<any> {\n const success = this.clients.delete(client);\n\n // call 'onLeave' method only if the client has been successfully accepted.\n if (success) {\n client.state = ClientState.LEAVING;\n\n if (this.onLeave) {\n try {\n await this.onLeave(client, (code === Protocol.WS_CLOSE_CONSENTED));\n\n } catch (e) {\n debugAndPrintError(`onLeave error: ${(e && e.message || e || 'promise rejected')}`);\n }\n }\n }\n\n if (client.state !== ClientState.RECONNECTED) {\n // try to dispose immediately if client reconnection isn't set up.\n const willDispose = await this._decrementClientCount();\n\n // trigger 'leave' only if seat reservation has been fully consumed\n if (this.reservedSeats[client.sessionId] === undefined) {\n this._events.emit('leave', client, willDispose);\n }\n }\n }\n\n private async _incrementClientCount() {\n // lock automatically when maxClients is reached\n if (!this._locked && this.hasReachedMaxClients()) {\n this._maxClientsReached = true;\n this.lock.call(this, true);\n }\n\n await this.listing.updateOne({\n $inc: { clients: 1 },\n $set: { locked: this._locked },\n });\n }\n\n private async _decrementClientCount() {\n const willDispose = this._disposeIfEmpty();\n\n if (this._internalState === RoomInternalState.DISPOSING) {\n return true;\n }\n\n // unlock if room is available for new connections\n if (!willDispose) {\n if (this._maxClientsReached && !this._lockedExplicitly) {\n this._maxClientsReached = false;\n this.unlock.call(this, true);\n }\n\n // update room listing cache\n await this.listing.updateOne({\n $inc: { clients: -1 },\n $set: { locked: this._locked },\n });\n }\n\n return willDispose;\n }\n\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["RoomInternalState", "Clock", "WebSocket"]
7
7
  }
package/build/Room.mjs CHANGED
@@ -68,8 +68,8 @@ class Room {
68
68
  }
69
69
  set autoDispose(value) {
70
70
  if (value !== this.#_autoDispose && this._internalState !== 2 /* DISPOSING */) {
71
- this.resetAutoDisposeTimeout();
72
71
  this.#_autoDispose = value;
72
+ this.resetAutoDisposeTimeout();
73
73
  }
74
74
  }
75
75
  get roomName() {
@@ -542,7 +542,9 @@ class Room {
542
542
  }
543
543
  if (client.state !== ClientState.RECONNECTED) {
544
544
  const willDispose = await this._decrementClientCount();
545
- this._events.emit("leave", client, willDispose);
545
+ if (this.reservedSeats[client.sessionId] === void 0) {
546
+ this._events.emit("leave", client, willDispose);
547
+ }
546
548
  }
547
549
  }
548
550
  async _incrementClientCount() {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/Room.ts"],
4
- "sourcesContent": ["import http, { IncomingMessage } from 'http';\nimport WebSocket from 'ws'; // TODO: move this to Transport\n\nimport { unpack } from 'msgpackr';\nimport { decode, Iterator, Schema } from '@colyseus/schema';\n\nimport Clock from '@gamestdio/timer';\nimport { EventEmitter } from 'events';\nimport { logger } from './Logger';\n\nimport { Presence } from './presence/Presence';\n\nimport { NoneSerializer } from './serializer/NoneSerializer';\nimport { SchemaSerializer } from './serializer/SchemaSerializer';\nimport { Serializer } from './serializer/Serializer';\n\nimport { ErrorCode, getMessageBytes, Protocol } from './Protocol';\nimport { Deferred, generateId } from './utils/Utils';\nimport { isDevMode } from './utils/DevMode';\n\nimport { debugAndPrintError, debugMessage } from './Debug';\nimport { ServerError } from './errors/ServerError';\nimport { RoomListingData } from './matchmaker/driver';\nimport { Client, ClientArray, ClientState, ISendOptions } from './Transport';\n\nconst DEFAULT_PATCH_RATE = 1000 / 20; // 20fps (50ms)\nconst DEFAULT_SIMULATION_INTERVAL = 1000 / 60; // 60fps (16.66ms)\nconst noneSerializer = new NoneSerializer();\n\nexport const DEFAULT_SEAT_RESERVATION_TIME = Number(process.env.COLYSEUS_SEAT_RESERVATION_TIME || 15);\n\nexport type SimulationCallback = (deltaTime: number) => void;\n\nexport interface IBroadcastOptions extends ISendOptions {\n except?: Client | Client[];\n}\n\nexport enum RoomInternalState {\n CREATING = 0,\n CREATED = 1,\n DISPOSING = 2,\n}\n\ntype ExtractUserData<T> = T extends ClientArray<infer U> ? U : never;\ntype ExtractAuthData<T> = T extends ClientArray<infer _, infer U> ? U : never;\n\n/**\n * A Room class is meant to implement a game session, and/or serve as the communication channel\n * between a group of clients.\n *\n * - Rooms are created on demand during matchmaking by default\n * - Room classes must be exposed using `.define()`\n */\nexport abstract class Room<State extends object= any, Metadata= any> {\n\n /**\n * This property will change on these situations:\n * - The maximum number of allowed clients has been reached (`maxClients`)\n * - You manually locked, or unlocked the room using lock() or `unlock()`.\n *\n * @readonly\n */\n public get locked() {\n return this._locked;\n }\n\n public get metadata() {\n return this.listing.metadata;\n }\n\n public listing: RoomListingData<Metadata>;\n\n /**\n * A ClockTimer instance, used for timing events.\n */\n public clock: Clock = new Clock();\n\n #_roomId: string;\n #_roomName: string;\n #_autoDispose: boolean = true;\n\n /**\n * Maximum number of clients allowed to connect into the room. When room reaches this limit,\n * it is locked automatically. Unless the room was explicitly locked by you via `lock()` method,\n * the room will be unlocked as soon as a client disconnects from it.\n */\n public maxClients: number = Infinity;\n /**\n * Frequency to send the room state to connected clients, in milliseconds.\n *\n * @default 50ms (20fps)\n */\n public patchRate: number = DEFAULT_PATCH_RATE;\n\n /**\n * The state instance you provided to `setState()`.\n */\n public state: State;\n /**\n * The presence instance. Check Presence API for more details.\n *\n * @see {@link https://docs.colyseus.io/colyseus/server/presence/|Presence API}\n */\n public presence: Presence;\n\n /**\n * The array of connected clients.\n *\n * @see {@link https://docs.colyseus.io/colyseus/server/room/#client|Client instance}\n */\n public clients: ClientArray = new ClientArray();\n\n /** @internal */\n public _events = new EventEmitter();\n\n // seat reservation & reconnection\n protected seatReservationTime: number = DEFAULT_SEAT_RESERVATION_TIME;\n protected reservedSeats: { [sessionId: string]: [any, any] } = {};\n protected reservedSeatTimeouts: { [sessionId: string]: NodeJS.Timer } = {};\n\n protected _reconnections: { [reconnectionToken: string]: [string, Deferred] } = {};\n private _reconnectingSessionId = new Map<string, string>();\n\n private onMessageHandlers: {[id: string]: (client: Client, message: any) => void} = {};\n\n private _serializer: Serializer<State> = noneSerializer;\n private _afterNextPatchQueue: Array<[string | Client, IArguments]> = [];\n\n private _simulationInterval: NodeJS.Timer;\n private _patchInterval: NodeJS.Timer;\n\n private _internalState: RoomInternalState = RoomInternalState.CREATING;\n private _locked: boolean = false;\n private _lockedExplicitly: boolean = false;\n private _maxClientsReached: boolean = false;\n\n // this timeout prevents rooms that are created by one process, but no client\n // ever had success joining into it on the specified interval.\n private _autoDisposeTimeout: NodeJS.Timer;\n\n // TODO: remove \"presence\" from constructor on 0.16.0\n constructor(presence?: Presence) {\n this.presence = presence;\n\n this._events.once('dispose', async () => {\n try {\n await this._dispose();\n\n } catch (e) {\n debugAndPrintError(`onDispose error: ${(e && e.message || e || 'promise rejected')}`);\n }\n this._events.emit('disconnect');\n });\n\n this.setPatchRate(this.patchRate);\n // set default _autoDisposeTimeout\n this.resetAutoDisposeTimeout(this.seatReservationTime);\n }\n\n /**\n * Automatically dispose the room when last client disconnects.\n *\n * @default true\n */\n public get autoDispose() { return this.#_autoDispose; }\n public set autoDispose(value: boolean) {\n if (\n value !== this.#_autoDispose &&\n this._internalState !== RoomInternalState.DISPOSING\n ) {\n this.resetAutoDisposeTimeout();\n this.#_autoDispose = value;\n }\n }\n\n /**\n * The name of the room you provided as first argument for `gameServer.define()`.\n *\n * @returns roomName string\n */\n public get roomName() { return this.#_roomName; }\n /**\n * Setting the name of the room. Overwriting this property is restricted.\n *\n * @param roomName\n */\n public set roomName(roomName: string) {\n if (this.#_roomName) {\n // prevent user from setting roomName after it has been defined.\n throw new ServerError(ErrorCode.APPLICATION_ERROR, \"'roomName' cannot be overwritten.\");\n }\n this.#_roomName = roomName;\n }\n\n /**\n * A unique, auto-generated, 9-character-long id of the room.\n * You may replace `this.roomId` during `onCreate()`.\n *\n * @returns roomId string\n */\n public get roomId() { return this.#_roomId; }\n /**\n * Setting the roomId, is restricted in room lifetime except upon room creation.\n *\n * @param roomId\n * @returns roomId string\n */\n public set roomId(roomId: string) {\n if (this._internalState !== RoomInternalState.CREATING && !isDevMode) {\n // prevent user from setting roomId after room has been created.\n throw new ServerError(ErrorCode.APPLICATION_ERROR, \"'roomId' can only be overridden upon room creation.\");\n }\n this.#_roomId = roomId;\n }\n\n // Optional abstract methods\n public onBeforePatch?(state: State): void | Promise<any>;\n public onCreate?(options: any): void | Promise<any>;\n public onJoin?(\n client: Client<ExtractUserData<typeof this['clients']>, ExtractAuthData<typeof this['clients']>>,\n options?: any,\n auth?: ExtractAuthData<typeof this['clients']>,\n ): void | Promise<any>;\n public onLeave?(\n client: Client<ExtractUserData<typeof this['clients']>, ExtractAuthData<typeof this['clients']>>,\n consented?: boolean,\n ): void | Promise<any>;\n public onDispose?(): void | Promise<any>;\n\n // TODO: flag as @deprecated on v0.16\n // TOOD: remove instance level `onAuth` on 1.0\n /**\n * onAuth at the instance level will be deprecated in the future.\n * Please use \"static onAuth(token, req) instead\n */\n public onAuth(\n client: Client<ExtractUserData<typeof this['clients']>, ExtractAuthData<typeof this['clients']>>,\n options: any,\n request?: http.IncomingMessage\n ): any | Promise<any> {\n return true;\n }\n\n static async onAuth(token: string, req: IncomingMessage): Promise<unknown> {\n return true;\n }\n\n /**\n * devMode: When `devMode` is enabled, `onCacheRoom` method is called during\n * graceful shutdown.\n *\n * Implement this method to return custom data to be cached. `onRestoreRoom`\n * will be called with the data returned by `onCacheRoom`\n */\n public onCacheRoom?(): any;\n\n /**\n * devMode: When `devMode` is enabled, `onRestoreRoom` method is called during\n * process startup, with the data returned by the `onCacheRoom` method.\n */\n public onRestoreRoom?(cached?: any): void;\n\n /**\n * Returns whether the sum of connected clients and reserved seats exceeds maximum number of clients.\n *\n * @returns boolean\n */\n public hasReachedMaxClients(): boolean {\n return (\n (this.clients.length + Object.keys(this.reservedSeats).length) >= this.maxClients ||\n this._internalState === RoomInternalState.DISPOSING\n );\n }\n\n /**\n * Set the number of seconds a room can wait for a client to effectively join the room.\n * You should consider how long your `onAuth()` will have to wait for setting a different seat reservation time.\n * The default value is 15 seconds. You may set the `COLYSEUS_SEAT_RESERVATION_TIME`\n * environment variable if you'd like to change the seat reservation time globally.\n *\n * @default 15 seconds\n *\n * @param seconds - number of seconds.\n * @returns The modified Room object.\n */\n public setSeatReservationTime(seconds: number) {\n this.seatReservationTime = seconds;\n return this;\n }\n\n public hasReservedSeat(sessionId: string, reconnectionToken?: string): boolean {\n if (reconnectionToken) {\n const reconnection = this._reconnections[reconnectionToken];\n return (\n reconnection &&\n reconnection[0] === sessionId &&\n this.reservedSeats[sessionId] !== undefined &&\n this._reconnectingSessionId.has(sessionId)\n );\n\n } else {\n return (\n this.reservedSeats[sessionId] !== undefined &&\n (\n !this._reconnectingSessionId.has(sessionId) || // prevent possible \"reconnect\" requests without a reconnection token\n (this._reconnectingSessionId.get(sessionId) === sessionId) // devMode reconnection\n )\n );\n }\n }\n\n public checkReconnectionToken(reconnectionToken: string) {\n const reconnection = this._reconnections[reconnectionToken];\n const sessionId = (reconnection && reconnection[0]);\n\n if (this.hasReservedSeat(sessionId)) {\n this._reconnectingSessionId.set(sessionId, reconnectionToken);\n return sessionId;\n\n } else {\n return undefined;\n }\n }\n\n /**\n * (Optional) Set a simulation interval that can change the state of the game.\n * The simulation interval is your game loop.\n *\n * @default 16.6ms (60fps)\n *\n * @param onTickCallback - You can implement your physics or world updates here!\n * This is a good place to update the room state.\n * @param delay - Interval delay on executing `onTickCallback` in milliseconds.\n */\n public setSimulationInterval(onTickCallback?: SimulationCallback, delay: number = DEFAULT_SIMULATION_INTERVAL): void {\n // clear previous interval in case called setSimulationInterval more than once\n if (this._simulationInterval) { clearInterval(this._simulationInterval); }\n\n if (onTickCallback) {\n this._simulationInterval = setInterval(() => {\n this.clock.tick();\n onTickCallback(this.clock.deltaTime);\n }, delay);\n }\n }\n\n public setPatchRate(milliseconds: number | null): void {\n this.patchRate = milliseconds;\n\n // clear previous interval in case called setPatchRate more than once\n if (this._patchInterval) {\n clearInterval(this._patchInterval);\n this._patchInterval = undefined;\n }\n\n if (milliseconds !== null && milliseconds !== 0) {\n this._patchInterval = setInterval(() => this.broadcastPatch(), milliseconds);\n }\n }\n\n public setState(newState: State) {\n this.clock.start();\n\n if ('_definition' in newState) {\n this.setSerializer(new SchemaSerializer());\n }\n\n this._serializer.reset(newState);\n\n this.state = newState;\n }\n\n public setSerializer(serializer: Serializer<State>) {\n this._serializer = serializer;\n }\n\n public async setMetadata(meta: Partial<Metadata>) {\n if (!this.listing.metadata) {\n this.listing.metadata = meta as Metadata;\n\n } else {\n for (const field in meta) {\n if (!meta.hasOwnProperty(field)) { continue; }\n this.listing.metadata[field] = meta[field];\n }\n\n // `MongooseDriver` workaround: persit metadata mutations\n if ('markModified' in this.listing) {\n (this.listing as any).markModified('metadata');\n }\n }\n\n if (this._internalState === RoomInternalState.CREATED) {\n await this.listing.save();\n }\n }\n\n public async setPrivate(bool: boolean = true) {\n if (this.listing.private === bool) return;\n\n this.listing.private = bool;\n\n if (this._internalState === RoomInternalState.CREATED) {\n await this.listing.save();\n }\n\n this._events.emit('visibility-change', bool);\n }\n\n /**\n * Locking the room will remove it from the pool of available rooms for new clients to connect to.\n */\n public async lock() {\n // rooms locked internally aren't explicit locks.\n this._lockedExplicitly = (arguments[0] === undefined);\n\n // skip if already locked.\n if (this._locked) { return; }\n\n this._locked = true;\n\n await this.listing.updateOne({\n $set: { locked: this._locked },\n });\n\n this._events.emit('lock');\n }\n\n /**\n * Unlocking the room returns it to the pool of available rooms for new clients to connect to.\n */\n public async unlock() {\n // only internal usage passes arguments to this function.\n if (arguments[0] === undefined) {\n this._lockedExplicitly = false;\n }\n\n // skip if already locked\n if (!this._locked) { return; }\n\n this._locked = false;\n\n await this.listing.updateOne({\n $set: { locked: this._locked },\n });\n\n this._events.emit('unlock');\n }\n\n public send(client: Client, type: string | number, message: any, options?: ISendOptions): void;\n public send(client: Client, message: Schema, options?: ISendOptions): void;\n public send(client: Client, messageOrType: any, messageOrOptions?: any | ISendOptions, options?: ISendOptions): void {\n logger.warn('DEPRECATION WARNING: use client.send(...) instead of this.send(client, ...)');\n client.send(messageOrType, messageOrOptions, options);\n }\n\n public broadcast(type: string | number, message?: any, options?: IBroadcastOptions);\n public broadcast<T extends Schema>(message: T, options?: IBroadcastOptions);\n public broadcast(\n typeOrSchema: string | number | Schema,\n messageOrOptions?: any | IBroadcastOptions,\n options?: IBroadcastOptions,\n ) {\n const isSchema = (typeof(typeOrSchema) === 'object');\n const opts: IBroadcastOptions = ((isSchema) ? messageOrOptions : options);\n\n if (opts && opts.afterNextPatch) {\n delete opts.afterNextPatch;\n this._afterNextPatchQueue.push(['broadcast', arguments]);\n return;\n }\n\n if (isSchema) {\n this.broadcastMessageSchema(typeOrSchema as Schema, opts);\n\n } else {\n\n this.broadcastMessageType(typeOrSchema as string, messageOrOptions, opts);\n }\n }\n\n /**\n * Checks whether mutations have occurred in the state, and broadcast them to all connected clients.\n */\n public broadcastPatch() {\n if (this.onBeforePatch) {\n this.onBeforePatch(this.state);\n }\n\n if (!this._simulationInterval) {\n this.clock.tick();\n }\n\n if (!this.state) {\n return false;\n }\n\n const hasChanges = this._serializer.applyPatches(this.clients, this.state);\n\n // broadcast messages enqueued for \"after patch\"\n this._dequeueAfterPatchMessages();\n\n return hasChanges;\n }\n\n public onMessage<T = any>(\n messageType: '*',\n callback: (client: Client<ExtractUserData<typeof this['clients']>, ExtractAuthData<typeof this['clients']>>, type: string | number, message: T) => void\n );\n public onMessage<T = any>(\n messageType: string | number,\n callback: (client: Client<ExtractUserData<typeof this['clients']>, ExtractAuthData<typeof this['clients']>>, message: T) => void\n );\n public onMessage<T = any>(messageType: '*' | string | number, callback: (...args: any[]) => void) {\n this.onMessageHandlers[messageType] = callback;\n // returns a method to unbind the callback\n return () => delete this.onMessageHandlers[messageType];\n }\n\n /**\n * Disconnect all connected clients, and then dispose the room.\n *\n * @param closeCode WebSocket close code (default = 4000, which is a \"consented leave\")\n * @returns Promise<void>\n */\n public disconnect(closeCode: number = Protocol.WS_CLOSE_CONSENTED): Promise<any> {\n // skip if already disposing\n if (this._internalState === RoomInternalState.DISPOSING) {\n return;\n\n } else if (this._internalState === RoomInternalState.CREATING) {\n throw new Error(\"cannot disconnect during onCreate()\");\n }\n\n this._internalState = RoomInternalState.DISPOSING;\n this.listing.remove();\n\n this.#_autoDispose = true;\n\n const delayedDisconnection = new Promise<void>((resolve) =>\n this._events.once('disconnect', () => resolve()));\n\n for (const [_, reconnection] of Object.values(this._reconnections)) {\n reconnection.reject();\n }\n\n let numClients = this.clients.length;\n if (numClients > 0) {\n // clients may have `async onLeave`, room will be disposed after they're fulfilled\n while (numClients--) {\n this._forciblyCloseClient(this.clients[numClients], closeCode);\n }\n } else {\n // no clients connected, dispose immediately.\n this._events.emit('dispose');\n }\n\n return delayedDisconnection;\n }\n\n public async ['_onJoin'](client: Client, req?: http.IncomingMessage) {\n const sessionId = client.sessionId;\n\n // generate unique private reconnection token\n client._reconnectionToken = generateId();\n\n if (this.reservedSeatTimeouts[sessionId]) {\n clearTimeout(this.reservedSeatTimeouts[sessionId]);\n delete this.reservedSeatTimeouts[sessionId];\n }\n\n // clear auto-dispose timeout.\n if (this._autoDisposeTimeout) {\n clearTimeout(this._autoDisposeTimeout);\n this._autoDisposeTimeout = undefined;\n }\n\n // get seat reservation options and clear it\n const [joinOptions, authData] = this.reservedSeats[sessionId];\n\n //\n // TODO: remove this check on 1.0.0\n // - the seat reservation is used to keep track of number of clients and their pending seats (see `hasReachedMaxClients`)\n // - when we fully migrate to static onAuth(), the seat reservation can be removed immediately here\n // - if async onAuth() is in use, the seat reservation is removed after onAuth() is fulfilled.\n // - mark reservation as \"consumed\"\n //\n if (this.reservedSeats[sessionId].length > 2) {\n throw new ServerError(ErrorCode.MATCHMAKE_EXPIRED, \"already consumed\");\n }\n this.reservedSeats[sessionId].push(true);\n\n // share \"after next patch queue\" reference with every client.\n client._afterNextPatchQueue = this._afterNextPatchQueue;\n\n // add temporary callback to keep track of disconnections during `onJoin`.\n client.ref['onleave'] = (_) => client.state = ClientState.LEAVING;\n client.ref.once('close', client.ref['onleave']);\n\n const previousReconnectionToken = this._reconnectingSessionId.get(sessionId);\n if (previousReconnectionToken) {\n this.clients.push(client);\n this._reconnections[previousReconnectionToken]?.[1].resolve(client);\n\n } else {\n try {\n if (authData) {\n client.auth = authData;\n\n } else if (this.onAuth !== Room.prototype.onAuth) {\n client.auth = await this.onAuth(client, joinOptions, req);\n\n if (!client.auth) {\n throw new ServerError(ErrorCode.AUTH_FAILED, 'onAuth failed');\n }\n }\n\n //\n // On async onAuth, client may have been disconnected.\n //\n if (client.readyState !== WebSocket.OPEN) {\n throw new ServerError(Protocol.WS_CLOSE_GOING_AWAY, 'already disconnected');\n }\n\n this.clients.push(client);\n\n if (this.onJoin) {\n await this.onJoin(client, joinOptions, client.auth);\n }\n\n // emit 'join' to room handler (if not reconnecting)\n this._events.emit('join', client);\n\n // remove seat reservation\n delete this.reservedSeats[sessionId];\n\n // client left during `onJoin`, call _onLeave immediately.\n if (client.state === ClientState.LEAVING) {\n await this._onLeave(client, Protocol.WS_CLOSE_GOING_AWAY);\n }\n\n } catch (e) {\n this.clients.delete(client);\n\n // remove seat reservation\n delete this.reservedSeats[sessionId];\n\n this._decrementClientCount();\n\n // make sure an error code is provided.\n if (!e.code) {\n e.code = ErrorCode.APPLICATION_ERROR;\n }\n\n throw e;\n }\n }\n\n // state might already be ClientState.LEAVING here\n if (client.state === ClientState.JOINING) {\n client.ref.removeListener('close', client.ref['onleave']);\n\n // only bind _onLeave after onJoin has been successful\n client.ref['onleave'] = this._onLeave.bind(this, client);\n client.ref.once('close', client.ref['onleave']);\n\n // allow client to send messages after onJoin has succeeded.\n client.ref.on('message', this._onMessage.bind(this, client));\n\n // confirm room id that matches the room name requested to join\n client.raw(getMessageBytes[Protocol.JOIN_ROOM](\n client._reconnectionToken,\n this._serializer.id,\n this._serializer.handshake && this._serializer.handshake(),\n ));\n }\n }\n\n /**\n * Allow the specified client to reconnect into the room. Must be used inside `onLeave()` method.\n * If seconds is provided, the reconnection is going to be cancelled after the provided amount of seconds.\n *\n * @param previousClient - The client which is to be waiting until re-connection happens.\n * @param seconds - Timeout period on re-connection in seconds.\n *\n * @returns Deferred<Client> - The differed is a promise like type.\n * This type can forcibly reject the promise by calling `.reject()`.\n */\n public allowReconnection(previousClient: Client, seconds: number | \"manual\"): Deferred<Client> {\n //\n // skip reconnection if client has never fully JOINED.\n //\n // (having `_enqueuedMessages !== undefined` means that the client has never\n // been at \"ClientState.JOINED\" state)\n //\n if (previousClient._enqueuedMessages !== undefined) {\n return;\n }\n\n if (seconds === undefined) { // TODO: remove this check\n console.warn(\"DEPRECATED: allowReconnection() requires a second argument. Using \\\"manual\\\" mode.\");\n seconds = \"manual\";\n }\n\n if (seconds === \"manual\") {\n seconds = Infinity;\n }\n\n if (this._internalState === RoomInternalState.DISPOSING) {\n this._disposeIfEmpty(); // gracefully shutting down\n throw new Error('disconnecting');\n }\n\n const sessionId = previousClient.sessionId;\n const reconnectionToken = previousClient._reconnectionToken;\n\n this._reserveSeat(sessionId, true, previousClient.auth, seconds, true);\n\n // keep reconnection reference in case the user reconnects into this room.\n const reconnection = new Deferred<Client>();\n this._reconnections[reconnectionToken] = [sessionId, reconnection];\n\n if (seconds !== Infinity) {\n // expire seat reservation after timeout\n this.reservedSeatTimeouts[sessionId] = setTimeout(() =>\n reconnection.reject(false), seconds * 1000);\n }\n\n const cleanup = () => {\n delete this._reconnections[reconnectionToken];\n delete this.reservedSeats[sessionId];\n delete this.reservedSeatTimeouts[sessionId];\n this._reconnectingSessionId.delete(sessionId);\n };\n\n reconnection.\n then((newClient) => {\n newClient.auth = previousClient.auth;\n newClient.userData = previousClient.userData;\n previousClient.ref = newClient.ref; // swap \"ref\" for convenience\n previousClient.state = ClientState.RECONNECTED;\n clearTimeout(this.reservedSeatTimeouts[sessionId]);\n cleanup();\n }).\n catch(() => {\n cleanup();\n this.resetAutoDisposeTimeout();\n });\n\n return reconnection;\n }\n\n protected resetAutoDisposeTimeout(timeoutInSeconds: number = 1) {\n clearTimeout(this._autoDisposeTimeout);\n\n if (!this.#_autoDispose) {\n return;\n }\n\n this._autoDisposeTimeout = setTimeout(() => {\n this._autoDisposeTimeout = undefined;\n this._disposeIfEmpty();\n }, timeoutInSeconds * 1000);\n }\n\n private broadcastMessageSchema<T extends Schema>(message: T, options: IBroadcastOptions = {}) {\n debugMessage(\"broadcast: %O\", message);\n const encodedMessage = getMessageBytes[Protocol.ROOM_DATA_SCHEMA](message);\n const except = (typeof (options.except) !== \"undefined\")\n ? Array.isArray(options.except)\n ? options.except\n : [options.except]\n : undefined;\n\n let numClients = this.clients.length;\n while (numClients--) {\n const client = this.clients[numClients];\n\n if (!except || !except.includes(client)) {\n client.enqueueRaw(encodedMessage);\n }\n }\n }\n\n private broadcastMessageType(type: string, message?: any, options: IBroadcastOptions = {}) {\n debugMessage(\"broadcast: %O\", message);\n const encodedMessage = getMessageBytes.raw(Protocol.ROOM_DATA, type, message);\n const except = (typeof (options.except) !== \"undefined\")\n ? Array.isArray(options.except)\n ? options.except\n : [options.except]\n : undefined;\n\n let numClients = this.clients.length;\n while (numClients--) {\n const client = this.clients[numClients];\n\n if (!except || !except.includes(client)) {\n client.enqueueRaw(encodedMessage);\n }\n }\n }\n\n private sendFullState(client: Client): void {\n client.enqueueRaw(getMessageBytes[Protocol.ROOM_STATE](this._serializer.getFullState(client)));\n }\n\n private _dequeueAfterPatchMessages() {\n const length = this._afterNextPatchQueue.length;\n\n if (length > 0) {\n for (let i = 0; i < length; i++) {\n const [target, args] = this._afterNextPatchQueue[i];\n\n if (target === \"broadcast\") {\n this.broadcast.apply(this, args);\n\n } else {\n (target as Client).raw.apply(target, args);\n }\n }\n\n // new messages may have been added in the meantime,\n // let's splice the ones that have been processed\n this._afterNextPatchQueue.splice(0, length);\n }\n }\n\n private async _reserveSeat(\n sessionId: string,\n joinOptions: any = true,\n authData: any = undefined,\n seconds: number = this.seatReservationTime,\n allowReconnection: boolean = false,\n devModeReconnection?: boolean,\n ) {\n if (!allowReconnection && this.hasReachedMaxClients()) {\n return false;\n }\n\n this.reservedSeats[sessionId] = [joinOptions, authData];\n\n if (!allowReconnection) {\n await this._incrementClientCount();\n\n this.reservedSeatTimeouts[sessionId] = setTimeout(async () => {\n delete this.reservedSeats[sessionId];\n delete this.reservedSeatTimeouts[sessionId];\n await this._decrementClientCount();\n }, seconds * 1000);\n\n this.resetAutoDisposeTimeout(seconds);\n }\n\n //\n // isDevMode workaround to allow players to reconnect on devMode\n //\n if (devModeReconnection) {\n this._reconnectingSessionId.set(sessionId, sessionId);\n }\n\n return true;\n }\n\n private _disposeIfEmpty() {\n const willDispose = (\n this.#_autoDispose &&\n this._autoDisposeTimeout === undefined &&\n this.clients.length === 0 &&\n Object.keys(this.reservedSeats).length === 0\n );\n\n if (willDispose) {\n this._events.emit('dispose');\n }\n\n return willDispose;\n }\n\n private async _dispose(): Promise<any> {\n this._internalState = RoomInternalState.DISPOSING;\n\n await this.listing.remove();\n\n let userReturnData;\n if (this.onDispose) {\n userReturnData = this.onDispose();\n }\n\n if (this._patchInterval) {\n clearInterval(this._patchInterval);\n this._patchInterval = undefined;\n }\n\n if (this._simulationInterval) {\n clearInterval(this._simulationInterval);\n this._simulationInterval = undefined;\n }\n\n if (this._autoDisposeTimeout) {\n clearInterval(this._autoDisposeTimeout);\n this._autoDisposeTimeout = undefined;\n }\n\n // clear all timeouts/intervals + force to stop ticking\n this.clock.clear();\n this.clock.stop();\n\n return await (userReturnData || Promise.resolve());\n }\n\n private _onMessage(client: Client, bytes: number[]) {\n // skip if client is on LEAVING state.\n if (client.state === ClientState.LEAVING) { return; }\n\n const it: Iterator = { offset: 0 };\n const code = decode.uint8(bytes, it);\n\n if (!bytes) {\n debugAndPrintError(`${this.roomName} (${this.roomId}), couldn't decode message: ${bytes}`);\n return;\n }\n\n if (code === Protocol.ROOM_DATA) {\n const messageType = (decode.stringCheck(bytes, it))\n ? decode.string(bytes, it)\n : decode.number(bytes, it);\n\n let message;\n try {\n message = (bytes.length > it.offset)\n ? unpack(new Uint8Array(bytes.slice(it.offset, bytes.length)))\n : undefined;\n debugMessage(\"received: '%s' -> %j\", messageType, message);\n } catch (e) {\n debugAndPrintError(e);\n return;\n }\n\n if (this.onMessageHandlers[messageType]) {\n this.onMessageHandlers[messageType](client, message);\n\n } else if (this.onMessageHandlers['*']) {\n (this.onMessageHandlers['*'] as any)(client, messageType, message);\n\n } else {\n debugAndPrintError(`onMessage for \"${messageType}\" not registered.`);\n }\n\n } else if (code === Protocol.ROOM_DATA_BYTES) {\n const messageType = (decode.stringCheck(bytes, it))\n ? decode.string(bytes, it)\n : decode.number(bytes, it);\n\n const message = bytes.slice(it.offset, bytes.length);\n debugMessage(\"received: '%s' -> %j\", messageType, message);\n\n if (this.onMessageHandlers[messageType]) {\n this.onMessageHandlers[messageType](client, message);\n\n } else if (this.onMessageHandlers['*']) {\n (this.onMessageHandlers['*'] as any)(client, messageType, message);\n\n } else {\n debugAndPrintError(`onMessage for \"${messageType}\" not registered.`);\n }\n\n } else if (code === Protocol.JOIN_ROOM && client.state === ClientState.JOINING) {\n // join room has been acknowledged by the client\n client.state = ClientState.JOINED;\n\n // send current state when new client joins the room\n if (this.state) {\n this.sendFullState(client);\n }\n\n // dequeue messages sent before client has joined effectively (on user-defined `onJoin`)\n if (client._enqueuedMessages.length > 0) {\n client._enqueuedMessages.forEach((enqueued) => client.raw(enqueued));\n }\n delete client._enqueuedMessages;\n\n } else if (code === Protocol.LEAVE_ROOM) {\n this._forciblyCloseClient(client, Protocol.WS_CLOSE_CONSENTED);\n }\n\n }\n\n private _forciblyCloseClient(client: Client, closeCode: number) {\n // stop receiving messages from this client\n client.ref.removeAllListeners('message');\n\n // prevent \"onLeave\" from being called twice if player asks to leave\n client.ref.removeListener('close', client.ref['onleave']);\n\n // only effectively close connection when \"onLeave\" is fulfilled\n this._onLeave(client, closeCode).then(() => client.leave(closeCode));\n }\n\n private async _onLeave(client: Client, code?: number): Promise<any> {\n const success = this.clients.delete(client);\n\n // call 'onLeave' method only if the client has been successfully accepted.\n if (success) {\n client.state = ClientState.LEAVING;\n\n if (this.onLeave) {\n try {\n await this.onLeave(client, (code === Protocol.WS_CLOSE_CONSENTED));\n\n } catch (e) {\n debugAndPrintError(`onLeave error: ${(e && e.message || e || 'promise rejected')}`);\n }\n }\n }\n\n if (client.state !== ClientState.RECONNECTED) {\n // try to dispose immediately if client reconnection isn't set up.\n const willDispose = await this._decrementClientCount();\n\n this._events.emit('leave', client, willDispose);\n }\n }\n\n private async _incrementClientCount() {\n // lock automatically when maxClients is reached\n if (!this._locked && this.hasReachedMaxClients()) {\n this._maxClientsReached = true;\n this.lock.call(this, true);\n }\n\n await this.listing.updateOne({\n $inc: { clients: 1 },\n $set: { locked: this._locked },\n });\n }\n\n private async _decrementClientCount() {\n const willDispose = this._disposeIfEmpty();\n\n if (this._internalState === RoomInternalState.DISPOSING) {\n return true;\n }\n\n // unlock if room is available for new connections\n if (!willDispose) {\n if (this._maxClientsReached && !this._lockedExplicitly) {\n this._maxClientsReached = false;\n this.unlock.call(this, true);\n }\n\n // update room listing cache\n await this.listing.updateOne({\n $inc: { clients: -1 },\n $set: { locked: this._locked },\n });\n }\n\n return willDispose;\n }\n\n}\n"],
5
- "mappings": "AACA,OAAO,eAAe;AAEtB,SAAS,cAAc;AACvB,SAAS,cAAgC;AAEzC,OAAO,WAAW;AAClB,SAAS,oBAAoB;AAC7B,SAAS,cAAc;AAIvB,SAAS,sBAAsB;AAC/B,SAAS,wBAAwB;AAGjC,SAAS,WAAW,iBAAiB,gBAAgB;AACrD,SAAS,UAAU,kBAAkB;AACrC,SAAS,iBAAiB;AAE1B,SAAS,oBAAoB,oBAAoB;AACjD,SAAS,mBAAmB;AAE5B,SAAiB,aAAa,mBAAiC;AAE/D,MAAM,qBAAqB,MAAO;AAClC,MAAM,8BAA8B,MAAO;AAC3C,MAAM,iBAAiB,IAAI,eAAe;AAEnC,MAAM,gCAAgC,OAAO,QAAQ,IAAI,kCAAkC,EAAE;AAQ7F,IAAK,oBAAL,kBAAKA,uBAAL;AACL,EAAAA,sCAAA,cAAW,KAAX;AACA,EAAAA,sCAAA,aAAU,KAAV;AACA,EAAAA,sCAAA,eAAY,KAAZ;AAHU,SAAAA;AAAA,GAAA;AAgBL,MAAe,KAA+C;AAAA,EAwFnE,YAAY,UAAqB;AAlEjC,SAAO,QAAe,IAAI,MAAM;AAIhC,yBAAyB;AAOzB,SAAO,aAAqB;AAM5B,SAAO,YAAoB;AAkB3B,SAAO,UAAuB,IAAI,YAAY;AAG9C,SAAO,UAAU,IAAI,aAAa;AAGlC,SAAU,sBAA8B;AACxC,SAAU,gBAAqD,CAAC;AAChE,SAAU,uBAA8D,CAAC;AAEzE,SAAU,iBAAsE,CAAC;AACjF,SAAQ,yBAAyB,oBAAI,IAAoB;AAEzD,SAAQ,oBAA4E,CAAC;AAErF,SAAQ,cAAiC;AACzC,SAAQ,uBAA6D,CAAC;AAKtE,SAAQ,iBAAoC;AAC5C,SAAQ,UAAmB;AAC3B,SAAQ,oBAA6B;AACrC,SAAQ,qBAA8B;AAQpC,SAAK,WAAW;AAEhB,SAAK,QAAQ,KAAK,WAAW,YAAY;AACvC,UAAI;AACF,cAAM,KAAK,SAAS;AAAA,MAEtB,SAAS,GAAP;AACA,2BAAmB,oBAAqB,KAAK,EAAE,WAAW,KAAK,oBAAqB;AAAA,MACtF;AACA,WAAK,QAAQ,KAAK,YAAY;AAAA,IAChC,CAAC;AAED,SAAK,aAAa,KAAK,SAAS;AAEhC,SAAK,wBAAwB,KAAK,mBAAmB;AAAA,EACvD;AAAA,EA/FA,IAAW,SAAS;AAClB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAW,WAAW;AACpB,WAAO,KAAK,QAAQ;AAAA,EACtB;AAAA,EASA;AAAA,EACA;AAAA,EACA;AAAA,EAqFA,IAAW,cAAc;AAAE,WAAO,KAAK;AAAA,EAAe;AAAA,EACtD,IAAW,YAAY,OAAgB;AACrC,QACE,UAAU,KAAK,iBACf,KAAK,mBAAmB,mBACxB;AACA,WAAK,wBAAwB;AAC7B,WAAK,gBAAgB;AAAA,IACvB;AAAA,EACF;AAAA,EAOA,IAAW,WAAW;AAAE,WAAO,KAAK;AAAA,EAAY;AAAA,EAMhD,IAAW,SAAS,UAAkB;AACpC,QAAI,KAAK,YAAY;AAEnB,YAAM,IAAI,YAAY,UAAU,mBAAmB,mCAAmC;AAAA,IACxF;AACA,SAAK,aAAa;AAAA,EACpB;AAAA,EAQA,IAAW,SAAS;AAAE,WAAO,KAAK;AAAA,EAAU;AAAA,EAO5C,IAAW,OAAO,QAAgB;AAChC,QAAI,KAAK,mBAAmB,oBAA8B,CAAC,WAAW;AAEpE,YAAM,IAAI,YAAY,UAAU,mBAAmB,qDAAqD;AAAA,IAC1G;AACA,SAAK,WAAW;AAAA,EAClB;AAAA,EAsBO,OACL,QACA,SACA,SACoB;AACpB,WAAO;AAAA,EACT;AAAA,EAEA,aAAa,OAAO,OAAe,KAAwC;AACzE,WAAO;AAAA,EACT;AAAA,EAsBO,uBAAgC;AACrC,WACG,KAAK,QAAQ,SAAS,OAAO,KAAK,KAAK,aAAa,EAAE,UAAW,KAAK,cACvE,KAAK,mBAAmB;AAAA,EAE5B;AAAA,EAaO,uBAAuB,SAAiB;AAC7C,SAAK,sBAAsB;AAC3B,WAAO;AAAA,EACT;AAAA,EAEO,gBAAgB,WAAmB,mBAAqC;AAC7E,QAAI,mBAAmB;AACrB,YAAM,eAAe,KAAK,eAAe;AACzC,aACE,gBACA,aAAa,OAAO,aACpB,KAAK,cAAc,eAAe,UAClC,KAAK,uBAAuB,IAAI,SAAS;AAAA,IAG7C,OAAO;AACL,aACE,KAAK,cAAc,eAAe,WAEhC,CAAC,KAAK,uBAAuB,IAAI,SAAS,KACzC,KAAK,uBAAuB,IAAI,SAAS,MAAM;AAAA,IAGtD;AAAA,EACF;AAAA,EAEO,uBAAuB,mBAA2B;AACvD,UAAM,eAAe,KAAK,eAAe;AACzC,UAAM,YAAa,gBAAgB,aAAa;AAEhD,QAAI,KAAK,gBAAgB,SAAS,GAAG;AACnC,WAAK,uBAAuB,IAAI,WAAW,iBAAiB;AAC5D,aAAO;AAAA,IAET,OAAO;AACL,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAYO,sBAAsB,gBAAqC,QAAgB,6BAAmC;AAEnH,QAAI,KAAK,qBAAqB;AAAE,oBAAc,KAAK,mBAAmB;AAAA,IAAG;AAEzE,QAAI,gBAAgB;AAClB,WAAK,sBAAsB,YAAY,MAAM;AAC3C,aAAK,MAAM,KAAK;AAChB,uBAAe,KAAK,MAAM,SAAS;AAAA,MACrC,GAAG,KAAK;AAAA,IACV;AAAA,EACF;AAAA,EAEO,aAAa,cAAmC;AACrD,SAAK,YAAY;AAGjB,QAAI,KAAK,gBAAgB;AACvB,oBAAc,KAAK,cAAc;AACjC,WAAK,iBAAiB;AAAA,IACxB;AAEA,QAAI,iBAAiB,QAAQ,iBAAiB,GAAG;AAC/C,WAAK,iBAAiB,YAAY,MAAM,KAAK,eAAe,GAAG,YAAY;AAAA,IAC7E;AAAA,EACF;AAAA,EAEO,SAAS,UAAiB;AAC/B,SAAK,MAAM,MAAM;AAEjB,QAAI,iBAAiB,UAAU;AAC7B,WAAK,cAAc,IAAI,iBAAiB,CAAC;AAAA,IAC3C;AAEA,SAAK,YAAY,MAAM,QAAQ;AAE/B,SAAK,QAAQ;AAAA,EACf;AAAA,EAEO,cAAc,YAA+B;AAClD,SAAK,cAAc;AAAA,EACrB;AAAA,EAEA,MAAa,YAAY,MAAyB;AAChD,QAAI,CAAC,KAAK,QAAQ,UAAU;AAC1B,WAAK,QAAQ,WAAW;AAAA,IAE1B,OAAO;AACL,iBAAW,SAAS,MAAM;AACxB,YAAI,CAAC,KAAK,eAAe,KAAK,GAAG;AAAE;AAAA,QAAU;AAC7C,aAAK,QAAQ,SAAS,SAAS,KAAK;AAAA,MACtC;AAGA,UAAI,kBAAkB,KAAK,SAAS;AAClC,QAAC,KAAK,QAAgB,aAAa,UAAU;AAAA,MAC/C;AAAA,IACF;AAEA,QAAI,KAAK,mBAAmB,iBAA2B;AACrD,YAAM,KAAK,QAAQ,KAAK;AAAA,IAC1B;AAAA,EACF;AAAA,EAEA,MAAa,WAAW,OAAgB,MAAM;AAC5C,QAAI,KAAK,QAAQ,YAAY;AAAM;AAEnC,SAAK,QAAQ,UAAU;AAEvB,QAAI,KAAK,mBAAmB,iBAA2B;AACrD,YAAM,KAAK,QAAQ,KAAK;AAAA,IAC1B;AAEA,SAAK,QAAQ,KAAK,qBAAqB,IAAI;AAAA,EAC7C;AAAA,EAKA,MAAa,OAAO;AAElB,SAAK,oBAAqB,UAAU,OAAO;AAG3C,QAAI,KAAK,SAAS;AAAE;AAAA,IAAQ;AAE5B,SAAK,UAAU;AAEf,UAAM,KAAK,QAAQ,UAAU;AAAA,MAC3B,MAAM,EAAE,QAAQ,KAAK,QAAQ;AAAA,IAC/B,CAAC;AAED,SAAK,QAAQ,KAAK,MAAM;AAAA,EAC1B;AAAA,EAKA,MAAa,SAAS;AAEpB,QAAI,UAAU,OAAO,QAAW;AAC9B,WAAK,oBAAoB;AAAA,IAC3B;AAGA,QAAI,CAAC,KAAK,SAAS;AAAE;AAAA,IAAQ;AAE7B,SAAK,UAAU;AAEf,UAAM,KAAK,QAAQ,UAAU;AAAA,MAC3B,MAAM,EAAE,QAAQ,KAAK,QAAQ;AAAA,IAC/B,CAAC;AAED,SAAK,QAAQ,KAAK,QAAQ;AAAA,EAC5B;AAAA,EAIO,KAAK,QAAgB,eAAoB,kBAAuC,SAA8B;AACnH,WAAO,KAAK,6EAA6E;AACzF,WAAO,KAAK,eAAe,kBAAkB,OAAO;AAAA,EACtD;AAAA,EAIO,UACL,cACA,kBACA,SACA;AACA,UAAM,WAAY,OAAO,iBAAkB;AAC3C,UAAM,OAA4B,WAAY,mBAAmB;AAEjE,QAAI,QAAQ,KAAK,gBAAgB;AAC/B,aAAO,KAAK;AACZ,WAAK,qBAAqB,KAAK,CAAC,aAAa,SAAS,CAAC;AACvD;AAAA,IACF;AAEA,QAAI,UAAU;AACZ,WAAK,uBAAuB,cAAwB,IAAI;AAAA,IAE1D,OAAO;AAEL,WAAK,qBAAqB,cAAwB,kBAAkB,IAAI;AAAA,IAC1E;AAAA,EACF;AAAA,EAKO,iBAAiB;AACtB,QAAI,KAAK,eAAe;AACtB,WAAK,cAAc,KAAK,KAAK;AAAA,IAC/B;AAEA,QAAI,CAAC,KAAK,qBAAqB;AAC7B,WAAK,MAAM,KAAK;AAAA,IAClB;AAEA,QAAI,CAAC,KAAK,OAAO;AACf,aAAO;AAAA,IACT;AAEA,UAAM,aAAa,KAAK,YAAY,aAAa,KAAK,SAAS,KAAK,KAAK;AAGzE,SAAK,2BAA2B;AAEhC,WAAO;AAAA,EACT;AAAA,EAUO,UAAmB,aAAoC,UAAoC;AAChG,SAAK,kBAAkB,eAAe;AAEtC,WAAO,MAAM,OAAO,KAAK,kBAAkB;AAAA,EAC7C;AAAA,EAQO,WAAW,YAAoB,SAAS,oBAAkC;AAE/E,QAAI,KAAK,mBAAmB,mBAA6B;AACvD;AAAA,IAEF,WAAW,KAAK,mBAAmB,kBAA4B;AAC7D,YAAM,IAAI,MAAM,qCAAqC;AAAA,IACvD;AAEA,SAAK,iBAAiB;AACtB,SAAK,QAAQ,OAAO;AAEpB,SAAK,gBAAgB;AAErB,UAAM,uBAAuB,IAAI,QAAc,CAAC,YAC9C,KAAK,QAAQ,KAAK,cAAc,MAAM,QAAQ,CAAC,CAAC;AAElD,eAAW,CAAC,GAAG,YAAY,KAAK,OAAO,OAAO,KAAK,cAAc,GAAG;AAClE,mBAAa,OAAO;AAAA,IACtB;AAEA,QAAI,aAAa,KAAK,QAAQ;AAC9B,QAAI,aAAa,GAAG;AAElB,aAAO,cAAc;AACnB,aAAK,qBAAqB,KAAK,QAAQ,aAAa,SAAS;AAAA,MAC/D;AAAA,IACF,OAAO;AAEL,WAAK,QAAQ,KAAK,SAAS;AAAA,IAC7B;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,OAAc,WAAW,QAAgB,KAA4B;AACnE,UAAM,YAAY,OAAO;AAGzB,WAAO,qBAAqB,WAAW;AAEvC,QAAI,KAAK,qBAAqB,YAAY;AACxC,mBAAa,KAAK,qBAAqB,UAAU;AACjD,aAAO,KAAK,qBAAqB;AAAA,IACnC;AAGA,QAAI,KAAK,qBAAqB;AAC5B,mBAAa,KAAK,mBAAmB;AACrC,WAAK,sBAAsB;AAAA,IAC7B;AAGA,UAAM,CAAC,aAAa,QAAQ,IAAI,KAAK,cAAc;AASnD,QAAI,KAAK,cAAc,WAAW,SAAS,GAAG;AAC5C,YAAM,IAAI,YAAY,UAAU,mBAAmB,kBAAkB;AAAA,IACvE;AACA,SAAK,cAAc,WAAW,KAAK,IAAI;AAGvC,WAAO,uBAAuB,KAAK;AAGnC,WAAO,IAAI,aAAa,CAAC,MAAM,OAAO,QAAQ,YAAY;AAC1D,WAAO,IAAI,KAAK,SAAS,OAAO,IAAI,UAAU;AAE9C,UAAM,4BAA4B,KAAK,uBAAuB,IAAI,SAAS;AAC3E,QAAI,2BAA2B;AAC7B,WAAK,QAAQ,KAAK,MAAM;AACxB,WAAK,eAAe,6BAA6B,GAAG,QAAQ,MAAM;AAAA,IAEpE,OAAO;AACL,UAAI;AACF,YAAI,UAAU;AACZ,iBAAO,OAAO;AAAA,QAEhB,WAAW,KAAK,WAAW,KAAK,UAAU,QAAQ;AAChD,iBAAO,OAAO,MAAM,KAAK,OAAO,QAAQ,aAAa,GAAG;AAExD,cAAI,CAAC,OAAO,MAAM;AAChB,kBAAM,IAAI,YAAY,UAAU,aAAa,eAAe;AAAA,UAC9D;AAAA,QACF;AAKA,YAAI,OAAO,eAAe,UAAU,MAAM;AACxC,gBAAM,IAAI,YAAY,SAAS,qBAAqB,sBAAsB;AAAA,QAC5E;AAEA,aAAK,QAAQ,KAAK,MAAM;AAExB,YAAI,KAAK,QAAQ;AACf,gBAAM,KAAK,OAAO,QAAQ,aAAa,OAAO,IAAI;AAAA,QACpD;AAGA,aAAK,QAAQ,KAAK,QAAQ,MAAM;AAGhC,eAAO,KAAK,cAAc;AAG1B,YAAI,OAAO,UAAU,YAAY,SAAS;AACxC,gBAAM,KAAK,SAAS,QAAQ,SAAS,mBAAmB;AAAA,QAC1D;AAAA,MAEF,SAAS,GAAP;AACA,aAAK,QAAQ,OAAO,MAAM;AAG1B,eAAO,KAAK,cAAc;AAE1B,aAAK,sBAAsB;AAG3B,YAAI,CAAC,EAAE,MAAM;AACX,YAAE,OAAO,UAAU;AAAA,QACrB;AAEA,cAAM;AAAA,MACR;AAAA,IACF;AAGA,QAAI,OAAO,UAAU,YAAY,SAAS;AACxC,aAAO,IAAI,eAAe,SAAS,OAAO,IAAI,UAAU;AAGxD,aAAO,IAAI,aAAa,KAAK,SAAS,KAAK,MAAM,MAAM;AACvD,aAAO,IAAI,KAAK,SAAS,OAAO,IAAI,UAAU;AAG9C,aAAO,IAAI,GAAG,WAAW,KAAK,WAAW,KAAK,MAAM,MAAM,CAAC;AAG3D,aAAO,IAAI,gBAAgB,SAAS;AAAA,QAClC,OAAO;AAAA,QACP,KAAK,YAAY;AAAA,QACjB,KAAK,YAAY,aAAa,KAAK,YAAY,UAAU;AAAA,MAC3D,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAYO,kBAAkB,gBAAwB,SAA8C;AAO7F,QAAI,eAAe,sBAAsB,QAAW;AAClD;AAAA,IACF;AAEA,QAAI,YAAY,QAAW;AACzB,cAAQ,KAAK,kFAAoF;AACjG,gBAAU;AAAA,IACZ;AAEA,QAAI,YAAY,UAAU;AACxB,gBAAU;AAAA,IACZ;AAEA,QAAI,KAAK,mBAAmB,mBAA6B;AACvD,WAAK,gBAAgB;AACrB,YAAM,IAAI,MAAM,eAAe;AAAA,IACjC;AAEA,UAAM,YAAY,eAAe;AACjC,UAAM,oBAAoB,eAAe;AAEzC,SAAK,aAAa,WAAW,MAAM,eAAe,MAAM,SAAS,IAAI;AAGrE,UAAM,eAAe,IAAI,SAAiB;AAC1C,SAAK,eAAe,qBAAqB,CAAC,WAAW,YAAY;AAEjE,QAAI,YAAY,UAAU;AAExB,WAAK,qBAAqB,aAAa,WAAW,MAChD,aAAa,OAAO,KAAK,GAAG,UAAU,GAAI;AAAA,IAC9C;AAEA,UAAM,UAAU,MAAM;AACpB,aAAO,KAAK,eAAe;AAC3B,aAAO,KAAK,cAAc;AAC1B,aAAO,KAAK,qBAAqB;AACjC,WAAK,uBAAuB,OAAO,SAAS;AAAA,IAC9C;AAEA,iBACE,KAAK,CAAC,cAAc;AAClB,gBAAU,OAAO,eAAe;AAChC,gBAAU,WAAW,eAAe;AACpC,qBAAe,MAAM,UAAU;AAC/B,qBAAe,QAAQ,YAAY;AACnC,mBAAa,KAAK,qBAAqB,UAAU;AACjD,cAAQ;AAAA,IACV,CAAC,EACD,MAAM,MAAM;AACV,cAAQ;AACR,WAAK,wBAAwB;AAAA,IAC/B,CAAC;AAEH,WAAO;AAAA,EACT;AAAA,EAEU,wBAAwB,mBAA2B,GAAG;AAC9D,iBAAa,KAAK,mBAAmB;AAErC,QAAI,CAAC,KAAK,eAAe;AACvB;AAAA,IACF;AAEA,SAAK,sBAAsB,WAAW,MAAM;AAC1C,WAAK,sBAAsB;AAC3B,WAAK,gBAAgB;AAAA,IACvB,GAAG,mBAAmB,GAAI;AAAA,EAC5B;AAAA,EAEQ,uBAAyC,SAAY,UAA6B,CAAC,GAAG;AAC5F,iBAAa,iBAAiB,OAAO;AACrC,UAAM,iBAAiB,gBAAgB,SAAS,kBAAkB,OAAO;AACzE,UAAM,SAAU,OAAQ,QAAQ,WAAY,cACxC,MAAM,QAAQ,QAAQ,MAAM,IAC1B,QAAQ,SACR,CAAC,QAAQ,MAAM,IACjB;AAEJ,QAAI,aAAa,KAAK,QAAQ;AAC9B,WAAO,cAAc;AACnB,YAAM,SAAS,KAAK,QAAQ;AAE5B,UAAI,CAAC,UAAU,CAAC,OAAO,SAAS,MAAM,GAAG;AACvC,eAAO,WAAW,cAAc;AAAA,MAClC;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,qBAAqB,MAAc,SAAe,UAA6B,CAAC,GAAG;AACzF,iBAAa,iBAAiB,OAAO;AACrC,UAAM,iBAAiB,gBAAgB,IAAI,SAAS,WAAW,MAAM,OAAO;AAC5E,UAAM,SAAU,OAAQ,QAAQ,WAAY,cACxC,MAAM,QAAQ,QAAQ,MAAM,IAC1B,QAAQ,SACR,CAAC,QAAQ,MAAM,IACjB;AAEJ,QAAI,aAAa,KAAK,QAAQ;AAC9B,WAAO,cAAc;AACnB,YAAM,SAAS,KAAK,QAAQ;AAE5B,UAAI,CAAC,UAAU,CAAC,OAAO,SAAS,MAAM,GAAG;AACvC,eAAO,WAAW,cAAc;AAAA,MAClC;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,cAAc,QAAsB;AAC1C,WAAO,WAAW,gBAAgB,SAAS,YAAY,KAAK,YAAY,aAAa,MAAM,CAAC,CAAC;AAAA,EAC/F;AAAA,EAEQ,6BAA6B;AACnC,UAAM,SAAS,KAAK,qBAAqB;AAEzC,QAAI,SAAS,GAAG;AACd,eAAS,IAAI,GAAG,IAAI,QAAQ,KAAK;AAC/B,cAAM,CAAC,QAAQ,IAAI,IAAI,KAAK,qBAAqB;AAEjD,YAAI,WAAW,aAAa;AAC1B,eAAK,UAAU,MAAM,MAAM,IAAI;AAAA,QAEjC,OAAO;AACL,UAAC,OAAkB,IAAI,MAAM,QAAQ,IAAI;AAAA,QAC3C;AAAA,MACF;AAIA,WAAK,qBAAqB,OAAO,GAAG,MAAM;AAAA,IAC5C;AAAA,EACF;AAAA,EAEA,MAAc,aACZ,WACA,cAAmB,MACnB,WAAgB,QAChB,UAAkB,KAAK,qBACvB,oBAA6B,OAC7B,qBACA;AACA,QAAI,CAAC,qBAAqB,KAAK,qBAAqB,GAAG;AACrD,aAAO;AAAA,IACT;AAEA,SAAK,cAAc,aAAa,CAAC,aAAa,QAAQ;AAEtD,QAAI,CAAC,mBAAmB;AACtB,YAAM,KAAK,sBAAsB;AAEjC,WAAK,qBAAqB,aAAa,WAAW,YAAY;AAC5D,eAAO,KAAK,cAAc;AAC1B,eAAO,KAAK,qBAAqB;AACjC,cAAM,KAAK,sBAAsB;AAAA,MACnC,GAAG,UAAU,GAAI;AAEjB,WAAK,wBAAwB,OAAO;AAAA,IACtC;AAKA,QAAI,qBAAqB;AACvB,WAAK,uBAAuB,IAAI,WAAW,SAAS;AAAA,IACtD;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,kBAAkB;AACxB,UAAM,cACJ,KAAK,iBACL,KAAK,wBAAwB,UAC7B,KAAK,QAAQ,WAAW,KACxB,OAAO,KAAK,KAAK,aAAa,EAAE,WAAW;AAG7C,QAAI,aAAa;AACf,WAAK,QAAQ,KAAK,SAAS;AAAA,IAC7B;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,WAAyB;AACrC,SAAK,iBAAiB;AAEtB,UAAM,KAAK,QAAQ,OAAO;AAE1B,QAAI;AACJ,QAAI,KAAK,WAAW;AAClB,uBAAiB,KAAK,UAAU;AAAA,IAClC;AAEA,QAAI,KAAK,gBAAgB;AACvB,oBAAc,KAAK,cAAc;AACjC,WAAK,iBAAiB;AAAA,IACxB;AAEA,QAAI,KAAK,qBAAqB;AAC5B,oBAAc,KAAK,mBAAmB;AACtC,WAAK,sBAAsB;AAAA,IAC7B;AAEA,QAAI,KAAK,qBAAqB;AAC5B,oBAAc,KAAK,mBAAmB;AACtC,WAAK,sBAAsB;AAAA,IAC7B;AAGA,SAAK,MAAM,MAAM;AACjB,SAAK,MAAM,KAAK;AAEhB,WAAO,OAAO,kBAAkB,QAAQ,QAAQ;AAAA,EAClD;AAAA,EAEQ,WAAW,QAAgB,OAAiB;AAElD,QAAI,OAAO,UAAU,YAAY,SAAS;AAAE;AAAA,IAAQ;AAEpD,UAAM,KAAe,EAAE,QAAQ,EAAE;AACjC,UAAM,OAAO,OAAO,MAAM,OAAO,EAAE;AAEnC,QAAI,CAAC,OAAO;AACV,yBAAmB,GAAG,KAAK,aAAa,KAAK,qCAAqC,OAAO;AACzF;AAAA,IACF;AAEA,QAAI,SAAS,SAAS,WAAW;AAC/B,YAAM,cAAe,OAAO,YAAY,OAAO,EAAE,IAC7C,OAAO,OAAO,OAAO,EAAE,IACvB,OAAO,OAAO,OAAO,EAAE;AAE3B,UAAI;AACJ,UAAI;AACF,kBAAW,MAAM,SAAS,GAAG,SACzB,OAAO,IAAI,WAAW,MAAM,MAAM,GAAG,QAAQ,MAAM,MAAM,CAAC,CAAC,IAC3D;AACJ,qBAAa,wBAAwB,aAAa,OAAO;AAAA,MAC3D,SAAS,GAAP;AACA,2BAAmB,CAAC;AACpB;AAAA,MACF;AAEA,UAAI,KAAK,kBAAkB,cAAc;AACvC,aAAK,kBAAkB,aAAa,QAAQ,OAAO;AAAA,MAErD,WAAW,KAAK,kBAAkB,MAAM;AACtC,QAAC,KAAK,kBAAkB,KAAa,QAAQ,aAAa,OAAO;AAAA,MAEnE,OAAO;AACL,2BAAmB,kBAAkB,8BAA8B;AAAA,MACrE;AAAA,IAEF,WAAW,SAAS,SAAS,iBAAiB;AAC5C,YAAM,cAAe,OAAO,YAAY,OAAO,EAAE,IAC7C,OAAO,OAAO,OAAO,EAAE,IACvB,OAAO,OAAO,OAAO,EAAE;AAE3B,YAAM,UAAU,MAAM,MAAM,GAAG,QAAQ,MAAM,MAAM;AACnD,mBAAa,wBAAwB,aAAa,OAAO;AAEzD,UAAI,KAAK,kBAAkB,cAAc;AACvC,aAAK,kBAAkB,aAAa,QAAQ,OAAO;AAAA,MAErD,WAAW,KAAK,kBAAkB,MAAM;AACtC,QAAC,KAAK,kBAAkB,KAAa,QAAQ,aAAa,OAAO;AAAA,MAEnE,OAAO;AACL,2BAAmB,kBAAkB,8BAA8B;AAAA,MACrE;AAAA,IAEF,WAAW,SAAS,SAAS,aAAa,OAAO,UAAU,YAAY,SAAS;AAE9E,aAAO,QAAQ,YAAY;AAG3B,UAAI,KAAK,OAAO;AACd,aAAK,cAAc,MAAM;AAAA,MAC3B;AAGA,UAAI,OAAO,kBAAkB,SAAS,GAAG;AACvC,eAAO,kBAAkB,QAAQ,CAAC,aAAa,OAAO,IAAI,QAAQ,CAAC;AAAA,MACrE;AACA,aAAO,OAAO;AAAA,IAEhB,WAAW,SAAS,SAAS,YAAY;AACvC,WAAK,qBAAqB,QAAQ,SAAS,kBAAkB;AAAA,IAC/D;AAAA,EAEF;AAAA,EAEQ,qBAAqB,QAAgB,WAAmB;AAE9D,WAAO,IAAI,mBAAmB,SAAS;AAGvC,WAAO,IAAI,eAAe,SAAS,OAAO,IAAI,UAAU;AAGxD,SAAK,SAAS,QAAQ,SAAS,EAAE,KAAK,MAAM,OAAO,MAAM,SAAS,CAAC;AAAA,EACrE;AAAA,EAEA,MAAc,SAAS,QAAgB,MAA6B;AAClE,UAAM,UAAU,KAAK,QAAQ,OAAO,MAAM;AAG1C,QAAI,SAAS;AACX,aAAO,QAAQ,YAAY;AAE3B,UAAI,KAAK,SAAS;AAChB,YAAI;AACF,gBAAM,KAAK,QAAQ,QAAS,SAAS,SAAS,kBAAmB;AAAA,QAEnE,SAAS,GAAP;AACA,6BAAmB,kBAAmB,KAAK,EAAE,WAAW,KAAK,oBAAqB;AAAA,QACpF;AAAA,MACF;AAAA,IACF;AAEA,QAAI,OAAO,UAAU,YAAY,aAAa;AAE5C,YAAM,cAAc,MAAM,KAAK,sBAAsB;AAErD,WAAK,QAAQ,KAAK,SAAS,QAAQ,WAAW;AAAA,IAChD;AAAA,EACF;AAAA,EAEA,MAAc,wBAAwB;AAEpC,QAAI,CAAC,KAAK,WAAW,KAAK,qBAAqB,GAAG;AAChD,WAAK,qBAAqB;AAC1B,WAAK,KAAK,KAAK,MAAM,IAAI;AAAA,IAC3B;AAEA,UAAM,KAAK,QAAQ,UAAU;AAAA,MAC3B,MAAM,EAAE,SAAS,EAAE;AAAA,MACnB,MAAM,EAAE,QAAQ,KAAK,QAAQ;AAAA,IAC/B,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,wBAAwB;AACpC,UAAM,cAAc,KAAK,gBAAgB;AAEzC,QAAI,KAAK,mBAAmB,mBAA6B;AACvD,aAAO;AAAA,IACT;AAGA,QAAI,CAAC,aAAa;AAChB,UAAI,KAAK,sBAAsB,CAAC,KAAK,mBAAmB;AACtD,aAAK,qBAAqB;AAC1B,aAAK,OAAO,KAAK,MAAM,IAAI;AAAA,MAC7B;AAGA,YAAM,KAAK,QAAQ,UAAU;AAAA,QAC3B,MAAM,EAAE,SAAS,GAAG;AAAA,QACpB,MAAM,EAAE,QAAQ,KAAK,QAAQ;AAAA,MAC/B,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AAEF;",
4
+ "sourcesContent": ["import http, { IncomingMessage } from 'http';\nimport WebSocket from 'ws'; // TODO: move this to Transport\n\nimport { unpack } from 'msgpackr';\nimport { decode, Iterator, Schema } from '@colyseus/schema';\n\nimport Clock from '@gamestdio/timer';\nimport { EventEmitter } from 'events';\nimport { logger } from './Logger';\n\nimport { Presence } from './presence/Presence';\n\nimport { NoneSerializer } from './serializer/NoneSerializer';\nimport { SchemaSerializer } from './serializer/SchemaSerializer';\nimport { Serializer } from './serializer/Serializer';\n\nimport { ErrorCode, getMessageBytes, Protocol } from './Protocol';\nimport { Deferred, generateId } from './utils/Utils';\nimport { isDevMode } from './utils/DevMode';\n\nimport { debugAndPrintError, debugMessage } from './Debug';\nimport { ServerError } from './errors/ServerError';\nimport { RoomListingData } from './matchmaker/driver';\nimport { Client, ClientArray, ClientState, ISendOptions } from './Transport';\n\nconst DEFAULT_PATCH_RATE = 1000 / 20; // 20fps (50ms)\nconst DEFAULT_SIMULATION_INTERVAL = 1000 / 60; // 60fps (16.66ms)\nconst noneSerializer = new NoneSerializer();\n\nexport const DEFAULT_SEAT_RESERVATION_TIME = Number(process.env.COLYSEUS_SEAT_RESERVATION_TIME || 15);\n\nexport type SimulationCallback = (deltaTime: number) => void;\n\nexport interface IBroadcastOptions extends ISendOptions {\n except?: Client | Client[];\n}\n\nexport enum RoomInternalState {\n CREATING = 0,\n CREATED = 1,\n DISPOSING = 2,\n}\n\ntype ExtractUserData<T> = T extends ClientArray<infer U> ? U : never;\ntype ExtractAuthData<T> = T extends ClientArray<infer _, infer U> ? U : never;\n\n/**\n * A Room class is meant to implement a game session, and/or serve as the communication channel\n * between a group of clients.\n *\n * - Rooms are created on demand during matchmaking by default\n * - Room classes must be exposed using `.define()`\n */\nexport abstract class Room<State extends object= any, Metadata= any> {\n\n /**\n * This property will change on these situations:\n * - The maximum number of allowed clients has been reached (`maxClients`)\n * - You manually locked, or unlocked the room using lock() or `unlock()`.\n *\n * @readonly\n */\n public get locked() {\n return this._locked;\n }\n\n public get metadata() {\n return this.listing.metadata;\n }\n\n public listing: RoomListingData<Metadata>;\n\n /**\n * A ClockTimer instance, used for timing events.\n */\n public clock: Clock = new Clock();\n\n #_roomId: string;\n #_roomName: string;\n #_autoDispose: boolean = true;\n\n /**\n * Maximum number of clients allowed to connect into the room. When room reaches this limit,\n * it is locked automatically. Unless the room was explicitly locked by you via `lock()` method,\n * the room will be unlocked as soon as a client disconnects from it.\n */\n public maxClients: number = Infinity;\n /**\n * Frequency to send the room state to connected clients, in milliseconds.\n *\n * @default 50ms (20fps)\n */\n public patchRate: number = DEFAULT_PATCH_RATE;\n\n /**\n * The state instance you provided to `setState()`.\n */\n public state: State;\n /**\n * The presence instance. Check Presence API for more details.\n *\n * @see {@link https://docs.colyseus.io/colyseus/server/presence/|Presence API}\n */\n public presence: Presence;\n\n /**\n * The array of connected clients.\n *\n * @see {@link https://docs.colyseus.io/colyseus/server/room/#client|Client instance}\n */\n public clients: ClientArray = new ClientArray();\n\n /** @internal */\n public _events = new EventEmitter();\n\n // seat reservation & reconnection\n protected seatReservationTime: number = DEFAULT_SEAT_RESERVATION_TIME;\n protected reservedSeats: { [sessionId: string]: [any, any] } = {};\n protected reservedSeatTimeouts: { [sessionId: string]: NodeJS.Timer } = {};\n\n protected _reconnections: { [reconnectionToken: string]: [string, Deferred] } = {};\n private _reconnectingSessionId = new Map<string, string>();\n\n private onMessageHandlers: {[id: string]: (client: Client, message: any) => void} = {};\n\n private _serializer: Serializer<State> = noneSerializer;\n private _afterNextPatchQueue: Array<[string | Client, IArguments]> = [];\n\n private _simulationInterval: NodeJS.Timer;\n private _patchInterval: NodeJS.Timer;\n\n private _internalState: RoomInternalState = RoomInternalState.CREATING;\n private _locked: boolean = false;\n private _lockedExplicitly: boolean = false;\n private _maxClientsReached: boolean = false;\n\n // this timeout prevents rooms that are created by one process, but no client\n // ever had success joining into it on the specified interval.\n private _autoDisposeTimeout: NodeJS.Timer;\n\n // TODO: remove \"presence\" from constructor on 0.16.0\n constructor(presence?: Presence) {\n this.presence = presence;\n\n this._events.once('dispose', async () => {\n try {\n await this._dispose();\n\n } catch (e) {\n debugAndPrintError(`onDispose error: ${(e && e.message || e || 'promise rejected')}`);\n }\n this._events.emit('disconnect');\n });\n\n this.setPatchRate(this.patchRate);\n // set default _autoDisposeTimeout\n this.resetAutoDisposeTimeout(this.seatReservationTime);\n }\n\n /**\n * Automatically dispose the room when last client disconnects.\n *\n * @default true\n */\n public get autoDispose() { return this.#_autoDispose; }\n public set autoDispose(value: boolean) {\n if (\n value !== this.#_autoDispose &&\n this._internalState !== RoomInternalState.DISPOSING\n ) {\n this.#_autoDispose = value;\n this.resetAutoDisposeTimeout();\n }\n }\n\n /**\n * The name of the room you provided as first argument for `gameServer.define()`.\n *\n * @returns roomName string\n */\n public get roomName() { return this.#_roomName; }\n /**\n * Setting the name of the room. Overwriting this property is restricted.\n *\n * @param roomName\n */\n public set roomName(roomName: string) {\n if (this.#_roomName) {\n // prevent user from setting roomName after it has been defined.\n throw new ServerError(ErrorCode.APPLICATION_ERROR, \"'roomName' cannot be overwritten.\");\n }\n this.#_roomName = roomName;\n }\n\n /**\n * A unique, auto-generated, 9-character-long id of the room.\n * You may replace `this.roomId` during `onCreate()`.\n *\n * @returns roomId string\n */\n public get roomId() { return this.#_roomId; }\n /**\n * Setting the roomId, is restricted in room lifetime except upon room creation.\n *\n * @param roomId\n * @returns roomId string\n */\n public set roomId(roomId: string) {\n if (this._internalState !== RoomInternalState.CREATING && !isDevMode) {\n // prevent user from setting roomId after room has been created.\n throw new ServerError(ErrorCode.APPLICATION_ERROR, \"'roomId' can only be overridden upon room creation.\");\n }\n this.#_roomId = roomId;\n }\n\n // Optional abstract methods\n public onBeforePatch?(state: State): void | Promise<any>;\n public onCreate?(options: any): void | Promise<any>;\n public onJoin?(\n client: Client<ExtractUserData<typeof this['clients']>, ExtractAuthData<typeof this['clients']>>,\n options?: any,\n auth?: ExtractAuthData<typeof this['clients']>,\n ): void | Promise<any>;\n public onLeave?(\n client: Client<ExtractUserData<typeof this['clients']>, ExtractAuthData<typeof this['clients']>>,\n consented?: boolean,\n ): void | Promise<any>;\n public onDispose?(): void | Promise<any>;\n\n // TODO: flag as @deprecated on v0.16\n // TOOD: remove instance level `onAuth` on 1.0\n /**\n * onAuth at the instance level will be deprecated in the future.\n * Please use \"static onAuth(token, req) instead\n */\n public onAuth(\n client: Client<ExtractUserData<typeof this['clients']>, ExtractAuthData<typeof this['clients']>>,\n options: any,\n request?: http.IncomingMessage\n ): any | Promise<any> {\n return true;\n }\n\n static async onAuth(token: string, req: IncomingMessage): Promise<unknown> {\n return true;\n }\n\n /**\n * devMode: When `devMode` is enabled, `onCacheRoom` method is called during\n * graceful shutdown.\n *\n * Implement this method to return custom data to be cached. `onRestoreRoom`\n * will be called with the data returned by `onCacheRoom`\n */\n public onCacheRoom?(): any;\n\n /**\n * devMode: When `devMode` is enabled, `onRestoreRoom` method is called during\n * process startup, with the data returned by the `onCacheRoom` method.\n */\n public onRestoreRoom?(cached?: any): void;\n\n /**\n * Returns whether the sum of connected clients and reserved seats exceeds maximum number of clients.\n *\n * @returns boolean\n */\n public hasReachedMaxClients(): boolean {\n return (\n (this.clients.length + Object.keys(this.reservedSeats).length) >= this.maxClients ||\n this._internalState === RoomInternalState.DISPOSING\n );\n }\n\n /**\n * Set the number of seconds a room can wait for a client to effectively join the room.\n * You should consider how long your `onAuth()` will have to wait for setting a different seat reservation time.\n * The default value is 15 seconds. You may set the `COLYSEUS_SEAT_RESERVATION_TIME`\n * environment variable if you'd like to change the seat reservation time globally.\n *\n * @default 15 seconds\n *\n * @param seconds - number of seconds.\n * @returns The modified Room object.\n */\n public setSeatReservationTime(seconds: number) {\n this.seatReservationTime = seconds;\n return this;\n }\n\n public hasReservedSeat(sessionId: string, reconnectionToken?: string): boolean {\n if (reconnectionToken) {\n const reconnection = this._reconnections[reconnectionToken];\n return (\n reconnection &&\n reconnection[0] === sessionId &&\n this.reservedSeats[sessionId] !== undefined &&\n this._reconnectingSessionId.has(sessionId)\n );\n\n } else {\n return (\n this.reservedSeats[sessionId] !== undefined &&\n (\n !this._reconnectingSessionId.has(sessionId) || // prevent possible \"reconnect\" requests without a reconnection token\n (this._reconnectingSessionId.get(sessionId) === sessionId) // devMode reconnection\n )\n );\n }\n }\n\n public checkReconnectionToken(reconnectionToken: string) {\n const reconnection = this._reconnections[reconnectionToken];\n const sessionId = (reconnection && reconnection[0]);\n\n if (this.hasReservedSeat(sessionId)) {\n this._reconnectingSessionId.set(sessionId, reconnectionToken);\n return sessionId;\n\n } else {\n return undefined;\n }\n }\n\n /**\n * (Optional) Set a simulation interval that can change the state of the game.\n * The simulation interval is your game loop.\n *\n * @default 16.6ms (60fps)\n *\n * @param onTickCallback - You can implement your physics or world updates here!\n * This is a good place to update the room state.\n * @param delay - Interval delay on executing `onTickCallback` in milliseconds.\n */\n public setSimulationInterval(onTickCallback?: SimulationCallback, delay: number = DEFAULT_SIMULATION_INTERVAL): void {\n // clear previous interval in case called setSimulationInterval more than once\n if (this._simulationInterval) { clearInterval(this._simulationInterval); }\n\n if (onTickCallback) {\n this._simulationInterval = setInterval(() => {\n this.clock.tick();\n onTickCallback(this.clock.deltaTime);\n }, delay);\n }\n }\n\n public setPatchRate(milliseconds: number | null): void {\n this.patchRate = milliseconds;\n\n // clear previous interval in case called setPatchRate more than once\n if (this._patchInterval) {\n clearInterval(this._patchInterval);\n this._patchInterval = undefined;\n }\n\n if (milliseconds !== null && milliseconds !== 0) {\n this._patchInterval = setInterval(() => this.broadcastPatch(), milliseconds);\n }\n }\n\n public setState(newState: State) {\n this.clock.start();\n\n if ('_definition' in newState) {\n this.setSerializer(new SchemaSerializer());\n }\n\n this._serializer.reset(newState);\n\n this.state = newState;\n }\n\n public setSerializer(serializer: Serializer<State>) {\n this._serializer = serializer;\n }\n\n public async setMetadata(meta: Partial<Metadata>) {\n if (!this.listing.metadata) {\n this.listing.metadata = meta as Metadata;\n\n } else {\n for (const field in meta) {\n if (!meta.hasOwnProperty(field)) { continue; }\n this.listing.metadata[field] = meta[field];\n }\n\n // `MongooseDriver` workaround: persit metadata mutations\n if ('markModified' in this.listing) {\n (this.listing as any).markModified('metadata');\n }\n }\n\n if (this._internalState === RoomInternalState.CREATED) {\n await this.listing.save();\n }\n }\n\n public async setPrivate(bool: boolean = true) {\n if (this.listing.private === bool) return;\n\n this.listing.private = bool;\n\n if (this._internalState === RoomInternalState.CREATED) {\n await this.listing.save();\n }\n\n this._events.emit('visibility-change', bool);\n }\n\n /**\n * Locking the room will remove it from the pool of available rooms for new clients to connect to.\n */\n public async lock() {\n // rooms locked internally aren't explicit locks.\n this._lockedExplicitly = (arguments[0] === undefined);\n\n // skip if already locked.\n if (this._locked) { return; }\n\n this._locked = true;\n\n await this.listing.updateOne({\n $set: { locked: this._locked },\n });\n\n this._events.emit('lock');\n }\n\n /**\n * Unlocking the room returns it to the pool of available rooms for new clients to connect to.\n */\n public async unlock() {\n // only internal usage passes arguments to this function.\n if (arguments[0] === undefined) {\n this._lockedExplicitly = false;\n }\n\n // skip if already locked\n if (!this._locked) { return; }\n\n this._locked = false;\n\n await this.listing.updateOne({\n $set: { locked: this._locked },\n });\n\n this._events.emit('unlock');\n }\n\n public send(client: Client, type: string | number, message: any, options?: ISendOptions): void;\n public send(client: Client, message: Schema, options?: ISendOptions): void;\n public send(client: Client, messageOrType: any, messageOrOptions?: any | ISendOptions, options?: ISendOptions): void {\n logger.warn('DEPRECATION WARNING: use client.send(...) instead of this.send(client, ...)');\n client.send(messageOrType, messageOrOptions, options);\n }\n\n public broadcast(type: string | number, message?: any, options?: IBroadcastOptions);\n public broadcast<T extends Schema>(message: T, options?: IBroadcastOptions);\n public broadcast(\n typeOrSchema: string | number | Schema,\n messageOrOptions?: any | IBroadcastOptions,\n options?: IBroadcastOptions,\n ) {\n const isSchema = (typeof(typeOrSchema) === 'object');\n const opts: IBroadcastOptions = ((isSchema) ? messageOrOptions : options);\n\n if (opts && opts.afterNextPatch) {\n delete opts.afterNextPatch;\n this._afterNextPatchQueue.push(['broadcast', arguments]);\n return;\n }\n\n if (isSchema) {\n this.broadcastMessageSchema(typeOrSchema as Schema, opts);\n\n } else {\n\n this.broadcastMessageType(typeOrSchema as string, messageOrOptions, opts);\n }\n }\n\n /**\n * Checks whether mutations have occurred in the state, and broadcast them to all connected clients.\n */\n public broadcastPatch() {\n if (this.onBeforePatch) {\n this.onBeforePatch(this.state);\n }\n\n if (!this._simulationInterval) {\n this.clock.tick();\n }\n\n if (!this.state) {\n return false;\n }\n\n const hasChanges = this._serializer.applyPatches(this.clients, this.state);\n\n // broadcast messages enqueued for \"after patch\"\n this._dequeueAfterPatchMessages();\n\n return hasChanges;\n }\n\n public onMessage<T = any>(\n messageType: '*',\n callback: (client: Client<ExtractUserData<typeof this['clients']>, ExtractAuthData<typeof this['clients']>>, type: string | number, message: T) => void\n );\n public onMessage<T = any>(\n messageType: string | number,\n callback: (client: Client<ExtractUserData<typeof this['clients']>, ExtractAuthData<typeof this['clients']>>, message: T) => void\n );\n public onMessage<T = any>(messageType: '*' | string | number, callback: (...args: any[]) => void) {\n this.onMessageHandlers[messageType] = callback;\n // returns a method to unbind the callback\n return () => delete this.onMessageHandlers[messageType];\n }\n\n /**\n * Disconnect all connected clients, and then dispose the room.\n *\n * @param closeCode WebSocket close code (default = 4000, which is a \"consented leave\")\n * @returns Promise<void>\n */\n public disconnect(closeCode: number = Protocol.WS_CLOSE_CONSENTED): Promise<any> {\n // skip if already disposing\n if (this._internalState === RoomInternalState.DISPOSING) {\n return;\n\n } else if (this._internalState === RoomInternalState.CREATING) {\n throw new Error(\"cannot disconnect during onCreate()\");\n }\n\n this._internalState = RoomInternalState.DISPOSING;\n this.listing.remove();\n\n this.#_autoDispose = true;\n\n const delayedDisconnection = new Promise<void>((resolve) =>\n this._events.once('disconnect', () => resolve()));\n\n // reject pending reconnections\n for (const [_, reconnection] of Object.values(this._reconnections)) {\n reconnection.reject();\n }\n\n let numClients = this.clients.length;\n if (numClients > 0) {\n // clients may have `async onLeave`, room will be disposed after they're fulfilled\n while (numClients--) {\n this._forciblyCloseClient(this.clients[numClients], closeCode);\n }\n\n } else {\n // no clients connected, dispose immediately.\n this._events.emit('dispose');\n }\n\n return delayedDisconnection;\n }\n\n public async ['_onJoin'](client: Client, req?: http.IncomingMessage) {\n const sessionId = client.sessionId;\n\n // generate unique private reconnection token\n client._reconnectionToken = generateId();\n\n if (this.reservedSeatTimeouts[sessionId]) {\n clearTimeout(this.reservedSeatTimeouts[sessionId]);\n delete this.reservedSeatTimeouts[sessionId];\n }\n\n // clear auto-dispose timeout.\n if (this._autoDisposeTimeout) {\n clearTimeout(this._autoDisposeTimeout);\n this._autoDisposeTimeout = undefined;\n }\n\n // get seat reservation options and clear it\n const [joinOptions, authData] = this.reservedSeats[sessionId];\n\n //\n // TODO: remove this check on 1.0.0\n // - the seat reservation is used to keep track of number of clients and their pending seats (see `hasReachedMaxClients`)\n // - when we fully migrate to static onAuth(), the seat reservation can be removed immediately here\n // - if async onAuth() is in use, the seat reservation is removed after onAuth() is fulfilled.\n // - mark reservation as \"consumed\"\n //\n if (this.reservedSeats[sessionId].length > 2) {\n throw new ServerError(ErrorCode.MATCHMAKE_EXPIRED, \"already consumed\");\n }\n this.reservedSeats[sessionId].push(true);\n\n // share \"after next patch queue\" reference with every client.\n client._afterNextPatchQueue = this._afterNextPatchQueue;\n\n // add temporary callback to keep track of disconnections during `onJoin`.\n client.ref['onleave'] = (_) => client.state = ClientState.LEAVING;\n client.ref.once('close', client.ref['onleave']);\n\n const previousReconnectionToken = this._reconnectingSessionId.get(sessionId);\n if (previousReconnectionToken) {\n this.clients.push(client);\n this._reconnections[previousReconnectionToken]?.[1].resolve(client);\n\n } else {\n try {\n if (authData) {\n client.auth = authData;\n\n } else if (this.onAuth !== Room.prototype.onAuth) {\n client.auth = await this.onAuth(client, joinOptions, req);\n\n if (!client.auth) {\n throw new ServerError(ErrorCode.AUTH_FAILED, 'onAuth failed');\n }\n }\n\n //\n // On async onAuth, client may have been disconnected.\n //\n if (client.readyState !== WebSocket.OPEN) {\n throw new ServerError(Protocol.WS_CLOSE_GOING_AWAY, 'already disconnected');\n }\n\n this.clients.push(client);\n\n if (this.onJoin) {\n await this.onJoin(client, joinOptions, client.auth);\n }\n\n // emit 'join' to room handler\n this._events.emit('join', client);\n\n // remove seat reservation\n delete this.reservedSeats[sessionId];\n\n // client left during `onJoin`, call _onLeave immediately.\n if (client.state === ClientState.LEAVING) {\n await this._onLeave(client, Protocol.WS_CLOSE_GOING_AWAY);\n }\n\n } catch (e) {\n this.clients.delete(client);\n\n // remove seat reservation\n delete this.reservedSeats[sessionId];\n\n this._decrementClientCount();\n\n // make sure an error code is provided.\n if (!e.code) {\n e.code = ErrorCode.APPLICATION_ERROR;\n }\n\n throw e;\n }\n }\n\n // state might already be ClientState.LEAVING here\n if (client.state === ClientState.JOINING) {\n client.ref.removeListener('close', client.ref['onleave']);\n\n // only bind _onLeave after onJoin has been successful\n client.ref['onleave'] = this._onLeave.bind(this, client);\n client.ref.once('close', client.ref['onleave']);\n\n // allow client to send messages after onJoin has succeeded.\n client.ref.on('message', this._onMessage.bind(this, client));\n\n // confirm room id that matches the room name requested to join\n client.raw(getMessageBytes[Protocol.JOIN_ROOM](\n client._reconnectionToken,\n this._serializer.id,\n this._serializer.handshake && this._serializer.handshake(),\n ));\n }\n }\n\n /**\n * Allow the specified client to reconnect into the room. Must be used inside `onLeave()` method.\n * If seconds is provided, the reconnection is going to be cancelled after the provided amount of seconds.\n *\n * @param previousClient - The client which is to be waiting until re-connection happens.\n * @param seconds - Timeout period on re-connection in seconds.\n *\n * @returns Deferred<Client> - The differed is a promise like type.\n * This type can forcibly reject the promise by calling `.reject()`.\n */\n public allowReconnection(previousClient: Client, seconds: number | \"manual\"): Deferred<Client> {\n //\n // skip reconnection if client has never fully JOINED.\n //\n // (having `_enqueuedMessages !== undefined` means that the client has never\n // been at \"ClientState.JOINED\" state)\n //\n if (previousClient._enqueuedMessages !== undefined) {\n return;\n }\n\n if (seconds === undefined) { // TODO: remove this check\n console.warn(\"DEPRECATED: allowReconnection() requires a second argument. Using \\\"manual\\\" mode.\");\n seconds = \"manual\";\n }\n\n if (seconds === \"manual\") {\n seconds = Infinity;\n }\n\n if (this._internalState === RoomInternalState.DISPOSING) {\n this._disposeIfEmpty(); // gracefully shutting down\n throw new Error('disconnecting');\n }\n\n const sessionId = previousClient.sessionId;\n const reconnectionToken = previousClient._reconnectionToken;\n\n this._reserveSeat(sessionId, true, previousClient.auth, seconds, true);\n\n // keep reconnection reference in case the user reconnects into this room.\n const reconnection = new Deferred<Client>();\n this._reconnections[reconnectionToken] = [sessionId, reconnection];\n\n if (seconds !== Infinity) {\n // expire seat reservation after timeout\n this.reservedSeatTimeouts[sessionId] = setTimeout(() =>\n reconnection.reject(false), seconds * 1000);\n }\n\n const cleanup = () => {\n delete this._reconnections[reconnectionToken];\n delete this.reservedSeats[sessionId];\n delete this.reservedSeatTimeouts[sessionId];\n this._reconnectingSessionId.delete(sessionId);\n };\n\n reconnection.\n then((newClient) => {\n newClient.auth = previousClient.auth;\n newClient.userData = previousClient.userData;\n previousClient.ref = newClient.ref; // swap \"ref\" for convenience\n previousClient.state = ClientState.RECONNECTED;\n clearTimeout(this.reservedSeatTimeouts[sessionId]);\n cleanup();\n }).\n catch(() => {\n cleanup();\n this.resetAutoDisposeTimeout();\n });\n\n return reconnection;\n }\n\n protected resetAutoDisposeTimeout(timeoutInSeconds: number = 1) {\n clearTimeout(this._autoDisposeTimeout);\n\n if (!this.#_autoDispose) {\n return;\n }\n\n this._autoDisposeTimeout = setTimeout(() => {\n this._autoDisposeTimeout = undefined;\n this._disposeIfEmpty();\n }, timeoutInSeconds * 1000);\n }\n\n private broadcastMessageSchema<T extends Schema>(message: T, options: IBroadcastOptions = {}) {\n debugMessage(\"broadcast: %O\", message);\n const encodedMessage = getMessageBytes[Protocol.ROOM_DATA_SCHEMA](message);\n const except = (typeof (options.except) !== \"undefined\")\n ? Array.isArray(options.except)\n ? options.except\n : [options.except]\n : undefined;\n\n let numClients = this.clients.length;\n while (numClients--) {\n const client = this.clients[numClients];\n\n if (!except || !except.includes(client)) {\n client.enqueueRaw(encodedMessage);\n }\n }\n }\n\n private broadcastMessageType(type: string, message?: any, options: IBroadcastOptions = {}) {\n debugMessage(\"broadcast: %O\", message);\n const encodedMessage = getMessageBytes.raw(Protocol.ROOM_DATA, type, message);\n const except = (typeof (options.except) !== \"undefined\")\n ? Array.isArray(options.except)\n ? options.except\n : [options.except]\n : undefined;\n\n let numClients = this.clients.length;\n while (numClients--) {\n const client = this.clients[numClients];\n\n if (!except || !except.includes(client)) {\n client.enqueueRaw(encodedMessage);\n }\n }\n }\n\n private sendFullState(client: Client): void {\n client.enqueueRaw(getMessageBytes[Protocol.ROOM_STATE](this._serializer.getFullState(client)));\n }\n\n private _dequeueAfterPatchMessages() {\n const length = this._afterNextPatchQueue.length;\n\n if (length > 0) {\n for (let i = 0; i < length; i++) {\n const [target, args] = this._afterNextPatchQueue[i];\n\n if (target === \"broadcast\") {\n this.broadcast.apply(this, args);\n\n } else {\n (target as Client).raw.apply(target, args);\n }\n }\n\n // new messages may have been added in the meantime,\n // let's splice the ones that have been processed\n this._afterNextPatchQueue.splice(0, length);\n }\n }\n\n private async _reserveSeat(\n sessionId: string,\n joinOptions: any = true,\n authData: any = undefined,\n seconds: number = this.seatReservationTime,\n allowReconnection: boolean = false,\n devModeReconnection?: boolean,\n ) {\n if (!allowReconnection && this.hasReachedMaxClients()) {\n return false;\n }\n\n this.reservedSeats[sessionId] = [joinOptions, authData];\n\n if (!allowReconnection) {\n await this._incrementClientCount();\n\n this.reservedSeatTimeouts[sessionId] = setTimeout(async () => {\n delete this.reservedSeats[sessionId];\n delete this.reservedSeatTimeouts[sessionId];\n await this._decrementClientCount();\n }, seconds * 1000);\n\n this.resetAutoDisposeTimeout(seconds);\n }\n\n //\n // isDevMode workaround to allow players to reconnect on devMode\n //\n if (devModeReconnection) {\n this._reconnectingSessionId.set(sessionId, sessionId);\n }\n\n return true;\n }\n\n private _disposeIfEmpty() {\n const willDispose = (\n this.#_autoDispose &&\n this._autoDisposeTimeout === undefined &&\n this.clients.length === 0 &&\n Object.keys(this.reservedSeats).length === 0\n );\n\n if (willDispose) {\n this._events.emit('dispose');\n }\n\n return willDispose;\n }\n\n private async _dispose(): Promise<any> {\n this._internalState = RoomInternalState.DISPOSING;\n\n await this.listing.remove();\n\n let userReturnData;\n if (this.onDispose) {\n userReturnData = this.onDispose();\n }\n\n if (this._patchInterval) {\n clearInterval(this._patchInterval);\n this._patchInterval = undefined;\n }\n\n if (this._simulationInterval) {\n clearInterval(this._simulationInterval);\n this._simulationInterval = undefined;\n }\n\n if (this._autoDisposeTimeout) {\n clearInterval(this._autoDisposeTimeout);\n this._autoDisposeTimeout = undefined;\n }\n\n // clear all timeouts/intervals + force to stop ticking\n this.clock.clear();\n this.clock.stop();\n\n return await (userReturnData || Promise.resolve());\n }\n\n private _onMessage(client: Client, bytes: number[]) {\n // skip if client is on LEAVING state.\n if (client.state === ClientState.LEAVING) { return; }\n\n const it: Iterator = { offset: 0 };\n const code = decode.uint8(bytes, it);\n\n if (!bytes) {\n debugAndPrintError(`${this.roomName} (${this.roomId}), couldn't decode message: ${bytes}`);\n return;\n }\n\n if (code === Protocol.ROOM_DATA) {\n const messageType = (decode.stringCheck(bytes, it))\n ? decode.string(bytes, it)\n : decode.number(bytes, it);\n\n let message;\n try {\n message = (bytes.length > it.offset)\n ? unpack(new Uint8Array(bytes.slice(it.offset, bytes.length)))\n : undefined;\n debugMessage(\"received: '%s' -> %j\", messageType, message);\n } catch (e) {\n debugAndPrintError(e);\n return;\n }\n\n if (this.onMessageHandlers[messageType]) {\n this.onMessageHandlers[messageType](client, message);\n\n } else if (this.onMessageHandlers['*']) {\n (this.onMessageHandlers['*'] as any)(client, messageType, message);\n\n } else {\n debugAndPrintError(`onMessage for \"${messageType}\" not registered.`);\n }\n\n } else if (code === Protocol.ROOM_DATA_BYTES) {\n const messageType = (decode.stringCheck(bytes, it))\n ? decode.string(bytes, it)\n : decode.number(bytes, it);\n\n const message = bytes.slice(it.offset, bytes.length);\n debugMessage(\"received: '%s' -> %j\", messageType, message);\n\n if (this.onMessageHandlers[messageType]) {\n this.onMessageHandlers[messageType](client, message);\n\n } else if (this.onMessageHandlers['*']) {\n (this.onMessageHandlers['*'] as any)(client, messageType, message);\n\n } else {\n debugAndPrintError(`onMessage for \"${messageType}\" not registered.`);\n }\n\n } else if (code === Protocol.JOIN_ROOM && client.state === ClientState.JOINING) {\n // join room has been acknowledged by the client\n client.state = ClientState.JOINED;\n\n // send current state when new client joins the room\n if (this.state) {\n this.sendFullState(client);\n }\n\n // dequeue messages sent before client has joined effectively (on user-defined `onJoin`)\n if (client._enqueuedMessages.length > 0) {\n client._enqueuedMessages.forEach((enqueued) => client.raw(enqueued));\n }\n delete client._enqueuedMessages;\n\n } else if (code === Protocol.LEAVE_ROOM) {\n this._forciblyCloseClient(client, Protocol.WS_CLOSE_CONSENTED);\n }\n\n }\n\n private _forciblyCloseClient(client: Client, closeCode: number) {\n // stop receiving messages from this client\n client.ref.removeAllListeners('message');\n\n // prevent \"onLeave\" from being called twice if player asks to leave\n client.ref.removeListener('close', client.ref['onleave']);\n\n // only effectively close connection when \"onLeave\" is fulfilled\n this._onLeave(client, closeCode).then(() => client.leave(closeCode));\n }\n\n private async _onLeave(client: Client, code?: number): Promise<any> {\n const success = this.clients.delete(client);\n\n // call 'onLeave' method only if the client has been successfully accepted.\n if (success) {\n client.state = ClientState.LEAVING;\n\n if (this.onLeave) {\n try {\n await this.onLeave(client, (code === Protocol.WS_CLOSE_CONSENTED));\n\n } catch (e) {\n debugAndPrintError(`onLeave error: ${(e && e.message || e || 'promise rejected')}`);\n }\n }\n }\n\n if (client.state !== ClientState.RECONNECTED) {\n // try to dispose immediately if client reconnection isn't set up.\n const willDispose = await this._decrementClientCount();\n\n // trigger 'leave' only if seat reservation has been fully consumed\n if (this.reservedSeats[client.sessionId] === undefined) {\n this._events.emit('leave', client, willDispose);\n }\n }\n }\n\n private async _incrementClientCount() {\n // lock automatically when maxClients is reached\n if (!this._locked && this.hasReachedMaxClients()) {\n this._maxClientsReached = true;\n this.lock.call(this, true);\n }\n\n await this.listing.updateOne({\n $inc: { clients: 1 },\n $set: { locked: this._locked },\n });\n }\n\n private async _decrementClientCount() {\n const willDispose = this._disposeIfEmpty();\n\n if (this._internalState === RoomInternalState.DISPOSING) {\n return true;\n }\n\n // unlock if room is available for new connections\n if (!willDispose) {\n if (this._maxClientsReached && !this._lockedExplicitly) {\n this._maxClientsReached = false;\n this.unlock.call(this, true);\n }\n\n // update room listing cache\n await this.listing.updateOne({\n $inc: { clients: -1 },\n $set: { locked: this._locked },\n });\n }\n\n return willDispose;\n }\n\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["RoomInternalState"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@colyseus/core",
3
- "version": "0.15.28",
3
+ "version": "0.15.30",
4
4
  "description": "Multiplayer Framework for Node.js.",
5
5
  "input": "./src/index.ts",
6
6
  "main": "./build/index.js",