{
  "version": 3,
  "sources": ["../../src/utils/DevMode.ts"],
  "sourcesContent": ["import fs from 'fs';\nimport path from 'path';\nimport { type Schema, MapSchema, ArraySchema, SetSchema, CollectionSchema, $childType, $changes } from '@colyseus/schema';\nimport { logger } from '../Logger.ts';\nimport { debugAndPrintError, debugDevMode } from '../Debug.ts';\nimport { getLocalRoomById, handleCreateRoom, presence, remoteRoomCall } from '../MatchMaker.ts';\nimport type { Room } from '../Room.ts';\n\nconst DEVMODE_CACHE_FILE_PATH = path.resolve(\".devmode.json\");\n\nexport let isDevMode: boolean = false;\n\nexport function hasDevModeCache() {\n  return fs.existsSync(DEVMODE_CACHE_FILE_PATH);\n}\n\nexport function getDevModeCache() {\n  return JSON.parse(fs.readFileSync(DEVMODE_CACHE_FILE_PATH, 'utf8')) || {};\n}\n\nexport function writeDevModeCache(cache: any) {\n  fs.writeFileSync(DEVMODE_CACHE_FILE_PATH, JSON.stringify(cache, null, 2), 'utf8');\n}\n\nexport function setDevMode(bool: boolean) {\n  isDevMode = bool;\n}\n\nexport async function reloadFromCache() {\n  const roomHistoryList = Object.entries(await presence.hgetall(getRoomRestoreListKey()));\n  debugDevMode(\"rooms to restore: %i\", roomHistoryList.length);\n\n  for (const [roomId, value] of roomHistoryList) {\n    const roomHistory = JSON.parse(value);\n    debugDevMode(\"restoring room %s (%s)\", roomHistory.roomName, roomId);\n\n    const recreatedRoomListing = await handleCreateRoom(roomHistory.roomName, roomHistory.clientOptions, roomId);\n    const recreatedRoom = getLocalRoomById(recreatedRoomListing.roomId);\n\n    // Restore previous state\n    if (roomHistory.hasOwnProperty(\"state\")) {\n      try {\n        const rawState = JSON.parse(roomHistory.state);\n        logger.debug(`\uD83D\uDCCB room '${roomId}' state =>`, rawState);\n\n        (recreatedRoom.state as Schema).restore(rawState);\n\n        // Restore the encoder's nextUniqueId so refIds increase\n        // monotonically across HMR cycles. Without this, restore()\n        // always produces the same refIds (0,1,2,3...) and onJoin()\n        // always assigns the same next refIds (4,5...), causing the\n        // client decoder to reuse stale instances on the 2nd+ cycle.\n        if (roomHistory.nextRefId !== undefined) {\n          const encoderRoot = recreatedRoom.state[$changes]?.root;\n          if (encoderRoot && roomHistory.nextRefId > encoderRoot['nextUniqueId']) {\n            encoderRoot['nextUniqueId'] = roomHistory.nextRefId;\n          }\n        }\n      } catch (e: any) {\n        debugAndPrintError(`\u274C couldn't restore room '${roomId}' state:\\n${e.stack}`);\n      }\n    }\n\n    // Reserve seats for clients from cached history.\n    // Skip entries without a reconnectionToken \u2014 these are stale\n    // seats from allowReconnection() where the client already left\n    // (e.g. page refresh). Restoring them would block room disposal.\n    if (roomHistory.clients) {\n      for (const clientData of roomHistory.clients) {\n        const { sessionId, reconnectionToken } = clientData;\n        if (!reconnectionToken) { continue; }\n        // TODO: need to restore each client's StateView as well\n        await remoteRoomCall(recreatedRoomListing.roomId, '_reserveSeat', [sessionId, {}, {}, recreatedRoom.seatReservationTimeout, false, reconnectionToken]);\n      }\n    }\n\n    // call `onRestoreRoom` with custom 'cache'd property.\n    recreatedRoom.onRestoreRoom?.(roomHistory[\"cache\"]);\n\n    logger.debug(`\uD83D\uDD04 room '${roomId}' has been restored with ${roomHistory.clients?.length || 0} reserved seats: ${roomHistory.clients?.map((c: any) => c.sessionId).join(\", \")}`);\n  }\n\n  if (roomHistoryList.length > 0) {\n    logger.debug(\"\u2705\", roomHistoryList.length, \"room(s) have been restored.\");\n  }\n}\n\nexport async function cacheRoomHistory(rooms: { [roomId: string]: Room }) {\n  for (const room of Object.values(rooms)) {\n    const roomHistoryResult = await presence.hget(getRoomRestoreListKey(), room.roomId);\n    if (roomHistoryResult) {\n      try {\n        const roomHistory = JSON.parse(roomHistoryResult);\n\n        // custom cache method\n        roomHistory[\"cache\"] = room.onCacheRoom?.();\n\n        // encode state\n        debugDevMode(\"caching room %s (%s)\", room.roomName, room.roomId);\n\n        if (room.state) {\n          roomHistory[\"state\"] = JSON.stringify(room.state);\n\n          // Cache the encoder's nextUniqueId so it can be restored.\n          // This ensures refIds increase monotonically across HMR cycles,\n          // preventing the client decoder from reusing stale refs that\n          // happen to have the same refId as newly created instances.\n          const encoderRoot = room.state[$changes]?.root;\n          if (encoderRoot) {\n            roomHistory[\"nextRefId\"] = encoderRoot['nextUniqueId'];\n          }\n        }\n\n        // cache active clients with their reconnection tokens\n        // TODO: need to cache each client's StateView as well\n        const activeClients = room.clients.map((client) => ({\n          sessionId: client.sessionId,\n          reconnectionToken: client.reconnectionToken,\n        }));\n\n        // collect active client sessionIds to avoid duplicates\n        const activeSessionIds = new Set(activeClients.map((c) => c.sessionId));\n\n        // also cache reserved seats (they don't have reconnectionTokens yet)\n        // filter out reserved seats that are already active clients (from devMode reconnection)\n        const reservedSeats = Object.keys(room['_reservedSeats'])\n          .filter((sessionId) => !activeSessionIds.has(sessionId))\n          .map((sessionId) => ({\n            sessionId,\n            reconnectionToken: undefined,\n          }));\n\n        roomHistory[\"clients\"] = activeClients.concat(reservedSeats);\n\n        await presence.hset(getRoomRestoreListKey(), room.roomId, JSON.stringify(roomHistory));\n\n        // Rewrite updated room history\n        logger.debug(`\uD83D\uDCBE caching room '${room.roomId}' (clients: ${room.clients.length}, has state: ${roomHistory[\"state\"] !== undefined ? \"yes\" : \"no\"})`);\n\n      } catch (e: any) {\n        debugAndPrintError(`\u274C couldn't cache room '${room.roomId}', due to:\\n${e.stack}`);\n      }\n    }\n  }\n}\n\nexport async function getPreviousProcessId(hostname: string = '') {\n  return await presence.hget(getProcessRestoreKey(), hostname);\n}\n\nexport function getRoomRestoreListKey() {\n  return 'roomhistory';\n}\n\nexport function getProcessRestoreKey() {\n  return 'processhistory';\n}\n"],
  "mappings": "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",
  "names": []
}
