@colyseus/core
Version:
Multiplayer Framework for Node.js.
117 lines (116 loc) • 4.76 kB
JavaScript
// packages/core/src/utils/DevMode.ts
import fs from "fs";
import path from "path";
import { $changes } from "@colyseus/schema";
import { logger } from "../Logger.mjs";
import { debugAndPrintError, debugDevMode } from "../Debug.mjs";
import { getLocalRoomById, handleCreateRoom, presence, remoteRoomCall } from "../MatchMaker.mjs";
var DEVMODE_CACHE_FILE_PATH = path.resolve(".devmode.json");
var isDevMode = false;
function hasDevModeCache() {
return fs.existsSync(DEVMODE_CACHE_FILE_PATH);
}
function getDevModeCache() {
return JSON.parse(fs.readFileSync(DEVMODE_CACHE_FILE_PATH, "utf8")) || {};
}
function writeDevModeCache(cache) {
fs.writeFileSync(DEVMODE_CACHE_FILE_PATH, JSON.stringify(cache, null, 2), "utf8");
}
function setDevMode(bool) {
isDevMode = bool;
}
async function reloadFromCache() {
const roomHistoryList = Object.entries(await presence.hgetall(getRoomRestoreListKey()));
debugDevMode("rooms to restore: %i", roomHistoryList.length);
for (const [roomId, value] of roomHistoryList) {
const roomHistory = JSON.parse(value);
debugDevMode("restoring room %s (%s)", roomHistory.roomName, roomId);
const recreatedRoomListing = await handleCreateRoom(roomHistory.roomName, roomHistory.clientOptions, roomId);
const recreatedRoom = getLocalRoomById(recreatedRoomListing.roomId);
if (roomHistory.hasOwnProperty("state")) {
try {
const rawState = JSON.parse(roomHistory.state);
logger.debug(`\u{1F4CB} room '${roomId}' state =>`, rawState);
recreatedRoom.state.restore(rawState);
if (roomHistory.nextRefId !== void 0) {
const encoderRoot = recreatedRoom.state[$changes]?.root;
if (encoderRoot && roomHistory.nextRefId > encoderRoot["nextUniqueId"]) {
encoderRoot["nextUniqueId"] = roomHistory.nextRefId;
}
}
} catch (e) {
debugAndPrintError(`\u274C couldn't restore room '${roomId}' state:
${e.stack}`);
}
}
if (roomHistory.clients) {
for (const clientData of roomHistory.clients) {
const { sessionId, reconnectionToken } = clientData;
if (!reconnectionToken) {
continue;
}
await remoteRoomCall(recreatedRoomListing.roomId, "_reserveSeat", [sessionId, {}, {}, recreatedRoom.seatReservationTimeout, false, reconnectionToken]);
}
}
recreatedRoom.onRestoreRoom?.(roomHistory["cache"]);
logger.debug(`\u{1F504} room '${roomId}' has been restored with ${roomHistory.clients?.length || 0} reserved seats: ${roomHistory.clients?.map((c) => c.sessionId).join(", ")}`);
}
if (roomHistoryList.length > 0) {
logger.debug("\u2705", roomHistoryList.length, "room(s) have been restored.");
}
}
async function cacheRoomHistory(rooms) {
for (const room of Object.values(rooms)) {
const roomHistoryResult = await presence.hget(getRoomRestoreListKey(), room.roomId);
if (roomHistoryResult) {
try {
const roomHistory = JSON.parse(roomHistoryResult);
roomHistory["cache"] = room.onCacheRoom?.();
debugDevMode("caching room %s (%s)", room.roomName, room.roomId);
if (room.state) {
roomHistory["state"] = JSON.stringify(room.state);
const encoderRoot = room.state[$changes]?.root;
if (encoderRoot) {
roomHistory["nextRefId"] = encoderRoot["nextUniqueId"];
}
}
const activeClients = room.clients.map((client) => ({
sessionId: client.sessionId,
reconnectionToken: client.reconnectionToken
}));
const activeSessionIds = new Set(activeClients.map((c) => c.sessionId));
const reservedSeats = Object.keys(room["_reservedSeats"]).filter((sessionId) => !activeSessionIds.has(sessionId)).map((sessionId) => ({
sessionId,
reconnectionToken: void 0
}));
roomHistory["clients"] = activeClients.concat(reservedSeats);
await presence.hset(getRoomRestoreListKey(), room.roomId, JSON.stringify(roomHistory));
logger.debug(`\u{1F4BE} caching room '${room.roomId}' (clients: ${room.clients.length}, has state: ${roomHistory["state"] !== void 0 ? "yes" : "no"})`);
} catch (e) {
debugAndPrintError(`\u274C couldn't cache room '${room.roomId}', due to:
${e.stack}`);
}
}
}
}
async function getPreviousProcessId(hostname = "") {
return await presence.hget(getProcessRestoreKey(), hostname);
}
function getRoomRestoreListKey() {
return "roomhistory";
}
function getProcessRestoreKey() {
return "processhistory";
}
export {
cacheRoomHistory,
getDevModeCache,
getPreviousProcessId,
getProcessRestoreKey,
getRoomRestoreListKey,
hasDevModeCache,
isDevMode,
reloadFromCache,
setDevMode,
writeDevModeCache
};