@colyseus/core 0.18.3 → 0.18.5

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 (136) hide show
  1. package/build/Debug.cjs +1 -0
  2. package/build/Debug.cjs.map +1 -1
  3. package/build/IPC.cjs +1 -0
  4. package/build/IPC.cjs.map +1 -1
  5. package/build/Logger.cjs +1 -0
  6. package/build/Logger.cjs.map +1 -1
  7. package/build/MatchMaker.cjs +13 -5
  8. package/build/MatchMaker.cjs.map +2 -2
  9. package/build/MatchMaker.d.ts +10 -4
  10. package/build/MatchMaker.mjs +12 -5
  11. package/build/MatchMaker.mjs.map +2 -2
  12. package/build/Protocol.cjs +1 -0
  13. package/build/Protocol.cjs.map +1 -1
  14. package/build/Rewind.cjs +1 -0
  15. package/build/Rewind.cjs.map +1 -1
  16. package/build/Room.cjs +113 -3
  17. package/build/Room.cjs.map +2 -2
  18. package/build/Room.d.ts +58 -1
  19. package/build/Room.mjs +112 -3
  20. package/build/Room.mjs.map +2 -2
  21. package/build/RoomMessages.cjs +3 -1
  22. package/build/RoomMessages.cjs.map +2 -2
  23. package/build/RoomMessages.d.ts +3 -1
  24. package/build/RoomMessages.mjs +2 -1
  25. package/build/RoomMessages.mjs.map +2 -2
  26. package/build/RoomPlugin.cjs +1 -0
  27. package/build/RoomPlugin.cjs.map +1 -1
  28. package/build/Server.cjs +1 -0
  29. package/build/Server.cjs.map +1 -1
  30. package/build/Stats.cjs +1 -0
  31. package/build/Stats.cjs.map +1 -1
  32. package/build/Transport.cjs +39 -0
  33. package/build/Transport.cjs.map +2 -2
  34. package/build/Transport.d.ts +66 -1
  35. package/build/Transport.mjs +37 -1
  36. package/build/Transport.mjs.map +2 -2
  37. package/build/errors/RoomExceptions.cjs +1 -0
  38. package/build/errors/RoomExceptions.cjs.map +1 -1
  39. package/build/errors/SeatReservationError.cjs +1 -0
  40. package/build/errors/SeatReservationError.cjs.map +1 -1
  41. package/build/errors/ServerError.cjs +1 -0
  42. package/build/errors/ServerError.cjs.map +1 -1
  43. package/build/index.cjs +5 -0
  44. package/build/index.cjs.map +2 -2
  45. package/build/index.d.ts +1 -1
  46. package/build/index.mjs +3 -1
  47. package/build/index.mjs.map +2 -2
  48. package/build/input/InputBuffer.cjs +1 -0
  49. package/build/input/InputBuffer.cjs.map +1 -1
  50. package/build/input/RoomInput.cjs +64 -3
  51. package/build/input/RoomInput.cjs.map +2 -2
  52. package/build/input/RoomInput.d.ts +16 -2
  53. package/build/input/RoomInput.mjs +63 -3
  54. package/build/input/RoomInput.mjs.map +2 -2
  55. package/build/input/types.cjs +1 -0
  56. package/build/input/types.cjs.map +1 -1
  57. package/build/internal.cjs +1 -0
  58. package/build/internal.cjs.map +1 -1
  59. package/build/matchmaker/Lobby.cjs +1 -0
  60. package/build/matchmaker/Lobby.cjs.map +1 -1
  61. package/build/matchmaker/LocalDriver/LocalDriver.cjs +1 -0
  62. package/build/matchmaker/LocalDriver/LocalDriver.cjs.map +1 -1
  63. package/build/matchmaker/LocalDriver/Query.cjs +1 -0
  64. package/build/matchmaker/LocalDriver/Query.cjs.map +1 -1
  65. package/build/matchmaker/RegisteredHandler.cjs +1 -0
  66. package/build/matchmaker/RegisteredHandler.cjs.map +1 -1
  67. package/build/matchmaker/controller.cjs +1 -0
  68. package/build/matchmaker/controller.cjs.map +1 -1
  69. package/build/matchmaker/driver.cjs +1 -0
  70. package/build/matchmaker/driver.cjs.map +2 -2
  71. package/build/matchmaker/driver.d.ts +4 -2
  72. package/build/matchmaker/driver.mjs.map +1 -1
  73. package/build/presence/LocalPresence.cjs +1 -0
  74. package/build/presence/LocalPresence.cjs.map +1 -1
  75. package/build/presence/Presence.cjs +1 -0
  76. package/build/presence/Presence.cjs.map +1 -1
  77. package/build/rooms/LobbyRoom.cjs +1 -0
  78. package/build/rooms/LobbyRoom.cjs.map +1 -1
  79. package/build/rooms/QueueRoom.cjs +1 -0
  80. package/build/rooms/QueueRoom.cjs.map +1 -1
  81. package/build/rooms/RelayRoom.cjs +1 -0
  82. package/build/rooms/RelayRoom.cjs.map +1 -1
  83. package/build/router/default_routes.cjs +4 -8
  84. package/build/router/default_routes.cjs.map +2 -2
  85. package/build/router/default_routes.mjs +4 -9
  86. package/build/router/default_routes.mjs.map +2 -2
  87. package/build/router/index.cjs +7 -1
  88. package/build/router/index.cjs.map +2 -2
  89. package/build/router/index.mjs +6 -1
  90. package/build/router/index.mjs.map +2 -2
  91. package/build/router/node.cjs +23 -2
  92. package/build/router/node.cjs.map +2 -2
  93. package/build/router/node.mjs +22 -2
  94. package/build/router/node.mjs.map +2 -2
  95. package/build/serializer/NoneSerializer.cjs +1 -0
  96. package/build/serializer/NoneSerializer.cjs.map +1 -1
  97. package/build/serializer/SchemaSerializer.cjs +98 -0
  98. package/build/serializer/SchemaSerializer.cjs.map +2 -2
  99. package/build/serializer/SchemaSerializer.d.ts +25 -0
  100. package/build/serializer/SchemaSerializer.mjs +98 -1
  101. package/build/serializer/SchemaSerializer.mjs.map +2 -2
  102. package/build/serializer/Serializer.cjs +1 -0
  103. package/build/serializer/Serializer.cjs.map +2 -2
  104. package/build/serializer/Serializer.d.ts +15 -0
  105. package/build/utils/DevMode.cjs +1 -0
  106. package/build/utils/DevMode.cjs.map +1 -1
  107. package/build/utils/Env.cjs +1 -0
  108. package/build/utils/Env.cjs.map +1 -1
  109. package/build/utils/StandardSchema.cjs +1 -0
  110. package/build/utils/StandardSchema.cjs.map +1 -1
  111. package/build/utils/UserSessionIndex.cjs +1 -0
  112. package/build/utils/UserSessionIndex.cjs.map +1 -1
  113. package/build/utils/Utils.cjs +1 -0
  114. package/build/utils/Utils.cjs.map +2 -2
  115. package/build/utils/Utils.d.ts +12 -0
  116. package/build/utils/Utils.mjs.map +2 -2
  117. package/build/utils/nanoevents.cjs +5 -1
  118. package/build/utils/nanoevents.cjs.map +2 -2
  119. package/build/utils/nanoevents.d.ts +3 -1
  120. package/build/utils/nanoevents.mjs +4 -1
  121. package/build/utils/nanoevents.mjs.map +2 -2
  122. package/package.json +8 -7
  123. package/src/MatchMaker.ts +53 -15
  124. package/src/Room.ts +145 -4
  125. package/src/RoomMessages.ts +2 -1
  126. package/src/Transport.ts +132 -4
  127. package/src/index.ts +1 -1
  128. package/src/input/RoomInput.ts +76 -3
  129. package/src/matchmaker/driver.ts +4 -2
  130. package/src/router/default_routes.ts +4 -9
  131. package/src/router/index.ts +7 -0
  132. package/src/router/node.ts +26 -2
  133. package/src/serializer/SchemaSerializer.ts +146 -1
  134. package/src/serializer/Serializer.ts +17 -0
  135. package/src/utils/Utils.ts +18 -0
  136. package/src/utils/nanoevents.ts +4 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/MatchMaker.ts"],
4
- "sourcesContent": ["import { EventEmitter } from 'events';\n\nimport { requestFromIPC, subscribeIPC, subscribeWithTimeout } from './IPC.ts';\n\nimport { type Type, Deferred, generateId, merge, retry, MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME, REMOTE_ROOM_SHORT_TIMEOUT, type MethodName, type ExtractMethodOrPropertyType } from './utils/Utils.ts';\nimport { isDevMode, cacheRoomHistory, getRoomRestoreListKey, reloadFromCache } from './utils/DevMode.ts';\n\nimport { RegisteredHandler } from './matchmaker/RegisteredHandler.ts';\nimport { type OnCreateOptions, Room, RoomInternalState } from './Room.ts';\n\nimport { LocalPresence } from './presence/LocalPresence.ts';\nimport { createScopedPresence, type Presence } from './presence/Presence.ts';\n\nimport { debugAndPrintError, debugMatchMaking } from './Debug.ts';\nimport { SeatReservationError } from './errors/SeatReservationError.ts';\nimport { ServerError } from './errors/ServerError.ts';\n\nimport { type IRoomCache, type MatchMakerDriver, type SortOptions, LocalDriver } from './matchmaker/LocalDriver/LocalDriver.ts';\nimport { controller } from './matchmaker/controller.ts';\nimport * as stats from './Stats.ts';\n\nimport { logger } from './Logger.ts';\nimport type { AuthContext, Client } from './Transport.ts';\nimport { getLockId, initializeRoomCache, type ExtractRoomCacheMetadata } from './matchmaker/driver.ts';\n\nimport { type ISeatReservation, CloseCode, ErrorCode } from '@colyseus/shared-types';\nimport { getDefaultDriver, getDefaultPresence, getDefaultPublicAddress } from './utils/Env.ts';\nexport type { ISeatReservation, ExtractRoomCacheMetadata };\n\nexport { controller, stats, type MatchMakerDriver };\n\nexport type ClientOptions = any;\nexport type SelectProcessIdCallback = (roomName: string, clientOptions: ClientOptions) => Promise<string>;\n\nconst handlers: {[id: string]: RegisteredHandler} = {};\nconst rooms: {[roomId: string]: Room} = {};\nconst events = new EventEmitter();\n\nexport let publicAddress: string;\nexport let processId: string;\nexport let presence: Presence;\nexport let driver: MatchMakerDriver;\n\n/**\n * Function to select the processId to create the room on.\n * By default, returns the process with least amount of rooms created.\n * @returns The processId to create the room on.\n */\nexport let selectProcessIdToCreateRoom: SelectProcessIdCallback = async function () {\n return (await stats.fetchAll())\n .sort((p1, p2) => p1.roomCount > p2.roomCount ? 1 : -1)[0]?.processId || processId;\n};\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 const MatchMakerState = {\n INITIALIZING: 0,\n READY: 1,\n SHUTTING_DOWN: 2,\n} as const;\nexport type MatchMakerState = (typeof MatchMakerState)[keyof typeof MatchMakerState];\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 || await getDefaultPresence();\n driver = _driver || await getDefaultDriver();\n publicAddress = _publicAddress || getDefaultPublicAddress();\n\n stats.reset(false);\n\n // ensure processId is set\n if (!processId) { processId = generateId(); }\n\n /**\n * Override default `selectProcessIdToCreateRoom` function.\n */\n if (_selectProcessIdToCreateRoom) {\n selectProcessIdToCreateRoom = _selectProcessIdToCreateRoom;\n }\n\n // boot driver if necessary (e.g. RedisDriver/PostgresDriver)\n if (driver.boot) {\n await driver.boot();\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(isStandalone: boolean = false) {\n await onReady; // make sure \"processId\" is available\n\n /**\n * Skip setting up IPC and health checks if this process is running as a\n * standalone match-maker. \n * \n * When in \"standalone\" mode, this process will not spawn rooms and will only\n * be responsible for matchmaking.\n */\n if (isStandalone) {\n state = MatchMakerState.READY;\n return; \n }\n\n /**\n * Process-level subscription\n * - handle remote process healthcheck\n * - handle remote room creation\n */\n await subscribeIPC(presence, getProcessChannel(), (method: string, args: any) => {\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 * persist processId every 1 minute\n *\n * FIXME: this is a workaround in case this `processId` gets excluded\n * (`stats.excludeProcess()`) by mistake due to health-check failure\n */\n stats.setAutoPersistInterval();\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 = {}, authContext?: AuthContext) {\n return await retry<Promise<ISeatReservation>>(async () => {\n const authData = await callOnAuth(roomName, clientOptions, authContext);\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 the room is not found or is already locked, try to find a new one\n if (!room || room.locked) {\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\n // Notify waiting concurrent requests about the new room\n presence.publish(`concurrent:${handler.name}:${concurrencyKey}`, room.roomId);\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 = {}, authContext?: AuthContext) {\n const authData = await callOnAuth(roomName, clientOptions, authContext);\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 = {}, authContext?: AuthContext) {\n return await retry<Promise<ISeatReservation>>(async () => {\n const authData = await callOnAuth(roomName, clientOptions, authContext);\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 }, 5, [SeatReservationError]);\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 miss .allowReconnection()?\\n\uD83D\uDC49 https://docs.colyseus.io/room#allow-reconnection`);\n }\n\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room \"${roomId}\" has been disposed.`);\n }\n\n // check for reconnection\n const reconnectionToken = clientOptions.reconnectionToken;\n if (!reconnectionToken) { throw new ServerError(ErrorCode.MATCHMAKE_UNHANDLED, `'reconnectionToken' must be provided for reconnection.`); }\n\n // respond to re-connection!\n const sessionId = await remoteRoomCall(room.roomId, 'checkReconnectionToken', [reconnectionToken]);\n if (sessionId) {\n return buildSeatReservation(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 miss .allowReconnection()?\\n\uD83D\uDC49 https://docs.colyseus.io/room#allow-reconnection`);\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 authContext - Optional authentication token\n *\n * @returns Promise<SeatReservation> - A promise which contains `sessionId` and `IRoomCache`.\n */\nexport async function joinById(roomId: string, clientOptions: ClientOptions = {}, authContext?: AuthContext) {\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, clientOptions, authContext);\n\n return reserveSeatFor(room, clientOptions, authData);\n}\n\n/**\n * Perform a query for all cached rooms\n */\nexport async function query<T extends Room = any>(\n conditions: Partial<IRoomCache & ExtractRoomCacheMetadata<T>> = {},\n sortOptions?: SortOptions,\n) {\n return await driver.query<T>(conditions, sortOptions);\n}\n\n/**\n * Batch-resolve room caches by roomId in a single backend round\n * trip. Returns a Map keyed by roomId; missing roomIds are absent.\n *\n * Hot paths (per-join uniqueness checks, by-user reverse-index\n * lookups) reach for this instead of `query({})` \u2014 the latter scans\n * the whole room cache and grows linearly with cluster size, while\n * `findRoomsByIds` is O(K) in the caller's input.\n */\nexport async function findRoomsByIds(roomIds: string[]): Promise<Map<string, IRoomCache>> {\n if (roomIds.length === 0) { return new Map(); }\n return await driver.findByIds(roomIds);\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<TRoom = Room>(\n roomId: string,\n method: keyof TRoom,\n args?: any[],\n rejectionTimeout = REMOTE_ROOM_SHORT_TIMEOUT,\n): Promise<ExtractMethodOrPropertyType<TRoom, typeof method>> {\n const room = rooms[roomId] as TRoom;\n\n if (!room) {\n try {\n return await requestFromIPC(presence, getRoomChannel(roomId), method as string, args, rejectionTimeout);\n\n } catch (e: any) {\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 = `${String(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 as string]\n : (await room[method as string].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?: OnCreateOptions<T>,\n): RegisteredHandler<InstanceType<T>> {\n const registeredHandler = new RegisteredHandler(klass, defaultOptions) as unknown as RegisteredHandler<InstanceType<T>>;\n registeredHandler.name = roomName;\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 addRoomType(handler: RegisteredHandler) {\n handlers[handler.name] = handler;\n}\n\nexport function removeRoomType(roomName: string) {\n delete handlers[roomName];\n}\n\nexport function getAllHandlers() {\n return handlers;\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\n if (isDevMode && processId === undefined) {\n //\n // WORKAROUND: wait for processId to be available\n // TODO: Remove this check on 1.0\n //\n // - This is a workaround when using matchMaker.createRoom() before the processId is available.\n // - We need to use top-level await to retrieve processId\n //\n await onReady;\n return createRoom(roomName, clientOptions);\n\n } else {\n throw new ServerError(ErrorCode.MATCHMAKE_UNHANDLED, `no processId available to create room ${roomName}`);\n }\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: any) {\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: Room = new handler.klass();\n\n // set room public attributes\n if (restoringRoomId && isDevMode) {\n // an ungraceful exit may have left a stale cache row for this roomId\n await driver.remove(restoringRoomId);\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 = createScopedPresence(room, presence);\n\n // initialize a RoomCache instance\n room['_listing'] = initializeRoomCache({\n name: roomName,\n processId,\n ...handler.getMetadataFromOptions(clientOptions)\n });\n\n // assign public host\n if (publicAddress) {\n room['_listing'].publicAddress = publicAddress;\n }\n\n if (room.onCreate) {\n try {\n await room.onCreate(merge({}, clientOptions, handler.options));\n\n } catch (e: any) {\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('creating room \\'%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(undefined, room));\n room['_events'].on('unlock', unlockRoom.bind(undefined, room));\n room['_events'].on('join', onClientJoinRoom.bind(undefined, room));\n room['_events'].on('leave', onClientLeaveRoom.bind(undefined, room));\n room['_events'].once('dispose', disposeRoom.bind(undefined, roomName, room));\n\n if (handler.realtimeListingEnabled) {\n room['_events'].on('visibility-change', onVisibilityChange.bind(undefined, room));\n room['_events'].on('metadata-change', onMetadataChange.bind(undefined, room));\n }\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('dispose');\n\n if (handler.realtimeListingEnabled) {\n room['_events'].removeAllListeners('visibility-change');\n room['_events'].removeAllListeners('metadata-change');\n }\n\n //\n // emit \"no active rooms\" event when there are no more rooms in this process\n // (used during graceful shutdown)\n //\n if (stats.local.roomCount <= 0) {\n events.emit('no-active-rooms');\n }\n });\n\n // room always start unlocked\n await createRoomReferences(room, true);\n\n // persist room data only if match-making is enabled\n if (state !== MatchMakerState.SHUTTING_DOWN) {\n await driver.persist(room['_listing'], true);\n }\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 promises.push(rooms[roomId].disconnect(closeCode));\n }\n\n return promises;\n}\n\nasync function lockAndDisposeAll(): Promise<any> {\n // remove processId from room count key\n // (stops accepting new rooms on this process)\n await stats.excludeProcess(processId);\n\n // clear auto-persisting stats interval\n if (enableHealthChecks) {\n stats.clearAutoPersistInterval();\n }\n\n const noActiveRooms = new Deferred();\n if (stats.local.roomCount <= 0) {\n // no active rooms to dispose\n noActiveRooms.resolve();\n\n } else {\n // wait for all rooms to be disposed\n // TODO: set generous timeout in case\n events.once('no-active-rooms', () => noActiveRooms.resolve());\n }\n\n // - lock all local rooms to prevent new joins\n // - trigger `onBeforeShutdown()` on each room\n for (const roomId in rooms) {\n if (!rooms.hasOwnProperty(roomId)) {\n continue;\n }\n\n const room = rooms[roomId];\n room.lock();\n\n if (isDevMode) {\n // call default implementation of onBeforeShutdown() in dev mode\n Room.prototype.onBeforeShutdown.call(room);\n\n } else {\n // call custom implementation of onBeforeShutdown() in production\n room.onBeforeShutdown();\n }\n }\n\n await noActiveRooms;\n}\n\nexport async function gracefullyShutdown(): Promise<any> {\n if (state === MatchMakerState.SHUTTING_DOWN) {\n return Promise.reject('already_shutting_down');\n }\n\n debugMatchMaking(`${processId} is shutting down!`);\n\n state = MatchMakerState.SHUTTING_DOWN;\n\n onReady = undefined;\n\n if (isDevMode) {\n // Reject pending allowReconnection() deferreds BEFORE caching.\n // This should trigger a state cleanup via user's onLeave (e.g. players.delete)\n // so the cached state doesn't contain stale player data from clients\n for (const roomId in rooms) {\n if (!rooms.hasOwnProperty(roomId)) { continue; }\n rooms[roomId]['_rejectPendingReconnections']?.(\"devmode_restart\");\n }\n\n // Wait for async onLeave handlers to finish state cleanup.\n await new Promise(resolve => setTimeout(resolve, 50));\n\n await cacheRoomHistory(rooms);\n }\n\n // - lock existing rooms\n // - stop accepting new rooms on this process\n // - wait for all rooms to be disposed\n await lockAndDisposeAll();\n\n // make sure rooms are removed from cache\n await removeRoomsByProcessId(processId);\n\n // unsubscribe from process id channel\n presence.unsubscribe(getProcessChannel());\n\n // make sure all rooms are disposed\n return Promise.all(disconnectAll(\n (isDevMode)\n ? CloseCode.MAY_TRY_RECONNECT\n : CloseCode.SERVER_SHUTDOWN\n ));\n}\n\n/**\n * DO NOT USE THIS IN PRODUCTION. \n * THIS METHOD IS MEANT TO BE USED FOR VITE DEV SERVER ONLY.\n * ---------------------------------------------------------\n *\n * \n * Lightweight HMR reload for dev mode. \n *\n * Unlike gracefullyShutdown() + setup() + accept(), this preserves the\n * matchMaker infrastructure (presence, driver, IPC subscriptions, processId)\n * and only cycles room instances: cache state \u2192 dispose \u2192 restore.\n */\nexport async function hotReload(): Promise<void> {\n state = MatchMakerState.SHUTTING_DOWN;\n\n // Reject pending allowReconnection() deferreds BEFORE caching.\n // Triggers onLeave state cleanup so cached state is clean.\n for (const roomId in rooms) {\n if (!rooms.hasOwnProperty(roomId)) { continue; }\n rooms[roomId]['_rejectPendingReconnections']?.(\"devmode_restart\");\n }\n\n // Wait for async onLeave handlers to finish state cleanup.\n await new Promise(resolve => setTimeout(resolve, 50));\n\n await cacheRoomHistory(rooms);\n\n // Lock all rooms and trigger default onBeforeShutdown (dev mode impl).\n const noActiveRooms = new Deferred();\n if (stats.local.roomCount <= 0) {\n noActiveRooms.resolve();\n } else {\n events.once('no-active-rooms', () => noActiveRooms.resolve());\n }\n\n for (const roomId in rooms) {\n if (!rooms.hasOwnProperty(roomId)) { continue; }\n const room = rooms[roomId];\n room.lock();\n Room.prototype.onBeforeShutdown.call(room);\n }\n\n await noActiveRooms;\n\n // Disconnect all clients \u2014 they will auto-reconnect.\n await Promise.all(disconnectAll(CloseCode.MAY_TRY_RECONNECT));\n\n // Clear driver cache so reloadFromCache() can recreate rooms.\n await removeRoomsByProcessId(processId);\n\n // Restore rooms from cached state.\n state = MatchMakerState.READY;\n await reloadFromCache();\n await stats.persist();\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 = authData?.sessionId || generateId();\n\n let successfulSeatReservation: boolean;\n\n try {\n successfulSeatReservation = await remoteRoomCall<Room>(\n room.roomId,\n '_reserveSeat' as keyof Room,\n [sessionId, options, authData],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n\n } catch (e: any) {\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 return buildSeatReservation(room, sessionId);\n}\n\n/**\n * Reserve multiple seats for clients in a room\n */\nexport async function reserveMultipleSeatsFor(room: IRoomCache, clientsData: Array<{ sessionId: string, options: ClientOptions, auth: any }>) {\n let sessionIds: string[] = [];\n let options: ClientOptions[] = [];\n let authData: any[] = [];\n\n for (const clientData of clientsData) {\n sessionIds.push(clientData.sessionId);\n options.push(clientData.options);\n authData.push(clientData.auth);\n }\n\n debugMatchMaking(\n 'reserving multiple seats. sessionIds: \\'%s\\', roomId: \\'%s\\', processId: \\'%s\\'',\n sessionIds.join(', '), room.roomId, processId,\n );\n\n let successfulSeatReservations: boolean[];\n\n try {\n successfulSeatReservations = await remoteRoomCall<Room>(\n room.roomId,\n '_reserveMultipleSeats' as keyof Room,\n [sessionIds, options, authData],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n\n } catch (e: any) {\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 throw new SeatReservationError(`${room.roomId} is already full.`);\n }\n }\n\n return successfulSeatReservations;\n}\n\n/**\n * Build a seat reservation object.\n * @param room - The room to build a seat reservation for.\n * @param sessionId - The session ID of the client.\n * @returns A seat reservation object.\n */\nexport function buildSeatReservation(room: IRoomCache, sessionId: string) {\n const seatReservation: ISeatReservation = {\n name: room.name,\n sessionId,\n roomId: room.roomId,\n processId: room.processId,\n };\n\n if (isDevMode) {\n seatReservation.devMode = isDevMode;\n }\n\n if (room.publicAddress) {\n seatReservation.publicAddress = room.publicAddress;\n }\n\n return seatReservation;\n}\n\nasync function callOnAuth(roomName: string, clientOptions?: ClientOptions, authContext?: AuthContext) {\n const roomClass = getRoomClass(roomName);\n const onAuth = roomClass?.['onAuth'];\n if (!onAuth) { return; }\n\n // Server-initiated joins (e.g. internal `matchMaker.create()` from\n // bots/tests/cron) skip auth \u2014 there's no transport, no headers, no\n // token. Treat the absence of authContext as \"no auth performed\",\n // which leaves client.auth undefined just like the legacy short-\n // circuit did.\n if (!authContext) { return; }\n\n // Always invoke onAuth when an auth context is present \u2014 even when\n // the room hasn't overridden it. The base `Room.onAuth` returns\n // `true` (or whatever a side-effect import like @colyseus/auth swapped\n // it for), and we use the return value to populate `client.auth` when\n // it's a payload object. Pre-change this call was skipped when the\n // subclass inherited the default, which made it impossible to install\n // a workspace-wide token decoder by patching `Room.onAuth` once.\n const result = await onAuth(authContext.token, clientOptions, authContext);\n\n // Auth-result semantics:\n // false / null / undefined \u2192 AUTH_FAILED\n // true \u2192 succeeded but no payload (don't set client.auth)\n // anything else (object) \u2192 succeeded; the value becomes client.auth\n if (result === false || result === null || result === undefined) {\n throw new ServerError(ErrorCode.AUTH_FAILED, 'onAuth failed');\n }\n return result === true ? undefined : result;\n}\n\n/**\n * Perform health check on all processes\n */\nexport async function healthCheckAllProcesses() {\n const allStats = await stats.fetchAll();\n const activeProcessChannels = (await presence.channels(\"p:*\")).map(c => c.substring(2));\n\n if (allStats.length > 0) {\n await Promise.all(\n allStats\n .filter(stat => (\n stat.processId !== processId && // skip current process\n !activeProcessChannels.includes(stat.processId) // skip if channel is still listening\n ))\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 await stats.excludeProcess(processId);\n\n // clean-up stale room ids \u2014 a dead process never comes back;\n // devMode restore reads 'roomhistory' (presence), never these rows\n await removeRoomsByProcessId(processId);\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 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.hincrbyex(hkey, concurrencyKey, -1, MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME * 2);\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 try {\n const roomId = await subscribeWithTimeout(\n presence,\n `concurrent:${handler.name}:${concurrencyKey}`,\n (MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME +\n (Math.min(concurrency, 3) * 0.2)) * 1000 // convert to milliseconds\n );\n\n return await fulfill(roomId);\n } catch (error) {\n // Ignore ipc_timeout error\n }\n }\n\n return await fulfill();\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\nfunction onMetadataChange(room: Room): void {\n handlers[room.roomName].emit('metadata-change', room);\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 driver.remove(room['_listing'].roomId);\n stats.local.roomCount--;\n\n // decrease amount of rooms this process is handling\n if (state !== MatchMakerState.SHUTTING_DOWN) {\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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",
4
+ "sourcesContent": ["import { EventEmitter } from 'events';\n\nimport { requestFromIPC, subscribeIPC, subscribeWithTimeout } from './IPC.ts';\n\nimport { type Type, Deferred, generateId, merge, retry, MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME, REMOTE_ROOM_SHORT_TIMEOUT, type MethodName, type RemoteRoomCallReturn } from './utils/Utils.ts';\nimport { isDevMode, cacheRoomHistory, getRoomRestoreListKey, reloadFromCache } from './utils/DevMode.ts';\n\nimport { RegisteredHandler } from './matchmaker/RegisteredHandler.ts';\nimport { type OnCreateOptions, Room, RoomInternalState } from './Room.ts';\n\nimport { LocalPresence } from './presence/LocalPresence.ts';\nimport { createScopedPresence, type Presence } from './presence/Presence.ts';\n\nimport { debugAndPrintError, debugMatchMaking } from './Debug.ts';\nimport { SeatReservationError } from './errors/SeatReservationError.ts';\nimport { ServerError } from './errors/ServerError.ts';\n\nimport { type IRoomCache, type MatchMakerDriver, type SortOptions, LocalDriver } from './matchmaker/LocalDriver/LocalDriver.ts';\nimport { controller } from './matchmaker/controller.ts';\nimport * as stats from './Stats.ts';\n\nimport { logger } from './Logger.ts';\nimport type { AuthContext, Client } from './Transport.ts';\nimport { getLockId, initializeRoomCache, type ExtractRoomCacheMetadata } from './matchmaker/driver.ts';\n\nimport { type ISeatReservation, CloseCode, ErrorCode } from '@colyseus/shared-types';\nimport { getDefaultDriver, getDefaultPresence, getDefaultPublicAddress } from './utils/Env.ts';\nexport type { ISeatReservation, ExtractRoomCacheMetadata };\n\nexport { controller, stats, type MatchMakerDriver };\n\nexport type ClientOptions = any;\nexport type SelectProcessIdCallback = (roomName: string, clientOptions: ClientOptions) => Promise<string>;\n\n// null-prototype: keyed by client-supplied room name (colyseus/colyseus#951)\nconst handlers: {[id: string]: RegisteredHandler} = Object.create(null);\nconst rooms: {[roomId: string]: Room} = {};\nconst events = new EventEmitter();\n\n// A room isn't gone when 'dispose' fires \u2014 that only *starts* `Room.#_dispose()`,\n// which awaits the user's async `onDispose()`. `roomCount` is decremented there,\n// so this covers the window until 'disconnect' (emitted once `#_dispose()`\n// settles). Invariant: roomCount + disposingRoomCount = rooms not yet fully gone.\nlet disposingRoomCount = 0;\n\nfunction hasActiveRooms() {\n return stats.local.roomCount > 0 || disposingRoomCount > 0;\n}\n\nexport let publicAddress: string;\nexport let processId: string;\nexport let presence: Presence;\nexport let driver: MatchMakerDriver;\n\n/**\n * Function to select the processId to create the room on.\n * By default, returns the process with least amount of rooms created.\n * @returns The processId to create the room on.\n */\nexport let selectProcessIdToCreateRoom: SelectProcessIdCallback = async function () {\n return (await stats.fetchAll())\n .sort((p1, p2) => p1.roomCount > p2.roomCount ? 1 : -1)[0]?.processId || processId;\n};\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 const MatchMakerState = {\n INITIALIZING: 0,\n READY: 1,\n SHUTTING_DOWN: 2,\n} as const;\nexport type MatchMakerState = (typeof MatchMakerState)[keyof typeof MatchMakerState];\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 || await getDefaultPresence();\n driver = _driver || await getDefaultDriver();\n publicAddress = _publicAddress || getDefaultPublicAddress();\n\n stats.reset(false);\n disposingRoomCount = 0;\n\n // ensure processId is set\n if (!processId) { processId = generateId(); }\n\n /**\n * Override default `selectProcessIdToCreateRoom` function.\n */\n if (_selectProcessIdToCreateRoom) {\n selectProcessIdToCreateRoom = _selectProcessIdToCreateRoom;\n }\n\n // boot driver if necessary (e.g. RedisDriver/PostgresDriver)\n if (driver.boot) {\n await driver.boot();\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(isStandalone: boolean = false) {\n await onReady; // make sure \"processId\" is available\n\n /**\n * Skip setting up IPC and health checks if this process is running as a\n * standalone match-maker. \n * \n * When in \"standalone\" mode, this process will not spawn rooms and will only\n * be responsible for matchmaking.\n */\n if (isStandalone) {\n state = MatchMakerState.READY;\n return; \n }\n\n /**\n * Process-level subscription\n * - handle remote process healthcheck\n * - handle remote room creation\n */\n await subscribeIPC(presence, getProcessChannel(), (method: string, args: any) => {\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 * persist processId every 1 minute\n *\n * FIXME: this is a workaround in case this `processId` gets excluded\n * (`stats.excludeProcess()`) by mistake due to health-check failure\n */\n stats.setAutoPersistInterval();\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 = {}, authContext?: AuthContext) {\n return await retry<Promise<ISeatReservation>>(async () => {\n const authData = await callOnAuth(roomName, clientOptions, authContext);\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 the room is not found or is already locked, try to find a new one\n if (!room || room.locked) {\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\n // Notify waiting concurrent requests about the new room\n presence.publish(`concurrent:${handler.name}:${concurrencyKey}`, room.roomId);\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 = {}, authContext?: AuthContext) {\n const authData = await callOnAuth(roomName, clientOptions, authContext);\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 = {}, authContext?: AuthContext) {\n return await retry<Promise<ISeatReservation>>(async () => {\n const authData = await callOnAuth(roomName, clientOptions, authContext);\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 }, 5, [SeatReservationError]);\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 miss .allowReconnection()?\\n\uD83D\uDC49 https://docs.colyseus.io/room#allow-reconnection`);\n }\n\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room \"${roomId}\" has been disposed.`);\n }\n\n // check for reconnection\n const reconnectionToken = clientOptions.reconnectionToken;\n if (!reconnectionToken) { throw new ServerError(ErrorCode.MATCHMAKE_UNHANDLED, `'reconnectionToken' must be provided for reconnection.`); }\n\n // respond to re-connection!\n const sessionId = await remoteRoomCall(room.roomId, 'checkReconnectionToken', [reconnectionToken]);\n if (sessionId) {\n return buildSeatReservation(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 miss .allowReconnection()?\\n\uD83D\uDC49 https://docs.colyseus.io/room#allow-reconnection`);\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 authContext - Optional authentication token\n *\n * @returns Promise<SeatReservation> - A promise which contains `sessionId` and `IRoomCache`.\n */\nexport async function joinById(roomId: string, clientOptions: ClientOptions = {}, authContext?: AuthContext) {\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, clientOptions, authContext);\n\n return reserveSeatFor(room, clientOptions, authData);\n}\n\n/**\n * Perform a query for all cached rooms\n */\nexport async function query<T extends Room = any>(\n conditions: Partial<IRoomCache & ExtractRoomCacheMetadata<T>> = {},\n sortOptions?: SortOptions,\n) {\n return await driver.query<T>(conditions, sortOptions);\n}\n\n/**\n * Batch-resolve room caches by roomId in a single backend round\n * trip. Returns a Map keyed by roomId; missing roomIds are absent.\n *\n * Hot paths (per-join uniqueness checks, by-user reverse-index\n * lookups) reach for this instead of `query({})` \u2014 the latter scans\n * the whole room cache and grows linearly with cluster size, while\n * `findRoomsByIds` is O(K) in the caller's input.\n */\nexport async function findRoomsByIds(roomIds: string[]): Promise<Map<string, IRoomCache>> {\n if (roomIds.length === 0) { return new Map(); }\n return await driver.findByIds(roomIds);\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 additionalSortOptions - Sorting options, merged over the ones declared on the room handler.\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 * Provide both type arguments for a precisely typed result, e.g.\n * `remoteRoomCall<MyRoom, 'myMethod'>(roomId, 'myMethod')`. With only the\n * room type given, TypeScript cannot infer the method literal\n * (microsoft/TypeScript#26242) and the result is typed `any`.\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 - Returned value from the called or retrieved method/attribute.\n */\nexport function remoteRoomCall<TRoom = Room, TMethod extends keyof TRoom = keyof TRoom>(\n roomId: string,\n method: TMethod,\n args?: any[],\n rejectionTimeout?: number,\n): Promise<RemoteRoomCallReturn<TRoom, TMethod>>;\n\n// fallback for dynamic method names not present on the room type\nexport function remoteRoomCall<TRoom = Room>(\n roomId: string,\n method: keyof TRoom,\n args?: any[],\n rejectionTimeout?: number,\n): Promise<any>;\n\nexport async function remoteRoomCall(\n roomId: string,\n method: any,\n args?: any[],\n rejectionTimeout = REMOTE_ROOM_SHORT_TIMEOUT,\n): Promise<any> {\n const room = rooms[roomId];\n\n if (!room) {\n try {\n return await requestFromIPC(presence, getRoomChannel(roomId), method, args, rejectionTimeout);\n\n } catch (e: any) {\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 as any)[method]) !== 'function')\n ? (room as any)[method]\n : (await (room as any)[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?: OnCreateOptions<T>,\n): RegisteredHandler<InstanceType<T>> {\n const registeredHandler = new RegisteredHandler(klass, defaultOptions) as unknown as RegisteredHandler<InstanceType<T>>;\n registeredHandler.name = roomName;\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 addRoomType(handler: RegisteredHandler) {\n handlers[handler.name] = handler;\n}\n\nexport function removeRoomType(roomName: string) {\n delete handlers[roomName];\n}\n\nexport function getAllHandlers() {\n return handlers;\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\n if (isDevMode && processId === undefined) {\n //\n // WORKAROUND: wait for processId to be available\n // TODO: Remove this check on 1.0\n //\n // - This is a workaround when using matchMaker.createRoom() before the processId is available.\n // - We need to use top-level await to retrieve processId\n //\n await onReady;\n return createRoom(roomName, clientOptions);\n\n } else {\n throw new ServerError(ErrorCode.MATCHMAKE_UNHANDLED, `no processId available to create room ${roomName}`);\n }\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: any) {\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: Room = new handler.klass();\n\n // set room public attributes\n if (restoringRoomId && isDevMode) {\n // an ungraceful exit may have left a stale cache row for this roomId\n await driver.remove(restoringRoomId);\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 = createScopedPresence(room, presence);\n\n // initialize a RoomCache instance\n room['_listing'] = initializeRoomCache({\n name: roomName,\n processId,\n ...handler.getMetadataFromOptions(clientOptions)\n });\n\n // assign public host\n if (publicAddress) {\n room['_listing'].publicAddress = publicAddress;\n }\n\n if (room.onCreate) {\n try {\n await room.onCreate(merge({}, clientOptions, handler.options));\n\n } catch (e: any) {\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('creating room \\'%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(undefined, room));\n room['_events'].on('unlock', unlockRoom.bind(undefined, room));\n room['_events'].on('join', onClientJoinRoom.bind(undefined, room));\n room['_events'].on('leave', onClientLeaveRoom.bind(undefined, room));\n room['_events'].once('dispose', disposeRoom.bind(undefined, roomName, room));\n\n if (handler.realtimeListingEnabled) {\n room['_events'].on('visibility-change', onVisibilityChange.bind(undefined, room));\n room['_events'].on('metadata-change', onMetadataChange.bind(undefined, room));\n }\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('dispose');\n\n if (handler.realtimeListingEnabled) {\n room['_events'].removeAllListeners('visibility-change');\n room['_events'].removeAllListeners('metadata-change');\n }\n\n // this room's `onDispose()` has settled\n disposingRoomCount--;\n\n //\n // emit \"no active rooms\" event when there are no more rooms in this process\n // (used during graceful shutdown)\n //\n if (!hasActiveRooms()) {\n events.emit('no-active-rooms');\n }\n });\n\n // room always start unlocked\n await createRoomReferences(room, true);\n\n // persist room data only if match-making is enabled\n if (state !== MatchMakerState.SHUTTING_DOWN) {\n await driver.persist(room['_listing'], true);\n }\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 promises.push(rooms[roomId].disconnect(closeCode));\n }\n\n return promises;\n}\n\nasync function lockAndDisposeAll(): Promise<any> {\n // remove processId from room count key\n // (stops accepting new rooms on this process)\n await stats.excludeProcess(processId);\n\n // clear auto-persisting stats interval\n if (enableHealthChecks) {\n stats.clearAutoPersistInterval();\n }\n\n const noActiveRooms = new Deferred();\n if (!hasActiveRooms()) {\n // no active rooms to dispose\n noActiveRooms.resolve();\n\n } else {\n // wait for all rooms to be disposed\n // TODO: set generous timeout in case\n events.once('no-active-rooms', () => noActiveRooms.resolve());\n }\n\n // - lock all local rooms to prevent new joins\n // - trigger `onBeforeShutdown()` on each room\n for (const roomId in rooms) {\n if (!rooms.hasOwnProperty(roomId)) {\n continue;\n }\n\n const room = rooms[roomId];\n room.lock();\n\n if (isDevMode) {\n // call default implementation of onBeforeShutdown() in dev mode\n Room.prototype.onBeforeShutdown.call(room);\n\n } else {\n // call custom implementation of onBeforeShutdown() in production\n room.onBeforeShutdown();\n }\n }\n\n await noActiveRooms;\n}\n\nexport async function gracefullyShutdown(): Promise<any> {\n if (state === MatchMakerState.SHUTTING_DOWN) {\n return Promise.reject('already_shutting_down');\n }\n\n debugMatchMaking(`${processId} is shutting down!`);\n\n state = MatchMakerState.SHUTTING_DOWN;\n\n onReady = undefined;\n\n if (isDevMode) {\n // Reject pending allowReconnection() deferreds BEFORE caching.\n // This should trigger a state cleanup via user's onLeave (e.g. players.delete)\n // so the cached state doesn't contain stale player data from clients\n for (const roomId in rooms) {\n if (!rooms.hasOwnProperty(roomId)) { continue; }\n rooms[roomId]['_rejectPendingReconnections']?.(\"devmode_restart\");\n }\n\n // Wait for async onLeave handlers to finish state cleanup.\n await new Promise(resolve => setTimeout(resolve, 50));\n\n await cacheRoomHistory(rooms);\n }\n\n // - lock existing rooms\n // - stop accepting new rooms on this process\n // - wait for all rooms to be disposed\n await lockAndDisposeAll();\n\n // make sure rooms are removed from cache\n await removeRoomsByProcessId(processId);\n\n // unsubscribe from process id channel\n presence.unsubscribe(getProcessChannel());\n\n // make sure all rooms are disposed\n return Promise.all(disconnectAll(\n (isDevMode)\n ? CloseCode.MAY_TRY_RECONNECT\n : CloseCode.SERVER_SHUTDOWN\n ));\n}\n\n/**\n * DO NOT USE THIS IN PRODUCTION. \n * THIS METHOD IS MEANT TO BE USED FOR VITE DEV SERVER ONLY.\n * ---------------------------------------------------------\n *\n * \n * Lightweight HMR reload for dev mode. \n *\n * Unlike gracefullyShutdown() + setup() + accept(), this preserves the\n * matchMaker infrastructure (presence, driver, IPC subscriptions, processId)\n * and only cycles room instances: cache state \u2192 dispose \u2192 restore.\n */\nexport async function hotReload(): Promise<void> {\n state = MatchMakerState.SHUTTING_DOWN;\n\n // Reject pending allowReconnection() deferreds BEFORE caching.\n // Triggers onLeave state cleanup so cached state is clean.\n for (const roomId in rooms) {\n if (!rooms.hasOwnProperty(roomId)) { continue; }\n rooms[roomId]['_rejectPendingReconnections']?.(\"devmode_restart\");\n }\n\n // Wait for async onLeave handlers to finish state cleanup.\n await new Promise(resolve => setTimeout(resolve, 50));\n\n await cacheRoomHistory(rooms);\n\n // Lock all rooms and trigger default onBeforeShutdown (dev mode impl).\n const noActiveRooms = new Deferred();\n if (!hasActiveRooms()) {\n noActiveRooms.resolve();\n } else {\n events.once('no-active-rooms', () => noActiveRooms.resolve());\n }\n\n for (const roomId in rooms) {\n if (!rooms.hasOwnProperty(roomId)) { continue; }\n const room = rooms[roomId];\n room.lock();\n Room.prototype.onBeforeShutdown.call(room);\n }\n\n await noActiveRooms;\n\n // Disconnect all clients \u2014 they will auto-reconnect.\n await Promise.all(disconnectAll(CloseCode.MAY_TRY_RECONNECT));\n\n // Clear driver cache so reloadFromCache() can recreate rooms.\n await removeRoomsByProcessId(processId);\n\n // Restore rooms from cached state.\n state = MatchMakerState.READY;\n await reloadFromCache();\n await stats.persist();\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 = authData?.sessionId || generateId();\n\n let successfulSeatReservation: boolean;\n\n try {\n successfulSeatReservation = await remoteRoomCall<Room>(\n room.roomId,\n '_reserveSeat' as keyof Room,\n [sessionId, options, authData],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n\n } catch (e: any) {\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 return buildSeatReservation(room, sessionId);\n}\n\n/**\n * Reserve multiple seats for clients in a room\n */\nexport async function reserveMultipleSeatsFor(room: IRoomCache, clientsData: Array<{ sessionId: string, options: ClientOptions, auth: any }>) {\n let sessionIds: string[] = [];\n let options: ClientOptions[] = [];\n let authData: any[] = [];\n\n for (const clientData of clientsData) {\n sessionIds.push(clientData.sessionId);\n options.push(clientData.options);\n authData.push(clientData.auth);\n }\n\n debugMatchMaking(\n 'reserving multiple seats. sessionIds: \\'%s\\', roomId: \\'%s\\', processId: \\'%s\\'',\n sessionIds.join(', '), room.roomId, processId,\n );\n\n let successfulSeatReservations: boolean[];\n\n try {\n successfulSeatReservations = await remoteRoomCall<Room>(\n room.roomId,\n '_reserveMultipleSeats' as keyof Room,\n [sessionIds, options, authData],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n\n } catch (e: any) {\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 throw new SeatReservationError(`${room.roomId} is already full.`);\n }\n }\n\n return successfulSeatReservations;\n}\n\n/**\n * Build a seat reservation object.\n * @param room - The room to build a seat reservation for.\n * @param sessionId - The session ID of the client.\n * @returns A seat reservation object.\n */\nexport function buildSeatReservation(room: IRoomCache, sessionId: string) {\n const seatReservation: ISeatReservation = {\n name: room.name,\n sessionId,\n roomId: room.roomId,\n processId: room.processId,\n };\n\n if (isDevMode) {\n seatReservation.devMode = isDevMode;\n }\n\n if (room.publicAddress) {\n seatReservation.publicAddress = room.publicAddress;\n }\n\n return seatReservation;\n}\n\nasync function callOnAuth(roomName: string, clientOptions?: ClientOptions, authContext?: AuthContext) {\n const roomClass = getRoomClass(roomName);\n const onAuth = roomClass?.['onAuth'];\n if (!onAuth) { return; }\n\n // Server-initiated joins (e.g. internal `matchMaker.create()` from\n // bots/tests/cron) skip auth \u2014 there's no transport, no headers, no\n // token. Treat the absence of authContext as \"no auth performed\",\n // which leaves client.auth undefined just like the legacy short-\n // circuit did.\n if (!authContext) { return; }\n\n // Always invoke onAuth when an auth context is present \u2014 even when\n // the room hasn't overridden it. The base `Room.onAuth` returns\n // `true` (or whatever a side-effect import like @colyseus/auth swapped\n // it for), and we use the return value to populate `client.auth` when\n // it's a payload object. Pre-change this call was skipped when the\n // subclass inherited the default, which made it impossible to install\n // a workspace-wide token decoder by patching `Room.onAuth` once.\n const result = await onAuth(authContext.token, clientOptions, authContext);\n\n // Auth-result semantics:\n // false / null / undefined \u2192 AUTH_FAILED\n // true \u2192 succeeded but no payload (don't set client.auth)\n // anything else (object) \u2192 succeeded; the value becomes client.auth\n if (result === false || result === null || result === undefined) {\n throw new ServerError(ErrorCode.AUTH_FAILED, 'onAuth failed');\n }\n return result === true ? undefined : result;\n}\n\n/**\n * Perform health check on all processes\n */\nexport async function healthCheckAllProcesses() {\n const allStats = await stats.fetchAll();\n const activeProcessChannels = (await presence.channels(\"p:*\")).map(c => c.substring(2));\n\n if (allStats.length > 0) {\n await Promise.all(\n allStats\n .filter(stat => (\n stat.processId !== processId && // skip current process\n !activeProcessChannels.includes(stat.processId) // skip if channel is still listening\n ))\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 await stats.excludeProcess(processId);\n\n // clean-up stale room ids \u2014 a dead process never comes back;\n // devMode restore reads 'roomhistory' (presence), never these rows\n await removeRoomsByProcessId(processId);\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 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.hincrbyex(hkey, concurrencyKey, -1, MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME * 2);\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 try {\n const roomId = await subscribeWithTimeout(\n presence,\n `concurrent:${handler.name}:${concurrencyKey}`,\n (MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME +\n (Math.min(concurrency, 3) * 0.2)) * 1000 // convert to milliseconds\n );\n\n return await fulfill(roomId);\n } catch (error) {\n // Ignore ipc_timeout error\n }\n }\n\n return await fulfill();\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\nfunction onMetadataChange(room: Room): void {\n handlers[room.roomName].emit('metadata-change', room);\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 driver.remove(room['_listing'].roomId);\n stats.local.roomCount--;\n\n // `onDispose()` is still pending \u2014 released on 'disconnect'\n disposingRoomCount++;\n\n // decrease amount of rooms this process is handling\n if (state !== MatchMakerState.SHUTTING_DOWN) {\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": ["processId"]
7
7
  }
@@ -1,3 +1,4 @@
1
+ const __cjsImportMetaUrl = require('node:url').pathToFileURL(__filename).href;
1
2
  "use strict";
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -2,6 +2,6 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/Protocol.ts"],
4
4
  "sourcesContent": ["import { Packr, RESERVE_START_SPACE } from 'msgpackr';\nimport { encode, type Iterator } from '@colyseus/schema';\nimport { Protocol } from '@colyseus/shared-types';\n\n// Inter-process communication protocol\nexport const IpcProtocol = {\n SUCCESS: 0,\n ERROR: 1,\n TIMEOUT: 2,\n} as const;\nexport type IpcProtocol = typeof IpcProtocol[keyof typeof IpcProtocol];\n\nconst packr = new Packr({\n useRecords: false, // interop with non-msgpackr decoders\n});\n\n// Buffer for assembling outgoing frames; keeps us off msgpackr's internal\n// buffer so `core` can use the upstream package directly.\nlet frameBuffer = Buffer.allocUnsafe(8192);\n\n// Grow to `capacity`, preserving written bytes (`rawMessage` writes the header\n// before the payload size is known).\nfunction ensureFrameCapacity(capacity: number) {\n if (capacity > frameBuffer.byteLength) {\n const next = Buffer.allocUnsafe(capacity);\n frameBuffer.copy(next);\n frameBuffer = next;\n }\n}\n\n// Shared across calls to avoid a per-call allocation on the hot send path.\n// Safe: encode.* mutate `.offset` in place and never re-enter this module.\nconst it: Iterator = { offset: 0 };\n\nexport const getMessageBytes = {\n /**\n * Build the JOIN_ROOM payload.\n *\n * Wire layout:\n * [JOIN_ROOM byte][rt-len][rt][sid-len][sid][stateReflectionLen varint][stateReflection][...sections]\n *\n * The varint length prefix on `stateReflection` is required because the\n * schema decoder on the SDK side runs `while (offset < bytes.byteLength)`\n * \u2014 without a boundary it consumes past the state reflection into the\n * trailing tagged-section bytes, producing \"definition mismatch\" warnings.\n * Length is `0` when no state reflection is present.\n *\n * Each trailing section is `[tag (uint8)][length (varint)][payload]`. The\n * SDK skips unknown tags via `length`, so new sections can be added without\n * breaking older clients. See {@link HandshakeSection} in shared-types.\n */\n [Protocol.JOIN_ROOM]: (\n reconnectionToken: string,\n serializerId: string,\n handshake?: Uint8Array,\n extraSections?: Array<{ tag: number; bytes: Uint8Array }>,\n ) => {\n const reconnectionTokenLength = Buffer.byteLength(reconnectionToken, \"utf8\");\n const serializerIdLength = Buffer.byteLength(serializerId, \"utf8\");\n let handshakeLength = handshake?.byteLength || 0;\n\n // per section: 1 tag byte + 9 (max varint) + payload\n let extraLength = 0;\n if (extraSections !== undefined) {\n for (let i = 0; i < extraSections.length; i++) {\n extraLength += 1 + 9 + extraSections[i].bytes.byteLength;\n }\n }\n\n // capacity: header + 9 (handshake-len varint) + handshake + sections\n ensureFrameCapacity(1 + 1 + reconnectionTokenLength + 1 + serializerIdLength + 9 + handshakeLength + extraLength);\n\n it.offset = 1;\n frameBuffer[0] = Protocol.JOIN_ROOM;\n\n frameBuffer[it.offset++] = reconnectionTokenLength;\n encode.utf8Write(frameBuffer, reconnectionToken, it);\n\n frameBuffer[it.offset++] = serializerIdLength;\n encode.utf8Write(frameBuffer, serializerId, it);\n\n encode.number(frameBuffer, handshakeLength, it);\n if (handshakeLength > 0) {\n frameBuffer.set(handshake, it.offset);\n it.offset += handshakeLength;\n }\n\n if (extraSections !== undefined) {\n for (let i = 0; i < extraSections.length; i++) {\n const section = extraSections[i];\n frameBuffer[it.offset++] = section.tag;\n encode.number(frameBuffer, section.bytes.byteLength, it);\n frameBuffer.set(section.bytes, it.offset);\n it.offset += section.bytes.byteLength;\n }\n }\n\n return Buffer.from(frameBuffer.subarray(0, it.offset));\n },\n\n [Protocol.ERROR]: (code: number, message: string = '') => {\n // capacity: 1 + code varint + length-prefixed message\n ensureFrameCapacity(1 + 9 + 9 + Buffer.byteLength(message, \"utf8\"));\n\n it.offset = 1;\n frameBuffer[0] = Protocol.ERROR;\n\n encode.number(frameBuffer, code, it);\n encode.string(frameBuffer, message, it);\n\n return Buffer.from(frameBuffer.subarray(0, it.offset));\n },\n\n [Protocol.ROOM_STATE]: (bytes: number[]) => {\n return [Protocol.ROOM_STATE, ...bytes];\n },\n\n /**\n * Reply to a client {@link Protocol.ROOM_REQUEST}.\n *\n * Wire layout: `[ROOM_RESPONSE byte][requestId varint][status uint8][msgpack payload?]`\n *\n * `requestId` is opaque \u2014 echoed back exactly as the SDK sent it so the\n * pending callback/promise can be correlated. `status` is a\n * {@link ResponseStatus} (0 = OK, 1 = REJECTED, 2 = ERROR). The payload is\n * omitted when a handler resolves with `undefined`. Returns a fresh Buffer copy\n * for the same back-pressure reason documented on `raw` below.\n */\n [Protocol.ROOM_RESPONSE]: (requestId: number, status: number, message?: any): Buffer => {\n it.offset = 1;\n frameBuffer[0] = Protocol.ROOM_RESPONSE;\n\n encode.number(frameBuffer, requestId, it);\n frameBuffer[it.offset++] = status;\n const headerLength = it.offset;\n\n if (message !== undefined) {\n // reserve `headerLength` bytes up front in the pack output for the header\n const packed = packr.pack(message, RESERVE_START_SPACE | headerLength);\n packed.set(frameBuffer.subarray(0, headerLength), 0);\n return Buffer.from(packed);\n }\n\n return Buffer.from(frameBuffer.subarray(0, headerLength));\n },\n\n [Protocol.PING]: () => {\n frameBuffer[0] = Protocol.PING;\n return Buffer.from(frameBuffer.subarray(0, 1));\n },\n\n // Returns a fresh copy: frameBuffer/packr are reused next call, but callers pass\n // the result to async consumers (Node retains it for libuv writev across ticks).\n // See benchmark/test-buffer-corruption.ts for the back-pressure repro.\n raw: (code: Protocol, type: string | number, message?: any, rawMessage?: Uint8Array | Buffer): Buffer => {\n it.offset = 1;\n frameBuffer[0] = code;\n\n if (typeof (type) === 'string') {\n encode.string(frameBuffer, type, it);\n\n } else {\n encode.number(frameBuffer, type, it);\n }\n const headerLength = it.offset;\n\n if (message !== undefined) {\n // reserve `headerLength` bytes up front in the pack output for the header\n const packed = packr.pack(message, RESERVE_START_SPACE | headerLength);\n packed.set(frameBuffer.subarray(0, headerLength), 0);\n return Buffer.from(packed);\n\n } else if (rawMessage !== undefined) {\n ensureFrameCapacity(headerLength + rawMessage.byteLength);\n frameBuffer.set(rawMessage, headerLength);\n return Buffer.from(frameBuffer.subarray(0, headerLength + rawMessage.byteLength));\n\n } else {\n return Buffer.from(frameBuffer.subarray(0, headerLength));\n }\n },\n\n};\n\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sBAA2C;AAC3C,oBAAsC;AACtC,0BAAyB;AAGlB,IAAM,cAAc;AAAA,EACzB,SAAS;AAAA,EACT,OAAO;AAAA,EACP,SAAS;AACX;AAGA,IAAM,QAAQ,IAAI,sBAAM;AAAA,EACtB,YAAY;AAAA;AACd,CAAC;AAID,IAAI,cAAc,OAAO,YAAY,IAAI;AAIzC,SAAS,oBAAoB,UAAkB;AAC7C,MAAI,WAAW,YAAY,YAAY;AACrC,UAAM,OAAO,OAAO,YAAY,QAAQ;AACxC,gBAAY,KAAK,IAAI;AACrB,kBAAc;AAAA,EAChB;AACF;AAIA,IAAM,KAAe,EAAE,QAAQ,EAAE;AAE1B,IAAM,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiB7B,CAAC,6BAAS,SAAS,GAAG,CACpB,mBACA,cACA,WACA,kBACG;AACH,UAAM,0BAA0B,OAAO,WAAW,mBAAmB,MAAM;AAC3E,UAAM,qBAAqB,OAAO,WAAW,cAAc,MAAM;AACjE,QAAI,kBAAkB,WAAW,cAAc;AAG/C,QAAI,cAAc;AAClB,QAAI,kBAAkB,QAAW;AAC/B,eAAS,IAAI,GAAG,IAAI,cAAc,QAAQ,KAAK;AAC7C,uBAAe,IAAI,IAAI,cAAc,CAAC,EAAE,MAAM;AAAA,MAChD;AAAA,IACF;AAGA,wBAAoB,IAAI,IAAI,0BAA0B,IAAI,qBAAqB,IAAI,kBAAkB,WAAW;AAEhH,OAAG,SAAS;AACZ,gBAAY,CAAC,IAAI,6BAAS;AAE1B,gBAAY,GAAG,QAAQ,IAAI;AAC3B,yBAAO,UAAU,aAAa,mBAAmB,EAAE;AAEnD,gBAAY,GAAG,QAAQ,IAAI;AAC3B,yBAAO,UAAU,aAAa,cAAc,EAAE;AAE9C,yBAAO,OAAO,aAAa,iBAAiB,EAAE;AAC9C,QAAI,kBAAkB,GAAG;AACvB,kBAAY,IAAI,WAAW,GAAG,MAAM;AACpC,SAAG,UAAU;AAAA,IACf;AAEA,QAAI,kBAAkB,QAAW;AAC/B,eAAS,IAAI,GAAG,IAAI,cAAc,QAAQ,KAAK;AAC7C,cAAM,UAAU,cAAc,CAAC;AAC/B,oBAAY,GAAG,QAAQ,IAAI,QAAQ;AACnC,6BAAO,OAAO,aAAa,QAAQ,MAAM,YAAY,EAAE;AACvD,oBAAY,IAAI,QAAQ,OAAO,GAAG,MAAM;AACxC,WAAG,UAAU,QAAQ,MAAM;AAAA,MAC7B;AAAA,IACF;AAEA,WAAO,OAAO,KAAK,YAAY,SAAS,GAAG,GAAG,MAAM,CAAC;AAAA,EACvD;AAAA,EAEA,CAAC,6BAAS,KAAK,GAAG,CAAC,MAAc,UAAkB,OAAO;AAExD,wBAAoB,IAAI,IAAI,IAAI,OAAO,WAAW,SAAS,MAAM,CAAC;AAElE,OAAG,SAAS;AACZ,gBAAY,CAAC,IAAI,6BAAS;AAE1B,yBAAO,OAAO,aAAa,MAAM,EAAE;AACnC,yBAAO,OAAO,aAAa,SAAS,EAAE;AAEtC,WAAO,OAAO,KAAK,YAAY,SAAS,GAAG,GAAG,MAAM,CAAC;AAAA,EACvD;AAAA,EAEA,CAAC,6BAAS,UAAU,GAAG,CAAC,UAAoB;AAC1C,WAAO,CAAC,6BAAS,YAAY,GAAG,KAAK;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,CAAC,6BAAS,aAAa,GAAG,CAAC,WAAmB,QAAgB,YAA0B;AACtF,OAAG,SAAS;AACZ,gBAAY,CAAC,IAAI,6BAAS;AAE1B,yBAAO,OAAO,aAAa,WAAW,EAAE;AACxC,gBAAY,GAAG,QAAQ,IAAI;AAC3B,UAAM,eAAe,GAAG;AAExB,QAAI,YAAY,QAAW;AAEzB,YAAM,SAAS,MAAM,KAAK,SAAS,sCAAsB,YAAY;AACrE,aAAO,IAAI,YAAY,SAAS,GAAG,YAAY,GAAG,CAAC;AACnD,aAAO,OAAO,KAAK,MAAM;AAAA,IAC3B;AAEA,WAAO,OAAO,KAAK,YAAY,SAAS,GAAG,YAAY,CAAC;AAAA,EAC1D;AAAA,EAEA,CAAC,6BAAS,IAAI,GAAG,MAAM;AACrB,gBAAY,CAAC,IAAI,6BAAS;AAC1B,WAAO,OAAO,KAAK,YAAY,SAAS,GAAG,CAAC,CAAC;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA,EAKA,KAAK,CAAC,MAAgB,MAAuB,SAAe,eAA6C;AACvG,OAAG,SAAS;AACZ,gBAAY,CAAC,IAAI;AAEjB,QAAI,OAAQ,SAAU,UAAU;AAC9B,2BAAO,OAAO,aAAa,MAAM,EAAE;AAAA,IAErC,OAAO;AACL,2BAAO,OAAO,aAAa,MAAM,EAAE;AAAA,IACrC;AACA,UAAM,eAAe,GAAG;AAExB,QAAI,YAAY,QAAW;AAEzB,YAAM,SAAS,MAAM,KAAK,SAAS,sCAAsB,YAAY;AACrE,aAAO,IAAI,YAAY,SAAS,GAAG,YAAY,GAAG,CAAC;AACnD,aAAO,OAAO,KAAK,MAAM;AAAA,IAE3B,WAAW,eAAe,QAAW;AACnC,0BAAoB,eAAe,WAAW,UAAU;AACxD,kBAAY,IAAI,YAAY,YAAY;AACxC,aAAO,OAAO,KAAK,YAAY,SAAS,GAAG,eAAe,WAAW,UAAU,CAAC;AAAA,IAElF,OAAO;AACL,aAAO,OAAO,KAAK,YAAY,SAAS,GAAG,YAAY,CAAC;AAAA,IAC1D;AAAA,EACF;AAEF;",
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/build/Rewind.cjs CHANGED
@@ -1,3 +1,4 @@
1
+ const __cjsImportMetaUrl = require('node:url').pathToFileURL(__filename).href;
1
2
  "use strict";
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -2,6 +2,6 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/Rewind.ts"],
4
4
  "sourcesContent": ["import { $values, type MapSchema, type ArraySchema, type SetSchema } from \"@colyseus/schema\";\nimport { $METADATA } from \"./utils/Utils.ts\";\n\nconst DEFAULT_MAX_REWIND_MS = 500;\n/** Ring-sizing fallback when the record cadence isn't supplied (manual `record(now)`\n * without a hint). `allowRewindState` feeds the real sim interval, so this only\n * matters for ad-hoc manual use. */\nconst DEFAULT_SAMPLE_INTERVAL_MS = 1000 / 60;\n\n// Hot record() path: dense `$values[index]` is ~15\u00D7 faster than `inst[fieldName]`; entity symbol prop ~8\u00D7 faster than a WeakMap.\n/** Per-entity history, under this private symbol ON the entity. Symbol-keyed so it's\n * invisible to Object.keys / `assign` / `clone`, and GC'd with the entity. */\nconst $HISTORY = Symbol(\"rewind.history\");\n\n/** Keys of T whose value type is `number` \u2014 the only rewindable fields. */\ntype NumericKeys<T> = { [K in keyof T]-?: T[K] extends number ? K : never }[keyof T] & string;\n/** A Colyseus collection whose element type is `E`. Inferring `E` from the live\n * collection the caller passes is what lets `fields` narrow with zero state-type\n * plumbing. */\ntype Collection<E> = MapSchema<E> | ArraySchema<E> | SetSchema<E>;\n\n/** Options for {@link Rewind.get} / {@link Room.allowRewindState}. */\nexport interface RewindOptions {\n /** Default rewind window (ms) for attaches that don't pass their own \u2014 sizes the\n * per-entity history ring. Per-attach `maxRewindMs` overrides it. Default 500. */\n maxRewindMs?: number;\n}\n\n/** Field's `$values` index from the schema metadata (`metadata[name]` = index), or\n * -1 if unknown. Resolved ONCE per constructor (cold path), never per tick. */\nfunction fieldIndexOf(instance: object, field: string): number {\n const md = (instance.constructor as any)[$METADATA] as Record<string, unknown> | undefined;\n const idx = md?.[field];\n return typeof idx === \"number\" ? idx : -1;\n}\n\n/** The lag-comp technique an attach-group rewinds to \u2014 the TIMELINE the client\n * displays those targets on. `\"snapshot\"` (default): the renderTime stamp (what\n * a lerping/damped client shows, behind real time). `\"reckon\"`: the reckonTime\n * stamp (what a forward-extrapolated client shows, \u2248 serverNow). Declared at\n * {@link Rewind.attachAll}, not on the schema. */\nexport type RewindMode = \"snapshot\" | \"reckon\";\n\n/**\n * Per-entity ring of recent field snapshots. `valueAt(time, col)` reconstructs\n * one field's recorded PATH at an arbitrary past time \u2014 `linear` interp for\n * continuous motion (patrol, a sine bob), or `step` (hold the last value) for\n * discrete motion (teleport snaps), where a lerp would smear across the jump.\n */\nclass EntityHistory {\n readonly fields: readonly string[];\n /** The attach-group's `mode: \"reckon\"` technique, mirrored here so a view can\n * pick its aim time PER FIELD (each field's covering history carries it). */\n readonly reckoned: boolean;\n /** The attach-group this history belongs to. One entity can have several\n * histories (attached to multiple groups over disjoint fields); `record()`\n * finds the right one by this id. */\n readonly groupId: number;\n /** Each tracked field's index into the entity's dense `$values` array \u2014\n * resolved per CONSTRUCTOR (mixed-type collections differ per type). */\n private readonly fieldIdx: readonly number[];\n private readonly cap: number;\n private readonly t: Float64Array;\n private readonly cols: Float64Array[]; // one column per field\n private readonly step: boolean; // hold (vs lerp) between samples\n private head = 0; // next write slot\n private count = 0;\n\n constructor(fields: readonly string[], fieldIdx: readonly number[], maxRewindMs: number, sampleIntervalMs: number, step: boolean, reckoned: boolean, groupId: number) {\n this.fields = fields;\n this.fieldIdx = fieldIdx;\n this.step = step;\n this.reckoned = reckoned;\n this.groupId = groupId;\n this.cap = Math.max(2, Math.ceil(maxRewindMs / sampleIntervalMs) + 4);\n this.t = new Float64Array(this.cap);\n this.cols = fields.map(() => new Float64Array(this.cap));\n }\n\n /** `values` = the entity's dense `$values` array. Direct array reads \u2014 no\n * per-field megamorphic accessor. */\n record(time: number, values: ArrayLike<number>): void {\n const fieldIdx = this.fieldIdx;\n this.t[this.head] = time;\n for (let f = 0; f < fieldIdx.length; f++) this.cols[f][this.head] = values[fieldIdx[f]];\n this.head = (this.head + 1) % this.cap;\n if (this.count < this.cap) this.count++;\n }\n\n /** Interpolated value of column `col` at `time`, clamped to the retained range. */\n valueAt(time: number, col: number): number {\n const c = this.cols[col];\n const oldest = (this.head - this.count + this.cap) % this.cap;\n if (time <= this.t[oldest]) return c[oldest];\n const newest = (this.head - 1 + this.cap) % this.cap;\n if (time >= this.t[newest]) return c[newest];\n let prev = oldest;\n for (let i = 1; i < this.count; i++) {\n const idx = (oldest + i) % this.cap;\n if (this.t[idx] >= time) {\n if (this.step) return c[prev]; // discrete: hold the value before `time`\n const t0 = this.t[prev], t1 = this.t[idx];\n const a = t1 > t0 ? (time - t0) / (t1 - t0) : 0;\n return c[prev] + (c[idx] - c[prev]) * a;\n }\n prev = idx;\n }\n return c[newest]; // unreachable: `newest` guard above covers it\n }\n}\n\n/** The histories an entity accumulates \u2014 one per attach-group it belongs to\n * (length 1 in the common single-group case). Stored under {@link $HISTORY} on\n * the entity; GC'd with it. */\ntype EntityHistories = EntityHistory[];\n\n/** Find the history covering `field` (the group that recorded it), or undefined\n * \u2192 read live. Disjoint field sets across groups make the first match the only\n * match; the scan is length-1 in every common case. */\nfunction historyForField(instance: object, field: string): EntityHistory | undefined {\n const arr = (instance as any)[$HISTORY] as EntityHistories | undefined;\n if (arr === undefined) return undefined;\n for (let i = 0; i < arr.length; i++) {\n if (arr[i].fields.indexOf(field) >= 0) return arr[i];\n }\n return undefined;\n}\n\n/** How `valueAt` reconstructs a value between recorded samples: `linear` interp\n * (continuous motion) or `step` = hold the last sample (discrete motion). */\ntype Interp = \"linear\" | \"step\";\n\n/**\n * A read-only view of the tracked world at ONE past time \u2014 the rewound state a\n * hit test reads instead of live positions. Created by {@link Rewind.at} /\n * {@link Rewind.lastSeenBy}, which bake in the `maxRewindMs` clamp and the\n * live-fallback so callers never re-implement either.\n *\n * The view assumes NOTHING about your schema's field names \u2014 you name the\n * fields (and therefore the shape) at the call site:\n *\n * ```ts\n * const seen = rewind.lastSeenBy(shooterSessionId);\n * // read fields directly\u2026\n * overlaps(bullet, seen.value(target, \"x\"), seen.value(target, \"y\"));\n * // \u2026or batch them into an object shaped by YOUR field list:\n * const pos = seen.read(target, [\"x\", \"y\"]); // { x, y }\n * seen.read(enemy, [\"x\", \"y\"], this.seenScratch); // fill + return a reused scratch\n * ```\n *\n * By default `at`/`lastSeenBy` re-aim and return the Rewind's own internal\n * view \u2014 the usual \"one view at a time, read it right away\" flow is zero-alloc\n * AND zero-setup (the server is single-threaded; nothing interleaves a\n * synchronous read). Need more than one view alive at once (compare two\n * shooters' perspectives, A/B a rewind window)? Pass your own instance as\n * their `out` \u2014 it is re-aimed and returned instead of the shared one:\n *\n * ```ts\n * const a = rewind.lastSeenBy(shooterA); // shared default view\n * const b = rewind.lastSeenBy(shooterB, new RewindView()); // independent second view\n * ```\n *\n * Don't store a view across calls or ticks: the default is re-aimed by the\n * next `at`/`lastSeenBy`, and ANY view's clamp was computed against\n * `lastRecordedAt`, so it goes stale at the next `record()` regardless.\n */\nexport class RewindView {\n /** Bound (and re-bound) by {@link _retarget} \u2014 a bare `new RewindView()` is\n * un-aimed scratch until it first passes through at()/lastSeenBy(). */\n private rewind?: Rewind;\n private _time = 0;\n private _reckonTime = 0;\n\n /** The clamped server-time (ms) this view reads at \u2014 after the maxRewindMs\n * clamp and the live (newest-sample) fallback. Mostly for logging/debug. */\n get time(): number { return this._time; }\n\n /** The clamped server-time (ms) `mode: \"reckon\"` groups read at \u2014 the\n * client's reconstructed simulation instant, not the raw stamp (see\n * {@link Rewind.at}). Always a resolved instant: a clamped direct stamp,\n * or the midpoint/live reconstruction when unstamped. `0` only before the\n * first `record()`. */\n get reckonTime(): number { return this._reckonTime; }\n\n /** @internal Aim at a rewind + clamped times ({@link Rewind.at} owns the clamps). */\n _retarget(rewind: Rewind, at: number, reckonAt: number): this {\n this.rewind = rewind;\n this._time = at;\n this._reckonTime = reckonAt;\n return this;\n }\n\n /** Rewound value of a numeric `field` on `entity` (live if it isn't tracked). */\n value<T extends object>(entity: T, field: NumericKeys<T>): number {\n if (this.rewind === undefined) {\n throw new Error(\"RewindView is not aimed \u2014 obtain it from rewind.at()/lastSeenBy(), or pass it to them as `out`.\");\n }\n // The field's covering attach-group picks the timeline: mode:\"reckon\" reads\n // at the reckon-display instant, mode:\"snapshot\" at the renderTime stamp.\n const h = historyForField(entity, field);\n if (h === undefined) return (entity as Record<string, number>)[field]; // untracked \u2192 live\n return h.valueAt(h.reckoned ? this._reckonTime : this._time, h.fields.indexOf(field));\n }\n\n /**\n * Batch {@link value} reads: the `fields` YOU list define the result's shape\n * (`Record<field, number>`). Pass `out` to fill (and return) a reused scratch\n * instead of allocating \u2014 its properties beyond `fields` are left untouched,\n * so a scratch can carry extra context (an `alive` flag, say).\n */\n read<T extends object, F extends NumericKeys<T>, O extends Record<F, number> = Record<F, number>>(\n entity: T,\n fields: readonly F[],\n out?: O,\n ): O {\n const o = (out ?? {}) as Record<F, number>;\n for (let i = 0; i < fields.length; i++) o[fields[i]] = this.value(entity, fields[i]);\n return o as O;\n }\n}\n\n/** Array-form `fields` resolved against one constructor: names with a known\n * `$values` index, in lock-step. Fields a type doesn't declare are dropped\n * (they read live) instead of recording `values[-1]` garbage. */\ninterface ResolvedFields { fields: readonly string[]; idx: readonly number[]; }\n\ninterface TrackedGroup {\n entities: () => Iterable<object>;\n fields: readonly string[] | ((entity: any) => readonly string[]);\n /** Per-constructor resolution of array-form `fields` \u2014 one entry per entity\n * type seen in the group (mixed-type collections differ per type). */\n resolvedByCtor: Map<Function, ResolvedFields>;\n maxRewindMs: number;\n interpolate: Interp | ((entity: any) => Interp);\n /** `true` when this group's `mode` is `\"reckon\"` (vs the `\"snapshot\"` default). */\n reckoned: boolean;\n /** Stable id (push index) \u2014 links an entity's per-group {@link EntityHistory}\n * back to its group so the same collection can be attached more than once. */\n groupId: number;\n}\n\n/**\n * Per-attach options for {@link Rewind.attachAll} / {@link Rewind.attach}.\n */\ninterface AttachOptions<E> {\n /** Numeric fields to record per tick. An array applies to every type in the\n * collection (fields a type lacks are skipped \u2014 they read live); a per-entity\n * fn picks the list per entity, mirroring `interpolate`'s fn form. */\n fields: readonly NumericKeys<E>[] | ((entity: E) => readonly NumericKeys<E>[]);\n maxRewindMs?: number;\n /** How `valueAt` reconstructs between samples (a mode or a per-entity fn,\n * default `linear`) \u2014 use `step` for discrete motion (e.g. teleporters). */\n interpolate?: Interp | ((entity: E) => Interp);\n /**\n * The lag-comp TIMELINE these fields rewind to \u2014 must match how the CLIENT\n * displays this target (\"what you see is what you hit\"):\n * - `\"snapshot\"` (default): rewind to the client's `renderTime` \u2014 for\n * targets it shows INTERPOLATED (Predict `lerp`/`damped`), behind real\n * time by its interp buffer + rtt/2.\n * - `\"reckon\"`: rewind to the client's `reckonTime` (\u2248 its serverNow) \u2014 for\n * targets it forward-extrapolates (Predict `reckon`).\n * Attach the same collection twice with disjoint `fields` to mix techniques.\n * The room ships exactly the stamp(s) its attached groups need (see the\n * handshake derivation in `Room`).\n */\n mode?: RewindMode;\n}\n\n/**\n * Server-side lag compensation \u2014 the dual of the client's `Predict`. Where\n * `Predict` forward-reckons entities it RECEIVES, `Rewind` records the recent\n * positions of entities it OWNS and rewinds field reads to a past (client\n * render) time, so a hit test judges against where the client actually SAW an\n * entity \u2014 not where it has slid to ~RTT later.\n *\n * Prefer {@link Room.allowRewindState}, which creates a `Rewind` and records it\n * automatically each simulation tick:\n *\n * @example\n * ```ts\n * onCreate() {\n * const rewind = this.allowRewindState({ maxRewindMs: 500 });\n * rewind.attachAll(this.state.enemies, { fields: [\"x\", \"y\"] }); // fields \u2190 Enemy's numeric keys\n * this.setTimestep((dt) => { ...move enemies... }, 1000 / 30);\n * // framework calls rewind.record() after each tick.\n * }\n * // in your hit test, with the client's renderTime:\n * const seenX = rewind.valueAt(enemy, renderTime, \"x\");\n * ```\n *\n * `attachAll` takes the live collection (not a string key), so the element type\n * \u2014 and the legal `fields` / `valueAt` field names \u2014 are inferred with no\n * state-type generic. Entities are keyed by object identity \u2014 removed entities\n * and their history are reclaimed automatically.\n *\n * The rewind TIMELINE is declared per attach-group via `attachAll(coll, { mode })`:\n * `\"snapshot\"` (default) reads at the client's `renderTime` stamp, `\"reckon\"` at\n * its `reckonTime` (see {@link at}). Not-attached = not rewound (reads are live)\n * \u2014 there is no \"none\". The same collection may be attached more than once with\n * disjoint `fields` to put different fields on different timelines. The room\n * ships exactly the stamp(s) these groups need (handshake derivation reads\n * {@link timelineMode}); the client declares its display mode independently.\n */\nexport class Rewind {\n private static readonly byRoom = new WeakMap<object, Rewind>();\n\n /** One `Rewind` per room (idempotent). `room` is only the cache key. */\n static get(room: object, opts?: RewindOptions): Rewind {\n let r = Rewind.byRoom.get(room);\n if (r === undefined) { r = new Rewind(opts?.maxRewindMs ?? DEFAULT_MAX_REWIND_MS); Rewind.byRoom.set(room, r); }\n return r;\n }\n\n private readonly groups: TrackedGroup[] = [];\n private readonly defaultMaxRewindMs: number;\n private _sampleIntervalMs = DEFAULT_SAMPLE_INTERVAL_MS;\n private _lastRecordedAt = -1;\n /** Resolves a client's auto-stamped render time (set by `Room.allowRewindState`\n * when a `mode:\"snapshot\"` group is attached); powers {@link lastSeenBy}. */\n private _renderTimeOf?: (sessionId: string) => number;\n /** Resolves a client's auto-stamped reckon-display instant (its serverNow\n * estimate at input-sample time). When present (> 0), {@link lastSeenBy}\n * reads `mode:\"reckon\"` groups AT this stamp directly \u2014 exact regardless\n * of the client's clock/RTT-estimation error (it displayed f(est), we read\n * f(est)) \u2014 instead of reconstructing it via the midpoint. */\n private _reckonTimeOf?: (sessionId: string) => number;\n /** Resolves the CURRENT server time (set by `Room.allowRewindState`) \u2014 the\n * reckon midpoint's \"now\" anchor in {@link at}. Without it, the anchor falls\n * back to `lastRecordedAt`, which is one tick stale at hit-test time. */\n private _nowOf?: () => number;\n /** Default view returned by at/lastSeenBy when no `out` is given \u2014 one per\n * Rewind, re-aimed per call (zero alloc, zero userland setup). */\n private readonly _view = new RewindView();\n\n private constructor(defaultMaxRewindMs: number) { this.defaultMaxRewindMs = defaultMaxRewindMs; }\n\n /** The default rewind window (ms). Use it to bound a hit test:\n * `Math.max(renderTime, now - rewind.maxRewindMs)`. */\n get maxRewindMs(): number { return this.defaultMaxRewindMs; }\n /** Server time of the last {@link record} (or -1). The auto-record skips a tick\n * whose time was already recorded manually \u2014 your `record()` wins. */\n get lastRecordedAt(): number { return this._lastRecordedAt; }\n\n /** Track every entity in a collection (Map/Array/Set schema). `fields` narrows\n * to its element's numeric fields. `interpolate` (a mode or a per-entity fn,\n * default `linear`) picks how `valueAt` reconstructs between samples \u2014 use\n * `step` for discrete-motion entities (e.g. teleporters). `mode` picks the\n * lag-comp timeline (default `\"snapshot\"`; see {@link AttachOptions}). */\n attachAll<E extends object>(\n collection: Collection<E>,\n opts: AttachOptions<E>,\n ): this {\n const c = collection as unknown as { values(): Iterable<object> };\n this.groups.push({ entities: () => c.values(), fields: opts.fields, resolvedByCtor: new Map(), maxRewindMs: opts.maxRewindMs ?? this.defaultMaxRewindMs, interpolate: opts.interpolate ?? \"linear\", reckoned: opts.mode === \"reckon\", groupId: this.groups.length });\n return this;\n }\n\n /** Track a single entity (e.g. a boss). `fields` narrows to its numeric fields. */\n attach<E extends object>(instance: E, opts: AttachOptions<E>): this {\n const one: object[] = [instance]; // reused; no per-tick alloc\n this.groups.push({ entities: () => one, fields: opts.fields, resolvedByCtor: new Map(), maxRewindMs: opts.maxRewindMs ?? this.defaultMaxRewindMs, interpolate: opts.interpolate ?? \"linear\", reckoned: opts.mode === \"reckon\", groupId: this.groups.length });\n return this;\n }\n\n /**\n * Snapshot every tracked entity at `now`. Call once per tick, AFTER they move \u2014\n * {@link Room.allowRewindState} does this for you. `sampleIntervalMs` (the gap\n * between records) sizes the history rings; the framework passes the sim\n * interval. Calling this yourself during a tick suppresses that tick's\n * auto-record (see {@link lastRecordedAt}).\n */\n record(now: number, sampleIntervalMs?: number): void {\n if (sampleIntervalMs !== undefined && sampleIntervalMs > 0) this._sampleIntervalMs = sampleIntervalMs;\n this._lastRecordedAt = now;\n for (const g of this.groups) {\n for (const e of g.entities()) {\n const t = e as any; // private-symbol access on a foreign schema instance\n let arr = t[$HISTORY] as EntityHistories | undefined;\n if (arr === undefined) { arr = []; t[$HISTORY] = arr; }\n let h: EntityHistory | undefined;\n for (let i = 0; i < arr.length; i++) { if (arr[i].groupId === g.groupId) { h = arr[i]; break; } }\n if (h === undefined) { h = this.createHistory(g, e); arr.push(h); } // cold: once per (entity, group)\n h.record(now, t[$values] as ArrayLike<number>);\n }\n }\n }\n\n /** Cold path \u2014 first time an entity is seen by `record()` for a given group.\n * Resolves the field\u2192`$values` indices (cached per constructor for array-form\n * `fields`; fn-form resolves per entity) and bakes in the group's `mode`. */\n private createHistory(g: TrackedGroup, e: object): EntityHistory {\n let rf: ResolvedFields;\n if (typeof g.fields === \"function\") {\n rf = this.resolveFields(e, g.fields(e));\n } else {\n let cached = g.resolvedByCtor.get(e.constructor);\n if (cached === undefined) {\n cached = this.resolveFields(e, g.fields);\n g.resolvedByCtor.set(e.constructor, cached);\n }\n rf = cached;\n }\n const interp = typeof g.interpolate === \"function\" ? g.interpolate(e) : g.interpolate;\n return new EntityHistory(rf.fields, rf.idx, g.maxRewindMs, this._sampleIntervalMs, interp === \"step\", g.reckoned, g.groupId);\n }\n\n /** Keep only the fields this entity's type declares (unknown \u2192 read live). */\n private resolveFields(e: object, names: readonly string[]): ResolvedFields {\n const fields: string[] = [];\n const idx: number[] = [];\n for (const name of names) {\n const i = fieldIndexOf(e, name);\n if (i >= 0) { fields.push(name); idx.push(i); }\n }\n return { fields, idx };\n }\n\n /**\n * `instance`'s `field` value at past `time` (interpolated from history). Falls\n * back to the live value when the entity has no history yet, or the field\n * isn't tracked.\n */\n valueAt<T extends object>(instance: T, time: number, field: NumericKeys<T>): number {\n const h = historyForField(instance, field);\n if (h === undefined) return (instance as Record<string, number>)[field];\n return h.valueAt(time, h.fields.indexOf(field));\n }\n\n /**\n * Which timeline(s) the attached groups rewind to \u2014 `reckon` true iff any\n * group is `mode:\"reckon\"`, `snapshot` true iff any is `mode:\"snapshot\"`. The\n * room reads this once (groups are declared in `onCreate`, before any join) to\n * pick which stamp(s) the client must ship: reckon-only / render-only / both /\n * none. @internal\n */\n timelineMode(): { reckon: boolean; snapshot: boolean } {\n let reckon = false, snapshot = false;\n for (const g of this.groups) {\n if (g.reckoned) reckon = true; else snapshot = true;\n }\n return { reckon, snapshot };\n }\n\n /**\n * @internal Wire a resolver from sessionId \u2192 that client's auto-stamped\n * `renderTime`. Called by {@link Room.allowRewindState} so {@link lastSeenBy}\n * works for `mode:\"snapshot\"` groups; auto-enabled by attaching one.\n */\n bindRenderTime(resolver: (sessionId: string) => number): void {\n this._renderTimeOf = resolver;\n }\n\n /**\n * @internal Wire a resolver for the CURRENT server time (ms, same epoch as\n * `record` times). Called by {@link Room.allowRewindState}; standalone users\n * (tests/harnesses) should bind their own. Anchors the reckon midpoint in\n * {@link at} at the true processing instant \u2014 the `lastRecordedAt` fallback\n * is one tick stale by hit-test time, biasing the aim ~half a tick early:\n * imperceptible on slow horizontal motion, but enough to flip knife-edge\n * stomp/hit verdicts on fast vertical movers (a bobbing jumper).\n */\n bindNow(resolver: () => number): void {\n this._nowOf = resolver;\n }\n\n /**\n * @internal Wire a resolver from sessionId \u2192 that client's auto-stamped\n * reckon-display instant (`input(sid).reckonTime`). Called by\n * {@link Room.allowRewindState}. With it, {@link lastSeenBy} aims\n * `mode:\"reckon\"` groups at the EXACT instant the client displayed them\n * (immune to its RTT-estimation error); without it (or while the stamp is\n * still 0), the midpoint reconstruction in {@link at} is the fallback.\n */\n bindReckonTime(resolver: (sessionId: string) => number): void {\n this._reckonTimeOf = resolver;\n }\n\n /**\n * A {@link RewindView} of the tracked world at `time` (an acting client's render\n * time), with the two things every hit test re-implements baked in:\n * - **clamp** to `[lastRecordedAt \u2212 maxRewindMs, lastRecordedAt]` \u2014 an\n * anti-spoof / clock-skew bound (a client can't rewind arbitrarily far), and\n * - **live fallback**: `time <= 0` (the client's clock hasn't synced) \u2192 the\n * newest recorded sample (\u2248 current position).\n *\n * Sugar over {@link valueAt}; pass the render time yourself (e.g. from\n * `input(sid).renderTime`, or a value stored on the entity). For the common\n * \"rewind to a specific client's view\" case use {@link lastSeenBy}.\n *\n * Re-aims and returns this Rewind's internal view by default \u2014 zero alloc,\n * nothing to declare. Pass `out` to aim a view of your own instead; needed\n * only to hold several views at once \u2014 see the {@link RewindView} doc.\n */\n at(time: number, out?: RewindView): RewindView {\n return this._aim(time, 0, out);\n }\n\n /**\n * Shared aiming: clamp the snapshot-timeline `time`, and resolve the reckon\n * timeline either from the DIRECT `reckonStamp` (the instant the client's\n * forward-reckoned entities were displayed at \u2014 exact, immune to the\n * client's RTT-estimation error) or, when absent (0), by reconstruction.\n */\n private _aim(time: number, reckonStamp: number, out?: RewindView): RewindView {\n const newest = this._lastRecordedAt;\n const oldest = newest - this.defaultMaxRewindMs;\n // time<=0 \u2192 not synced yet \u2192 read live (newest). Else clamp into the window.\n const at = time <= 0 ? newest : (time < oldest ? oldest : time > newest ? newest : time);\n // mode:\"reckon\" groups display forward-extrapolated to \u2248 the client's\n // serverNow \u2014 rewinding them to the raw snapshot stamp would double-\n // compensate (client extrapolates forward + server rewinds back = a\n // full-RTT ghost in the entity's past).\n let reckonAt: number;\n if (reckonStamp > 0) {\n // Direct stamp: read at exactly what the client displayed. The clamp is\n // the anti-spoof bound, same as the snapshot timeline's.\n reckonAt = reckonStamp < oldest ? oldest : reckonStamp > newest ? newest : reckonStamp;\n } else if (time <= 0) {\n reckonAt = newest;\n } else {\n // Reconstruction fallback (custom `at(time)` users / old clients): with\n // symmetric latency the client's instant is the midpoint of the stamp\n // and ARRIVAL \u2014 anchored at the bound \"now\" (the true processing\n // instant; the lastRecordedAt fallback is one tick stale by hit-test\n // time and biases the aim ~half a tick early \u2014 see bindNow). NOTE: the\n // stamp itself embeds the client's rtt/2 estimate, so this path inherits\n // its error \u2014 the direct stamp above is the accurate one.\n const anchor = this._nowOf !== undefined ? this._nowOf() : newest;\n const mid = (time + anchor) / 2;\n reckonAt = mid < oldest ? oldest : mid > newest ? newest : mid;\n }\n return (out ?? this._view)._retarget(this, at, reckonAt);\n }\n\n /**\n * A {@link RewindView} of the world as session `sessionId` LAST saw it \u2014\n * resolves the render time stamped on that client's most recently CONSUMED\n * input (hence \"last\": under a multi-frame `drain()` it is the newest frame's\n * stamp) and hands off to {@link at}. This is the \"what you see is what you\n * hit\" path:\n *\n * ```ts\n * const seen = this.rewind.lastSeenBy(shooterSessionId);\n * const hit = overlaps(bullet, seen.value(target, \"x\"), seen.value(target, \"y\"));\n * ```\n *\n * Requires the room's framework input (`this.defineInput(Input)`) \u2014 the\n * per-client stamp auto-enables from the `attachAll` `mode` of the groups you\n * rewind (declaration order vs `allowRewindState` doesn't matter). A client\n * that merely hasn't stamped yet (clock still syncing, unknown sessionId) is\n * NOT an error \u2014 it reads 0 and the view falls back to live. Use\n * {@link at} with an explicit time if you stamp render times yourself.\n *\n * Re-aims and returns this Rewind's internal view by default \u2014 zero alloc,\n * nothing to declare. Pass `out` to aim a view of your own instead; needed\n * only to hold several views at once \u2014 see the {@link RewindView} doc.\n */\n lastSeenBy(sessionId: string, out?: RewindView): RewindView {\n if (this._renderTimeOf === undefined) {\n throw new Error(\n \"Rewind.lastSeenBy(sessionId) needs the framework input API. Declare \" +\n \"`this.defineInput(Input)` (stamps auto-enable from your attachAll `mode`), \" +\n \"or call `at(time)` and pass the render time yourself.\",\n );\n }\n return this._aim(\n this._renderTimeOf(sessionId),\n this._reckonTimeOf !== undefined ? this._reckonTimeOf(sessionId) : 0,\n out,\n );\n }\n}\n"],
5
- "mappings": "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",
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }