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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/build/MatchMaker.d.ts +23 -15
  2. package/build/MatchMaker.js +89 -73
  3. package/build/MatchMaker.js.map +2 -2
  4. package/build/MatchMaker.mjs +87 -71
  5. package/build/MatchMaker.mjs.map +2 -2
  6. package/build/Protocol.d.ts +3 -4
  7. package/build/Protocol.js +25 -20
  8. package/build/Protocol.js.map +2 -2
  9. package/build/Protocol.mjs +25 -20
  10. package/build/Protocol.mjs.map +2 -2
  11. package/build/Room.d.ts +30 -29
  12. package/build/Room.js +123 -84
  13. package/build/Room.js.map +2 -2
  14. package/build/Room.mjs +123 -84
  15. package/build/Room.mjs.map +2 -2
  16. package/build/Server.d.ts +0 -1
  17. package/build/Server.js +4 -4
  18. package/build/Server.js.map +2 -2
  19. package/build/Server.mjs +3 -3
  20. package/build/Server.mjs.map +2 -2
  21. package/build/Transport.d.ts +0 -5
  22. package/build/index.d.ts +1 -1
  23. package/build/index.js +1 -1
  24. package/build/index.js.map +2 -2
  25. package/build/index.mjs +1 -1
  26. package/build/index.mjs.map +1 -1
  27. package/build/matchmaker/Lobby.d.ts +2 -2
  28. package/build/matchmaker/Lobby.js.map +2 -2
  29. package/build/matchmaker/Lobby.mjs.map +2 -2
  30. package/build/matchmaker/RegisteredHandler.d.ts +4 -5
  31. package/build/matchmaker/RegisteredHandler.js +4 -3
  32. package/build/matchmaker/RegisteredHandler.js.map +2 -2
  33. package/build/matchmaker/RegisteredHandler.mjs +4 -3
  34. package/build/matchmaker/RegisteredHandler.mjs.map +2 -2
  35. package/build/matchmaker/controller.d.ts +1 -2
  36. package/build/matchmaker/driver/RoomData.d.ts +3 -3
  37. package/build/matchmaker/driver/RoomData.js +3 -3
  38. package/build/matchmaker/driver/RoomData.js.map +2 -2
  39. package/build/matchmaker/driver/RoomData.mjs +2 -2
  40. package/build/matchmaker/driver/RoomData.mjs.map +2 -2
  41. package/build/matchmaker/driver/api.d.ts +104 -0
  42. package/build/matchmaker/driver/api.js +29 -0
  43. package/build/matchmaker/driver/api.js.map +7 -0
  44. package/build/matchmaker/driver/api.mjs +6 -0
  45. package/build/matchmaker/driver/api.mjs.map +7 -0
  46. package/build/matchmaker/driver/index.d.ts +7 -7
  47. package/build/matchmaker/driver/index.js +1 -1
  48. package/build/matchmaker/driver/index.js.map +2 -2
  49. package/build/matchmaker/driver/index.mjs +2 -2
  50. package/build/matchmaker/driver/index.mjs.map +2 -2
  51. package/build/matchmaker/driver/interfaces.d.ts +7 -11
  52. package/build/matchmaker/driver/interfaces.js.map +1 -1
  53. package/build/matchmaker/driver/local/LocalDriver.d.ts +13 -0
  54. package/build/matchmaker/driver/local/LocalDriver.js +65 -0
  55. package/build/matchmaker/driver/local/LocalDriver.js.map +7 -0
  56. package/build/matchmaker/driver/local/LocalDriver.mjs +42 -0
  57. package/build/matchmaker/driver/local/LocalDriver.mjs.map +7 -0
  58. package/build/matchmaker/driver/local/Query.d.ts +9 -0
  59. package/build/matchmaker/driver/local/Query.js +78 -0
  60. package/build/matchmaker/driver/local/Query.js.map +7 -0
  61. package/build/matchmaker/driver/local/Query.mjs +55 -0
  62. package/build/matchmaker/driver/local/Query.mjs.map +7 -0
  63. package/build/matchmaker/driver/local/RoomData.d.ts +19 -0
  64. package/build/matchmaker/driver/local/RoomData.js +79 -0
  65. package/build/matchmaker/driver/local/RoomData.js.map +7 -0
  66. package/build/matchmaker/driver/local/RoomData.mjs +56 -0
  67. package/build/matchmaker/driver/local/RoomData.mjs.map +7 -0
  68. package/build/presence/LocalPresence.d.ts +9 -2
  69. package/build/presence/LocalPresence.js +77 -3
  70. package/build/presence/LocalPresence.js.map +3 -3
  71. package/build/presence/LocalPresence.mjs +77 -3
  72. package/build/presence/LocalPresence.mjs.map +3 -3
  73. package/build/presence/Presence.d.ts +38 -2
  74. package/build/presence/Presence.js.map +1 -1
  75. package/build/rooms/LobbyRoom.d.ts +4 -4
  76. package/build/rooms/LobbyRoom.js.map +2 -2
  77. package/build/rooms/LobbyRoom.mjs.map +2 -2
  78. package/build/serializer/SchemaSerializer.d.ts +11 -10
  79. package/build/serializer/SchemaSerializer.js.map +2 -2
  80. package/build/serializer/SchemaSerializer.mjs.map +2 -2
  81. package/build/serializer/Serializer.d.ts +0 -1
  82. package/build/utils/DevMode.js +1 -1
  83. package/build/utils/DevMode.js.map +2 -2
  84. package/build/utils/DevMode.mjs +2 -2
  85. package/build/utils/DevMode.mjs.map +2 -2
  86. package/build/utils/Utils.d.ts +4 -2
  87. package/build/utils/Utils.js +11 -2
  88. package/build/utils/Utils.js.map +2 -2
  89. package/build/utils/Utils.mjs +10 -2
  90. package/build/utils/Utils.mjs.map +2 -2
  91. package/package.json +2 -2
@@ -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; // TODO: remove me on 1.0, use 'state' instead\nexport let onReady: Deferred = new Deferred(); // onReady needs to be immediately available to @colyseus/auth integration.\n\nexport enum MatchMakerState {\n INITIALIZING,\n READY,\n SHUTTING_DOWN\n}\n\n/**\n * Internal MatchMaker state\n */\nexport let state: MatchMakerState;\n\n/**\n * @private\n */\nexport async function setup(\n _presence?: Presence,\n _driver?: MatchMakerDriver,\n _publicAddress?: string,\n _selectProcessIdToCreateRoom?: SelectProcessIdCallback,\n) {\n if (onReady === undefined) {\n //\n // for testing purposes only: onReady is turned into undefined on shutdown\n // (needs refactoring.)\n //\n onReady = new Deferred();\n }\n\n isGracefullyShuttingDown = false;\n state = MatchMakerState.INITIALIZING;\n\n presence = _presence || new LocalPresence();\n\n driver = _driver || new LocalDriver();\n publicAddress = _publicAddress;\n\n stats.reset(false);\n\n // devMode: try to retrieve previous processId\n if (isDevMode) { processId = await getPreviousProcessId(await getHostname()); }\n\n // ensure processId is set\n if (!processId) { processId = generateId(); }\n\n /**\n * Define default `assignRoomToProcessId` method.\n * By default, return the process with least amount of rooms created\n */\n selectProcessIdToCreateRoom = _selectProcessIdToCreateRoom || async function () {\n return (await stats.fetchAll())\n .sort((p1, p2) => p1.roomCount > p2.roomCount ? 1 : -1)[0]?.processId || processId;\n };\n\n onReady.resolve();\n}\n\n/**\n * - Accept receiving remote room creation requests\n * - Check for leftover/invalid processId's on startup\n * @private\n */\nexport async function accept() {\n await onReady; // make sure \"processId\" is available\n\n /**\n * Process-level subscription\n * - handle remote process healthcheck\n * - handle remote room creation\n */\n await subscribeIPC(presence, processId, getProcessChannel(), (method, args) => {\n if (method === 'healthcheck') {\n // health check for this processId\n return true;\n\n } else {\n // handle room creation\n return handleCreateRoom.apply(undefined, args);\n }\n });\n\n /**\n * Check for leftover/invalid processId's on startup\n */\n await healthCheckAllProcesses();\n\n state = MatchMakerState.READY;\n\n await stats.persist();\n\n if (isDevMode) {\n await reloadFromCache();\n }\n}\n\n/**\n * Join or create into a room and return seat reservation\n */\nexport async function joinOrCreate(roomName: string, clientOptions: ClientOptions = {}, authOptions?: AuthOptions) {\n return await retry<Promise<SeatReservation>>(async () => {\n const authData = await callOnAuth(roomName, authOptions);\n let room = await findOneRoomAvailable(roomName, clientOptions);\n\n //\n // TODO [?]\n // should we expose the \"creator\" auth data of the room during `onCreate()`?\n // it would be useful, though it could be accessed via `onJoin()` for now.\n //\n\n if (!room) {\n room = await createRoom(roomName, clientOptions);\n }\n\n return await reserveSeatFor(room, clientOptions, authData);\n }, 5, [SeatReservationError]);\n}\n\n/**\n * Create a room and return seat reservation\n */\nexport async function create(roomName: string, clientOptions: ClientOptions = {}, authOptions?: AuthOptions) {\n const authData = await callOnAuth(roomName, authOptions);\n const room = await createRoom(roomName, clientOptions);\n return reserveSeatFor(room, clientOptions, authData);\n}\n\n/**\n * Join a room and return seat reservation\n */\nexport async function join(roomName: string, clientOptions: ClientOptions = {}, authOptions?: AuthOptions) {\n return await retry<Promise<SeatReservation>>(async () => {\n const authData = await callOnAuth(roomName, authOptions);\n const room = await findOneRoomAvailable(roomName, clientOptions);\n\n if (!room) {\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_CRITERIA, `no rooms found with provided criteria`);\n }\n\n return reserveSeatFor(room, clientOptions, authData);\n });\n}\n\n/**\n * Join a room by id and return seat reservation\n */\nexport async function reconnect(roomId: string, clientOptions: ClientOptions = {}) {\n const room = await driver.findOne({ roomId });\n if (!room) {\n // TODO: support a \"logLevel\" out of the box?\n if (process.env.NODE_ENV !== 'production') {\n logger.info(`\u274C room \"${roomId}\" has been disposed. Did you missed .allowReconnection()?\\n\uD83D\uDC49 https://docs.colyseus.io/server/room/#allowreconnection-client-seconds`);\n }\n\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room \"${roomId}\" has been disposed.`);\n }\n\n // check for reconnection\n const reconnectionToken = clientOptions.reconnectionToken;\n if (!reconnectionToken) { throw new ServerError(ErrorCode.MATCHMAKE_UNHANDLED, `'reconnectionToken' must be provided for reconnection.`); }\n\n\n // respond to re-connection!\n const sessionId = await remoteRoomCall(room.roomId, 'checkReconnectionToken', [reconnectionToken]);\n if (sessionId) {\n return { room, sessionId };\n\n } else {\n // TODO: support a \"logLevel\" out of the box?\n if (process.env.NODE_ENV !== 'production') {\n logger.info(`\u274C reconnection token invalid or expired. Did you missed .allowReconnection()?\\n\uD83D\uDC49 https://docs.colyseus.io/server/room/#allowreconnection-client-seconds`);\n }\n throw new ServerError(ErrorCode.MATCHMAKE_EXPIRED, `reconnection token invalid or expired.`);\n }\n}\n\n/**\n * Join a room by id and return client seat reservation. An exception is thrown if a room is not found for roomId.\n *\n * @param roomId - The Id of the specific room instance.\n * @param clientOptions - Options for the client seat reservation (for `onJoin`/`onAuth`)\n * @param authOptions - Optional authentication token\n *\n * @returns Promise<SeatReservation> - A promise which contains `sessionId` and `RoomListingData`.\n */\nexport async function joinById(roomId: string, clientOptions: ClientOptions = {}, authOptions?: AuthOptions) {\n const room = await driver.findOne({ roomId });\n\n if (!room) {\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room \"${roomId}\" not found`);\n\n } else if (room.locked) {\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room \"${roomId}\" is locked`);\n }\n\n const authData = await callOnAuth(room.name, authOptions);\n\n return reserveSeatFor(room, clientOptions, authData);\n}\n\n/**\n * Perform a query for all cached rooms\n */\nexport async function query(conditions: Partial<IRoomListingData> = {}) {\n return await driver.find(conditions);\n}\n\n/**\n * Find for a public and unlocked room available.\n *\n * @param roomName - The Id of the specific room.\n * @param clientOptions - Options for the client seat reservation (for `onJoin`/`onAuth`).\n *\n * @returns Promise<RoomListingData> - A promise contaning an object which includes room metadata and configurations.\n */\nexport async function findOneRoomAvailable(roomName: string, clientOptions: ClientOptions): Promise<RoomListingData> {\n return await awaitRoomAvailable(roomName, async () => {\n const handler = getHandler(roomName);\n\n const roomQuery = driver.findOne({\n locked: false,\n name: roomName,\n private: false,\n ...handler.getFilterOptions(clientOptions),\n });\n\n if (handler.sortOptions) {\n roomQuery.sort(handler.sortOptions);\n }\n\n return await roomQuery;\n });\n}\n\n/**\n * Call a method or return a property on a remote room.\n *\n * @param roomId - The Id of the specific room instance.\n * @param method - Method or attribute to call or retrive.\n * @param args - Array of arguments for the method\n *\n * @returns Promise<any> - Returned value from the called or retrieved method/attribute.\n */\nexport async function remoteRoomCall<R= any>(\n roomId: string,\n method: string,\n args?: any[],\n rejectionTimeout = REMOTE_ROOM_SHORT_TIMEOUT,\n): Promise<R> {\n const room = rooms[roomId];\n\n if (!room) {\n try {\n return await requestFromIPC<R>(presence, getRoomChannel(roomId), method, args, rejectionTimeout);\n\n } catch (e) {\n\n //\n // the room cache from an unavailable process might've been used here.\n // perform a health-check on the process before proceeding.\n // (this is a broken state when a process wasn't gracefully shut down)\n //\n if (method === '_reserveSeat' && e.message === \"ipc_timeout\") {\n throw e;\n }\n\n // TODO: for 1.0, consider always throwing previous error directly.\n\n const request = `${method}${args && ' with args ' + JSON.stringify(args) || ''}`;\n throw new ServerError(\n ErrorCode.MATCHMAKE_UNHANDLED,\n `remote room (${roomId}) timed out, requesting \"${request}\". (${rejectionTimeout}ms exceeded)`,\n );\n }\n\n } else {\n return (!args && typeof (room[method]) !== 'function')\n ? room[method]\n : (await room[method].apply(room, args && JSON.parse(JSON.stringify(args))));\n }\n}\n\nexport function defineRoomType<T extends Type<Room>>(\n roomName: string,\n klass: T,\n defaultOptions?: Parameters<NonNullable<InstanceType<T>['onCreate']>>[0],\n) {\n const registeredHandler = new RegisteredHandler(klass, defaultOptions);\n\n handlers[roomName] = registeredHandler;\n\n if (klass.prototype['onAuth'] !== Room.prototype['onAuth']) {\n // TODO: soft-deprecate instance level `onAuth` on 0.16\n // logger.warn(\"DEPRECATION WARNING: onAuth() at the instance level will be deprecated soon. Please use static onAuth() instead.\");\n\n if (klass['onAuth'] !== Room['onAuth']) {\n logger.info(`\u274C \"${roomName}\"'s onAuth() defined at the instance level will be ignored.`);\n }\n }\n\n if (!isDevMode) {\n cleanupStaleRooms(roomName);\n }\n\n return registeredHandler;\n}\n\nexport function removeRoomType(roomName: string) {\n delete handlers[roomName];\n\n if (!isDevMode) {\n cleanupStaleRooms(roomName);\n }\n}\n\n// TODO: legacy; remove me on 1.0\nexport function hasHandler(roomName: string) {\n logger.warn(\"hasHandler() is deprecated. Use getHandler() instead.\");\n return handlers[roomName] !== undefined;\n}\n\nexport function getHandler(roomName: string) {\n const handler = handlers[roomName];\n\n if (!handler) {\n throw new ServerError(ErrorCode.MATCHMAKE_NO_HANDLER, `provided room name \"${roomName}\" not defined`);\n }\n\n return handler;\n}\n\nexport function getRoomClass(roomName: string): Type<Room> {\n return handlers[roomName]?.klass;\n}\n\n\n/**\n * Creates a new room.\n *\n * @param roomName - The identifier you defined on `gameServer.define()`\n * @param clientOptions - Options for `onCreate`\n *\n * @returns Promise<RoomListingData> - A promise contaning an object which includes room metadata and configurations.\n */\nexport async function createRoom(roomName: string, clientOptions: ClientOptions): Promise<RoomListingData> {\n //\n // - select a process to create the room\n // - use local processId if MatchMaker is not ready yet\n //\n const selectedProcessId = (state === MatchMakerState.READY)\n ? await selectProcessIdToCreateRoom(roomName, clientOptions)\n : processId;\n\n let room: RoomListingData;\n if (selectedProcessId === undefined) {\n throw new ServerError(ErrorCode.MATCHMAKE_UNHANDLED, `no processId available to create room ${roomName}`);\n\n } else if (selectedProcessId === processId) {\n // create the room on this process!\n room = await handleCreateRoom(roomName, clientOptions);\n\n } else {\n // ask other process to create the room!\n try {\n room = await requestFromIPC<RoomListingData>(\n presence,\n getProcessChannel(selectedProcessId),\n undefined,\n [roomName, clientOptions],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n\n } catch (e) {\n if (e.message === \"ipc_timeout\") {\n debugAndPrintError(`${e.message}: create room request timed out for ${roomName} on processId ${selectedProcessId}.`);\n\n //\n // clean-up possibly stale process from redis.\n // when a process disconnects ungracefully, it may leave its previous processId under \"roomcount\"\n // if the process is still alive, it will re-add itself shortly after the load-balancer selects it again.\n //\n await stats.excludeProcess(selectedProcessId);\n\n // if other process failed to respond, create the room on this process\n room = await handleCreateRoom(roomName, clientOptions);\n\n } else {\n // re-throw intentional exception thrown during remote onCreate()\n throw e;\n }\n }\n }\n\n if (isDevMode) {\n presence.hset(getRoomRestoreListKey(), room.roomId, JSON.stringify({\n \"clientOptions\": clientOptions,\n \"roomName\": roomName,\n \"processId\": processId\n }));\n }\n\n return room;\n}\n\nexport async function handleCreateRoom(roomName: string, clientOptions: ClientOptions, restoringRoomId?: string): Promise<RoomListingData> {\n const handler = getHandler(roomName);\n const room = new handler.klass();\n\n // set room public attributes\n if (restoringRoomId && isDevMode) {\n room.roomId = restoringRoomId;\n\n } else {\n room.roomId = generateId();\n }\n\n if (room.state) {\n room.setState(room.state);\n }\n\n room.roomName = roomName;\n room.presence = presence;\n\n const additionalListingData: any = handler.getFilterOptions(clientOptions);\n\n // assign public host\n if (publicAddress) {\n additionalListingData.publicAddress = publicAddress;\n }\n\n // create a RoomCache reference.\n room.listing = driver.createInstance({\n name: roomName,\n processId,\n ...additionalListingData\n });\n\n if (room.onCreate) {\n try {\n await room.onCreate(merge({}, clientOptions, handler.options));\n\n } catch (e) {\n debugAndPrintError(e);\n throw new ServerError(\n e.code || ErrorCode.MATCHMAKE_UNHANDLED,\n e.message,\n );\n }\n }\n\n room['_internalState'] = RoomInternalState.CREATED;\n\n room.listing.roomId = room.roomId;\n room.listing.maxClients = room.maxClients;\n\n // imediatelly ask client to join the room\n debugMatchMaking('spawning \\'%s\\', roomId: %s, processId: %s', roomName, room.roomId, processId);\n\n // increment amount of rooms this process is handling\n stats.local.roomCount++;\n stats.persist();\n\n room._events.on('lock', lockRoom.bind(this, room));\n room._events.on('unlock', unlockRoom.bind(this, room));\n room._events.on('join', onClientJoinRoom.bind(this, room));\n room._events.on('leave', onClientLeaveRoom.bind(this, room));\n room._events.on('visibility-change', onVisibilityChange.bind(this, room));\n room._events.once('dispose', disposeRoom.bind(this, roomName, room));\n\n // when disconnect()'ing, keep only join/leave events for stat counting\n room._events.once('disconnect', () => {\n room._events.removeAllListeners('lock');\n room._events.removeAllListeners('unlock');\n room._events.removeAllListeners('visibility-change');\n room._events.removeAllListeners('dispose');\n });\n\n // room always start unlocked\n await createRoomReferences(room, true);\n await room.listing.save();\n\n handler.emit('create', room);\n\n return room.listing;\n}\n\nexport function getRoomById(roomId: string) {\n return rooms[roomId];\n}\n\n/**\n * Disconnects every client on every room in the current process.\n */\nexport function disconnectAll(closeCode?: number) {\n const promises: Array<Promise<any>> = [];\n\n for (const roomId in rooms) {\n if (!rooms.hasOwnProperty(roomId)) {\n continue;\n }\n\n const room = rooms[roomId];\n\n // prevent touching stats when process is shutting down\n room._events.removeAllListeners(\"leave\");\n\n promises.push(room.disconnect(closeCode));\n }\n\n return promises;\n}\n\nexport async function gracefullyShutdown(): Promise<any> {\n if (isGracefullyShuttingDown) {\n return Promise.reject('already_shutting_down');\n }\n\n isGracefullyShuttingDown = true;\n state = MatchMakerState.SHUTTING_DOWN;\n\n onReady = undefined;\n\n debugMatchMaking(`${processId} is shutting down!`);\n\n if (isDevMode) {\n await cacheRoomHistory(rooms);\n }\n\n // remove processId from room count key\n await stats.excludeProcess(processId);\n\n // remove cached rooms of this process\n await removeRoomsByProcessId(processId);\n\n // unsubscribe from process id channel\n presence.unsubscribe(getProcessChannel());\n\n return Promise.all(disconnectAll(\n (isDevMode)\n ? Protocol.WS_CLOSE_DEVMODE_RESTART\n : undefined\n ));\n}\n\n/**\n * Reserve a seat for a client in a room\n */\nexport async function reserveSeatFor(room: RoomListingData, options: ClientOptions, authData?: any) {\n const sessionId: string = generateId();\n\n debugMatchMaking(\n 'reserving seat. sessionId: \\'%s\\', roomId: \\'%s\\', processId: \\'%s\\'',\n sessionId, room.roomId, processId,\n );\n\n let successfulSeatReservation: boolean;\n\n try {\n successfulSeatReservation = await remoteRoomCall(\n room.roomId,\n '_reserveSeat',\n [sessionId, options, authData],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n\n } catch (e) {\n debugMatchMaking(e);\n\n //\n // the room cache from an unavailable process might've been used here.\n // (this is a broken state when a process wasn't gracefully shut down)\n // perform a health-check on the process before proceeding.\n //\n if (e.message === \"ipc_timeout\" && !(await healthCheckProcessId(room.processId))) {\n throw new SeatReservationError(`process ${room.processId} is not available.`);\n\n } else {\n successfulSeatReservation = false;\n }\n }\n\n if (!successfulSeatReservation) {\n throw new SeatReservationError(`${room.roomId} is already full.`);\n }\n\n const response: SeatReservation = { room, sessionId };\n\n if (isDevMode) {\n response.devMode = isDevMode;\n }\n\n return response;\n}\n\nfunction callOnAuth(roomName: string, authOptions?: AuthOptions) {\n const roomClass = getRoomClass(roomName);\n return (roomClass && roomClass['onAuth'] && roomClass['onAuth'] !== Room['onAuth'])\n ? roomClass['onAuth'](authOptions.token, authOptions.request)\n : undefined;\n}\n\nexport async function cleanupStaleRooms(roomName: string) {\n // remove connecting counts\n await presence.del(getHandlerConcurrencyKey(roomName));\n}\n\n/**\n * Perform health check on all processes\n */\nexport async function healthCheckAllProcesses() {\n const allStats = await stats.fetchAll();\n if (allStats.length > 0) {\n await Promise.all(\n allStats\n .filter(stat => stat.processId !== processId) // skip current process\n .map(stat => healthCheckProcessId(stat.processId))\n );\n }\n}\n\n/**\n * Perform health check on a remote process\n * @param processId\n */\nconst _healthCheckByProcessId: { [processId: string]: Promise<any> } = {};\nexport function healthCheckProcessId(processId: string) {\n //\n // re-use the same promise if health-check is already in progress\n // (may occur when _reserveSeat() fails multiple times for the same 'processId')\n //\n if (_healthCheckByProcessId[processId] !== undefined) {\n return _healthCheckByProcessId[processId];\n }\n\n _healthCheckByProcessId[processId] = new Promise<boolean>(async (resolve, reject) => {\n logger.debug(`> Performing health-check against processId: '${processId}'...`);\n\n try {\n const requestTime = Date.now();\n\n await requestFromIPC<RoomListingData>(\n presence,\n getProcessChannel(processId),\n 'healthcheck',\n [],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n\n logger.debug(`\u2705 Process '${processId}' successfully responded (${Date.now() - requestTime}ms)`);\n\n // succeeded to respond\n resolve(true)\n\n } catch (e) {\n // process failed to respond - remove it from stats\n logger.debug(`\u274C Process '${processId}' failed to respond. Cleaning it up.`);\n const isProcessExcluded = await stats.excludeProcess(processId);\n\n // clean-up possibly stale room ids\n if (isProcessExcluded && !isDevMode) {\n await removeRoomsByProcessId(processId);\n }\n\n resolve(false);\n } finally {\n delete _healthCheckByProcessId[processId];\n }\n });\n\n return _healthCheckByProcessId[processId];\n}\n\n/**\n * Remove cached rooms by processId\n * @param processId\n */\nasync function removeRoomsByProcessId(processId: string) {\n //\n // clean-up possibly stale room ids\n // (ungraceful shutdowns using Redis can result on stale room ids still on memory.)\n //\n if (typeof(driver.cleanup) === \"function\") {\n await driver.cleanup(processId);\n\n } else {\n //\n // TODO: remove this block on 1.0.\n //\n // driver.cleanup() has been added mid-way through 0.15\n // some users may still be using older versions of the driver.\n //\n const cachedRooms = await driver.find({ processId }, { _id: 1 });\n logger.debug(\"> Removing stale rooms by processId:\", processId, `(${cachedRooms.length} rooms found)`);\n cachedRooms.forEach((room) => room.remove());\n }\n}\n\nasync function createRoomReferences(room: Room, init: boolean = false): Promise<boolean> {\n rooms[room.roomId] = room;\n\n if (init) {\n await subscribeIPC(\n presence,\n processId,\n getRoomChannel(room.roomId),\n (method, args) => {\n return (!args && typeof (room[method]) !== 'function')\n ? room[method]\n : room[method].apply(room, args);\n },\n );\n }\n\n return true;\n}\n\nasync function awaitRoomAvailable(roomToJoin: string, callback: Function): Promise<RoomListingData> {\n return new Promise(async (resolve, reject) => {\n const concurrencyKey = getHandlerConcurrencyKey(roomToJoin);\n const concurrency = await presence.incr(concurrencyKey) - 1;\n\n //\n // avoid having too long timeout if 10+ clients ask to join at the same time\n //\n // TODO: we need a better solution here. either a lock or queue system should be implemented instead.\n // https://github.com/colyseus/colyseus/issues/466\n //\n const concurrencyTimeout = Math.min(concurrency * 100, 500);\n\n if (concurrency > 0) {\n debugMatchMaking(\n 'receiving %d concurrent requests for joining \\'%s\\' (waiting %d ms)',\n concurrency, roomToJoin, concurrencyTimeout,\n );\n }\n\n setTimeout(async () => {\n try {\n const result = await callback();\n resolve(result);\n\n } catch (e) {\n reject(e);\n\n } finally {\n await presence.decr(concurrencyKey);\n }\n }, concurrencyTimeout);\n });\n}\n\nfunction onClientJoinRoom(room: Room, client: Client) {\n // increment local CCU\n stats.local.ccu++;\n stats.persist();\n\n handlers[room.roomName].emit('join', room, client);\n}\n\nfunction onClientLeaveRoom(room: Room, client: Client, willDispose: boolean) {\n // decrement local CCU\n stats.local.ccu--;\n stats.persist();\n\n handlers[room.roomName].emit('leave', room, client, willDispose);\n}\n\nfunction lockRoom(room: Room): void {\n // emit public event on registered handler\n handlers[room.roomName].emit('lock', room);\n}\n\nasync function unlockRoom(room: Room) {\n if (await createRoomReferences(room)) {\n // emit public event on registered handler\n handlers[room.roomName].emit('unlock', room);\n }\n}\n\nfunction onVisibilityChange(room: Room, isInvisible: boolean): void {\n handlers[room.roomName].emit('visibility-change', room, isInvisible);\n}\n\nasync function disposeRoom(roomName: string, room: Room) {\n debugMatchMaking('disposing \\'%s\\' (%s) on processId \\'%s\\' (graceful shutdown: %s)', roomName, room.roomId, processId, isGracefullyShuttingDown);\n\n // decrease amount of rooms this process is handling\n if (!isGracefullyShuttingDown) {\n stats.local.roomCount--;\n stats.persist();\n\n // remove from devMode restore list\n if (isDevMode) {\n await presence.hdel(getRoomRestoreListKey(), room.roomId);\n }\n }\n\n // emit disposal on registered session handler\n handlers[roomName].emit('dispose', room);\n\n // remove concurrency key\n presence.del(getHandlerConcurrencyKey(roomName));\n\n // unsubscribe from remote connections\n presence.unsubscribe(getRoomChannel(room.roomId));\n\n // remove actual room reference\n delete rooms[room.roomId];\n}\n\n//\n// Presence keys\n//\nfunction getRoomChannel(roomId: string) {\n return `$${roomId}`;\n}\n\nfunction getHandlerConcurrencyKey(name: string) {\n return `c:${name}`;\n}\n\nfunction getProcessChannel(id: string = processId) {\n return `p:${id}`;\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { ErrorCode, Protocol } from './Protocol';\n\nimport { requestFromIPC, subscribeIPC } from './IPC';\n\nimport { Deferred, generateId, merge, retry, MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME, REMOTE_ROOM_SHORT_TIMEOUT } 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 { IRoomCache, LocalDriver, MatchMakerDriver, SortOptions } from './matchmaker/driver/local/LocalDriver';\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\";\nimport { getLockId } from './matchmaker/driver/api';\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: IRoomCache;\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\n/**\n * Whether health checks are enabled or not. (default: true)\n *\n * Health checks are automatically performed on theses scenarios:\n * - At startup, to check for leftover/invalid processId's\n * - When a remote room creation request times out\n * - When a remote seat reservation request times out\n */\nlet enableHealthChecks: boolean = true;\nexport function setHealthChecksEnabled(value: boolean) {\n enableHealthChecks = value;\n}\n\nexport let onReady: Deferred = new Deferred(); // onReady needs to be immediately available to @colyseus/auth integration.\n\nexport enum MatchMakerState {\n INITIALIZING,\n READY,\n SHUTTING_DOWN\n}\n\n/**\n * Internal MatchMaker state\n */\nexport let state: MatchMakerState;\n\n/**\n * @private\n */\nexport async function setup(\n _presence?: Presence,\n _driver?: MatchMakerDriver,\n _publicAddress?: string,\n _selectProcessIdToCreateRoom?: SelectProcessIdCallback,\n) {\n if (onReady === undefined) {\n //\n // for testing purposes only: onReady is turned into undefined on shutdown\n // (needs refactoring.)\n //\n onReady = new Deferred();\n }\n\n state = MatchMakerState.INITIALIZING;\n\n presence = _presence || new LocalPresence();\n\n driver = _driver || new LocalDriver();\n publicAddress = _publicAddress;\n\n stats.reset(false);\n\n // devMode: try to retrieve previous processId\n if (isDevMode) { processId = await getPreviousProcessId(await getHostname()); }\n\n // ensure processId is set\n if (!processId) { processId = generateId(); }\n\n /**\n * Define default `assignRoomToProcessId` method.\n * By default, return the process with least amount of rooms created\n */\n selectProcessIdToCreateRoom = _selectProcessIdToCreateRoom || async function () {\n return (await stats.fetchAll())\n .sort((p1, p2) => p1.roomCount > p2.roomCount ? 1 : -1)[0]?.processId || processId;\n };\n\n onReady.resolve();\n}\n\n/**\n * - Accept receiving remote room creation requests\n * - Check for leftover/invalid processId's on startup\n * @private\n */\nexport async function accept() {\n await onReady; // make sure \"processId\" is available\n\n /**\n * Process-level subscription\n * - handle remote process healthcheck\n * - handle remote room creation\n */\n await subscribeIPC(presence, processId, getProcessChannel(), (method, args) => {\n if (method === 'healthcheck') {\n // health check for this processId\n return true;\n\n } else {\n // handle room creation\n return handleCreateRoom.apply(undefined, args);\n }\n });\n\n /**\n * Check for leftover/invalid processId's on startup\n */\n if (enableHealthChecks) {\n await healthCheckAllProcesses();\n }\n\n state = MatchMakerState.READY;\n\n await stats.persist();\n\n if (isDevMode) {\n await reloadFromCache();\n }\n}\n\n/**\n * Join or create into a room and return seat reservation\n */\nexport async function joinOrCreate(roomName: string, clientOptions: ClientOptions = {}, authOptions?: AuthOptions) {\n return await retry<Promise<SeatReservation>>(async () => {\n const authData = await callOnAuth(roomName, authOptions);\n let room: IRoomCache = await findOneRoomAvailable(roomName, clientOptions);\n\n if (!room) {\n const handler = getHandler(roomName);\n const filterOptions = handler.getFilterOptions(clientOptions);\n const concurrencyKey = getLockId(filterOptions);\n\n //\n // Prevent multiple rooms of same filter from being created concurrently\n //\n await concurrentJoinOrCreateRoomLock(handler, concurrencyKey, async (roomId?: string) => {\n if (roomId) {\n room = await driver.findOne({ roomId })\n }\n\n if (!room) {\n room = await findOneRoomAvailable(roomName, clientOptions);\n }\n\n if (!room) {\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 room = await createRoom(roomName, clientOptions);\n presence.lpush(`l:${handler.name}:${concurrencyKey}`, room.roomId);\n presence.expire(`l:${handler.name}:${concurrencyKey}`, MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME * 2);\n }\n\n return room;\n });\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 `IRoomCache`.\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<IRoomCache> = {}, sortOptions?: SortOptions) {\n return await driver.query(conditions, sortOptions);\n}\n\n/**\n * Find for a public and unlocked room available.\n *\n * @param roomName - The Id of the specific room.\n * @param filterOptions - Filter options.\n * @param sortOptions - Sorting options.\n *\n * @returns Promise<IRoomCache> - A promise contaning an object which includes room metadata and configurations.\n */\nexport async function findOneRoomAvailable(\n roomName: string,\n filterOptions: ClientOptions,\n additionalSortOptions?: SortOptions,\n) {\n const handler = getHandler(roomName);\n const sortOptions = Object.assign({}, handler.sortOptions ?? {});\n\n if (additionalSortOptions) {\n Object.assign(sortOptions, additionalSortOptions);\n }\n\n return await driver.findOne({\n locked: false,\n name: roomName,\n private: false,\n ...handler.getFilterOptions(filterOptions),\n }, sortOptions);\n}\n\n/**\n * Call a method or return a property on a remote room.\n *\n * @param roomId - The Id of the specific room instance.\n * @param method - Method or attribute to call or retrive.\n * @param args - Array of arguments for the method\n *\n * @returns Promise<any> - Returned value from the called or retrieved method/attribute.\n */\nexport async function remoteRoomCall<R= any>(\n roomId: string,\n method: string,\n args?: any[],\n rejectionTimeout = REMOTE_ROOM_SHORT_TIMEOUT,\n): Promise<R> {\n const room = rooms[roomId];\n\n if (!room) {\n try {\n return await requestFromIPC<R>(presence, getRoomChannel(roomId), method, args, rejectionTimeout);\n\n } catch (e) {\n\n //\n // the room cache from an unavailable process might've been used here.\n // perform a health-check on the process before proceeding.\n // (this is a broken state when a process wasn't gracefully shut down)\n //\n if (method === '_reserveSeat' && e.message === \"ipc_timeout\") {\n throw e;\n }\n\n // TODO: for 1.0, consider always throwing previous error directly.\n\n const request = `${method}${args && ' with args ' + JSON.stringify(args) || ''}`;\n throw new ServerError(\n ErrorCode.MATCHMAKE_UNHANDLED,\n `remote room (${roomId}) timed out, requesting \"${request}\". (${rejectionTimeout}ms exceeded)`,\n );\n }\n\n } else {\n return (!args && typeof (room[method]) !== 'function')\n ? room[method]\n : (await room[method].apply(room, args && JSON.parse(JSON.stringify(args))));\n }\n}\n\nexport function defineRoomType<T extends Type<Room>>(\n roomName: string,\n klass: T,\n defaultOptions?: Parameters<NonNullable<InstanceType<T>['onCreate']>>[0],\n) {\n const registeredHandler = new RegisteredHandler(roomName, klass, defaultOptions);\n\n handlers[roomName] = registeredHandler;\n\n if (klass.prototype['onAuth'] !== Room.prototype['onAuth']) {\n // TODO: soft-deprecate instance level `onAuth` on 0.16\n // logger.warn(\"DEPRECATION WARNING: onAuth() at the instance level will be deprecated soon. Please use static onAuth() instead.\");\n\n if (klass['onAuth'] !== Room['onAuth']) {\n logger.info(`\u274C \"${roomName}\"'s onAuth() defined at the instance level will be ignored.`);\n }\n }\n\n return registeredHandler;\n}\n\nexport function removeRoomType(roomName: string) {\n delete handlers[roomName];\n}\n\n// TODO: legacy; remove me on 1.0\nexport function hasHandler(roomName: string) {\n logger.warn(\"hasHandler() is deprecated. Use getHandler() instead.\");\n return handlers[roomName] !== undefined;\n}\n\nexport function getHandler(roomName: string) {\n const handler = handlers[roomName];\n\n if (!handler) {\n throw new ServerError(ErrorCode.MATCHMAKE_NO_HANDLER, `provided room name \"${roomName}\" not defined`);\n }\n\n return handler;\n}\n\nexport function getRoomClass(roomName: string): Type<Room> {\n return handlers[roomName]?.klass;\n}\n\n\n/**\n * Creates a new room.\n *\n * @param roomName - The identifier you defined on `gameServer.define()`\n * @param clientOptions - Options for `onCreate`\n *\n * @returns Promise<IRoomCache> - A promise contaning an object which includes room metadata and configurations.\n */\nexport async function createRoom(roomName: string, clientOptions: ClientOptions): Promise<IRoomCache> {\n //\n // - select a process to create the room\n // - use local processId if MatchMaker is not ready yet\n //\n const selectedProcessId = (state === MatchMakerState.READY)\n ? await selectProcessIdToCreateRoom(roomName, clientOptions)\n : processId;\n\n let room: IRoomCache;\n if (selectedProcessId === undefined) {\n throw new ServerError(ErrorCode.MATCHMAKE_UNHANDLED, `no processId available to create room ${roomName}`);\n\n } else if (selectedProcessId === processId) {\n // create the room on this process!\n room = await handleCreateRoom(roomName, clientOptions);\n\n } else {\n // ask other process to create the room!\n try {\n room = await requestFromIPC<IRoomCache>(\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 if (enableHealthChecks) {\n await stats.excludeProcess(selectedProcessId);\n }\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<IRoomCache> {\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 //\n // Initialize .state (if set).\n //\n // Define getters and setters for:\n // - autoDispose\n // - patchRate\n //\n room['__init']();\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\n/**\n * Get room data by roomId.\n * This method does not return the actual room instance, use `getLocalRoomById` for that.\n */\nexport function getRoomById(roomId: string) {\n return driver.findOne({ roomId });\n}\n\n/**\n * Get local room instance by roomId. (Can return \"undefined\" if the room is not available on this process)\n */\nexport function getLocalRoomById(roomId: string) {\n return rooms[roomId];\n}\n\n/**\n * Disconnects every client on every room in the current process.\n */\nexport function disconnectAll(closeCode?: number) {\n const promises: Array<Promise<any>> = [];\n\n for (const roomId in rooms) {\n if (!rooms.hasOwnProperty(roomId)) {\n continue;\n }\n\n const room = rooms[roomId];\n\n // prevent touching stats when process is shutting down\n room._events.removeAllListeners(\"leave\");\n\n promises.push(room.disconnect(closeCode));\n }\n\n return promises;\n}\n\nexport async function gracefullyShutdown(): Promise<any> {\n if (state === MatchMakerState.SHUTTING_DOWN) {\n return Promise.reject('already_shutting_down');\n }\n\n state = MatchMakerState.SHUTTING_DOWN;\n\n onReady = undefined;\n\n debugMatchMaking(`${processId} is shutting down!`);\n\n if (isDevMode) {\n await cacheRoomHistory(rooms);\n }\n\n // remove processId from room count key\n await stats.excludeProcess(processId);\n\n // remove cached rooms of this process\n await removeRoomsByProcessId(processId);\n\n // unsubscribe from process id channel\n presence.unsubscribe(getProcessChannel());\n\n return Promise.all(disconnectAll(\n (isDevMode)\n ? Protocol.WS_CLOSE_DEVMODE_RESTART\n : undefined\n ));\n}\n\n/**\n * Reserve a seat for a client in a room\n */\nexport async function reserveSeatFor(room: IRoomCache, options: ClientOptions, authData?: any) {\n const sessionId: string = generateId();\n\n debugMatchMaking(\n 'reserving seat. sessionId: \\'%s\\', roomId: \\'%s\\', processId: \\'%s\\'',\n sessionId, room.roomId, processId,\n );\n\n let successfulSeatReservation: boolean;\n\n try {\n successfulSeatReservation = await remoteRoomCall(\n room.roomId,\n '_reserveSeat',\n [sessionId, options, authData],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n\n } catch (e) {\n debugMatchMaking(e);\n\n //\n // the room cache from an unavailable process might've been used here.\n // (this is a broken state when a process wasn't gracefully shut down)\n // perform a health-check on the process before proceeding.\n //\n if (\n e.message === \"ipc_timeout\" &&\n !(\n enableHealthChecks &&\n await healthCheckProcessId(room.processId)\n )\n ) {\n throw new SeatReservationError(`process ${room.processId} is not available.`);\n\n } else {\n successfulSeatReservation = false;\n }\n }\n\n if (!successfulSeatReservation) {\n throw new SeatReservationError(`${room.roomId} is already full.`);\n }\n\n const response: SeatReservation = { room, sessionId };\n\n if (isDevMode) {\n response.devMode = isDevMode;\n }\n\n return response;\n}\n\nfunction callOnAuth(roomName: string, authOptions?: AuthOptions) {\n const roomClass = getRoomClass(roomName);\n return (roomClass && roomClass['onAuth'] && roomClass['onAuth'] !== Room['onAuth'])\n ? roomClass['onAuth'](authOptions.token, authOptions.request)\n : undefined;\n}\n\n/**\n * Perform health check on all processes\n */\nexport async function healthCheckAllProcesses() {\n const allStats = await stats.fetchAll();\n if (allStats.length > 0) {\n await Promise.all(\n allStats\n .filter(stat => stat.processId !== processId) // skip current process\n .map(stat => healthCheckProcessId(stat.processId))\n );\n }\n}\n\n/**\n * Perform health check on a remote process\n * @param processId\n */\nconst _healthCheckByProcessId: { [processId: string]: Promise<any> } = {};\nexport function healthCheckProcessId(processId: string) {\n //\n // re-use the same promise if health-check is already in progress\n // (may occur when _reserveSeat() fails multiple times for the same 'processId')\n //\n if (_healthCheckByProcessId[processId] !== undefined) {\n return _healthCheckByProcessId[processId];\n }\n\n _healthCheckByProcessId[processId] = new Promise<boolean>(async (resolve, reject) => {\n logger.debug(`> Performing health-check against processId: '${processId}'...`);\n\n try {\n const requestTime = Date.now();\n\n await requestFromIPC<IRoomCache>(\n presence,\n getProcessChannel(processId),\n 'healthcheck',\n [],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n\n logger.debug(`\u2705 Process '${processId}' successfully responded (${Date.now() - requestTime}ms)`);\n\n // succeeded to respond\n resolve(true)\n\n } catch (e) {\n // process failed to respond - remove it from stats\n logger.debug(`\u274C Process '${processId}' failed to respond. Cleaning it up.`);\n const isProcessExcluded = await stats.excludeProcess(processId);\n\n // clean-up possibly stale room ids\n if (isProcessExcluded && !isDevMode) {\n await removeRoomsByProcessId(processId);\n }\n\n resolve(false);\n } finally {\n delete _healthCheckByProcessId[processId];\n }\n });\n\n return _healthCheckByProcessId[processId];\n}\n\n/**\n * Remove cached rooms by processId\n * @param processId\n */\nasync function removeRoomsByProcessId(processId: string) {\n //\n // clean-up possibly stale room ids\n // (ungraceful shutdowns using Redis can result on stale room ids still on memory.)\n //\n await driver.cleanup(processId);\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\n/**\n * Used only during `joinOrCreate` to handle concurrent requests for creating a room.\n */\nasync function concurrentJoinOrCreateRoomLock(\n handler: RegisteredHandler,\n concurrencyKey: string,\n callback: (roomId?: string) => Promise<IRoomCache>\n): Promise<IRoomCache> {\n return new Promise(async (resolve, reject) => {\n const hkey = getConcurrencyHashKey(handler.name);\n const concurrency = await presence.hincrbyex(\n hkey,\n concurrencyKey,\n 1, // increment by 1\n MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME * 2 // expire in 2x the time of MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME\n ) - 1; // do not consider the current request\n\n const fulfill = async (roomId?: string) => {\n try {\n resolve(await callback(roomId));\n\n } catch (e) {\n reject(e);\n\n } finally {\n await presence.hincrby(hkey, concurrencyKey, -1);\n }\n };\n\n if (concurrency > 0) {\n debugMatchMaking(\n 'receiving %d concurrent joinOrCreate for \\'%s\\' (%s)',\n concurrency, handler.name, concurrencyKey\n );\n\n const result = await presence.brpop(\n `l:${handler.name}:${concurrencyKey}`,\n MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME +\n (Math.min(concurrency, 3) * 0.2) // add extra milliseconds for each concurrent request\n );\n\n return await fulfill(result && result[1]);\n\n } else {\n return await fulfill();\n }\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, state === MatchMakerState.SHUTTING_DOWN);\n\n //\n // FIXME: this call should not be necessary.\n //\n // there's an unidentified edge case using LocalDriver where Room._dispose()\n // doesn't seem to be called [?], but \"disposeRoom\" is, leaving the matchmaker\n // in a broken state. (repeated ipc_timeout's for seat reservation on\n // non-existing rooms)\n //\n room.listing.remove();\n\n // decrease amount of rooms this process is handling\n if (state !== MatchMakerState.SHUTTING_DOWN) {\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 // 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 getConcurrencyHashKey(roomName: string) {\n // concurrency hash\n return `ch:${roomName}`;\n}\n\nfunction getProcessChannel(id: string = processId) {\n return `p:${id}`;\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["MatchMakerState", "processId"]
7
7
  }
@@ -1,4 +1,3 @@
1
- /// <reference types="node" />
2
1
  export declare enum Protocol {
3
2
  JOIN_ROOM = 10,
4
3
  ERROR = 11,
@@ -31,8 +30,8 @@ export declare enum IpcProtocol {
31
30
  TIMEOUT = 2
32
31
  }
33
32
  export declare const getMessageBytes: {
34
- 10: (reconnectionToken: string, serializerId: string, handshake?: Buffer) => Buffer;
35
- 11: (code: number, message?: string) => Buffer;
33
+ 10: (reconnectionToken: string, serializerId: string, handshake?: Buffer) => Buffer | Uint8Array;
34
+ 11: (code: number, message?: string) => Buffer | Uint8Array;
36
35
  14: (bytes: number[]) => number[];
37
- raw: (code: Protocol, type: string | number, message?: any, rawMessage?: Uint8Array | Buffer) => Buffer;
36
+ raw: (code: Protocol, type: string | number, message?: any, rawMessage?: Uint8Array | Buffer) => Buffer | Uint8Array;
38
37
  };
package/build/Protocol.js CHANGED
@@ -59,47 +59,52 @@ var IpcProtocol = /* @__PURE__ */ ((IpcProtocol2) => {
59
59
  IpcProtocol2[IpcProtocol2["TIMEOUT"] = 2] = "TIMEOUT";
60
60
  return IpcProtocol2;
61
61
  })(IpcProtocol || {});
62
- const sendBuffer = Buffer.allocUnsafe(8192);
63
62
  const packr = new import_msgpackr.Packr();
64
- packr.useBuffer(sendBuffer);
63
+ packr.encode(void 0);
65
64
  const getMessageBytes = {
66
65
  [10 /* JOIN_ROOM */]: (reconnectionToken, serializerId, handshake) => {
67
66
  const it = { offset: 1 };
68
- sendBuffer[0] = 10 /* JOIN_ROOM */;
69
- sendBuffer[it.offset++] = Buffer.byteLength(reconnectionToken, "utf8");
70
- import_schema.encode.utf8Write(sendBuffer, reconnectionToken, it);
71
- sendBuffer[it.offset++] = Buffer.byteLength(serializerId, "utf8");
72
- import_schema.encode.utf8Write(sendBuffer, serializerId, it);
73
- handshake.copy(sendBuffer, it.offset, 0, handshake.byteLength);
74
- return sendBuffer.subarray(0, it.offset + handshake.byteLength);
67
+ packr.buffer[0] = 10 /* JOIN_ROOM */;
68
+ packr.buffer[it.offset++] = Buffer.byteLength(reconnectionToken, "utf8");
69
+ import_schema.encode.utf8Write(packr.buffer, reconnectionToken, it);
70
+ packr.buffer[it.offset++] = Buffer.byteLength(serializerId, "utf8");
71
+ import_schema.encode.utf8Write(packr.buffer, serializerId, it);
72
+ let handshakeLength = handshake?.byteLength || 0;
73
+ if (handshakeLength > 0) {
74
+ handshake.copy(packr.buffer, it.offset, 0, handshakeLength);
75
+ }
76
+ return packr.buffer.subarray(0, it.offset + handshakeLength);
75
77
  },
76
78
  [11 /* ERROR */]: (code, message = "") => {
77
79
  const it = { offset: 1 };
78
- sendBuffer[0] = 11 /* ERROR */;
79
- import_schema.encode.number(sendBuffer, code, it);
80
- import_schema.encode.string(sendBuffer, message, it);
81
- return sendBuffer.subarray(0, it.offset);
80
+ packr.buffer[0] = 11 /* ERROR */;
81
+ import_schema.encode.number(packr.buffer, code, it);
82
+ import_schema.encode.string(packr.buffer, message, it);
83
+ return packr.buffer.subarray(0, it.offset);
82
84
  },
83
85
  [14 /* ROOM_STATE */]: (bytes) => {
84
86
  return [14 /* ROOM_STATE */, ...bytes];
85
87
  },
86
88
  raw: (code, type, message, rawMessage) => {
87
89
  const it = { offset: 1 };
88
- sendBuffer[0] = code;
90
+ packr.buffer[0] = code;
89
91
  if (typeof type === "string") {
90
- import_schema.encode.string(sendBuffer, type, it);
92
+ import_schema.encode.string(packr.buffer, type, it);
91
93
  } else {
92
- import_schema.encode.number(sendBuffer, type, it);
94
+ import_schema.encode.number(packr.buffer, type, it);
93
95
  }
94
96
  if (message !== void 0) {
95
97
  packr.position = 0;
98
+ if (process.env.NODE_ENV !== "production") {
99
+ packr.useBuffer(packr.buffer);
100
+ }
96
101
  const endOfBufferOffset = packr.pack(message, 2048 + it.offset).byteLength;
97
- return sendBuffer.subarray(0, endOfBufferOffset);
102
+ return packr.buffer.subarray(0, endOfBufferOffset);
98
103
  } else if (rawMessage !== void 0) {
99
- sendBuffer.set(rawMessage, it.offset);
100
- return sendBuffer.subarray(0, it.offset + rawMessage.byteLength);
104
+ packr.buffer.set(rawMessage, it.offset);
105
+ return packr.buffer.subarray(0, it.offset + rawMessage.byteLength);
101
106
  } else {
102
- return sendBuffer.subarray(0, it.offset);
107
+ return packr.buffer.subarray(0, it.offset);
103
108
  }
104
109
  }
105
110
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/Protocol.ts"],
4
- "sourcesContent": ["import { pack, Packr } from '@colyseus/msgpackr';\nimport { encode, Iterator } from '@colyseus/schema';\n\n// Colyseus protocol codes range between 0~100\nexport enum Protocol {\n // Room-related (10~19)\n JOIN_ROOM = 10,\n ERROR = 11,\n LEAVE_ROOM = 12,\n ROOM_DATA = 13,\n ROOM_STATE = 14,\n ROOM_STATE_PATCH = 15,\n // ROOM_DATA_SCHEMA = 16, // DEPRECATED: used to send schema instances via room.send()\n ROOM_DATA_BYTES = 17,\n\n // WebSocket close codes (https://github.com/Luka967/websocket-close-codes)\n WS_CLOSE_NORMAL = 1000,\n WS_CLOSE_GOING_AWAY = 1001,\n\n // WebSocket error codes\n WS_CLOSE_CONSENTED = 4000,\n WS_CLOSE_WITH_ERROR = 4002,\n WS_CLOSE_DEVMODE_RESTART = 4010,\n\n WS_SERVER_DISCONNECT = 4201,\n WS_TOO_MANY_CLIENTS = 4202,\n}\n\nexport enum ErrorCode {\n // MatchMaking Error Codes\n MATCHMAKE_NO_HANDLER = 4210,\n MATCHMAKE_INVALID_CRITERIA = 4211,\n MATCHMAKE_INVALID_ROOM_ID = 4212,\n MATCHMAKE_UNHANDLED = 4213, // generic exception during onCreate/onJoin\n MATCHMAKE_EXPIRED = 4214, // generic exception during onCreate/onJoin\n\n AUTH_FAILED = 4215,\n APPLICATION_ERROR = 4216,\n\n INVALID_PAYLOAD = 4217,\n}\n\n// Inter-process communication protocol\nexport enum IpcProtocol {\n SUCCESS = 0,\n ERROR = 1,\n TIMEOUT = 2,\n}\n\nconst sendBuffer = Buffer.allocUnsafe(8192);\n\nconst packr = new Packr();\npackr.useBuffer(sendBuffer);\n\nexport const getMessageBytes = {\n [Protocol.JOIN_ROOM]: (reconnectionToken: string, serializerId: string, handshake?: Buffer) => {\n const it: Iterator = { offset: 1 };\n sendBuffer[0] = Protocol.JOIN_ROOM;\n\n sendBuffer[it.offset++] = Buffer.byteLength(reconnectionToken, \"utf8\");\n encode.utf8Write(sendBuffer, reconnectionToken, it);\n\n sendBuffer[it.offset++] = Buffer.byteLength(serializerId, \"utf8\");\n encode.utf8Write(sendBuffer, serializerId, it);\n\n handshake.copy(sendBuffer, it.offset, 0, handshake.byteLength);\n return sendBuffer.subarray(0, it.offset + handshake.byteLength);\n },\n\n [Protocol.ERROR]: (code: number, message: string = '') => {\n const it: Iterator = { offset: 1 };\n sendBuffer[0] = Protocol.ERROR;\n\n encode.number(sendBuffer, code, it);\n encode.string(sendBuffer, message, it);\n\n return sendBuffer.subarray(0, it.offset);\n },\n\n [Protocol.ROOM_STATE]: (bytes: number[]) => {\n return [Protocol.ROOM_STATE, ...bytes];\n },\n\n raw: (code: Protocol, type: string | number, message?: any, rawMessage?: Uint8Array | Buffer) => {\n\n const it: Iterator = { offset: 1 };\n sendBuffer[0] = code;\n\n if (typeof (type) === 'string') {\n encode.string(sendBuffer, type as string, it);\n\n } else {\n encode.number(sendBuffer, type, it);\n }\n\n if (message !== undefined) {\n // pack message into the same sendBuffer\n packr.position = 0; // force to encode from the beginning\n const endOfBufferOffset = packr.pack(message, 2048 + it.offset).byteLength;\n // 2048 = RESERVE_START_SPACE\n return sendBuffer.subarray(0, endOfBufferOffset);\n\n } else if (rawMessage !== undefined) {\n\n // copy raw message into sendBuffer\n sendBuffer.set(rawMessage, it.offset);\n return sendBuffer.subarray(0, it.offset + rawMessage.byteLength);\n\n } else {\n return sendBuffer.subarray(0, it.offset);\n }\n },\n\n};\n\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { pack, Packr } from '@colyseus/msgpackr';\nimport { encode, Iterator } from '@colyseus/schema';\n\n// Colyseus protocol codes range between 0~100\nexport enum Protocol {\n // Room-related (10~19)\n JOIN_ROOM = 10,\n ERROR = 11,\n LEAVE_ROOM = 12,\n ROOM_DATA = 13,\n ROOM_STATE = 14,\n ROOM_STATE_PATCH = 15,\n // ROOM_DATA_SCHEMA = 16, // DEPRECATED: used to send schema instances via room.send()\n ROOM_DATA_BYTES = 17,\n\n // WebSocket close codes (https://github.com/Luka967/websocket-close-codes)\n WS_CLOSE_NORMAL = 1000,\n WS_CLOSE_GOING_AWAY = 1001,\n\n // WebSocket error codes\n WS_CLOSE_CONSENTED = 4000,\n WS_CLOSE_WITH_ERROR = 4002,\n WS_CLOSE_DEVMODE_RESTART = 4010,\n\n WS_SERVER_DISCONNECT = 4201,\n WS_TOO_MANY_CLIENTS = 4202,\n}\n\nexport enum ErrorCode {\n // MatchMaking Error Codes\n MATCHMAKE_NO_HANDLER = 4210,\n MATCHMAKE_INVALID_CRITERIA = 4211,\n MATCHMAKE_INVALID_ROOM_ID = 4212,\n MATCHMAKE_UNHANDLED = 4213, // generic exception during onCreate/onJoin\n MATCHMAKE_EXPIRED = 4214, // generic exception during onCreate/onJoin\n\n AUTH_FAILED = 4215,\n APPLICATION_ERROR = 4216,\n\n INVALID_PAYLOAD = 4217,\n}\n\n// Inter-process communication protocol\nexport enum IpcProtocol {\n SUCCESS = 0,\n ERROR = 1,\n TIMEOUT = 2,\n}\n\n\nconst packr = new Packr();\n\n// msgpackr workaround: initialize buffer\npackr.encode(undefined);\n\nexport const getMessageBytes = {\n [Protocol.JOIN_ROOM]: (reconnectionToken: string, serializerId: string, handshake?: Buffer) => {\n const it: Iterator = { offset: 1 };\n packr.buffer[0] = Protocol.JOIN_ROOM;\n\n packr.buffer[it.offset++] = Buffer.byteLength(reconnectionToken, \"utf8\");\n encode.utf8Write(packr.buffer, reconnectionToken, it);\n\n packr.buffer[it.offset++] = Buffer.byteLength(serializerId, \"utf8\");\n encode.utf8Write(packr.buffer, serializerId, it);\n\n let handshakeLength = handshake?.byteLength || 0;\n if (handshakeLength > 0) {\n handshake.copy(packr.buffer, it.offset, 0, handshakeLength);\n }\n\n return packr.buffer.subarray(0, it.offset + handshakeLength);\n },\n\n [Protocol.ERROR]: (code: number, message: string = '') => {\n const it: Iterator = { offset: 1 };\n packr.buffer[0] = Protocol.ERROR;\n\n encode.number(packr.buffer, code, it);\n encode.string(packr.buffer, message, it);\n\n return packr.buffer.subarray(0, it.offset);\n },\n\n [Protocol.ROOM_STATE]: (bytes: number[]) => {\n return [Protocol.ROOM_STATE, ...bytes];\n },\n\n raw: (code: Protocol, type: string | number, message?: any, rawMessage?: Uint8Array | Buffer) => {\n const it: Iterator = { offset: 1 };\n packr.buffer[0] = code;\n\n if (typeof (type) === 'string') {\n encode.string(packr.buffer, type as string, it);\n\n } else {\n encode.number(packr.buffer, type, it);\n }\n\n if (message !== undefined) {\n // force to encode from offset\n packr.position = 0;\n\n //\n // TODO: remove this after issue is fixed https://github.com/kriszyp/msgpackr/issues/139\n //\n // - This check is only required when running integration tests.\n // (colyseus.js' usage of msgpackr/buffer is conflicting)\n //\n if (process.env.NODE_ENV !== \"production\") {\n packr.useBuffer(packr.buffer);\n }\n\n // pack message into the same packr.buffer\n const endOfBufferOffset = packr.pack(message, 2048 + it.offset).byteLength;\n // 2048 = RESERVE_START_SPACE\n return packr.buffer.subarray(0, endOfBufferOffset);\n\n } else if (rawMessage !== undefined) {\n\n // copy raw message into packr.buffer\n packr.buffer.set(rawMessage, it.offset);\n return packr.buffer.subarray(0, it.offset + rawMessage.byteLength);\n\n } else {\n return packr.buffer.subarray(0, it.offset);\n }\n },\n\n};\n\n"],
5
+ "mappings": "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",
6
6
  "names": ["Protocol", "ErrorCode", "IpcProtocol"]
7
7
  }
@@ -34,47 +34,52 @@ var IpcProtocol = /* @__PURE__ */ ((IpcProtocol2) => {
34
34
  IpcProtocol2[IpcProtocol2["TIMEOUT"] = 2] = "TIMEOUT";
35
35
  return IpcProtocol2;
36
36
  })(IpcProtocol || {});
37
- const sendBuffer = Buffer.allocUnsafe(8192);
38
37
  const packr = new Packr();
39
- packr.useBuffer(sendBuffer);
38
+ packr.encode(void 0);
40
39
  const getMessageBytes = {
41
40
  [10 /* JOIN_ROOM */]: (reconnectionToken, serializerId, handshake) => {
42
41
  const it = { offset: 1 };
43
- sendBuffer[0] = 10 /* JOIN_ROOM */;
44
- sendBuffer[it.offset++] = Buffer.byteLength(reconnectionToken, "utf8");
45
- encode.utf8Write(sendBuffer, reconnectionToken, it);
46
- sendBuffer[it.offset++] = Buffer.byteLength(serializerId, "utf8");
47
- encode.utf8Write(sendBuffer, serializerId, it);
48
- handshake.copy(sendBuffer, it.offset, 0, handshake.byteLength);
49
- return sendBuffer.subarray(0, it.offset + handshake.byteLength);
42
+ packr.buffer[0] = 10 /* JOIN_ROOM */;
43
+ packr.buffer[it.offset++] = Buffer.byteLength(reconnectionToken, "utf8");
44
+ encode.utf8Write(packr.buffer, reconnectionToken, it);
45
+ packr.buffer[it.offset++] = Buffer.byteLength(serializerId, "utf8");
46
+ encode.utf8Write(packr.buffer, serializerId, it);
47
+ let handshakeLength = handshake?.byteLength || 0;
48
+ if (handshakeLength > 0) {
49
+ handshake.copy(packr.buffer, it.offset, 0, handshakeLength);
50
+ }
51
+ return packr.buffer.subarray(0, it.offset + handshakeLength);
50
52
  },
51
53
  [11 /* ERROR */]: (code, message = "") => {
52
54
  const it = { offset: 1 };
53
- sendBuffer[0] = 11 /* ERROR */;
54
- encode.number(sendBuffer, code, it);
55
- encode.string(sendBuffer, message, it);
56
- return sendBuffer.subarray(0, it.offset);
55
+ packr.buffer[0] = 11 /* ERROR */;
56
+ encode.number(packr.buffer, code, it);
57
+ encode.string(packr.buffer, message, it);
58
+ return packr.buffer.subarray(0, it.offset);
57
59
  },
58
60
  [14 /* ROOM_STATE */]: (bytes) => {
59
61
  return [14 /* ROOM_STATE */, ...bytes];
60
62
  },
61
63
  raw: (code, type, message, rawMessage) => {
62
64
  const it = { offset: 1 };
63
- sendBuffer[0] = code;
65
+ packr.buffer[0] = code;
64
66
  if (typeof type === "string") {
65
- encode.string(sendBuffer, type, it);
67
+ encode.string(packr.buffer, type, it);
66
68
  } else {
67
- encode.number(sendBuffer, type, it);
69
+ encode.number(packr.buffer, type, it);
68
70
  }
69
71
  if (message !== void 0) {
70
72
  packr.position = 0;
73
+ if (process.env.NODE_ENV !== "production") {
74
+ packr.useBuffer(packr.buffer);
75
+ }
71
76
  const endOfBufferOffset = packr.pack(message, 2048 + it.offset).byteLength;
72
- return sendBuffer.subarray(0, endOfBufferOffset);
77
+ return packr.buffer.subarray(0, endOfBufferOffset);
73
78
  } else if (rawMessage !== void 0) {
74
- sendBuffer.set(rawMessage, it.offset);
75
- return sendBuffer.subarray(0, it.offset + rawMessage.byteLength);
79
+ packr.buffer.set(rawMessage, it.offset);
80
+ return packr.buffer.subarray(0, it.offset + rawMessage.byteLength);
76
81
  } else {
77
- return sendBuffer.subarray(0, it.offset);
82
+ return packr.buffer.subarray(0, it.offset);
78
83
  }
79
84
  }
80
85
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/Protocol.ts"],
4
- "sourcesContent": ["import { pack, Packr } from '@colyseus/msgpackr';\nimport { encode, Iterator } from '@colyseus/schema';\n\n// Colyseus protocol codes range between 0~100\nexport enum Protocol {\n // Room-related (10~19)\n JOIN_ROOM = 10,\n ERROR = 11,\n LEAVE_ROOM = 12,\n ROOM_DATA = 13,\n ROOM_STATE = 14,\n ROOM_STATE_PATCH = 15,\n // ROOM_DATA_SCHEMA = 16, // DEPRECATED: used to send schema instances via room.send()\n ROOM_DATA_BYTES = 17,\n\n // WebSocket close codes (https://github.com/Luka967/websocket-close-codes)\n WS_CLOSE_NORMAL = 1000,\n WS_CLOSE_GOING_AWAY = 1001,\n\n // WebSocket error codes\n WS_CLOSE_CONSENTED = 4000,\n WS_CLOSE_WITH_ERROR = 4002,\n WS_CLOSE_DEVMODE_RESTART = 4010,\n\n WS_SERVER_DISCONNECT = 4201,\n WS_TOO_MANY_CLIENTS = 4202,\n}\n\nexport enum ErrorCode {\n // MatchMaking Error Codes\n MATCHMAKE_NO_HANDLER = 4210,\n MATCHMAKE_INVALID_CRITERIA = 4211,\n MATCHMAKE_INVALID_ROOM_ID = 4212,\n MATCHMAKE_UNHANDLED = 4213, // generic exception during onCreate/onJoin\n MATCHMAKE_EXPIRED = 4214, // generic exception during onCreate/onJoin\n\n AUTH_FAILED = 4215,\n APPLICATION_ERROR = 4216,\n\n INVALID_PAYLOAD = 4217,\n}\n\n// Inter-process communication protocol\nexport enum IpcProtocol {\n SUCCESS = 0,\n ERROR = 1,\n TIMEOUT = 2,\n}\n\nconst sendBuffer = Buffer.allocUnsafe(8192);\n\nconst packr = new Packr();\npackr.useBuffer(sendBuffer);\n\nexport const getMessageBytes = {\n [Protocol.JOIN_ROOM]: (reconnectionToken: string, serializerId: string, handshake?: Buffer) => {\n const it: Iterator = { offset: 1 };\n sendBuffer[0] = Protocol.JOIN_ROOM;\n\n sendBuffer[it.offset++] = Buffer.byteLength(reconnectionToken, \"utf8\");\n encode.utf8Write(sendBuffer, reconnectionToken, it);\n\n sendBuffer[it.offset++] = Buffer.byteLength(serializerId, \"utf8\");\n encode.utf8Write(sendBuffer, serializerId, it);\n\n handshake.copy(sendBuffer, it.offset, 0, handshake.byteLength);\n return sendBuffer.subarray(0, it.offset + handshake.byteLength);\n },\n\n [Protocol.ERROR]: (code: number, message: string = '') => {\n const it: Iterator = { offset: 1 };\n sendBuffer[0] = Protocol.ERROR;\n\n encode.number(sendBuffer, code, it);\n encode.string(sendBuffer, message, it);\n\n return sendBuffer.subarray(0, it.offset);\n },\n\n [Protocol.ROOM_STATE]: (bytes: number[]) => {\n return [Protocol.ROOM_STATE, ...bytes];\n },\n\n raw: (code: Protocol, type: string | number, message?: any, rawMessage?: Uint8Array | Buffer) => {\n\n const it: Iterator = { offset: 1 };\n sendBuffer[0] = code;\n\n if (typeof (type) === 'string') {\n encode.string(sendBuffer, type as string, it);\n\n } else {\n encode.number(sendBuffer, type, it);\n }\n\n if (message !== undefined) {\n // pack message into the same sendBuffer\n packr.position = 0; // force to encode from the beginning\n const endOfBufferOffset = packr.pack(message, 2048 + it.offset).byteLength;\n // 2048 = RESERVE_START_SPACE\n return sendBuffer.subarray(0, endOfBufferOffset);\n\n } else if (rawMessage !== undefined) {\n\n // copy raw message into sendBuffer\n sendBuffer.set(rawMessage, it.offset);\n return sendBuffer.subarray(0, it.offset + rawMessage.byteLength);\n\n } else {\n return sendBuffer.subarray(0, it.offset);\n }\n },\n\n};\n\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { pack, Packr } from '@colyseus/msgpackr';\nimport { encode, Iterator } from '@colyseus/schema';\n\n// Colyseus protocol codes range between 0~100\nexport enum Protocol {\n // Room-related (10~19)\n JOIN_ROOM = 10,\n ERROR = 11,\n LEAVE_ROOM = 12,\n ROOM_DATA = 13,\n ROOM_STATE = 14,\n ROOM_STATE_PATCH = 15,\n // ROOM_DATA_SCHEMA = 16, // DEPRECATED: used to send schema instances via room.send()\n ROOM_DATA_BYTES = 17,\n\n // WebSocket close codes (https://github.com/Luka967/websocket-close-codes)\n WS_CLOSE_NORMAL = 1000,\n WS_CLOSE_GOING_AWAY = 1001,\n\n // WebSocket error codes\n WS_CLOSE_CONSENTED = 4000,\n WS_CLOSE_WITH_ERROR = 4002,\n WS_CLOSE_DEVMODE_RESTART = 4010,\n\n WS_SERVER_DISCONNECT = 4201,\n WS_TOO_MANY_CLIENTS = 4202,\n}\n\nexport enum ErrorCode {\n // MatchMaking Error Codes\n MATCHMAKE_NO_HANDLER = 4210,\n MATCHMAKE_INVALID_CRITERIA = 4211,\n MATCHMAKE_INVALID_ROOM_ID = 4212,\n MATCHMAKE_UNHANDLED = 4213, // generic exception during onCreate/onJoin\n MATCHMAKE_EXPIRED = 4214, // generic exception during onCreate/onJoin\n\n AUTH_FAILED = 4215,\n APPLICATION_ERROR = 4216,\n\n INVALID_PAYLOAD = 4217,\n}\n\n// Inter-process communication protocol\nexport enum IpcProtocol {\n SUCCESS = 0,\n ERROR = 1,\n TIMEOUT = 2,\n}\n\n\nconst packr = new Packr();\n\n// msgpackr workaround: initialize buffer\npackr.encode(undefined);\n\nexport const getMessageBytes = {\n [Protocol.JOIN_ROOM]: (reconnectionToken: string, serializerId: string, handshake?: Buffer) => {\n const it: Iterator = { offset: 1 };\n packr.buffer[0] = Protocol.JOIN_ROOM;\n\n packr.buffer[it.offset++] = Buffer.byteLength(reconnectionToken, \"utf8\");\n encode.utf8Write(packr.buffer, reconnectionToken, it);\n\n packr.buffer[it.offset++] = Buffer.byteLength(serializerId, \"utf8\");\n encode.utf8Write(packr.buffer, serializerId, it);\n\n let handshakeLength = handshake?.byteLength || 0;\n if (handshakeLength > 0) {\n handshake.copy(packr.buffer, it.offset, 0, handshakeLength);\n }\n\n return packr.buffer.subarray(0, it.offset + handshakeLength);\n },\n\n [Protocol.ERROR]: (code: number, message: string = '') => {\n const it: Iterator = { offset: 1 };\n packr.buffer[0] = Protocol.ERROR;\n\n encode.number(packr.buffer, code, it);\n encode.string(packr.buffer, message, it);\n\n return packr.buffer.subarray(0, it.offset);\n },\n\n [Protocol.ROOM_STATE]: (bytes: number[]) => {\n return [Protocol.ROOM_STATE, ...bytes];\n },\n\n raw: (code: Protocol, type: string | number, message?: any, rawMessage?: Uint8Array | Buffer) => {\n const it: Iterator = { offset: 1 };\n packr.buffer[0] = code;\n\n if (typeof (type) === 'string') {\n encode.string(packr.buffer, type as string, it);\n\n } else {\n encode.number(packr.buffer, type, it);\n }\n\n if (message !== undefined) {\n // force to encode from offset\n packr.position = 0;\n\n //\n // TODO: remove this after issue is fixed https://github.com/kriszyp/msgpackr/issues/139\n //\n // - This check is only required when running integration tests.\n // (colyseus.js' usage of msgpackr/buffer is conflicting)\n //\n if (process.env.NODE_ENV !== \"production\") {\n packr.useBuffer(packr.buffer);\n }\n\n // pack message into the same packr.buffer\n const endOfBufferOffset = packr.pack(message, 2048 + it.offset).byteLength;\n // 2048 = RESERVE_START_SPACE\n return packr.buffer.subarray(0, endOfBufferOffset);\n\n } else if (rawMessage !== undefined) {\n\n // copy raw message into packr.buffer\n packr.buffer.set(rawMessage, it.offset);\n return packr.buffer.subarray(0, it.offset + rawMessage.byteLength);\n\n } else {\n return packr.buffer.subarray(0, it.offset);\n }\n },\n\n};\n\n"],
5
+ "mappings": "AAAA,SAAe,aAAa;AAC5B,SAAS,cAAwB;AAG1B,IAAK,WAAL,kBAAKA,cAAL;AAEL,EAAAA,oBAAA,eAAY,MAAZ;AACA,EAAAA,oBAAA,WAAQ,MAAR;AACA,EAAAA,oBAAA,gBAAa,MAAb;AACA,EAAAA,oBAAA,eAAY,MAAZ;AACA,EAAAA,oBAAA,gBAAa,MAAb;AACA,EAAAA,oBAAA,sBAAmB,MAAnB;AAEA,EAAAA,oBAAA,qBAAkB,MAAlB;AAGA,EAAAA,oBAAA,qBAAkB,OAAlB;AACA,EAAAA,oBAAA,yBAAsB,QAAtB;AAGA,EAAAA,oBAAA,wBAAqB,OAArB;AACA,EAAAA,oBAAA,yBAAsB,QAAtB;AACA,EAAAA,oBAAA,8BAA2B,QAA3B;AAEA,EAAAA,oBAAA,0BAAuB,QAAvB;AACA,EAAAA,oBAAA,yBAAsB,QAAtB;AArBU,SAAAA;AAAA,GAAA;AAwBL,IAAK,YAAL,kBAAKC,eAAL;AAEL,EAAAA,sBAAA,0BAAuB,QAAvB;AACA,EAAAA,sBAAA,gCAA6B,QAA7B;AACA,EAAAA,sBAAA,+BAA4B,QAA5B;AACA,EAAAA,sBAAA,yBAAsB,QAAtB;AACA,EAAAA,sBAAA,uBAAoB,QAApB;AAEA,EAAAA,sBAAA,iBAAc,QAAd;AACA,EAAAA,sBAAA,uBAAoB,QAApB;AAEA,EAAAA,sBAAA,qBAAkB,QAAlB;AAXU,SAAAA;AAAA,GAAA;AAeL,IAAK,cAAL,kBAAKC,iBAAL;AACL,EAAAA,0BAAA,aAAU,KAAV;AACA,EAAAA,0BAAA,WAAQ,KAAR;AACA,EAAAA,0BAAA,aAAU,KAAV;AAHU,SAAAA;AAAA,GAAA;AAOZ,MAAM,QAAQ,IAAI,MAAM;AAGxB,MAAM,OAAO,MAAS;AAEf,MAAM,kBAAkB;AAAA,EAC7B,CAAC,qBAAqB,CAAC,mBAA2B,cAAsB,cAAuB;AAC7F,UAAM,KAAe,EAAE,QAAQ,EAAE;AACjC,UAAM,OAAO,KAAK;AAElB,UAAM,OAAO,GAAG,YAAY,OAAO,WAAW,mBAAmB,MAAM;AACvE,WAAO,UAAU,MAAM,QAAQ,mBAAmB,EAAE;AAEpD,UAAM,OAAO,GAAG,YAAY,OAAO,WAAW,cAAc,MAAM;AAClE,WAAO,UAAU,MAAM,QAAQ,cAAc,EAAE;AAE/C,QAAI,kBAAkB,WAAW,cAAc;AAC/C,QAAI,kBAAkB,GAAG;AACvB,gBAAU,KAAK,MAAM,QAAQ,GAAG,QAAQ,GAAG,eAAe;AAAA,IAC5D;AAEA,WAAO,MAAM,OAAO,SAAS,GAAG,GAAG,SAAS,eAAe;AAAA,EAC7D;AAAA,EAEA,CAAC,iBAAiB,CAAC,MAAc,UAAkB,OAAO;AACxD,UAAM,KAAe,EAAE,QAAQ,EAAE;AACjC,UAAM,OAAO,KAAK;AAElB,WAAO,OAAO,MAAM,QAAQ,MAAM,EAAE;AACpC,WAAO,OAAO,MAAM,QAAQ,SAAS,EAAE;AAEvC,WAAO,MAAM,OAAO,SAAS,GAAG,GAAG,MAAM;AAAA,EAC3C;AAAA,EAEA,CAAC,sBAAsB,CAAC,UAAoB;AAC1C,WAAO,CAAC,qBAAqB,GAAG,KAAK;AAAA,EACvC;AAAA,EAEA,KAAK,CAAC,MAAgB,MAAuB,SAAe,eAAqC;AAC/F,UAAM,KAAe,EAAE,QAAQ,EAAE;AACjC,UAAM,OAAO,KAAK;AAElB,QAAI,OAAQ,SAAU,UAAU;AAC9B,aAAO,OAAO,MAAM,QAAQ,MAAgB,EAAE;AAAA,IAEhD,OAAO;AACL,aAAO,OAAO,MAAM,QAAQ,MAAM,EAAE;AAAA,IACtC;AAEA,QAAI,YAAY,QAAW;AAEzB,YAAM,WAAW;AAQjB,UAAI,QAAQ,IAAI,aAAa,cAAc;AACzC,cAAM,UAAU,MAAM,MAAM;AAAA,MAC9B;AAGA,YAAM,oBAAoB,MAAM,KAAK,SAAS,OAAO,GAAG,MAAM,EAAE;AAEhE,aAAO,MAAM,OAAO,SAAS,GAAG,iBAAiB;AAAA,IAEnD,WAAW,eAAe,QAAW;AAGnC,YAAM,OAAO,IAAI,YAAY,GAAG,MAAM;AACtC,aAAO,MAAM,OAAO,SAAS,GAAG,GAAG,SAAS,WAAW,UAAU;AAAA,IAEnE,OAAO;AACL,aAAO,MAAM,OAAO,SAAS,GAAG,GAAG,MAAM;AAAA,IAC3C;AAAA,EACF;AAEF;",
6
6
  "names": ["Protocol", "ErrorCode", "IpcProtocol"]
7
7
  }
package/build/Room.d.ts CHANGED
@@ -1,14 +1,10 @@
1
- /// <reference types="node" />
2
- /// <reference types="node" />
3
- /// <reference types="node" />
4
1
  import http, { IncomingMessage } from 'http';
5
- import { Schema } from '@colyseus/schema';
6
2
  import Clock from '@gamestdio/timer';
7
3
  import { EventEmitter } from 'events';
8
4
  import { Presence } from './presence/Presence';
9
5
  import { Serializer } from './serializer/Serializer';
10
6
  import { Deferred } from './utils/Utils';
11
- import { RoomListingData } from './matchmaker/driver';
7
+ import { RoomCache } from './matchmaker/driver/local/LocalDriver';
12
8
  import { Client, ClientArray, ClientPrivate, ISendOptions } from './Transport';
13
9
  export declare const DEFAULT_SEAT_RESERVATION_TIME: number;
14
10
  export type SimulationCallback = (deltaTime: number) => void;
@@ -38,7 +34,7 @@ export declare abstract class Room<State extends object = any, Metadata = any, U
38
34
  */
39
35
  get locked(): boolean;
40
36
  get metadata(): Metadata;
41
- listing: RoomListingData<Metadata>;
37
+ listing: RoomCache<Metadata>;
42
38
  /**
43
39
  * Timing events tied to the room instance.
44
40
  * Intervals and timeouts are cleared when the room is disposed.
@@ -50,6 +46,12 @@ export declare abstract class Room<State extends object = any, Metadata = any, U
50
46
  * the room will be unlocked as soon as a client disconnects from it.
51
47
  */
52
48
  maxClients: number;
49
+ /**
50
+ * Automatically dispose the room when last client disconnects.
51
+ *
52
+ * @default true
53
+ */
54
+ autoDispose: boolean;
53
55
  /**
54
56
  * Frequency to send the room state to connected clients, in milliseconds.
55
57
  *
@@ -76,7 +78,7 @@ export declare abstract class Room<State extends object = any, Metadata = any, U
76
78
  _events: EventEmitter<[never]>;
77
79
  protected seatReservationTime: number;
78
80
  protected reservedSeats: {
79
- [sessionId: string]: [any, any];
81
+ [sessionId: string]: [any, any, boolean?, boolean?];
80
82
  };
81
83
  protected reservedSeatTimeouts: {
82
84
  [sessionId: string]: NodeJS.Timeout;
@@ -89,20 +91,13 @@ export declare abstract class Room<State extends object = any, Metadata = any, U
89
91
  private _serializer;
90
92
  private _afterNextPatchQueue;
91
93
  private _simulationInterval;
92
- private _patchInterval;
93
94
  private _internalState;
94
95
  private _locked;
95
96
  private _lockedExplicitly;
96
97
  private _maxClientsReached;
97
98
  private _autoDisposeTimeout;
98
99
  constructor();
99
- /**
100
- * Automatically dispose the room when last client disconnects.
101
- *
102
- * @default true
103
- */
104
- get autoDispose(): boolean;
105
- set autoDispose(value: boolean);
100
+ protected __init(): void;
106
101
  /**
107
102
  * The name of the room you provided as first argument for `gameServer.define()`.
108
103
  *
@@ -184,6 +179,9 @@ export declare abstract class Room<State extends object = any, Metadata = any, U
184
179
  * @param delay - Interval delay on executing `onTickCallback` in milliseconds.
185
180
  */
186
181
  setSimulationInterval(onTickCallback?: SimulationCallback, delay?: number): void;
182
+ /**
183
+ * @deprecated Use `.patchRate=` instead.
184
+ */
187
185
  setPatchRate(milliseconds: number | null): void;
188
186
  setState(newState: State): void;
189
187
  setSerializer(serializer: Serializer<State>): void;
@@ -198,15 +196,17 @@ export declare abstract class Room<State extends object = any, Metadata = any, U
198
196
  */
199
197
  unlock(): Promise<void>;
200
198
  send(client: Client, type: string | number, message: any, options?: ISendOptions): void;
201
- send(client: Client, message: Schema, options?: ISendOptions): void;
202
- broadcast(type: string | number, message?: any, options?: IBroadcastOptions): any;
203
- broadcast<T extends Schema>(message: T, options?: IBroadcastOptions): any;
199
+ broadcast(type: string | number, message?: any, options?: IBroadcastOptions): void;
200
+ /**
201
+ * Broadcast bytes (UInt8Arrays) to a particular room
202
+ */
203
+ broadcastBytes(type: string | number, message: Uint8Array, options: IBroadcastOptions): void;
204
204
  /**
205
205
  * Checks whether mutations have occurred in the state, and broadcast them to all connected clients.
206
206
  */
207
207
  broadcastPatch(): boolean;
208
208
  onMessage<T = any>(messageType: '*', callback: (client: Client<UserData, AuthData>, type: string | number, message: T) => void): any;
209
- onMessage<T = any>(messageType: string | number, callback: (client: Client<UserData, AuthData>, message: T) => void): any;
209
+ onMessage<T = any>(messageType: string | number, callback: (client: Client<UserData, AuthData>, message: T) => void, validate?: (message: unknown) => T): any;
210
210
  /**
211
211
  * Disconnect all connected clients, and then dispose the room.
212
212
  *
@@ -228,14 +228,15 @@ export declare abstract class Room<State extends object = any, Metadata = any, U
228
228
  allowReconnection(previousClient: Client, seconds: number | "manual"): Deferred<Client>;
229
229
  protected resetAutoDisposeTimeout(timeoutInSeconds?: number): void;
230
230
  private broadcastMessageType;
231
- private sendFullState;
232
- private _dequeueAfterPatchMessages;
233
- private _reserveSeat;
234
- private _disposeIfEmpty;
235
- private _dispose;
236
- private _onMessage;
237
- private _forciblyCloseClient;
238
- private _onLeave;
239
- private _incrementClientCount;
240
- private _decrementClientCount;
231
+ protected sendFullState(client: Client): void;
232
+ protected _dequeueAfterPatchMessages(): void;
233
+ protected _reserveSeat(sessionId: string, joinOptions?: any, authData?: any, seconds?: number, allowReconnection?: boolean, devModeReconnection?: boolean): Promise<boolean>;
234
+ protected _disposeIfEmpty(): boolean;
235
+ protected _dispose(): Promise<any>;
236
+ protected _onMessage(client: Client & ClientPrivate, buffer: Buffer): void;
237
+ protected _forciblyCloseClient(client: Client & ClientPrivate, closeCode: number): void;
238
+ protected _onLeave(client: Client, code?: number): Promise<any>;
239
+ protected _onAfterLeave(client: Client): Promise<void>;
240
+ protected _incrementClientCount(): Promise<void>;
241
+ protected _decrementClientCount(): Promise<boolean>;
241
242
  }