@colyseus/core 0.15.22 → 0.15.23
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.
- package/build/MatchMaker.d.ts +10 -6
- package/build/MatchMaker.js +19 -11
- package/build/MatchMaker.js.map +2 -2
- package/build/MatchMaker.mjs +19 -11
- package/build/MatchMaker.mjs.map +2 -2
- package/build/Room.d.ts +1 -1
- package/build/Room.js +8 -9
- package/build/Room.js.map +2 -2
- package/build/Room.mjs +8 -9
- package/build/Room.mjs.map +2 -2
- package/build/Server.js +6 -7
- package/build/Server.js.map +2 -2
- package/build/Server.mjs +7 -8
- package/build/Server.mjs.map +2 -2
- package/build/index.d.ts +1 -1
- package/build/index.js +2 -0
- package/build/index.js.map +2 -2
- package/build/index.mjs +2 -1
- package/build/index.mjs.map +2 -2
- package/build/matchmaker/controller.d.ts +2 -1
- package/build/matchmaker/controller.js +2 -2
- package/build/matchmaker/controller.js.map +2 -2
- package/build/matchmaker/controller.mjs +2 -2
- package/build/matchmaker/controller.mjs.map +2 -2
- package/build/utils/Utils.d.ts +1 -0
- package/build/utils/Utils.js +5 -0
- package/build/utils/Utils.js.map +2 -2
- package/build/utils/Utils.mjs +4 -0
- package/build/utils/Utils.mjs.map +2 -2
- package/package.json +1 -1
package/build/MatchMaker.d.ts
CHANGED
|
@@ -8,6 +8,10 @@ import * as stats from "./Stats";
|
|
|
8
8
|
import { Type } from './utils/types';
|
|
9
9
|
export { controller, stats, type MatchMakerDriver };
|
|
10
10
|
export type ClientOptions = any;
|
|
11
|
+
export type AuthOptions = {
|
|
12
|
+
token?: string;
|
|
13
|
+
request?: any;
|
|
14
|
+
};
|
|
11
15
|
export type SelectProcessIdCallback = (roomName: string, clientOptions: ClientOptions) => Promise<string>;
|
|
12
16
|
export interface SeatReservation {
|
|
13
17
|
sessionId: string;
|
|
@@ -28,15 +32,15 @@ export declare function setup(_presence?: Presence, _driver?: MatchMakerDriver,
|
|
|
28
32
|
/**
|
|
29
33
|
* Join or create into a room and return seat reservation
|
|
30
34
|
*/
|
|
31
|
-
export declare function joinOrCreate(roomName: string, clientOptions?: ClientOptions): Promise<SeatReservation>;
|
|
35
|
+
export declare function joinOrCreate(roomName: string, clientOptions?: ClientOptions, authOptions?: AuthOptions): Promise<SeatReservation>;
|
|
32
36
|
/**
|
|
33
37
|
* Create a room and return seat reservation
|
|
34
38
|
*/
|
|
35
|
-
export declare function create(roomName: string, clientOptions?: ClientOptions): Promise<SeatReservation>;
|
|
39
|
+
export declare function create(roomName: string, clientOptions?: ClientOptions, authOptions?: AuthOptions): Promise<SeatReservation>;
|
|
36
40
|
/**
|
|
37
41
|
* Join a room and return seat reservation
|
|
38
42
|
*/
|
|
39
|
-
export declare function join(roomName: string, clientOptions?: ClientOptions): Promise<SeatReservation>;
|
|
43
|
+
export declare function join(roomName: string, clientOptions?: ClientOptions, authOptions?: AuthOptions): Promise<SeatReservation>;
|
|
40
44
|
/**
|
|
41
45
|
* Join a room by id and return seat reservation
|
|
42
46
|
*/
|
|
@@ -52,7 +56,7 @@ export declare function reconnect(roomId: string, clientOptions?: ClientOptions)
|
|
|
52
56
|
*
|
|
53
57
|
* @returns Promise<SeatReservation> - A promise which contains `sessionId` and `RoomListingData`.
|
|
54
58
|
*/
|
|
55
|
-
export declare function joinById(roomId: string, clientOptions?: ClientOptions): Promise<SeatReservation>;
|
|
59
|
+
export declare function joinById(roomId: string, clientOptions?: ClientOptions, authOptions?: AuthOptions): Promise<SeatReservation>;
|
|
56
60
|
/**
|
|
57
61
|
* Perform a query for all cached rooms
|
|
58
62
|
*/
|
|
@@ -80,7 +84,7 @@ export declare function defineRoomType<T extends Type<Room>>(roomName: string, k
|
|
|
80
84
|
export declare function removeRoomType(roomName: string): void;
|
|
81
85
|
export declare function hasHandler(roomName: string): boolean;
|
|
82
86
|
export declare function getHandler(roomName: string): RegisteredHandler;
|
|
83
|
-
export declare function getRoomClass(roomName: string): Type<Room
|
|
87
|
+
export declare function getRoomClass(roomName: string): Type<Room>;
|
|
84
88
|
/**
|
|
85
89
|
* Creates a new room.
|
|
86
90
|
*
|
|
@@ -100,4 +104,4 @@ export declare function gracefullyShutdown(): Promise<any>;
|
|
|
100
104
|
/**
|
|
101
105
|
* Reserve a seat for a client in a room
|
|
102
106
|
*/
|
|
103
|
-
export declare function reserveSeatFor(room: RoomListingData, options: any): Promise<SeatReservation>;
|
|
107
|
+
export declare function reserveSeatFor(room: RoomListingData, options: ClientOptions, authData?: any): Promise<SeatReservation>;
|
package/build/MatchMaker.js
CHANGED
|
@@ -126,26 +126,29 @@ async function setup(_presence, _driver, _publicAddress, _selectProcessIdToCreat
|
|
|
126
126
|
}
|
|
127
127
|
onReady.resolve();
|
|
128
128
|
}
|
|
129
|
-
async function joinOrCreate(roomName, clientOptions = {}) {
|
|
129
|
+
async function joinOrCreate(roomName, clientOptions = {}, authOptions) {
|
|
130
130
|
return await (0, import_Utils.retry)(async () => {
|
|
131
|
+
const authData = await callOnAuth(roomName, authOptions);
|
|
131
132
|
let room = await findOneRoomAvailable(roomName, clientOptions);
|
|
132
133
|
if (!room) {
|
|
133
134
|
room = await createRoom(roomName, clientOptions);
|
|
134
135
|
}
|
|
135
|
-
return await reserveSeatFor(room, clientOptions);
|
|
136
|
+
return await reserveSeatFor(room, clientOptions, authData);
|
|
136
137
|
}, 5, [import_SeatReservationError.SeatReservationError]);
|
|
137
138
|
}
|
|
138
|
-
async function create(roomName, clientOptions = {}) {
|
|
139
|
+
async function create(roomName, clientOptions = {}, authOptions) {
|
|
140
|
+
const authData = await callOnAuth(roomName, authOptions);
|
|
139
141
|
const room = await createRoom(roomName, clientOptions);
|
|
140
|
-
return reserveSeatFor(room, clientOptions);
|
|
142
|
+
return reserveSeatFor(room, clientOptions, authData);
|
|
141
143
|
}
|
|
142
|
-
async function join(roomName, clientOptions = {}) {
|
|
144
|
+
async function join(roomName, clientOptions = {}, authOptions) {
|
|
143
145
|
return await (0, import_Utils.retry)(async () => {
|
|
146
|
+
const authData = await callOnAuth(roomName, authOptions);
|
|
144
147
|
const room = await findOneRoomAvailable(roomName, clientOptions);
|
|
145
148
|
if (!room) {
|
|
146
149
|
throw new import_ServerError.ServerError(import_Protocol.ErrorCode.MATCHMAKE_INVALID_CRITERIA, `no rooms found with provided criteria`);
|
|
147
150
|
}
|
|
148
|
-
return reserveSeatFor(room, clientOptions);
|
|
151
|
+
return reserveSeatFor(room, clientOptions, authData);
|
|
149
152
|
});
|
|
150
153
|
}
|
|
151
154
|
async function reconnect(roomId, clientOptions = {}) {
|
|
@@ -172,14 +175,15 @@ async function reconnect(roomId, clientOptions = {}) {
|
|
|
172
175
|
throw new import_ServerError.ServerError(import_Protocol.ErrorCode.MATCHMAKE_EXPIRED, `reconnection token invalid or expired.`);
|
|
173
176
|
}
|
|
174
177
|
}
|
|
175
|
-
async function joinById(roomId, clientOptions = {}) {
|
|
178
|
+
async function joinById(roomId, clientOptions = {}, authOptions) {
|
|
176
179
|
const room = await driver.findOne({ roomId });
|
|
177
180
|
if (!room) {
|
|
178
181
|
throw new import_ServerError.ServerError(import_Protocol.ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room "${roomId}" not found`);
|
|
179
182
|
} else if (room.locked) {
|
|
180
183
|
throw new import_ServerError.ServerError(import_Protocol.ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room "${roomId}" is locked`);
|
|
181
184
|
}
|
|
182
|
-
|
|
185
|
+
const authData = await callOnAuth(room.name, authOptions);
|
|
186
|
+
return reserveSeatFor(room, clientOptions, authData);
|
|
183
187
|
}
|
|
184
188
|
async function query(conditions = {}) {
|
|
185
189
|
return await driver.find(conditions);
|
|
@@ -212,7 +216,7 @@ async function remoteRoomCall(roomId, method, args, rejectionTimeout = import_Ut
|
|
|
212
216
|
);
|
|
213
217
|
}
|
|
214
218
|
} else {
|
|
215
|
-
return !args && typeof room[method] !== "function" ? room[method] : await room[method].apply(room, args &&
|
|
219
|
+
return !args && typeof room[method] !== "function" ? room[method] : await room[method].apply(room, args && JSON.parse(JSON.stringify(args)));
|
|
216
220
|
}
|
|
217
221
|
}
|
|
218
222
|
function defineRoomType(roomName, klass, defaultOptions) {
|
|
@@ -358,7 +362,7 @@ async function gracefullyShutdown() {
|
|
|
358
362
|
import_DevMode.isDevMode ? import_Protocol.Protocol.WS_CLOSE_DEVMODE_RESTART : void 0
|
|
359
363
|
));
|
|
360
364
|
}
|
|
361
|
-
async function reserveSeatFor(room, options) {
|
|
365
|
+
async function reserveSeatFor(room, options, authData) {
|
|
362
366
|
const sessionId = (0, import_Utils.generateId)();
|
|
363
367
|
(0, import_Debug.debugMatchMaking)(
|
|
364
368
|
"reserving seat. sessionId: '%s', roomId: '%s', processId: '%s'",
|
|
@@ -368,7 +372,7 @@ async function reserveSeatFor(room, options) {
|
|
|
368
372
|
);
|
|
369
373
|
let successfulSeatReservation;
|
|
370
374
|
try {
|
|
371
|
-
successfulSeatReservation = await remoteRoomCall(room.roomId, "_reserveSeat", [sessionId, options]);
|
|
375
|
+
successfulSeatReservation = await remoteRoomCall(room.roomId, "_reserveSeat", [sessionId, options, authData]);
|
|
372
376
|
} catch (e) {
|
|
373
377
|
(0, import_Debug.debugMatchMaking)(e);
|
|
374
378
|
successfulSeatReservation = false;
|
|
@@ -382,6 +386,10 @@ async function reserveSeatFor(room, options) {
|
|
|
382
386
|
}
|
|
383
387
|
return response;
|
|
384
388
|
}
|
|
389
|
+
function callOnAuth(roomName, authOptions) {
|
|
390
|
+
const roomClass = getRoomClass(roomName);
|
|
391
|
+
return roomClass && roomClass["onAuth"] && roomClass["onAuth"] !== import_Room.Room["onAuth"] ? roomClass["onAuth"](authOptions.token, authOptions.request) : void 0;
|
|
392
|
+
}
|
|
385
393
|
async function cleanupStaleRooms(roomName) {
|
|
386
394
|
const cachedRooms = await driver.find({ name: roomName }, { _id: 1 });
|
|
387
395
|
await presence.del(getHandlerConcurrencyKey(roomName));
|
package/build/MatchMaker.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/MatchMaker.ts"],
|
|
4
|
-
"sourcesContent": ["import { ErrorCode, Protocol } from './Protocol';\n\nimport { requestFromIPC, subscribeIPC } from './IPC';\n\nimport { Deferred, generateId, merge, REMOTE_ROOM_SHORT_TIMEOUT, retry } from './utils/Utils';\nimport { isDevMode, cacheRoomHistory, getPreviousProcessId, getRoomRestoreListKey, reloadFromCache } from './utils/DevMode';\n\nimport { RegisteredHandler } from './matchmaker/RegisteredHandler';\nimport { Room, RoomInternalState } from './Room';\n\nimport { LocalPresence } from './presence/LocalPresence';\nimport { Presence } from './presence/Presence';\n\nimport { debugAndPrintError, debugMatchMaking } from './Debug';\nimport { SeatReservationError } from './errors/SeatReservationError';\nimport { ServerError } from './errors/ServerError';\n\nimport { IRoomListingData, RoomListingData, LocalDriver, MatchMakerDriver } from './matchmaker/driver';\nimport controller from './matchmaker/controller';\nimport * as stats from \"./Stats\";\n\nimport { logger } from './Logger';\nimport { Client } from './Transport';\nimport { Type } from './utils/types';\nimport { getHostname } from \"./discovery\";\n\nexport { controller, stats, type MatchMakerDriver };\n\nexport type ClientOptions = any;\nexport type SelectProcessIdCallback = (roomName: string, clientOptions: ClientOptions) => Promise<string>;\n\nexport interface SeatReservation {\n sessionId: string;\n room: RoomListingData;\n devMode?: boolean;\n}\n\nconst handlers: {[id: string]: RegisteredHandler} = {};\nconst rooms: {[roomId: string]: Room} = {};\n\nexport let publicAddress: string;\nexport let processId: string;\nexport let presence: Presence;\nexport let driver: MatchMakerDriver;\nexport let selectProcessIdToCreateRoom: SelectProcessIdCallback;\n\nexport let isGracefullyShuttingDown: boolean;\nexport let onReady: Deferred = new Deferred(); // onReady needs to be immediately available to @colyseus/auth integration.\n\n/**\n * @private\n */\nexport async function setup(\n _presence?: Presence,\n _driver?: MatchMakerDriver,\n _publicAddress?: string,\n _selectProcessIdToCreateRoom?: SelectProcessIdCallback,\n) {\n if (onReady === undefined) {\n //\n // for testing purposes only: onReady is turned into undefined on shutdown\n // (needs refactoring.)\n //\n onReady = new Deferred();\n }\n\n presence = _presence || new LocalPresence();\n driver = _driver || new LocalDriver();\n publicAddress = _publicAddress;\n\n // devMode: try to retrieve previous processId\n if (isDevMode) { processId = await getPreviousProcessId(await getHostname()); }\n\n // ensure processId is set\n if (!processId) { processId = generateId(); }\n\n isGracefullyShuttingDown = false;\n\n /**\n * Define default `assignRoomToProcessId` method.\n * By default, return the process with least amount of rooms created\n */\n selectProcessIdToCreateRoom = _selectProcessIdToCreateRoom || async function () {\n return (await stats.fetchAll())\n .sort((p1, p2) => p1.roomCount > p2.roomCount ? 1 : -1)[0]\n .processId;\n };\n\n /**\n * Process-level subscription\n * - handle remote process healthcheck\n * - handle remote room creation\n */\n subscribeIPC(presence, processId, getProcessChannel(), (method, args) => {\n if (method === 'healthcheck') {\n // health check for this processId\n return true;\n\n } else {\n // handle room creation\n return handleCreateRoom.apply(undefined, args);\n }\n });\n\n /**\n * Check for leftover/invalid processId's on startup\n */\n const previousStats = await stats.fetchAll();\n if (previousStats.length > 0) {\n logger.debug(`${previousStats.length} previous processId(s) found, health-checking...`);\n await Promise.all(previousStats.map(async (stat) => {\n try {\n await requestFromIPC<RoomListingData>(\n presence,\n getProcessChannel(stat.processId),\n 'healthcheck',\n [],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n // process succeeded to respond - nothing to do\n } catch (e) {\n // process failed to respond - remove it from stats\n logger.debug(`process ${stat.processId} failed to respond. excluding from stats`);\n await stats.excludeProcess(stat.processId);\n }\n }));\n }\n\n await stats.reset();\n\n if (isDevMode) {\n await reloadFromCache();\n }\n\n onReady.resolve();\n}\n\n/**\n * Join or create into a room and return seat reservation\n */\nexport async function joinOrCreate(roomName: string, clientOptions: ClientOptions = {}) {\n return await retry<Promise<SeatReservation>>(async () => {\n let room = await findOneRoomAvailable(roomName, clientOptions);\n\n if (!room) {\n room = await createRoom(roomName, clientOptions);\n }\n\n return await reserveSeatFor(room, clientOptions);\n }, 5, [SeatReservationError]);\n}\n\n/**\n * Create a room and return seat reservation\n */\nexport async function create(roomName: string, clientOptions: ClientOptions = {}) {\n const room = await createRoom(roomName, clientOptions);\n return reserveSeatFor(room, clientOptions);\n}\n\n/**\n * Join a room and return seat reservation\n */\nexport async function join(roomName: string, clientOptions: ClientOptions = {}) {\n return await retry<Promise<SeatReservation>>(async () => {\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);\n });\n}\n\n/**\n * Join a room by id and return seat reservation\n */\nexport async function reconnect(roomId: string, clientOptions: ClientOptions = {}) {\n const room = await driver.findOne({ roomId });\n if (!room) {\n // TODO: support a \"logLevel\" out of the box?\n if (process.env.NODE_ENV !== 'production') {\n logger.info(`\u274C room \"${roomId}\" has been disposed. Did you missed .allowReconnection()?\\n\uD83D\uDC49 https://docs.colyseus.io/server/room/#allowreconnection-client-seconds`);\n }\n\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room \"${roomId}\" has been disposed.`);\n }\n\n // check for reconnection\n const reconnectionToken = clientOptions.reconnectionToken;\n if (!reconnectionToken) { throw new ServerError(ErrorCode.MATCHMAKE_UNHANDLED, `'reconnectionToken' must be provided for reconnection.`); }\n\n\n // respond to re-connection!\n const sessionId = await remoteRoomCall(room.roomId, 'checkReconnectionToken', [reconnectionToken]);\n if (sessionId) {\n return { room, sessionId };\n\n } else {\n // TODO: support a \"logLevel\" out of the box?\n if (process.env.NODE_ENV !== 'production') {\n logger.info(`\u274C reconnection token invalid or expired. Did you missed .allowReconnection()?\\n\uD83D\uDC49 https://docs.colyseus.io/server/room/#allowreconnection-client-seconds`);\n }\n throw new ServerError(ErrorCode.MATCHMAKE_EXPIRED, `reconnection token invalid or expired.`);\n }\n}\n\n/**\n * Join a room by id and return client seat reservation. An exception is thrown if a room is not found for roomId.\n *\n * @param roomId - The Id of the specific room instance.\n * @param clientOptions - Options for the client seat reservation (for `onJoin`/`onAuth`)\n *\n * @returns Promise<SeatReservation> - A promise which contains `sessionId` and `RoomListingData`.\n */\nexport async function joinById(roomId: string, clientOptions: ClientOptions = {}) {\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 return reserveSeatFor(room, clientOptions);\n}\n\n/**\n * Perform a query for all cached rooms\n */\nexport async function query(conditions: Partial<IRoomListingData> = {}) {\n return await driver.find(conditions);\n}\n\n/**\n * Find for a public and unlocked room available.\n *\n * @param roomName - The Id of the specific room.\n * @param clientOptions - Options for the client seat reservation (for `onJoin`/`onAuth`).\n *\n * @returns Promise<RoomListingData> - A promise contaning an object which includes room metadata and configurations.\n */\nexport async function findOneRoomAvailable(roomName: string, clientOptions: ClientOptions): Promise<RoomListingData> {\n return await awaitRoomAvailable(roomName, async () => {\n const handler = getHandler(roomName);\n\n const roomQuery = driver.findOne({\n locked: false,\n name: roomName,\n private: false,\n ...handler.getFilterOptions(clientOptions),\n });\n\n if (handler.sortOptions) {\n roomQuery.sort(handler.sortOptions);\n }\n\n return await roomQuery;\n });\n}\n\n/**\n * Call a method or return a property on a remote room.\n *\n * @param roomId - The Id of the specific room instance.\n * @param method - Method or attribute to call or retrive.\n * @param args - Array of arguments for the method\n *\n * @returns Promise<any> - Returned value from the called or retrieved method/attribute.\n */\nexport async function remoteRoomCall<R= any>(\n roomId: string,\n method: string,\n args?: any[],\n rejectionTimeout = REMOTE_ROOM_SHORT_TIMEOUT,\n): Promise<R> {\n const room = rooms[roomId];\n\n if (!room) {\n try {\n return await requestFromIPC<R>(presence, getRoomChannel(roomId), method, args, rejectionTimeout);\n\n } catch (e) {\n const request = `${method}${args && ' with args ' + JSON.stringify(args) || ''}`;\n throw new ServerError(\n ErrorCode.MATCHMAKE_UNHANDLED,\n `remote room (${roomId}) timed out, requesting \"${request}\". (${rejectionTimeout}ms exceeded)`,\n );\n }\n\n } else {\n return (!args && typeof (room[method]) !== 'function')\n ? room[method]\n : (await room[method].apply(room, args && args.map((arg) => JSON.parse(JSON.stringify(arg)))));\n }\n}\n\nexport function defineRoomType<T extends Type<Room>>(\n roomName: string,\n klass: T,\n defaultOptions?: Parameters<NonNullable<InstanceType<T>['onCreate']>>[0],\n) {\n const registeredHandler = new RegisteredHandler(klass, defaultOptions);\n\n handlers[roomName] = registeredHandler;\n\n if (klass.prototype['onAuth'] !== Room.prototype['onAuth']) {\n // TODO: soft-deprecate instance level `onAuth` on 0.16\n // console.warn(\"DEPRECATION WARNING: onAuth() at the instance level will be deprecated soon. Please use static onAuth() instead.\");\n\n if (klass['onAuth'] !== Room['onAuth']) {\n console.log(`\u274C \"${roomName}\"'s onAuth() defined at the instance level will be ignored.`);\n }\n }\n\n if (!isDevMode) {\n cleanupStaleRooms(roomName);\n }\n\n return registeredHandler;\n}\n\nexport function removeRoomType(roomName: string) {\n delete handlers[roomName];\n\n if (!isDevMode) {\n cleanupStaleRooms(roomName);\n }\n}\n\n// TODO: legacy; remove me on 1.0\nexport function hasHandler(roomName: string) {\n console.warn(\"hasHandler() is deprecated. Use getHandler() instead.\");\n return handlers[roomName] !== undefined;\n}\n\nexport function getHandler(roomName: string) {\n const handler = handlers[roomName];\n\n if (!handler) {\n throw new ServerError(ErrorCode.MATCHMAKE_NO_HANDLER, `provided room name \"${roomName}\" not defined`);\n }\n\n return handler;\n}\n\nexport function getRoomClass(roomName: string) {\n return handlers[roomName]?.klass;\n}\n\n/**\n * Creates a new room.\n *\n * @param roomName - The identifier you defined on `gameServer.define()`\n * @param clientOptions - Options for `onCreate`\n *\n * @returns Promise<RoomListingData> - A promise contaning an object which includes room metadata and configurations.\n */\nexport async function createRoom(roomName: string, clientOptions: ClientOptions): Promise<RoomListingData> {\n const selectedProcessId = await selectProcessIdToCreateRoom(roomName, clientOptions);\n\n let room: RoomListingData;\n if (selectedProcessId === processId) {\n // create the room on this process!\n room = await handleCreateRoom(roomName, clientOptions);\n\n } else {\n // ask other process to create the room!\n try {\n room = await requestFromIPC<RoomListingData>(\n presence,\n getProcessChannel(selectedProcessId),\n undefined,\n [roomName, clientOptions],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n\n } catch (e) {\n if (e.message === \"ipc_timeout\") {\n debugAndPrintError(`${e.message}: create room request timed out for ${roomName} on processId ${selectedProcessId}.`);\n\n //\n // clean-up possibly stale process from redis.\n // when a process disconnects ungracefully, it may leave its previous processId under \"roomcount\"\n // if the process is still alive, it will re-add itself shortly after the load-balancer selects it again.\n //\n await stats.excludeProcess(selectedProcessId);\n\n // if other process failed to respond, create the room on this process\n room = await handleCreateRoom(roomName, clientOptions);\n\n } else {\n // re-throw intentional exception thrown during remote onCreate()\n throw e;\n }\n }\n }\n\n if (isDevMode) {\n presence.hset(getRoomRestoreListKey(), room.roomId, JSON.stringify({\n \"clientOptions\": clientOptions,\n \"roomName\": roomName,\n \"processId\": processId\n }));\n }\n\n return room;\n}\n\nexport async function handleCreateRoom(roomName: string, clientOptions: ClientOptions, restoringRoomId?: string): Promise<RoomListingData> {\n const handler = getHandler(roomName);\n const room = new handler.klass();\n\n // set room public attributes\n if (restoringRoomId && isDevMode) {\n room.roomId = restoringRoomId;\n\n } else {\n room.roomId = generateId();\n }\n\n room.roomName = roomName;\n room.presence = presence;\n\n const additionalListingData: any = handler.getFilterOptions(clientOptions);\n\n // assign public host\n if (publicAddress) {\n additionalListingData.publicAddress = publicAddress;\n }\n\n // create a RoomCache reference.\n room.listing = driver.createInstance({\n name: roomName,\n processId,\n ...additionalListingData\n });\n\n if (room.onCreate) {\n try {\n await room.onCreate(merge({}, clientOptions, handler.options));\n\n } catch (e) {\n debugAndPrintError(e);\n throw new ServerError(\n e.code || ErrorCode.MATCHMAKE_UNHANDLED,\n e.message,\n );\n }\n }\n\n room['_internalState'] = RoomInternalState.CREATED;\n\n room.listing.roomId = room.roomId;\n room.listing.maxClients = room.maxClients;\n\n // imediatelly ask client to join the room\n debugMatchMaking('spawning \\'%s\\', roomId: %s, processId: %s', roomName, room.roomId, processId);\n\n // increment amount of rooms this process is handling\n stats.local.roomCount++;\n stats.persist();\n\n room._events.on('lock', lockRoom.bind(this, room));\n room._events.on('unlock', unlockRoom.bind(this, room));\n room._events.on('join', onClientJoinRoom.bind(this, room));\n room._events.on('leave', onClientLeaveRoom.bind(this, room));\n room._events.on('visibility-change', onVisibilityChange.bind(this, room));\n room._events.once('dispose', disposeRoom.bind(this, roomName, room));\n room._events.once('disconnect', () => room._events.removeAllListeners());\n\n // room always start unlocked\n await createRoomReferences(room, true);\n await room.listing.save();\n\n handler.emit('create', room);\n\n return room.listing;\n}\n\nexport function getRoomById(roomId: string) {\n return rooms[roomId];\n}\n\n/**\n * Disconnects every client on every room in the current process.\n */\nexport function disconnectAll(closeCode?: number) {\n const promises: Array<Promise<any>> = [];\n\n for (const roomId in rooms) {\n if (!rooms.hasOwnProperty(roomId)) { continue; }\n promises.push(rooms[roomId].disconnect(closeCode));\n }\n\n return promises;\n}\n\nexport async function gracefullyShutdown(): Promise<any> {\n if (isGracefullyShuttingDown) {\n return Promise.reject('already_shutting_down');\n }\n\n isGracefullyShuttingDown = true;\n onReady = undefined;\n\n debugMatchMaking(`${processId} is shutting down!`);\n\n if (isDevMode) {\n await cacheRoomHistory(rooms);\n }\n\n // remove processId from room count key\n await stats.excludeProcess(processId);\n\n // unsubscribe from process id channel\n presence.unsubscribe(getProcessChannel());\n\n return Promise.all(disconnectAll(\n (isDevMode)\n ? Protocol.WS_CLOSE_DEVMODE_RESTART\n : undefined\n ));\n}\n\n/**\n * Reserve a seat for a client in a room\n */\nexport async function reserveSeatFor(room: RoomListingData, options: any) {\n const sessionId: string = generateId();\n\n debugMatchMaking(\n 'reserving seat. sessionId: \\'%s\\', roomId: \\'%s\\', processId: \\'%s\\'',\n sessionId, room.roomId, processId,\n );\n\n let successfulSeatReservation: boolean;\n\n try {\n successfulSeatReservation = await remoteRoomCall(room.roomId, '_reserveSeat', [sessionId, options]);\n\n } catch (e) {\n debugMatchMaking(e);\n successfulSeatReservation = false;\n }\n\n if (!successfulSeatReservation) {\n throw new SeatReservationError(`${room.roomId} is already full.`);\n }\n\n const response: SeatReservation = { room, sessionId };\n\n if (isDevMode) {\n response.devMode = isDevMode;\n }\n\n return response;\n}\n\nasync function cleanupStaleRooms(roomName: string) {\n //\n // clean-up possibly stale room ids\n // (ungraceful shutdowns using Redis can result on stale room ids still on memory.)\n //\n const cachedRooms = await driver.find({ name: roomName }, { _id: 1 });\n\n // remove connecting counts\n await presence.del(getHandlerConcurrencyKey(roomName));\n\n await Promise.all(cachedRooms.map(async (room) => {\n try {\n // use hardcoded short timeout for cleaning up stale rooms.\n await remoteRoomCall(room.roomId, 'roomId');\n\n } catch (e) {\n debugMatchMaking(`cleaning up stale room '${roomName}', roomId: ${room.roomId}`);\n room.remove();\n }\n }));\n}\n\nasync function createRoomReferences(room: Room, init: boolean = false): Promise<boolean> {\n rooms[room.roomId] = room;\n\n if (init) {\n await subscribeIPC(\n presence,\n processId,\n getRoomChannel(room.roomId),\n (method, args) => {\n return (!args && typeof (room[method]) !== 'function')\n ? room[method]\n : room[method].apply(room, args);\n },\n );\n }\n\n return true;\n}\n\nasync function awaitRoomAvailable(roomToJoin: string, callback: Function): Promise<RoomListingData> {\n return new Promise(async (resolve, reject) => {\n const concurrencyKey = getHandlerConcurrencyKey(roomToJoin);\n const concurrency = await presence.incr(concurrencyKey) - 1;\n\n //\n // avoid having too long timeout if 10+ clients ask to join at the same time\n //\n // TODO: we need a better solution here. either a lock or queue system should be implemented instead.\n // https://github.com/colyseus/colyseus/issues/466\n //\n const concurrencyTimeout = Math.min(concurrency * 100, 500);\n\n if (concurrency > 0) {\n debugMatchMaking(\n 'receiving %d concurrent requests for joining \\'%s\\' (waiting %d ms)',\n concurrency, roomToJoin, concurrencyTimeout,\n );\n }\n\n setTimeout(async () => {\n try {\n const result = await callback();\n resolve(result);\n\n } catch (e) {\n reject(e);\n\n } finally {\n await presence.decr(concurrencyKey);\n }\n }, concurrencyTimeout);\n });\n}\n\nfunction onClientJoinRoom(room: Room, client: Client) {\n // increment local CCU\n stats.local.ccu++;\n stats.persist();\n\n handlers[room.roomName].emit('join', room, client);\n}\n\nfunction onClientLeaveRoom(room: Room, client: Client, willDispose: boolean) {\n // decrement local CCU\n stats.local.ccu--;\n stats.persist();\n\n handlers[room.roomName].emit('leave', room, client, willDispose);\n}\n\nfunction lockRoom(room: Room): void {\n // emit public event on registered handler\n handlers[room.roomName].emit('lock', room);\n}\n\nasync function unlockRoom(room: Room) {\n if (await createRoomReferences(room)) {\n // emit public event on registered handler\n handlers[room.roomName].emit('unlock', room);\n }\n}\n\nfunction onVisibilityChange(room: Room, isInvisible: boolean): void {\n handlers[room.roomName].emit('visibility-change', room, isInvisible);\n}\n\nasync function disposeRoom(roomName: string, room: Room) {\n debugMatchMaking('disposing \\'%s\\' (%s) on processId \\'%s\\'', roomName, room.roomId, processId);\n\n // decrease amount of rooms this process is handling\n if (!isGracefullyShuttingDown) {\n stats.local.roomCount--;\n stats.persist();\n\n // remove from devMode restore list\n if (isDevMode) {\n await presence.hdel(getRoomRestoreListKey(), room.roomId);\n }\n }\n\n // emit disposal on registered session handler\n handlers[roomName].emit('dispose', room);\n\n // remove concurrency key\n presence.del(getHandlerConcurrencyKey(roomName));\n\n // unsubscribe from remote connections\n presence.unsubscribe(getRoomChannel(room.roomId));\n\n // remove actual room reference\n delete rooms[room.roomId];\n}\n\n//\n// Presence keys\n//\nfunction getRoomChannel(roomId: string) {\n return `$${roomId}`;\n}\n\nfunction getHandlerConcurrencyKey(name: string) {\n return `c:${name}`;\n}\n\nfunction getProcessChannel(id: string = processId) {\n return `p:${id}`;\n}"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA,sCAAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sBAAoC;AAEpC,iBAA6C;AAE7C,mBAA8E;AAC9E,qBAA0G;AAE1G,+BAAkC;AAClC,kBAAwC;AAExC,2BAA8B;AAG9B,mBAAqD;AACrD,kCAAqC;AACrC,yBAA4B;AAE5B,oBAAiF;AACjF,wBAAuB;AACvB,YAAuB;AAEvB,oBAAuB;AAGvB,uBAA4B;
|
|
4
|
+
"sourcesContent": ["import { ErrorCode, Protocol } from './Protocol';\n\nimport { requestFromIPC, subscribeIPC } from './IPC';\n\nimport { Deferred, generateId, merge, REMOTE_ROOM_SHORT_TIMEOUT, retry } from './utils/Utils';\nimport { isDevMode, cacheRoomHistory, getPreviousProcessId, getRoomRestoreListKey, reloadFromCache } from './utils/DevMode';\n\nimport { RegisteredHandler } from './matchmaker/RegisteredHandler';\nimport { Room, RoomInternalState } from './Room';\n\nimport { LocalPresence } from './presence/LocalPresence';\nimport { Presence } from './presence/Presence';\n\nimport { debugAndPrintError, debugMatchMaking } from './Debug';\nimport { SeatReservationError } from './errors/SeatReservationError';\nimport { ServerError } from './errors/ServerError';\n\nimport { IRoomListingData, RoomListingData, LocalDriver, MatchMakerDriver } from './matchmaker/driver';\nimport controller from './matchmaker/controller';\nimport * as stats from \"./Stats\";\n\nimport { logger } from './Logger';\nimport { Client } from './Transport';\nimport { Type } from './utils/types';\nimport { getHostname } from \"./discovery\";\n\nexport { controller, stats, type MatchMakerDriver };\n\nexport type ClientOptions = any;\nexport type AuthOptions = { token?: string, request?: any };\nexport type SelectProcessIdCallback = (roomName: string, clientOptions: ClientOptions) => Promise<string>;\n\nexport interface SeatReservation {\n sessionId: string;\n room: RoomListingData;\n devMode?: boolean;\n}\n\nconst handlers: {[id: string]: RegisteredHandler} = {};\nconst rooms: {[roomId: string]: Room} = {};\n\nexport let publicAddress: string;\nexport let processId: string;\nexport let presence: Presence;\nexport let driver: MatchMakerDriver;\nexport let selectProcessIdToCreateRoom: SelectProcessIdCallback;\n\nexport let isGracefullyShuttingDown: boolean;\nexport let onReady: Deferred = new Deferred(); // onReady needs to be immediately available to @colyseus/auth integration.\n\n/**\n * @private\n */\nexport async function setup(\n _presence?: Presence,\n _driver?: MatchMakerDriver,\n _publicAddress?: string,\n _selectProcessIdToCreateRoom?: SelectProcessIdCallback,\n) {\n if (onReady === undefined) {\n //\n // for testing purposes only: onReady is turned into undefined on shutdown\n // (needs refactoring.)\n //\n onReady = new Deferred();\n }\n\n presence = _presence || new LocalPresence();\n driver = _driver || new LocalDriver();\n publicAddress = _publicAddress;\n\n // devMode: try to retrieve previous processId\n if (isDevMode) { processId = await getPreviousProcessId(await getHostname()); }\n\n // ensure processId is set\n if (!processId) { processId = generateId(); }\n\n isGracefullyShuttingDown = false;\n\n /**\n * Define default `assignRoomToProcessId` method.\n * By default, return the process with least amount of rooms created\n */\n selectProcessIdToCreateRoom = _selectProcessIdToCreateRoom || async function () {\n return (await stats.fetchAll())\n .sort((p1, p2) => p1.roomCount > p2.roomCount ? 1 : -1)[0]\n .processId;\n };\n\n /**\n * Process-level subscription\n * - handle remote process healthcheck\n * - handle remote room creation\n */\n subscribeIPC(presence, processId, getProcessChannel(), (method, args) => {\n if (method === 'healthcheck') {\n // health check for this processId\n return true;\n\n } else {\n // handle room creation\n return handleCreateRoom.apply(undefined, args);\n }\n });\n\n /**\n * Check for leftover/invalid processId's on startup\n */\n const previousStats = await stats.fetchAll();\n if (previousStats.length > 0) {\n logger.debug(`${previousStats.length} previous processId(s) found, health-checking...`);\n await Promise.all(previousStats.map(async (stat) => {\n try {\n await requestFromIPC<RoomListingData>(\n presence,\n getProcessChannel(stat.processId),\n 'healthcheck',\n [],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n // process succeeded to respond - nothing to do\n } catch (e) {\n // process failed to respond - remove it from stats\n logger.debug(`process ${stat.processId} failed to respond. excluding from stats`);\n await stats.excludeProcess(stat.processId);\n }\n }));\n }\n\n await stats.reset();\n\n if (isDevMode) {\n await reloadFromCache();\n }\n\n onReady.resolve();\n}\n\n/**\n * Join or create into a room and return seat reservation\n */\nexport async function joinOrCreate(roomName: string, clientOptions: ClientOptions = {}, authOptions?: AuthOptions) {\n return await retry<Promise<SeatReservation>>(async () => {\n const authData = await callOnAuth(roomName, authOptions);\n let room = await findOneRoomAvailable(roomName, clientOptions);\n\n //\n // TODO [?]\n // should we expose the \"creator\" auth data of the room during `onCreate()`?\n // it would be useful, though it could be accessed via `onJoin()` for now.\n //\n\n if (!room) {\n room = await createRoom(roomName, clientOptions);\n }\n\n return await reserveSeatFor(room, clientOptions, authData);\n }, 5, [SeatReservationError]);\n}\n\n/**\n * Create a room and return seat reservation\n */\nexport async function create(roomName: string, clientOptions: ClientOptions = {}, authOptions?: AuthOptions) {\n const authData = await callOnAuth(roomName, authOptions);\n const room = await createRoom(roomName, clientOptions);\n return reserveSeatFor(room, clientOptions, authData);\n}\n\n/**\n * Join a room and return seat reservation\n */\nexport async function join(roomName: string, clientOptions: ClientOptions = {}, authOptions?: AuthOptions) {\n return await retry<Promise<SeatReservation>>(async () => {\n const authData = await callOnAuth(roomName, authOptions);\n const room = await findOneRoomAvailable(roomName, clientOptions);\n\n if (!room) {\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_CRITERIA, `no rooms found with provided criteria`);\n }\n\n return reserveSeatFor(room, clientOptions, authData);\n });\n}\n\n/**\n * Join a room by id and return seat reservation\n */\nexport async function reconnect(roomId: string, clientOptions: ClientOptions = {}) {\n const room = await driver.findOne({ roomId });\n if (!room) {\n // TODO: support a \"logLevel\" out of the box?\n if (process.env.NODE_ENV !== 'production') {\n logger.info(`\u274C room \"${roomId}\" has been disposed. Did you missed .allowReconnection()?\\n\uD83D\uDC49 https://docs.colyseus.io/server/room/#allowreconnection-client-seconds`);\n }\n\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room \"${roomId}\" has been disposed.`);\n }\n\n // check for reconnection\n const reconnectionToken = clientOptions.reconnectionToken;\n if (!reconnectionToken) { throw new ServerError(ErrorCode.MATCHMAKE_UNHANDLED, `'reconnectionToken' must be provided for reconnection.`); }\n\n\n // respond to re-connection!\n const sessionId = await remoteRoomCall(room.roomId, 'checkReconnectionToken', [reconnectionToken]);\n if (sessionId) {\n return { room, sessionId };\n\n } else {\n // TODO: support a \"logLevel\" out of the box?\n if (process.env.NODE_ENV !== 'production') {\n logger.info(`\u274C reconnection token invalid or expired. Did you missed .allowReconnection()?\\n\uD83D\uDC49 https://docs.colyseus.io/server/room/#allowreconnection-client-seconds`);\n }\n throw new ServerError(ErrorCode.MATCHMAKE_EXPIRED, `reconnection token invalid or expired.`);\n }\n}\n\n/**\n * Join a room by id and return client seat reservation. An exception is thrown if a room is not found for roomId.\n *\n * @param roomId - The Id of the specific room instance.\n * @param clientOptions - Options for the client seat reservation (for `onJoin`/`onAuth`)\n *\n * @returns Promise<SeatReservation> - A promise which contains `sessionId` and `RoomListingData`.\n */\nexport async function joinById(roomId: string, clientOptions: ClientOptions = {}, authOptions?: AuthOptions) {\n const room = await driver.findOne({ roomId });\n\n if (!room) {\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room \"${roomId}\" not found`);\n\n } else if (room.locked) {\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room \"${roomId}\" is locked`);\n }\n\n const authData = await callOnAuth(room.name, authOptions);\n\n return reserveSeatFor(room, clientOptions, authData);\n}\n\n/**\n * Perform a query for all cached rooms\n */\nexport async function query(conditions: Partial<IRoomListingData> = {}) {\n return await driver.find(conditions);\n}\n\n/**\n * Find for a public and unlocked room available.\n *\n * @param roomName - The Id of the specific room.\n * @param clientOptions - Options for the client seat reservation (for `onJoin`/`onAuth`).\n *\n * @returns Promise<RoomListingData> - A promise contaning an object which includes room metadata and configurations.\n */\nexport async function findOneRoomAvailable(roomName: string, clientOptions: ClientOptions): Promise<RoomListingData> {\n return await awaitRoomAvailable(roomName, async () => {\n const handler = getHandler(roomName);\n\n const roomQuery = driver.findOne({\n locked: false,\n name: roomName,\n private: false,\n ...handler.getFilterOptions(clientOptions),\n });\n\n if (handler.sortOptions) {\n roomQuery.sort(handler.sortOptions);\n }\n\n return await roomQuery;\n });\n}\n\n/**\n * Call a method or return a property on a remote room.\n *\n * @param roomId - The Id of the specific room instance.\n * @param method - Method or attribute to call or retrive.\n * @param args - Array of arguments for the method\n *\n * @returns Promise<any> - Returned value from the called or retrieved method/attribute.\n */\nexport async function remoteRoomCall<R= any>(\n roomId: string,\n method: string,\n args?: any[],\n rejectionTimeout = REMOTE_ROOM_SHORT_TIMEOUT,\n): Promise<R> {\n const room = rooms[roomId];\n\n if (!room) {\n try {\n return await requestFromIPC<R>(presence, getRoomChannel(roomId), method, args, rejectionTimeout);\n\n } catch (e) {\n const request = `${method}${args && ' with args ' + JSON.stringify(args) || ''}`;\n throw new ServerError(\n ErrorCode.MATCHMAKE_UNHANDLED,\n `remote room (${roomId}) timed out, requesting \"${request}\". (${rejectionTimeout}ms exceeded)`,\n );\n }\n\n } else {\n return (!args && typeof (room[method]) !== 'function')\n ? room[method]\n : (await room[method].apply(room, args && JSON.parse(JSON.stringify(args))));\n }\n}\n\nexport function defineRoomType<T extends Type<Room>>(\n roomName: string,\n klass: T,\n defaultOptions?: Parameters<NonNullable<InstanceType<T>['onCreate']>>[0],\n) {\n const registeredHandler = new RegisteredHandler(klass, defaultOptions);\n\n handlers[roomName] = registeredHandler;\n\n if (klass.prototype['onAuth'] !== Room.prototype['onAuth']) {\n // TODO: soft-deprecate instance level `onAuth` on 0.16\n // console.warn(\"DEPRECATION WARNING: onAuth() at the instance level will be deprecated soon. Please use static onAuth() instead.\");\n\n if (klass['onAuth'] !== Room['onAuth']) {\n console.log(`\u274C \"${roomName}\"'s onAuth() defined at the instance level will be ignored.`);\n }\n }\n\n if (!isDevMode) {\n cleanupStaleRooms(roomName);\n }\n\n return registeredHandler;\n}\n\nexport function removeRoomType(roomName: string) {\n delete handlers[roomName];\n\n if (!isDevMode) {\n cleanupStaleRooms(roomName);\n }\n}\n\n// TODO: legacy; remove me on 1.0\nexport function hasHandler(roomName: string) {\n console.warn(\"hasHandler() is deprecated. Use getHandler() instead.\");\n return handlers[roomName] !== undefined;\n}\n\nexport function getHandler(roomName: string) {\n const handler = handlers[roomName];\n\n if (!handler) {\n throw new ServerError(ErrorCode.MATCHMAKE_NO_HANDLER, `provided room name \"${roomName}\" not defined`);\n }\n\n return handler;\n}\n\nexport function getRoomClass(roomName: string): Type<Room> {\n return handlers[roomName]?.klass;\n}\n\n\n/**\n * Creates a new room.\n *\n * @param roomName - The identifier you defined on `gameServer.define()`\n * @param clientOptions - Options for `onCreate`\n *\n * @returns Promise<RoomListingData> - A promise contaning an object which includes room metadata and configurations.\n */\nexport async function createRoom(roomName: string, clientOptions: ClientOptions): Promise<RoomListingData> {\n const selectedProcessId = await selectProcessIdToCreateRoom(roomName, clientOptions);\n\n let room: RoomListingData;\n if (selectedProcessId === processId) {\n // create the room on this process!\n room = await handleCreateRoom(roomName, clientOptions);\n\n } else {\n // ask other process to create the room!\n try {\n room = await requestFromIPC<RoomListingData>(\n presence,\n getProcessChannel(selectedProcessId),\n undefined,\n [roomName, clientOptions],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n\n } catch (e) {\n if (e.message === \"ipc_timeout\") {\n debugAndPrintError(`${e.message}: create room request timed out for ${roomName} on processId ${selectedProcessId}.`);\n\n //\n // clean-up possibly stale process from redis.\n // when a process disconnects ungracefully, it may leave its previous processId under \"roomcount\"\n // if the process is still alive, it will re-add itself shortly after the load-balancer selects it again.\n //\n await stats.excludeProcess(selectedProcessId);\n\n // if other process failed to respond, create the room on this process\n room = await handleCreateRoom(roomName, clientOptions);\n\n } else {\n // re-throw intentional exception thrown during remote onCreate()\n throw e;\n }\n }\n }\n\n if (isDevMode) {\n presence.hset(getRoomRestoreListKey(), room.roomId, JSON.stringify({\n \"clientOptions\": clientOptions,\n \"roomName\": roomName,\n \"processId\": processId\n }));\n }\n\n return room;\n}\n\nexport async function handleCreateRoom(roomName: string, clientOptions: ClientOptions, restoringRoomId?: string): Promise<RoomListingData> {\n const handler = getHandler(roomName);\n const room = new handler.klass();\n\n // set room public attributes\n if (restoringRoomId && isDevMode) {\n room.roomId = restoringRoomId;\n\n } else {\n room.roomId = generateId();\n }\n\n room.roomName = roomName;\n room.presence = presence;\n\n const additionalListingData: any = handler.getFilterOptions(clientOptions);\n\n // assign public host\n if (publicAddress) {\n additionalListingData.publicAddress = publicAddress;\n }\n\n // create a RoomCache reference.\n room.listing = driver.createInstance({\n name: roomName,\n processId,\n ...additionalListingData\n });\n\n if (room.onCreate) {\n try {\n await room.onCreate(merge({}, clientOptions, handler.options));\n\n } catch (e) {\n debugAndPrintError(e);\n throw new ServerError(\n e.code || ErrorCode.MATCHMAKE_UNHANDLED,\n e.message,\n );\n }\n }\n\n room['_internalState'] = RoomInternalState.CREATED;\n\n room.listing.roomId = room.roomId;\n room.listing.maxClients = room.maxClients;\n\n // imediatelly ask client to join the room\n debugMatchMaking('spawning \\'%s\\', roomId: %s, processId: %s', roomName, room.roomId, processId);\n\n // increment amount of rooms this process is handling\n stats.local.roomCount++;\n stats.persist();\n\n room._events.on('lock', lockRoom.bind(this, room));\n room._events.on('unlock', unlockRoom.bind(this, room));\n room._events.on('join', onClientJoinRoom.bind(this, room));\n room._events.on('leave', onClientLeaveRoom.bind(this, room));\n room._events.on('visibility-change', onVisibilityChange.bind(this, room));\n room._events.once('dispose', disposeRoom.bind(this, roomName, room));\n room._events.once('disconnect', () => room._events.removeAllListeners());\n\n // room always start unlocked\n await createRoomReferences(room, true);\n await room.listing.save();\n\n handler.emit('create', room);\n\n return room.listing;\n}\n\nexport function getRoomById(roomId: string) {\n return rooms[roomId];\n}\n\n/**\n * Disconnects every client on every room in the current process.\n */\nexport function disconnectAll(closeCode?: number) {\n const promises: Array<Promise<any>> = [];\n\n for (const roomId in rooms) {\n if (!rooms.hasOwnProperty(roomId)) { continue; }\n promises.push(rooms[roomId].disconnect(closeCode));\n }\n\n return promises;\n}\n\nexport async function gracefullyShutdown(): Promise<any> {\n if (isGracefullyShuttingDown) {\n return Promise.reject('already_shutting_down');\n }\n\n isGracefullyShuttingDown = true;\n onReady = undefined;\n\n debugMatchMaking(`${processId} is shutting down!`);\n\n if (isDevMode) {\n await cacheRoomHistory(rooms);\n }\n\n // remove processId from room count key\n await stats.excludeProcess(processId);\n\n // unsubscribe from process id channel\n presence.unsubscribe(getProcessChannel());\n\n return Promise.all(disconnectAll(\n (isDevMode)\n ? Protocol.WS_CLOSE_DEVMODE_RESTART\n : undefined\n ));\n}\n\n/**\n * Reserve a seat for a client in a room\n */\nexport async function reserveSeatFor(room: RoomListingData, options: ClientOptions, authData?: any) {\n const sessionId: string = generateId();\n\n debugMatchMaking(\n 'reserving seat. sessionId: \\'%s\\', roomId: \\'%s\\', processId: \\'%s\\'',\n sessionId, room.roomId, processId,\n );\n\n let successfulSeatReservation: boolean;\n\n try {\n successfulSeatReservation = await remoteRoomCall(room.roomId, '_reserveSeat', [sessionId, options, authData]);\n\n } catch (e) {\n debugMatchMaking(e);\n successfulSeatReservation = false;\n }\n\n if (!successfulSeatReservation) {\n throw new SeatReservationError(`${room.roomId} is already full.`);\n }\n\n const response: SeatReservation = { room, sessionId };\n\n if (isDevMode) {\n response.devMode = isDevMode;\n }\n\n return response;\n}\n\nfunction callOnAuth(roomName: string, authOptions?: AuthOptions) {\n const roomClass = getRoomClass(roomName);\n return (roomClass && roomClass['onAuth'] && roomClass['onAuth'] !== Room['onAuth'])\n ? roomClass['onAuth'](authOptions.token, authOptions.request)\n : undefined;\n}\n\nasync function cleanupStaleRooms(roomName: string) {\n //\n // clean-up possibly stale room ids\n // (ungraceful shutdowns using Redis can result on stale room ids still on memory.)\n //\n const cachedRooms = await driver.find({ name: roomName }, { _id: 1 });\n\n // remove connecting counts\n await presence.del(getHandlerConcurrencyKey(roomName));\n\n await Promise.all(cachedRooms.map(async (room) => {\n try {\n // use hardcoded short timeout for cleaning up stale rooms.\n await remoteRoomCall(room.roomId, 'roomId');\n\n } catch (e) {\n debugMatchMaking(`cleaning up stale room '${roomName}', roomId: ${room.roomId}`);\n room.remove();\n }\n }));\n}\n\nasync function createRoomReferences(room: Room, init: boolean = false): Promise<boolean> {\n rooms[room.roomId] = room;\n\n if (init) {\n await subscribeIPC(\n presence,\n processId,\n getRoomChannel(room.roomId),\n (method, args) => {\n return (!args && typeof (room[method]) !== 'function')\n ? room[method]\n : room[method].apply(room, args);\n },\n );\n }\n\n return true;\n}\n\nasync function awaitRoomAvailable(roomToJoin: string, callback: Function): Promise<RoomListingData> {\n return new Promise(async (resolve, reject) => {\n const concurrencyKey = getHandlerConcurrencyKey(roomToJoin);\n const concurrency = await presence.incr(concurrencyKey) - 1;\n\n //\n // avoid having too long timeout if 10+ clients ask to join at the same time\n //\n // TODO: we need a better solution here. either a lock or queue system should be implemented instead.\n // https://github.com/colyseus/colyseus/issues/466\n //\n const concurrencyTimeout = Math.min(concurrency * 100, 500);\n\n if (concurrency > 0) {\n debugMatchMaking(\n 'receiving %d concurrent requests for joining \\'%s\\' (waiting %d ms)',\n concurrency, roomToJoin, concurrencyTimeout,\n );\n }\n\n setTimeout(async () => {\n try {\n const result = await callback();\n resolve(result);\n\n } catch (e) {\n reject(e);\n\n } finally {\n await presence.decr(concurrencyKey);\n }\n }, concurrencyTimeout);\n });\n}\n\nfunction onClientJoinRoom(room: Room, client: Client) {\n // increment local CCU\n stats.local.ccu++;\n stats.persist();\n\n handlers[room.roomName].emit('join', room, client);\n}\n\nfunction onClientLeaveRoom(room: Room, client: Client, willDispose: boolean) {\n // decrement local CCU\n stats.local.ccu--;\n stats.persist();\n\n handlers[room.roomName].emit('leave', room, client, willDispose);\n}\n\nfunction lockRoom(room: Room): void {\n // emit public event on registered handler\n handlers[room.roomName].emit('lock', room);\n}\n\nasync function unlockRoom(room: Room) {\n if (await createRoomReferences(room)) {\n // emit public event on registered handler\n handlers[room.roomName].emit('unlock', room);\n }\n}\n\nfunction onVisibilityChange(room: Room, isInvisible: boolean): void {\n handlers[room.roomName].emit('visibility-change', room, isInvisible);\n}\n\nasync function disposeRoom(roomName: string, room: Room) {\n debugMatchMaking('disposing \\'%s\\' (%s) on processId \\'%s\\'', roomName, room.roomId, processId);\n\n // decrease amount of rooms this process is handling\n if (!isGracefullyShuttingDown) {\n stats.local.roomCount--;\n stats.persist();\n\n // remove from devMode restore list\n if (isDevMode) {\n await presence.hdel(getRoomRestoreListKey(), room.roomId);\n }\n }\n\n // emit disposal on registered session handler\n handlers[roomName].emit('dispose', room);\n\n // remove concurrency key\n presence.del(getHandlerConcurrencyKey(roomName));\n\n // unsubscribe from remote connections\n presence.unsubscribe(getRoomChannel(room.roomId));\n\n // remove actual room reference\n delete rooms[room.roomId];\n}\n\n//\n// Presence keys\n//\nfunction getRoomChannel(roomId: string) {\n return `$${roomId}`;\n}\n\nfunction getHandlerConcurrencyKey(name: string) {\n return `c:${name}`;\n}\n\nfunction getProcessChannel(id: string = processId) {\n return `p:${id}`;\n}"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["controller"]
|
|
7
7
|
}
|
package/build/MatchMaker.mjs
CHANGED
|
@@ -70,26 +70,29 @@ async function setup(_presence, _driver, _publicAddress, _selectProcessIdToCreat
|
|
|
70
70
|
}
|
|
71
71
|
onReady.resolve();
|
|
72
72
|
}
|
|
73
|
-
async function joinOrCreate(roomName, clientOptions = {}) {
|
|
73
|
+
async function joinOrCreate(roomName, clientOptions = {}, authOptions) {
|
|
74
74
|
return await retry(async () => {
|
|
75
|
+
const authData = await callOnAuth(roomName, authOptions);
|
|
75
76
|
let room = await findOneRoomAvailable(roomName, clientOptions);
|
|
76
77
|
if (!room) {
|
|
77
78
|
room = await createRoom(roomName, clientOptions);
|
|
78
79
|
}
|
|
79
|
-
return await reserveSeatFor(room, clientOptions);
|
|
80
|
+
return await reserveSeatFor(room, clientOptions, authData);
|
|
80
81
|
}, 5, [SeatReservationError]);
|
|
81
82
|
}
|
|
82
|
-
async function create(roomName, clientOptions = {}) {
|
|
83
|
+
async function create(roomName, clientOptions = {}, authOptions) {
|
|
84
|
+
const authData = await callOnAuth(roomName, authOptions);
|
|
83
85
|
const room = await createRoom(roomName, clientOptions);
|
|
84
|
-
return reserveSeatFor(room, clientOptions);
|
|
86
|
+
return reserveSeatFor(room, clientOptions, authData);
|
|
85
87
|
}
|
|
86
|
-
async function join(roomName, clientOptions = {}) {
|
|
88
|
+
async function join(roomName, clientOptions = {}, authOptions) {
|
|
87
89
|
return await retry(async () => {
|
|
90
|
+
const authData = await callOnAuth(roomName, authOptions);
|
|
88
91
|
const room = await findOneRoomAvailable(roomName, clientOptions);
|
|
89
92
|
if (!room) {
|
|
90
93
|
throw new ServerError(ErrorCode.MATCHMAKE_INVALID_CRITERIA, `no rooms found with provided criteria`);
|
|
91
94
|
}
|
|
92
|
-
return reserveSeatFor(room, clientOptions);
|
|
95
|
+
return reserveSeatFor(room, clientOptions, authData);
|
|
93
96
|
});
|
|
94
97
|
}
|
|
95
98
|
async function reconnect(roomId, clientOptions = {}) {
|
|
@@ -116,14 +119,15 @@ async function reconnect(roomId, clientOptions = {}) {
|
|
|
116
119
|
throw new ServerError(ErrorCode.MATCHMAKE_EXPIRED, `reconnection token invalid or expired.`);
|
|
117
120
|
}
|
|
118
121
|
}
|
|
119
|
-
async function joinById(roomId, clientOptions = {}) {
|
|
122
|
+
async function joinById(roomId, clientOptions = {}, authOptions) {
|
|
120
123
|
const room = await driver.findOne({ roomId });
|
|
121
124
|
if (!room) {
|
|
122
125
|
throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room "${roomId}" not found`);
|
|
123
126
|
} else if (room.locked) {
|
|
124
127
|
throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room "${roomId}" is locked`);
|
|
125
128
|
}
|
|
126
|
-
|
|
129
|
+
const authData = await callOnAuth(room.name, authOptions);
|
|
130
|
+
return reserveSeatFor(room, clientOptions, authData);
|
|
127
131
|
}
|
|
128
132
|
async function query(conditions = {}) {
|
|
129
133
|
return await driver.find(conditions);
|
|
@@ -156,7 +160,7 @@ async function remoteRoomCall(roomId, method, args, rejectionTimeout = REMOTE_RO
|
|
|
156
160
|
);
|
|
157
161
|
}
|
|
158
162
|
} else {
|
|
159
|
-
return !args && typeof room[method] !== "function" ? room[method] : await room[method].apply(room, args &&
|
|
163
|
+
return !args && typeof room[method] !== "function" ? room[method] : await room[method].apply(room, args && JSON.parse(JSON.stringify(args)));
|
|
160
164
|
}
|
|
161
165
|
}
|
|
162
166
|
function defineRoomType(roomName, klass, defaultOptions) {
|
|
@@ -302,7 +306,7 @@ async function gracefullyShutdown() {
|
|
|
302
306
|
isDevMode ? Protocol.WS_CLOSE_DEVMODE_RESTART : void 0
|
|
303
307
|
));
|
|
304
308
|
}
|
|
305
|
-
async function reserveSeatFor(room, options) {
|
|
309
|
+
async function reserveSeatFor(room, options, authData) {
|
|
306
310
|
const sessionId = generateId();
|
|
307
311
|
debugMatchMaking(
|
|
308
312
|
"reserving seat. sessionId: '%s', roomId: '%s', processId: '%s'",
|
|
@@ -312,7 +316,7 @@ async function reserveSeatFor(room, options) {
|
|
|
312
316
|
);
|
|
313
317
|
let successfulSeatReservation;
|
|
314
318
|
try {
|
|
315
|
-
successfulSeatReservation = await remoteRoomCall(room.roomId, "_reserveSeat", [sessionId, options]);
|
|
319
|
+
successfulSeatReservation = await remoteRoomCall(room.roomId, "_reserveSeat", [sessionId, options, authData]);
|
|
316
320
|
} catch (e) {
|
|
317
321
|
debugMatchMaking(e);
|
|
318
322
|
successfulSeatReservation = false;
|
|
@@ -326,6 +330,10 @@ async function reserveSeatFor(room, options) {
|
|
|
326
330
|
}
|
|
327
331
|
return response;
|
|
328
332
|
}
|
|
333
|
+
function callOnAuth(roomName, authOptions) {
|
|
334
|
+
const roomClass = getRoomClass(roomName);
|
|
335
|
+
return roomClass && roomClass["onAuth"] && roomClass["onAuth"] !== Room["onAuth"] ? roomClass["onAuth"](authOptions.token, authOptions.request) : void 0;
|
|
336
|
+
}
|
|
329
337
|
async function cleanupStaleRooms(roomName) {
|
|
330
338
|
const cachedRooms = await driver.find({ name: roomName }, { _id: 1 });
|
|
331
339
|
await presence.del(getHandlerConcurrencyKey(roomName));
|
package/build/MatchMaker.mjs.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/MatchMaker.ts"],
|
|
4
|
-
"sourcesContent": ["import { ErrorCode, Protocol } from './Protocol';\n\nimport { requestFromIPC, subscribeIPC } from './IPC';\n\nimport { Deferred, generateId, merge, REMOTE_ROOM_SHORT_TIMEOUT, retry } from './utils/Utils';\nimport { isDevMode, cacheRoomHistory, getPreviousProcessId, getRoomRestoreListKey, reloadFromCache } from './utils/DevMode';\n\nimport { RegisteredHandler } from './matchmaker/RegisteredHandler';\nimport { Room, RoomInternalState } from './Room';\n\nimport { LocalPresence } from './presence/LocalPresence';\nimport { Presence } from './presence/Presence';\n\nimport { debugAndPrintError, debugMatchMaking } from './Debug';\nimport { SeatReservationError } from './errors/SeatReservationError';\nimport { ServerError } from './errors/ServerError';\n\nimport { IRoomListingData, RoomListingData, LocalDriver, MatchMakerDriver } from './matchmaker/driver';\nimport controller from './matchmaker/controller';\nimport * as stats from \"./Stats\";\n\nimport { logger } from './Logger';\nimport { Client } from './Transport';\nimport { Type } from './utils/types';\nimport { getHostname } from \"./discovery\";\n\nexport { controller, stats, type MatchMakerDriver };\n\nexport type ClientOptions = any;\nexport type SelectProcessIdCallback = (roomName: string, clientOptions: ClientOptions) => Promise<string>;\n\nexport interface SeatReservation {\n sessionId: string;\n room: RoomListingData;\n devMode?: boolean;\n}\n\nconst handlers: {[id: string]: RegisteredHandler} = {};\nconst rooms: {[roomId: string]: Room} = {};\n\nexport let publicAddress: string;\nexport let processId: string;\nexport let presence: Presence;\nexport let driver: MatchMakerDriver;\nexport let selectProcessIdToCreateRoom: SelectProcessIdCallback;\n\nexport let isGracefullyShuttingDown: boolean;\nexport let onReady: Deferred = new Deferred(); // onReady needs to be immediately available to @colyseus/auth integration.\n\n/**\n * @private\n */\nexport async function setup(\n _presence?: Presence,\n _driver?: MatchMakerDriver,\n _publicAddress?: string,\n _selectProcessIdToCreateRoom?: SelectProcessIdCallback,\n) {\n if (onReady === undefined) {\n //\n // for testing purposes only: onReady is turned into undefined on shutdown\n // (needs refactoring.)\n //\n onReady = new Deferred();\n }\n\n presence = _presence || new LocalPresence();\n driver = _driver || new LocalDriver();\n publicAddress = _publicAddress;\n\n // devMode: try to retrieve previous processId\n if (isDevMode) { processId = await getPreviousProcessId(await getHostname()); }\n\n // ensure processId is set\n if (!processId) { processId = generateId(); }\n\n isGracefullyShuttingDown = false;\n\n /**\n * Define default `assignRoomToProcessId` method.\n * By default, return the process with least amount of rooms created\n */\n selectProcessIdToCreateRoom = _selectProcessIdToCreateRoom || async function () {\n return (await stats.fetchAll())\n .sort((p1, p2) => p1.roomCount > p2.roomCount ? 1 : -1)[0]\n .processId;\n };\n\n /**\n * Process-level subscription\n * - handle remote process healthcheck\n * - handle remote room creation\n */\n subscribeIPC(presence, processId, getProcessChannel(), (method, args) => {\n if (method === 'healthcheck') {\n // health check for this processId\n return true;\n\n } else {\n // handle room creation\n return handleCreateRoom.apply(undefined, args);\n }\n });\n\n /**\n * Check for leftover/invalid processId's on startup\n */\n const previousStats = await stats.fetchAll();\n if (previousStats.length > 0) {\n logger.debug(`${previousStats.length} previous processId(s) found, health-checking...`);\n await Promise.all(previousStats.map(async (stat) => {\n try {\n await requestFromIPC<RoomListingData>(\n presence,\n getProcessChannel(stat.processId),\n 'healthcheck',\n [],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n // process succeeded to respond - nothing to do\n } catch (e) {\n // process failed to respond - remove it from stats\n logger.debug(`process ${stat.processId} failed to respond. excluding from stats`);\n await stats.excludeProcess(stat.processId);\n }\n }));\n }\n\n await stats.reset();\n\n if (isDevMode) {\n await reloadFromCache();\n }\n\n onReady.resolve();\n}\n\n/**\n * Join or create into a room and return seat reservation\n */\nexport async function joinOrCreate(roomName: string, clientOptions: ClientOptions = {}) {\n return await retry<Promise<SeatReservation>>(async () => {\n let room = await findOneRoomAvailable(roomName, clientOptions);\n\n if (!room) {\n room = await createRoom(roomName, clientOptions);\n }\n\n return await reserveSeatFor(room, clientOptions);\n }, 5, [SeatReservationError]);\n}\n\n/**\n * Create a room and return seat reservation\n */\nexport async function create(roomName: string, clientOptions: ClientOptions = {}) {\n const room = await createRoom(roomName, clientOptions);\n return reserveSeatFor(room, clientOptions);\n}\n\n/**\n * Join a room and return seat reservation\n */\nexport async function join(roomName: string, clientOptions: ClientOptions = {}) {\n return await retry<Promise<SeatReservation>>(async () => {\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);\n });\n}\n\n/**\n * Join a room by id and return seat reservation\n */\nexport async function reconnect(roomId: string, clientOptions: ClientOptions = {}) {\n const room = await driver.findOne({ roomId });\n if (!room) {\n // TODO: support a \"logLevel\" out of the box?\n if (process.env.NODE_ENV !== 'production') {\n logger.info(`\u274C room \"${roomId}\" has been disposed. Did you missed .allowReconnection()?\\n\uD83D\uDC49 https://docs.colyseus.io/server/room/#allowreconnection-client-seconds`);\n }\n\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room \"${roomId}\" has been disposed.`);\n }\n\n // check for reconnection\n const reconnectionToken = clientOptions.reconnectionToken;\n if (!reconnectionToken) { throw new ServerError(ErrorCode.MATCHMAKE_UNHANDLED, `'reconnectionToken' must be provided for reconnection.`); }\n\n\n // respond to re-connection!\n const sessionId = await remoteRoomCall(room.roomId, 'checkReconnectionToken', [reconnectionToken]);\n if (sessionId) {\n return { room, sessionId };\n\n } else {\n // TODO: support a \"logLevel\" out of the box?\n if (process.env.NODE_ENV !== 'production') {\n logger.info(`\u274C reconnection token invalid or expired. Did you missed .allowReconnection()?\\n\uD83D\uDC49 https://docs.colyseus.io/server/room/#allowreconnection-client-seconds`);\n }\n throw new ServerError(ErrorCode.MATCHMAKE_EXPIRED, `reconnection token invalid or expired.`);\n }\n}\n\n/**\n * Join a room by id and return client seat reservation. An exception is thrown if a room is not found for roomId.\n *\n * @param roomId - The Id of the specific room instance.\n * @param clientOptions - Options for the client seat reservation (for `onJoin`/`onAuth`)\n *\n * @returns Promise<SeatReservation> - A promise which contains `sessionId` and `RoomListingData`.\n */\nexport async function joinById(roomId: string, clientOptions: ClientOptions = {}) {\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 return reserveSeatFor(room, clientOptions);\n}\n\n/**\n * Perform a query for all cached rooms\n */\nexport async function query(conditions: Partial<IRoomListingData> = {}) {\n return await driver.find(conditions);\n}\n\n/**\n * Find for a public and unlocked room available.\n *\n * @param roomName - The Id of the specific room.\n * @param clientOptions - Options for the client seat reservation (for `onJoin`/`onAuth`).\n *\n * @returns Promise<RoomListingData> - A promise contaning an object which includes room metadata and configurations.\n */\nexport async function findOneRoomAvailable(roomName: string, clientOptions: ClientOptions): Promise<RoomListingData> {\n return await awaitRoomAvailable(roomName, async () => {\n const handler = getHandler(roomName);\n\n const roomQuery = driver.findOne({\n locked: false,\n name: roomName,\n private: false,\n ...handler.getFilterOptions(clientOptions),\n });\n\n if (handler.sortOptions) {\n roomQuery.sort(handler.sortOptions);\n }\n\n return await roomQuery;\n });\n}\n\n/**\n * Call a method or return a property on a remote room.\n *\n * @param roomId - The Id of the specific room instance.\n * @param method - Method or attribute to call or retrive.\n * @param args - Array of arguments for the method\n *\n * @returns Promise<any> - Returned value from the called or retrieved method/attribute.\n */\nexport async function remoteRoomCall<R= any>(\n roomId: string,\n method: string,\n args?: any[],\n rejectionTimeout = REMOTE_ROOM_SHORT_TIMEOUT,\n): Promise<R> {\n const room = rooms[roomId];\n\n if (!room) {\n try {\n return await requestFromIPC<R>(presence, getRoomChannel(roomId), method, args, rejectionTimeout);\n\n } catch (e) {\n const request = `${method}${args && ' with args ' + JSON.stringify(args) || ''}`;\n throw new ServerError(\n ErrorCode.MATCHMAKE_UNHANDLED,\n `remote room (${roomId}) timed out, requesting \"${request}\". (${rejectionTimeout}ms exceeded)`,\n );\n }\n\n } else {\n return (!args && typeof (room[method]) !== 'function')\n ? room[method]\n : (await room[method].apply(room, args && args.map((arg) => JSON.parse(JSON.stringify(arg)))));\n }\n}\n\nexport function defineRoomType<T extends Type<Room>>(\n roomName: string,\n klass: T,\n defaultOptions?: Parameters<NonNullable<InstanceType<T>['onCreate']>>[0],\n) {\n const registeredHandler = new RegisteredHandler(klass, defaultOptions);\n\n handlers[roomName] = registeredHandler;\n\n if (klass.prototype['onAuth'] !== Room.prototype['onAuth']) {\n // TODO: soft-deprecate instance level `onAuth` on 0.16\n // console.warn(\"DEPRECATION WARNING: onAuth() at the instance level will be deprecated soon. Please use static onAuth() instead.\");\n\n if (klass['onAuth'] !== Room['onAuth']) {\n console.log(`\u274C \"${roomName}\"'s onAuth() defined at the instance level will be ignored.`);\n }\n }\n\n if (!isDevMode) {\n cleanupStaleRooms(roomName);\n }\n\n return registeredHandler;\n}\n\nexport function removeRoomType(roomName: string) {\n delete handlers[roomName];\n\n if (!isDevMode) {\n cleanupStaleRooms(roomName);\n }\n}\n\n// TODO: legacy; remove me on 1.0\nexport function hasHandler(roomName: string) {\n console.warn(\"hasHandler() is deprecated. Use getHandler() instead.\");\n return handlers[roomName] !== undefined;\n}\n\nexport function getHandler(roomName: string) {\n const handler = handlers[roomName];\n\n if (!handler) {\n throw new ServerError(ErrorCode.MATCHMAKE_NO_HANDLER, `provided room name \"${roomName}\" not defined`);\n }\n\n return handler;\n}\n\nexport function getRoomClass(roomName: string) {\n return handlers[roomName]?.klass;\n}\n\n/**\n * Creates a new room.\n *\n * @param roomName - The identifier you defined on `gameServer.define()`\n * @param clientOptions - Options for `onCreate`\n *\n * @returns Promise<RoomListingData> - A promise contaning an object which includes room metadata and configurations.\n */\nexport async function createRoom(roomName: string, clientOptions: ClientOptions): Promise<RoomListingData> {\n const selectedProcessId = await selectProcessIdToCreateRoom(roomName, clientOptions);\n\n let room: RoomListingData;\n if (selectedProcessId === processId) {\n // create the room on this process!\n room = await handleCreateRoom(roomName, clientOptions);\n\n } else {\n // ask other process to create the room!\n try {\n room = await requestFromIPC<RoomListingData>(\n presence,\n getProcessChannel(selectedProcessId),\n undefined,\n [roomName, clientOptions],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n\n } catch (e) {\n if (e.message === \"ipc_timeout\") {\n debugAndPrintError(`${e.message}: create room request timed out for ${roomName} on processId ${selectedProcessId}.`);\n\n //\n // clean-up possibly stale process from redis.\n // when a process disconnects ungracefully, it may leave its previous processId under \"roomcount\"\n // if the process is still alive, it will re-add itself shortly after the load-balancer selects it again.\n //\n await stats.excludeProcess(selectedProcessId);\n\n // if other process failed to respond, create the room on this process\n room = await handleCreateRoom(roomName, clientOptions);\n\n } else {\n // re-throw intentional exception thrown during remote onCreate()\n throw e;\n }\n }\n }\n\n if (isDevMode) {\n presence.hset(getRoomRestoreListKey(), room.roomId, JSON.stringify({\n \"clientOptions\": clientOptions,\n \"roomName\": roomName,\n \"processId\": processId\n }));\n }\n\n return room;\n}\n\nexport async function handleCreateRoom(roomName: string, clientOptions: ClientOptions, restoringRoomId?: string): Promise<RoomListingData> {\n const handler = getHandler(roomName);\n const room = new handler.klass();\n\n // set room public attributes\n if (restoringRoomId && isDevMode) {\n room.roomId = restoringRoomId;\n\n } else {\n room.roomId = generateId();\n }\n\n room.roomName = roomName;\n room.presence = presence;\n\n const additionalListingData: any = handler.getFilterOptions(clientOptions);\n\n // assign public host\n if (publicAddress) {\n additionalListingData.publicAddress = publicAddress;\n }\n\n // create a RoomCache reference.\n room.listing = driver.createInstance({\n name: roomName,\n processId,\n ...additionalListingData\n });\n\n if (room.onCreate) {\n try {\n await room.onCreate(merge({}, clientOptions, handler.options));\n\n } catch (e) {\n debugAndPrintError(e);\n throw new ServerError(\n e.code || ErrorCode.MATCHMAKE_UNHANDLED,\n e.message,\n );\n }\n }\n\n room['_internalState'] = RoomInternalState.CREATED;\n\n room.listing.roomId = room.roomId;\n room.listing.maxClients = room.maxClients;\n\n // imediatelly ask client to join the room\n debugMatchMaking('spawning \\'%s\\', roomId: %s, processId: %s', roomName, room.roomId, processId);\n\n // increment amount of rooms this process is handling\n stats.local.roomCount++;\n stats.persist();\n\n room._events.on('lock', lockRoom.bind(this, room));\n room._events.on('unlock', unlockRoom.bind(this, room));\n room._events.on('join', onClientJoinRoom.bind(this, room));\n room._events.on('leave', onClientLeaveRoom.bind(this, room));\n room._events.on('visibility-change', onVisibilityChange.bind(this, room));\n room._events.once('dispose', disposeRoom.bind(this, roomName, room));\n room._events.once('disconnect', () => room._events.removeAllListeners());\n\n // room always start unlocked\n await createRoomReferences(room, true);\n await room.listing.save();\n\n handler.emit('create', room);\n\n return room.listing;\n}\n\nexport function getRoomById(roomId: string) {\n return rooms[roomId];\n}\n\n/**\n * Disconnects every client on every room in the current process.\n */\nexport function disconnectAll(closeCode?: number) {\n const promises: Array<Promise<any>> = [];\n\n for (const roomId in rooms) {\n if (!rooms.hasOwnProperty(roomId)) { continue; }\n promises.push(rooms[roomId].disconnect(closeCode));\n }\n\n return promises;\n}\n\nexport async function gracefullyShutdown(): Promise<any> {\n if (isGracefullyShuttingDown) {\n return Promise.reject('already_shutting_down');\n }\n\n isGracefullyShuttingDown = true;\n onReady = undefined;\n\n debugMatchMaking(`${processId} is shutting down!`);\n\n if (isDevMode) {\n await cacheRoomHistory(rooms);\n }\n\n // remove processId from room count key\n await stats.excludeProcess(processId);\n\n // unsubscribe from process id channel\n presence.unsubscribe(getProcessChannel());\n\n return Promise.all(disconnectAll(\n (isDevMode)\n ? Protocol.WS_CLOSE_DEVMODE_RESTART\n : undefined\n ));\n}\n\n/**\n * Reserve a seat for a client in a room\n */\nexport async function reserveSeatFor(room: RoomListingData, options: any) {\n const sessionId: string = generateId();\n\n debugMatchMaking(\n 'reserving seat. sessionId: \\'%s\\', roomId: \\'%s\\', processId: \\'%s\\'',\n sessionId, room.roomId, processId,\n );\n\n let successfulSeatReservation: boolean;\n\n try {\n successfulSeatReservation = await remoteRoomCall(room.roomId, '_reserveSeat', [sessionId, options]);\n\n } catch (e) {\n debugMatchMaking(e);\n successfulSeatReservation = false;\n }\n\n if (!successfulSeatReservation) {\n throw new SeatReservationError(`${room.roomId} is already full.`);\n }\n\n const response: SeatReservation = { room, sessionId };\n\n if (isDevMode) {\n response.devMode = isDevMode;\n }\n\n return response;\n}\n\nasync function cleanupStaleRooms(roomName: string) {\n //\n // clean-up possibly stale room ids\n // (ungraceful shutdowns using Redis can result on stale room ids still on memory.)\n //\n const cachedRooms = await driver.find({ name: roomName }, { _id: 1 });\n\n // remove connecting counts\n await presence.del(getHandlerConcurrencyKey(roomName));\n\n await Promise.all(cachedRooms.map(async (room) => {\n try {\n // use hardcoded short timeout for cleaning up stale rooms.\n await remoteRoomCall(room.roomId, 'roomId');\n\n } catch (e) {\n debugMatchMaking(`cleaning up stale room '${roomName}', roomId: ${room.roomId}`);\n room.remove();\n }\n }));\n}\n\nasync function createRoomReferences(room: Room, init: boolean = false): Promise<boolean> {\n rooms[room.roomId] = room;\n\n if (init) {\n await subscribeIPC(\n presence,\n processId,\n getRoomChannel(room.roomId),\n (method, args) => {\n return (!args && typeof (room[method]) !== 'function')\n ? room[method]\n : room[method].apply(room, args);\n },\n );\n }\n\n return true;\n}\n\nasync function awaitRoomAvailable(roomToJoin: string, callback: Function): Promise<RoomListingData> {\n return new Promise(async (resolve, reject) => {\n const concurrencyKey = getHandlerConcurrencyKey(roomToJoin);\n const concurrency = await presence.incr(concurrencyKey) - 1;\n\n //\n // avoid having too long timeout if 10+ clients ask to join at the same time\n //\n // TODO: we need a better solution here. either a lock or queue system should be implemented instead.\n // https://github.com/colyseus/colyseus/issues/466\n //\n const concurrencyTimeout = Math.min(concurrency * 100, 500);\n\n if (concurrency > 0) {\n debugMatchMaking(\n 'receiving %d concurrent requests for joining \\'%s\\' (waiting %d ms)',\n concurrency, roomToJoin, concurrencyTimeout,\n );\n }\n\n setTimeout(async () => {\n try {\n const result = await callback();\n resolve(result);\n\n } catch (e) {\n reject(e);\n\n } finally {\n await presence.decr(concurrencyKey);\n }\n }, concurrencyTimeout);\n });\n}\n\nfunction onClientJoinRoom(room: Room, client: Client) {\n // increment local CCU\n stats.local.ccu++;\n stats.persist();\n\n handlers[room.roomName].emit('join', room, client);\n}\n\nfunction onClientLeaveRoom(room: Room, client: Client, willDispose: boolean) {\n // decrement local CCU\n stats.local.ccu--;\n stats.persist();\n\n handlers[room.roomName].emit('leave', room, client, willDispose);\n}\n\nfunction lockRoom(room: Room): void {\n // emit public event on registered handler\n handlers[room.roomName].emit('lock', room);\n}\n\nasync function unlockRoom(room: Room) {\n if (await createRoomReferences(room)) {\n // emit public event on registered handler\n handlers[room.roomName].emit('unlock', room);\n }\n}\n\nfunction onVisibilityChange(room: Room, isInvisible: boolean): void {\n handlers[room.roomName].emit('visibility-change', room, isInvisible);\n}\n\nasync function disposeRoom(roomName: string, room: Room) {\n debugMatchMaking('disposing \\'%s\\' (%s) on processId \\'%s\\'', roomName, room.roomId, processId);\n\n // decrease amount of rooms this process is handling\n if (!isGracefullyShuttingDown) {\n stats.local.roomCount--;\n stats.persist();\n\n // remove from devMode restore list\n if (isDevMode) {\n await presence.hdel(getRoomRestoreListKey(), room.roomId);\n }\n }\n\n // emit disposal on registered session handler\n handlers[roomName].emit('dispose', room);\n\n // remove concurrency key\n presence.del(getHandlerConcurrencyKey(roomName));\n\n // unsubscribe from remote connections\n presence.unsubscribe(getRoomChannel(room.roomId));\n\n // remove actual room reference\n delete rooms[room.roomId];\n}\n\n//\n// Presence keys\n//\nfunction getRoomChannel(roomId: string) {\n return `$${roomId}`;\n}\n\nfunction getHandlerConcurrencyKey(name: string) {\n return `c:${name}`;\n}\n\nfunction getProcessChannel(id: string = processId) {\n return `p:${id}`;\n}"],
|
|
5
|
-
"mappings": "AAAA,SAAS,WAAW,gBAAgB;AAEpC,SAAS,gBAAgB,oBAAoB;AAE7C,SAAS,UAAU,YAAY,OAAO,2BAA2B,aAAa;AAC9E,SAAS,WAAW,kBAAkB,sBAAsB,uBAAuB,uBAAuB;AAE1G,SAAS,yBAAyB;AAClC,SAAS,MAAM,yBAAyB;AAExC,SAAS,qBAAqB;AAG9B,SAAS,oBAAoB,wBAAwB;AACrD,SAAS,4BAA4B;AACrC,SAAS,mBAAmB;AAE5B,SAA4C,mBAAqC;AACjF,OAAO,gBAAgB;AACvB,YAAY,WAAW;AAEvB,SAAS,cAAc;AAGvB,SAAS,mBAAmB;
|
|
4
|
+
"sourcesContent": ["import { ErrorCode, Protocol } from './Protocol';\n\nimport { requestFromIPC, subscribeIPC } from './IPC';\n\nimport { Deferred, generateId, merge, REMOTE_ROOM_SHORT_TIMEOUT, retry } from './utils/Utils';\nimport { isDevMode, cacheRoomHistory, getPreviousProcessId, getRoomRestoreListKey, reloadFromCache } from './utils/DevMode';\n\nimport { RegisteredHandler } from './matchmaker/RegisteredHandler';\nimport { Room, RoomInternalState } from './Room';\n\nimport { LocalPresence } from './presence/LocalPresence';\nimport { Presence } from './presence/Presence';\n\nimport { debugAndPrintError, debugMatchMaking } from './Debug';\nimport { SeatReservationError } from './errors/SeatReservationError';\nimport { ServerError } from './errors/ServerError';\n\nimport { IRoomListingData, RoomListingData, LocalDriver, MatchMakerDriver } from './matchmaker/driver';\nimport controller from './matchmaker/controller';\nimport * as stats from \"./Stats\";\n\nimport { logger } from './Logger';\nimport { Client } from './Transport';\nimport { Type } from './utils/types';\nimport { getHostname } from \"./discovery\";\n\nexport { controller, stats, type MatchMakerDriver };\n\nexport type ClientOptions = any;\nexport type AuthOptions = { token?: string, request?: any };\nexport type SelectProcessIdCallback = (roomName: string, clientOptions: ClientOptions) => Promise<string>;\n\nexport interface SeatReservation {\n sessionId: string;\n room: RoomListingData;\n devMode?: boolean;\n}\n\nconst handlers: {[id: string]: RegisteredHandler} = {};\nconst rooms: {[roomId: string]: Room} = {};\n\nexport let publicAddress: string;\nexport let processId: string;\nexport let presence: Presence;\nexport let driver: MatchMakerDriver;\nexport let selectProcessIdToCreateRoom: SelectProcessIdCallback;\n\nexport let isGracefullyShuttingDown: boolean;\nexport let onReady: Deferred = new Deferred(); // onReady needs to be immediately available to @colyseus/auth integration.\n\n/**\n * @private\n */\nexport async function setup(\n _presence?: Presence,\n _driver?: MatchMakerDriver,\n _publicAddress?: string,\n _selectProcessIdToCreateRoom?: SelectProcessIdCallback,\n) {\n if (onReady === undefined) {\n //\n // for testing purposes only: onReady is turned into undefined on shutdown\n // (needs refactoring.)\n //\n onReady = new Deferred();\n }\n\n presence = _presence || new LocalPresence();\n driver = _driver || new LocalDriver();\n publicAddress = _publicAddress;\n\n // devMode: try to retrieve previous processId\n if (isDevMode) { processId = await getPreviousProcessId(await getHostname()); }\n\n // ensure processId is set\n if (!processId) { processId = generateId(); }\n\n isGracefullyShuttingDown = false;\n\n /**\n * Define default `assignRoomToProcessId` method.\n * By default, return the process with least amount of rooms created\n */\n selectProcessIdToCreateRoom = _selectProcessIdToCreateRoom || async function () {\n return (await stats.fetchAll())\n .sort((p1, p2) => p1.roomCount > p2.roomCount ? 1 : -1)[0]\n .processId;\n };\n\n /**\n * Process-level subscription\n * - handle remote process healthcheck\n * - handle remote room creation\n */\n subscribeIPC(presence, processId, getProcessChannel(), (method, args) => {\n if (method === 'healthcheck') {\n // health check for this processId\n return true;\n\n } else {\n // handle room creation\n return handleCreateRoom.apply(undefined, args);\n }\n });\n\n /**\n * Check for leftover/invalid processId's on startup\n */\n const previousStats = await stats.fetchAll();\n if (previousStats.length > 0) {\n logger.debug(`${previousStats.length} previous processId(s) found, health-checking...`);\n await Promise.all(previousStats.map(async (stat) => {\n try {\n await requestFromIPC<RoomListingData>(\n presence,\n getProcessChannel(stat.processId),\n 'healthcheck',\n [],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n // process succeeded to respond - nothing to do\n } catch (e) {\n // process failed to respond - remove it from stats\n logger.debug(`process ${stat.processId} failed to respond. excluding from stats`);\n await stats.excludeProcess(stat.processId);\n }\n }));\n }\n\n await stats.reset();\n\n if (isDevMode) {\n await reloadFromCache();\n }\n\n onReady.resolve();\n}\n\n/**\n * Join or create into a room and return seat reservation\n */\nexport async function joinOrCreate(roomName: string, clientOptions: ClientOptions = {}, authOptions?: AuthOptions) {\n return await retry<Promise<SeatReservation>>(async () => {\n const authData = await callOnAuth(roomName, authOptions);\n let room = await findOneRoomAvailable(roomName, clientOptions);\n\n //\n // TODO [?]\n // should we expose the \"creator\" auth data of the room during `onCreate()`?\n // it would be useful, though it could be accessed via `onJoin()` for now.\n //\n\n if (!room) {\n room = await createRoom(roomName, clientOptions);\n }\n\n return await reserveSeatFor(room, clientOptions, authData);\n }, 5, [SeatReservationError]);\n}\n\n/**\n * Create a room and return seat reservation\n */\nexport async function create(roomName: string, clientOptions: ClientOptions = {}, authOptions?: AuthOptions) {\n const authData = await callOnAuth(roomName, authOptions);\n const room = await createRoom(roomName, clientOptions);\n return reserveSeatFor(room, clientOptions, authData);\n}\n\n/**\n * Join a room and return seat reservation\n */\nexport async function join(roomName: string, clientOptions: ClientOptions = {}, authOptions?: AuthOptions) {\n return await retry<Promise<SeatReservation>>(async () => {\n const authData = await callOnAuth(roomName, authOptions);\n const room = await findOneRoomAvailable(roomName, clientOptions);\n\n if (!room) {\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_CRITERIA, `no rooms found with provided criteria`);\n }\n\n return reserveSeatFor(room, clientOptions, authData);\n });\n}\n\n/**\n * Join a room by id and return seat reservation\n */\nexport async function reconnect(roomId: string, clientOptions: ClientOptions = {}) {\n const room = await driver.findOne({ roomId });\n if (!room) {\n // TODO: support a \"logLevel\" out of the box?\n if (process.env.NODE_ENV !== 'production') {\n logger.info(`\u274C room \"${roomId}\" has been disposed. Did you missed .allowReconnection()?\\n\uD83D\uDC49 https://docs.colyseus.io/server/room/#allowreconnection-client-seconds`);\n }\n\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room \"${roomId}\" has been disposed.`);\n }\n\n // check for reconnection\n const reconnectionToken = clientOptions.reconnectionToken;\n if (!reconnectionToken) { throw new ServerError(ErrorCode.MATCHMAKE_UNHANDLED, `'reconnectionToken' must be provided for reconnection.`); }\n\n\n // respond to re-connection!\n const sessionId = await remoteRoomCall(room.roomId, 'checkReconnectionToken', [reconnectionToken]);\n if (sessionId) {\n return { room, sessionId };\n\n } else {\n // TODO: support a \"logLevel\" out of the box?\n if (process.env.NODE_ENV !== 'production') {\n logger.info(`\u274C reconnection token invalid or expired. Did you missed .allowReconnection()?\\n\uD83D\uDC49 https://docs.colyseus.io/server/room/#allowreconnection-client-seconds`);\n }\n throw new ServerError(ErrorCode.MATCHMAKE_EXPIRED, `reconnection token invalid or expired.`);\n }\n}\n\n/**\n * Join a room by id and return client seat reservation. An exception is thrown if a room is not found for roomId.\n *\n * @param roomId - The Id of the specific room instance.\n * @param clientOptions - Options for the client seat reservation (for `onJoin`/`onAuth`)\n *\n * @returns Promise<SeatReservation> - A promise which contains `sessionId` and `RoomListingData`.\n */\nexport async function joinById(roomId: string, clientOptions: ClientOptions = {}, authOptions?: AuthOptions) {\n const room = await driver.findOne({ roomId });\n\n if (!room) {\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room \"${roomId}\" not found`);\n\n } else if (room.locked) {\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room \"${roomId}\" is locked`);\n }\n\n const authData = await callOnAuth(room.name, authOptions);\n\n return reserveSeatFor(room, clientOptions, authData);\n}\n\n/**\n * Perform a query for all cached rooms\n */\nexport async function query(conditions: Partial<IRoomListingData> = {}) {\n return await driver.find(conditions);\n}\n\n/**\n * Find for a public and unlocked room available.\n *\n * @param roomName - The Id of the specific room.\n * @param clientOptions - Options for the client seat reservation (for `onJoin`/`onAuth`).\n *\n * @returns Promise<RoomListingData> - A promise contaning an object which includes room metadata and configurations.\n */\nexport async function findOneRoomAvailable(roomName: string, clientOptions: ClientOptions): Promise<RoomListingData> {\n return await awaitRoomAvailable(roomName, async () => {\n const handler = getHandler(roomName);\n\n const roomQuery = driver.findOne({\n locked: false,\n name: roomName,\n private: false,\n ...handler.getFilterOptions(clientOptions),\n });\n\n if (handler.sortOptions) {\n roomQuery.sort(handler.sortOptions);\n }\n\n return await roomQuery;\n });\n}\n\n/**\n * Call a method or return a property on a remote room.\n *\n * @param roomId - The Id of the specific room instance.\n * @param method - Method or attribute to call or retrive.\n * @param args - Array of arguments for the method\n *\n * @returns Promise<any> - Returned value from the called or retrieved method/attribute.\n */\nexport async function remoteRoomCall<R= any>(\n roomId: string,\n method: string,\n args?: any[],\n rejectionTimeout = REMOTE_ROOM_SHORT_TIMEOUT,\n): Promise<R> {\n const room = rooms[roomId];\n\n if (!room) {\n try {\n return await requestFromIPC<R>(presence, getRoomChannel(roomId), method, args, rejectionTimeout);\n\n } catch (e) {\n const request = `${method}${args && ' with args ' + JSON.stringify(args) || ''}`;\n throw new ServerError(\n ErrorCode.MATCHMAKE_UNHANDLED,\n `remote room (${roomId}) timed out, requesting \"${request}\". (${rejectionTimeout}ms exceeded)`,\n );\n }\n\n } else {\n return (!args && typeof (room[method]) !== 'function')\n ? room[method]\n : (await room[method].apply(room, args && JSON.parse(JSON.stringify(args))));\n }\n}\n\nexport function defineRoomType<T extends Type<Room>>(\n roomName: string,\n klass: T,\n defaultOptions?: Parameters<NonNullable<InstanceType<T>['onCreate']>>[0],\n) {\n const registeredHandler = new RegisteredHandler(klass, defaultOptions);\n\n handlers[roomName] = registeredHandler;\n\n if (klass.prototype['onAuth'] !== Room.prototype['onAuth']) {\n // TODO: soft-deprecate instance level `onAuth` on 0.16\n // console.warn(\"DEPRECATION WARNING: onAuth() at the instance level will be deprecated soon. Please use static onAuth() instead.\");\n\n if (klass['onAuth'] !== Room['onAuth']) {\n console.log(`\u274C \"${roomName}\"'s onAuth() defined at the instance level will be ignored.`);\n }\n }\n\n if (!isDevMode) {\n cleanupStaleRooms(roomName);\n }\n\n return registeredHandler;\n}\n\nexport function removeRoomType(roomName: string) {\n delete handlers[roomName];\n\n if (!isDevMode) {\n cleanupStaleRooms(roomName);\n }\n}\n\n// TODO: legacy; remove me on 1.0\nexport function hasHandler(roomName: string) {\n console.warn(\"hasHandler() is deprecated. Use getHandler() instead.\");\n return handlers[roomName] !== undefined;\n}\n\nexport function getHandler(roomName: string) {\n const handler = handlers[roomName];\n\n if (!handler) {\n throw new ServerError(ErrorCode.MATCHMAKE_NO_HANDLER, `provided room name \"${roomName}\" not defined`);\n }\n\n return handler;\n}\n\nexport function getRoomClass(roomName: string): Type<Room> {\n return handlers[roomName]?.klass;\n}\n\n\n/**\n * Creates a new room.\n *\n * @param roomName - The identifier you defined on `gameServer.define()`\n * @param clientOptions - Options for `onCreate`\n *\n * @returns Promise<RoomListingData> - A promise contaning an object which includes room metadata and configurations.\n */\nexport async function createRoom(roomName: string, clientOptions: ClientOptions): Promise<RoomListingData> {\n const selectedProcessId = await selectProcessIdToCreateRoom(roomName, clientOptions);\n\n let room: RoomListingData;\n if (selectedProcessId === processId) {\n // create the room on this process!\n room = await handleCreateRoom(roomName, clientOptions);\n\n } else {\n // ask other process to create the room!\n try {\n room = await requestFromIPC<RoomListingData>(\n presence,\n getProcessChannel(selectedProcessId),\n undefined,\n [roomName, clientOptions],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n\n } catch (e) {\n if (e.message === \"ipc_timeout\") {\n debugAndPrintError(`${e.message}: create room request timed out for ${roomName} on processId ${selectedProcessId}.`);\n\n //\n // clean-up possibly stale process from redis.\n // when a process disconnects ungracefully, it may leave its previous processId under \"roomcount\"\n // if the process is still alive, it will re-add itself shortly after the load-balancer selects it again.\n //\n await stats.excludeProcess(selectedProcessId);\n\n // if other process failed to respond, create the room on this process\n room = await handleCreateRoom(roomName, clientOptions);\n\n } else {\n // re-throw intentional exception thrown during remote onCreate()\n throw e;\n }\n }\n }\n\n if (isDevMode) {\n presence.hset(getRoomRestoreListKey(), room.roomId, JSON.stringify({\n \"clientOptions\": clientOptions,\n \"roomName\": roomName,\n \"processId\": processId\n }));\n }\n\n return room;\n}\n\nexport async function handleCreateRoom(roomName: string, clientOptions: ClientOptions, restoringRoomId?: string): Promise<RoomListingData> {\n const handler = getHandler(roomName);\n const room = new handler.klass();\n\n // set room public attributes\n if (restoringRoomId && isDevMode) {\n room.roomId = restoringRoomId;\n\n } else {\n room.roomId = generateId();\n }\n\n room.roomName = roomName;\n room.presence = presence;\n\n const additionalListingData: any = handler.getFilterOptions(clientOptions);\n\n // assign public host\n if (publicAddress) {\n additionalListingData.publicAddress = publicAddress;\n }\n\n // create a RoomCache reference.\n room.listing = driver.createInstance({\n name: roomName,\n processId,\n ...additionalListingData\n });\n\n if (room.onCreate) {\n try {\n await room.onCreate(merge({}, clientOptions, handler.options));\n\n } catch (e) {\n debugAndPrintError(e);\n throw new ServerError(\n e.code || ErrorCode.MATCHMAKE_UNHANDLED,\n e.message,\n );\n }\n }\n\n room['_internalState'] = RoomInternalState.CREATED;\n\n room.listing.roomId = room.roomId;\n room.listing.maxClients = room.maxClients;\n\n // imediatelly ask client to join the room\n debugMatchMaking('spawning \\'%s\\', roomId: %s, processId: %s', roomName, room.roomId, processId);\n\n // increment amount of rooms this process is handling\n stats.local.roomCount++;\n stats.persist();\n\n room._events.on('lock', lockRoom.bind(this, room));\n room._events.on('unlock', unlockRoom.bind(this, room));\n room._events.on('join', onClientJoinRoom.bind(this, room));\n room._events.on('leave', onClientLeaveRoom.bind(this, room));\n room._events.on('visibility-change', onVisibilityChange.bind(this, room));\n room._events.once('dispose', disposeRoom.bind(this, roomName, room));\n room._events.once('disconnect', () => room._events.removeAllListeners());\n\n // room always start unlocked\n await createRoomReferences(room, true);\n await room.listing.save();\n\n handler.emit('create', room);\n\n return room.listing;\n}\n\nexport function getRoomById(roomId: string) {\n return rooms[roomId];\n}\n\n/**\n * Disconnects every client on every room in the current process.\n */\nexport function disconnectAll(closeCode?: number) {\n const promises: Array<Promise<any>> = [];\n\n for (const roomId in rooms) {\n if (!rooms.hasOwnProperty(roomId)) { continue; }\n promises.push(rooms[roomId].disconnect(closeCode));\n }\n\n return promises;\n}\n\nexport async function gracefullyShutdown(): Promise<any> {\n if (isGracefullyShuttingDown) {\n return Promise.reject('already_shutting_down');\n }\n\n isGracefullyShuttingDown = true;\n onReady = undefined;\n\n debugMatchMaking(`${processId} is shutting down!`);\n\n if (isDevMode) {\n await cacheRoomHistory(rooms);\n }\n\n // remove processId from room count key\n await stats.excludeProcess(processId);\n\n // unsubscribe from process id channel\n presence.unsubscribe(getProcessChannel());\n\n return Promise.all(disconnectAll(\n (isDevMode)\n ? Protocol.WS_CLOSE_DEVMODE_RESTART\n : undefined\n ));\n}\n\n/**\n * Reserve a seat for a client in a room\n */\nexport async function reserveSeatFor(room: RoomListingData, options: ClientOptions, authData?: any) {\n const sessionId: string = generateId();\n\n debugMatchMaking(\n 'reserving seat. sessionId: \\'%s\\', roomId: \\'%s\\', processId: \\'%s\\'',\n sessionId, room.roomId, processId,\n );\n\n let successfulSeatReservation: boolean;\n\n try {\n successfulSeatReservation = await remoteRoomCall(room.roomId, '_reserveSeat', [sessionId, options, authData]);\n\n } catch (e) {\n debugMatchMaking(e);\n successfulSeatReservation = false;\n }\n\n if (!successfulSeatReservation) {\n throw new SeatReservationError(`${room.roomId} is already full.`);\n }\n\n const response: SeatReservation = { room, sessionId };\n\n if (isDevMode) {\n response.devMode = isDevMode;\n }\n\n return response;\n}\n\nfunction callOnAuth(roomName: string, authOptions?: AuthOptions) {\n const roomClass = getRoomClass(roomName);\n return (roomClass && roomClass['onAuth'] && roomClass['onAuth'] !== Room['onAuth'])\n ? roomClass['onAuth'](authOptions.token, authOptions.request)\n : undefined;\n}\n\nasync function cleanupStaleRooms(roomName: string) {\n //\n // clean-up possibly stale room ids\n // (ungraceful shutdowns using Redis can result on stale room ids still on memory.)\n //\n const cachedRooms = await driver.find({ name: roomName }, { _id: 1 });\n\n // remove connecting counts\n await presence.del(getHandlerConcurrencyKey(roomName));\n\n await Promise.all(cachedRooms.map(async (room) => {\n try {\n // use hardcoded short timeout for cleaning up stale rooms.\n await remoteRoomCall(room.roomId, 'roomId');\n\n } catch (e) {\n debugMatchMaking(`cleaning up stale room '${roomName}', roomId: ${room.roomId}`);\n room.remove();\n }\n }));\n}\n\nasync function createRoomReferences(room: Room, init: boolean = false): Promise<boolean> {\n rooms[room.roomId] = room;\n\n if (init) {\n await subscribeIPC(\n presence,\n processId,\n getRoomChannel(room.roomId),\n (method, args) => {\n return (!args && typeof (room[method]) !== 'function')\n ? room[method]\n : room[method].apply(room, args);\n },\n );\n }\n\n return true;\n}\n\nasync function awaitRoomAvailable(roomToJoin: string, callback: Function): Promise<RoomListingData> {\n return new Promise(async (resolve, reject) => {\n const concurrencyKey = getHandlerConcurrencyKey(roomToJoin);\n const concurrency = await presence.incr(concurrencyKey) - 1;\n\n //\n // avoid having too long timeout if 10+ clients ask to join at the same time\n //\n // TODO: we need a better solution here. either a lock or queue system should be implemented instead.\n // https://github.com/colyseus/colyseus/issues/466\n //\n const concurrencyTimeout = Math.min(concurrency * 100, 500);\n\n if (concurrency > 0) {\n debugMatchMaking(\n 'receiving %d concurrent requests for joining \\'%s\\' (waiting %d ms)',\n concurrency, roomToJoin, concurrencyTimeout,\n );\n }\n\n setTimeout(async () => {\n try {\n const result = await callback();\n resolve(result);\n\n } catch (e) {\n reject(e);\n\n } finally {\n await presence.decr(concurrencyKey);\n }\n }, concurrencyTimeout);\n });\n}\n\nfunction onClientJoinRoom(room: Room, client: Client) {\n // increment local CCU\n stats.local.ccu++;\n stats.persist();\n\n handlers[room.roomName].emit('join', room, client);\n}\n\nfunction onClientLeaveRoom(room: Room, client: Client, willDispose: boolean) {\n // decrement local CCU\n stats.local.ccu--;\n stats.persist();\n\n handlers[room.roomName].emit('leave', room, client, willDispose);\n}\n\nfunction lockRoom(room: Room): void {\n // emit public event on registered handler\n handlers[room.roomName].emit('lock', room);\n}\n\nasync function unlockRoom(room: Room) {\n if (await createRoomReferences(room)) {\n // emit public event on registered handler\n handlers[room.roomName].emit('unlock', room);\n }\n}\n\nfunction onVisibilityChange(room: Room, isInvisible: boolean): void {\n handlers[room.roomName].emit('visibility-change', room, isInvisible);\n}\n\nasync function disposeRoom(roomName: string, room: Room) {\n debugMatchMaking('disposing \\'%s\\' (%s) on processId \\'%s\\'', roomName, room.roomId, processId);\n\n // decrease amount of rooms this process is handling\n if (!isGracefullyShuttingDown) {\n stats.local.roomCount--;\n stats.persist();\n\n // remove from devMode restore list\n if (isDevMode) {\n await presence.hdel(getRoomRestoreListKey(), room.roomId);\n }\n }\n\n // emit disposal on registered session handler\n handlers[roomName].emit('dispose', room);\n\n // remove concurrency key\n presence.del(getHandlerConcurrencyKey(roomName));\n\n // unsubscribe from remote connections\n presence.unsubscribe(getRoomChannel(room.roomId));\n\n // remove actual room reference\n delete rooms[room.roomId];\n}\n\n//\n// Presence keys\n//\nfunction getRoomChannel(roomId: string) {\n return `$${roomId}`;\n}\n\nfunction getHandlerConcurrencyKey(name: string) {\n return `c:${name}`;\n}\n\nfunction getProcessChannel(id: string = processId) {\n return `p:${id}`;\n}"],
|
|
5
|
+
"mappings": "AAAA,SAAS,WAAW,gBAAgB;AAEpC,SAAS,gBAAgB,oBAAoB;AAE7C,SAAS,UAAU,YAAY,OAAO,2BAA2B,aAAa;AAC9E,SAAS,WAAW,kBAAkB,sBAAsB,uBAAuB,uBAAuB;AAE1G,SAAS,yBAAyB;AAClC,SAAS,MAAM,yBAAyB;AAExC,SAAS,qBAAqB;AAG9B,SAAS,oBAAoB,wBAAwB;AACrD,SAAS,4BAA4B;AACrC,SAAS,mBAAmB;AAE5B,SAA4C,mBAAqC;AACjF,OAAO,gBAAgB;AACvB,YAAY,WAAW;AAEvB,SAAS,cAAc;AAGvB,SAAS,mBAAmB;AAc5B,MAAM,WAA8C,CAAC;AACrD,MAAM,QAAkC,CAAC;AAElC,IAAI;AACJ,IAAI;AACJ,IAAI;AACJ,IAAI;AACJ,IAAI;AAEJ,IAAI;AACJ,IAAI,UAAoB,IAAI,SAAS;AAK5C,eAAsB,MACpB,WACA,SACA,gBACA,8BACA;AACA,MAAI,YAAY,QAAW;AAKzB,cAAU,IAAI,SAAS;AAAA,EACzB;AAEA,aAAW,aAAa,IAAI,cAAc;AAC1C,WAAS,WAAW,IAAI,YAAY;AACpC,kBAAgB;AAGhB,MAAI,WAAW;AAAE,gBAAY,MAAM,qBAAqB,MAAM,YAAY,CAAC;AAAA,EAAG;AAG9E,MAAI,CAAC,WAAW;AAAE,gBAAY,WAAW;AAAA,EAAG;AAE5C,6BAA2B;AAM3B,gCAA8B,gCAAgC,iBAAkB;AAC9E,YAAQ,MAAM,MAAM,SAAS,GAC1B,KAAK,CAAC,IAAI,OAAO,GAAG,YAAY,GAAG,YAAY,IAAI,EAAE,EAAE,GACvD;AAAA,EACL;AAOA,eAAa,UAAU,WAAW,kBAAkB,GAAG,CAAC,QAAQ,SAAS;AACvE,QAAI,WAAW,eAAe;AAE5B,aAAO;AAAA,IAET,OAAO;AAEL,aAAO,iBAAiB,MAAM,QAAW,IAAI;AAAA,IAC/C;AAAA,EACF,CAAC;AAKD,QAAM,gBAAgB,MAAM,MAAM,SAAS;AAC3C,MAAI,cAAc,SAAS,GAAG;AAC5B,WAAO,MAAM,GAAG,cAAc,wDAAwD;AACtF,UAAM,QAAQ,IAAI,cAAc,IAAI,OAAO,SAAS;AAClD,UAAI;AACF,cAAM;AAAA,UACJ;AAAA,UACA,kBAAkB,KAAK,SAAS;AAAA,UAChC;AAAA,UACA,CAAC;AAAA,UACD;AAAA,QACF;AAAA,MAEF,SAAS,GAAP;AAEA,eAAO,MAAM,WAAW,KAAK,mDAAmD;AAChF,cAAM,MAAM,eAAe,KAAK,SAAS;AAAA,MAC3C;AAAA,IACF,CAAC,CAAC;AAAA,EACJ;AAEA,QAAM,MAAM,MAAM;AAElB,MAAI,WAAW;AACb,UAAM,gBAAgB;AAAA,EACxB;AAEA,UAAQ,QAAQ;AAClB;AAKA,eAAsB,aAAa,UAAkB,gBAA+B,CAAC,GAAG,aAA2B;AACjH,SAAO,MAAM,MAAgC,YAAY;AACvD,UAAM,WAAW,MAAM,WAAW,UAAU,WAAW;AACvD,QAAI,OAAO,MAAM,qBAAqB,UAAU,aAAa;AAQ7D,QAAI,CAAC,MAAM;AACT,aAAO,MAAM,WAAW,UAAU,aAAa;AAAA,IACjD;AAEA,WAAO,MAAM,eAAe,MAAM,eAAe,QAAQ;AAAA,EAC3D,GAAG,GAAG,CAAC,oBAAoB,CAAC;AAC9B;AAKA,eAAsB,OAAO,UAAkB,gBAA+B,CAAC,GAAG,aAA2B;AAC3G,QAAM,WAAW,MAAM,WAAW,UAAU,WAAW;AACvD,QAAM,OAAO,MAAM,WAAW,UAAU,aAAa;AACrD,SAAO,eAAe,MAAM,eAAe,QAAQ;AACrD;AAKA,eAAsB,KAAK,UAAkB,gBAA+B,CAAC,GAAG,aAA2B;AACzG,SAAO,MAAM,MAAgC,YAAY;AACvD,UAAM,WAAW,MAAM,WAAW,UAAU,WAAW;AACvD,UAAM,OAAO,MAAM,qBAAqB,UAAU,aAAa;AAE/D,QAAI,CAAC,MAAM;AACT,YAAM,IAAI,YAAY,UAAU,4BAA4B,uCAAuC;AAAA,IACrG;AAEA,WAAO,eAAe,MAAM,eAAe,QAAQ;AAAA,EACrD,CAAC;AACH;AAKA,eAAsB,UAAU,QAAgB,gBAA+B,CAAC,GAAG;AACjF,QAAM,OAAO,MAAM,OAAO,QAAQ,EAAE,OAAO,CAAC;AAC5C,MAAI,CAAC,MAAM;AAET,QAAI,QAAQ,IAAI,aAAa,cAAc;AACzC,aAAO,KAAK,gBAAW;AAAA,iFAA4I;AAAA,IACrK;AAEA,UAAM,IAAI,YAAY,UAAU,2BAA2B,SAAS,4BAA4B;AAAA,EAClG;AAGA,QAAM,oBAAoB,cAAc;AACxC,MAAI,CAAC,mBAAmB;AAAE,UAAM,IAAI,YAAY,UAAU,qBAAqB,wDAAwD;AAAA,EAAG;AAI1I,QAAM,YAAY,MAAM,eAAe,KAAK,QAAQ,0BAA0B,CAAC,iBAAiB,CAAC;AACjG,MAAI,WAAW;AACb,WAAO,EAAE,MAAM,UAAU;AAAA,EAE3B,OAAO;AAEL,QAAI,QAAQ,IAAI,aAAa,cAAc;AACzC,aAAO,KAAK;AAAA,iFAA0J;AAAA,IACxK;AACA,UAAM,IAAI,YAAY,UAAU,mBAAmB,wCAAwC;AAAA,EAC7F;AACF;AAUA,eAAsB,SAAS,QAAgB,gBAA+B,CAAC,GAAG,aAA2B;AAC3G,QAAM,OAAO,MAAM,OAAO,QAAQ,EAAE,OAAO,CAAC;AAE5C,MAAI,CAAC,MAAM;AACT,UAAM,IAAI,YAAY,UAAU,2BAA2B,SAAS,mBAAmB;AAAA,EAEzF,WAAW,KAAK,QAAQ;AACtB,UAAM,IAAI,YAAY,UAAU,2BAA2B,SAAS,mBAAmB;AAAA,EACzF;AAEA,QAAM,WAAW,MAAM,WAAW,KAAK,MAAM,WAAW;AAExD,SAAO,eAAe,MAAM,eAAe,QAAQ;AACrD;AAKA,eAAsB,MAAM,aAAwC,CAAC,GAAG;AACtE,SAAO,MAAM,OAAO,KAAK,UAAU;AACrC;AAUA,eAAsB,qBAAqB,UAAkB,eAAwD;AACnH,SAAO,MAAM,mBAAmB,UAAU,YAAY;AACpD,UAAM,UAAU,WAAW,QAAQ;AAEnC,UAAM,YAAY,OAAO,QAAQ;AAAA,MAC/B,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,SAAS;AAAA,MACT,GAAG,QAAQ,iBAAiB,aAAa;AAAA,IAC3C,CAAC;AAED,QAAI,QAAQ,aAAa;AACvB,gBAAU,KAAK,QAAQ,WAAW;AAAA,IACpC;AAEA,WAAO,MAAM;AAAA,EACf,CAAC;AACH;AAWA,eAAsB,eACpB,QACA,QACA,MACA,mBAAmB,2BACP;AACZ,QAAM,OAAO,MAAM;AAEnB,MAAI,CAAC,MAAM;AACT,QAAI;AACF,aAAO,MAAM,eAAkB,UAAU,eAAe,MAAM,GAAG,QAAQ,MAAM,gBAAgB;AAAA,IAEjG,SAAS,GAAP;AACA,YAAM,UAAU,GAAG,SAAS,QAAQ,gBAAgB,KAAK,UAAU,IAAI,KAAK;AAC5E,YAAM,IAAI;AAAA,QACR,UAAU;AAAA,QACV,gBAAgB,kCAAkC,cAAc;AAAA,MAClE;AAAA,IACF;AAAA,EAEF,OAAO;AACL,WAAQ,CAAC,QAAQ,OAAQ,KAAK,YAAa,aACrC,KAAK,UACJ,MAAM,KAAK,QAAQ,MAAM,MAAM,QAAQ,KAAK,MAAM,KAAK,UAAU,IAAI,CAAC,CAAC;AAAA,EAChF;AACF;AAEO,SAAS,eACd,UACA,OACA,gBACA;AACA,QAAM,oBAAoB,IAAI,kBAAkB,OAAO,cAAc;AAErE,WAAS,YAAY;AAErB,MAAI,MAAM,UAAU,cAAc,KAAK,UAAU,WAAW;AAI1D,QAAI,MAAM,cAAc,KAAK,WAAW;AACtC,cAAQ,IAAI,WAAM,qEAAqE;AAAA,IACzF;AAAA,EACF;AAEA,MAAI,CAAC,WAAW;AACd,sBAAkB,QAAQ;AAAA,EAC5B;AAEA,SAAO;AACT;AAEO,SAAS,eAAe,UAAkB;AAC/C,SAAO,SAAS;AAEhB,MAAI,CAAC,WAAW;AACd,sBAAkB,QAAQ;AAAA,EAC5B;AACF;AAGO,SAAS,WAAW,UAAkB;AAC3C,UAAQ,KAAK,uDAAuD;AACpE,SAAO,SAAS,cAAc;AAChC;AAEO,SAAS,WAAW,UAAkB;AAC3C,QAAM,UAAU,SAAS;AAEzB,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,YAAY,UAAU,sBAAsB,uBAAuB,uBAAuB;AAAA,EACtG;AAEA,SAAO;AACT;AAEO,SAAS,aAAa,UAA8B;AACzD,SAAO,SAAS,WAAW;AAC7B;AAWA,eAAsB,WAAW,UAAkB,eAAwD;AACzG,QAAM,oBAAoB,MAAM,4BAA4B,UAAU,aAAa;AAEnF,MAAI;AACJ,MAAI,sBAAsB,WAAW;AAEnC,WAAO,MAAM,iBAAiB,UAAU,aAAa;AAAA,EAEvD,OAAO;AAEL,QAAI;AACF,aAAO,MAAM;AAAA,QACX;AAAA,QACA,kBAAkB,iBAAiB;AAAA,QACnC;AAAA,QACA,CAAC,UAAU,aAAa;AAAA,QACxB;AAAA,MACF;AAAA,IAEF,SAAS,GAAP;AACA,UAAI,EAAE,YAAY,eAAe;AAC/B,2BAAmB,GAAG,EAAE,8CAA8C,yBAAyB,oBAAoB;AAOnH,cAAM,MAAM,eAAe,iBAAiB;AAG5C,eAAO,MAAM,iBAAiB,UAAU,aAAa;AAAA,MAEvD,OAAO;AAEL,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,MAAI,WAAW;AACb,aAAS,KAAK,sBAAsB,GAAG,KAAK,QAAQ,KAAK,UAAU;AAAA,MACjE,iBAAiB;AAAA,MACjB,YAAY;AAAA,MACZ,aAAa;AAAA,IACf,CAAC,CAAC;AAAA,EACJ;AAEA,SAAO;AACT;AAEA,eAAsB,iBAAiB,UAAkB,eAA8B,iBAAoD;AACzI,QAAM,UAAU,WAAW,QAAQ;AACnC,QAAM,OAAO,IAAI,QAAQ,MAAM;AAG/B,MAAI,mBAAmB,WAAW;AAChC,SAAK,SAAS;AAAA,EAEhB,OAAO;AACL,SAAK,SAAS,WAAW;AAAA,EAC3B;AAEA,OAAK,WAAW;AAChB,OAAK,WAAW;AAEhB,QAAM,wBAA6B,QAAQ,iBAAiB,aAAa;AAGzE,MAAI,eAAe;AACjB,0BAAsB,gBAAgB;AAAA,EACxC;AAGA,OAAK,UAAU,OAAO,eAAe;AAAA,IACnC,MAAM;AAAA,IACN;AAAA,IACA,GAAG;AAAA,EACL,CAAC;AAED,MAAI,KAAK,UAAU;AACjB,QAAI;AACF,YAAM,KAAK,SAAS,MAAM,CAAC,GAAG,eAAe,QAAQ,OAAO,CAAC;AAAA,IAE/D,SAAS,GAAP;AACA,yBAAmB,CAAC;AACpB,YAAM,IAAI;AAAA,QACR,EAAE,QAAQ,UAAU;AAAA,QACpB,EAAE;AAAA,MACJ;AAAA,IACF;AAAA,EACF;AAEA,OAAK,oBAAoB,kBAAkB;AAE3C,OAAK,QAAQ,SAAS,KAAK;AAC3B,OAAK,QAAQ,aAAa,KAAK;AAG/B,mBAAiB,4CAA8C,UAAU,KAAK,QAAQ,SAAS;AAG/F,QAAM,MAAM;AACZ,QAAM,QAAQ;AAEd,OAAK,QAAQ,GAAG,QAAQ,SAAS,KAAK,MAAM,IAAI,CAAC;AACjD,OAAK,QAAQ,GAAG,UAAU,WAAW,KAAK,MAAM,IAAI,CAAC;AACrD,OAAK,QAAQ,GAAG,QAAQ,iBAAiB,KAAK,MAAM,IAAI,CAAC;AACzD,OAAK,QAAQ,GAAG,SAAS,kBAAkB,KAAK,MAAM,IAAI,CAAC;AAC3D,OAAK,QAAQ,GAAG,qBAAqB,mBAAmB,KAAK,MAAM,IAAI,CAAC;AACxE,OAAK,QAAQ,KAAK,WAAW,YAAY,KAAK,MAAM,UAAU,IAAI,CAAC;AACnE,OAAK,QAAQ,KAAK,cAAc,MAAM,KAAK,QAAQ,mBAAmB,CAAC;AAGvE,QAAM,qBAAqB,MAAM,IAAI;AACrC,QAAM,KAAK,QAAQ,KAAK;AAExB,UAAQ,KAAK,UAAU,IAAI;AAE3B,SAAO,KAAK;AACd;AAEO,SAAS,YAAY,QAAgB;AAC1C,SAAO,MAAM;AACf;AAKO,SAAS,cAAc,WAAoB;AAChD,QAAM,WAAgC,CAAC;AAEvC,aAAW,UAAU,OAAO;AAC1B,QAAI,CAAC,MAAM,eAAe,MAAM,GAAG;AAAE;AAAA,IAAU;AAC/C,aAAS,KAAK,MAAM,QAAQ,WAAW,SAAS,CAAC;AAAA,EACnD;AAEA,SAAO;AACT;AAEA,eAAsB,qBAAmC;AACvD,MAAI,0BAA0B;AAC5B,WAAO,QAAQ,OAAO,uBAAuB;AAAA,EAC/C;AAEA,6BAA2B;AAC3B,YAAU;AAEV,mBAAiB,GAAG,6BAA6B;AAEjD,MAAI,WAAW;AACb,UAAM,iBAAiB,KAAK;AAAA,EAC9B;AAGA,QAAM,MAAM,eAAe,SAAS;AAGpC,WAAS,YAAY,kBAAkB,CAAC;AAExC,SAAO,QAAQ,IAAI;AAAA,IAChB,YACG,SAAS,2BACT;AAAA,EACN,CAAC;AACH;AAKA,eAAsB,eAAe,MAAuB,SAAwB,UAAgB;AAClG,QAAM,YAAoB,WAAW;AAErC;AAAA,IACE;AAAA,IACA;AAAA,IAAW,KAAK;AAAA,IAAQ;AAAA,EAC1B;AAEA,MAAI;AAEJ,MAAI;AACF,gCAA4B,MAAM,eAAe,KAAK,QAAQ,gBAAgB,CAAC,WAAW,SAAS,QAAQ,CAAC;AAAA,EAE9G,SAAS,GAAP;AACA,qBAAiB,CAAC;AAClB,gCAA4B;AAAA,EAC9B;AAEA,MAAI,CAAC,2BAA2B;AAC9B,UAAM,IAAI,qBAAqB,GAAG,KAAK,yBAAyB;AAAA,EAClE;AAEA,QAAM,WAA4B,EAAE,MAAM,UAAU;AAEpD,MAAI,WAAW;AACb,aAAS,UAAU;AAAA,EACrB;AAEA,SAAO;AACT;AAEA,SAAS,WAAW,UAAkB,aAA2B;AAC/D,QAAM,YAAY,aAAa,QAAQ;AACvC,SAAQ,aAAa,UAAU,aAAa,UAAU,cAAc,KAAK,YACrE,UAAU,UAAU,YAAY,OAAO,YAAY,OAAO,IAC1D;AACN;AAEA,eAAe,kBAAkB,UAAkB;AAKjD,QAAM,cAAc,MAAM,OAAO,KAAK,EAAE,MAAM,SAAS,GAAG,EAAE,KAAK,EAAE,CAAC;AAGpE,QAAM,SAAS,IAAI,yBAAyB,QAAQ,CAAC;AAErD,QAAM,QAAQ,IAAI,YAAY,IAAI,OAAO,SAAS;AAChD,QAAI;AAEF,YAAM,eAAe,KAAK,QAAQ,QAAQ;AAAA,IAE5C,SAAS,GAAP;AACA,uBAAiB,2BAA2B,sBAAsB,KAAK,QAAQ;AAC/E,WAAK,OAAO;AAAA,IACd;AAAA,EACF,CAAC,CAAC;AACJ;AAEA,eAAe,qBAAqB,MAAY,OAAgB,OAAyB;AACvF,QAAM,KAAK,UAAU;AAErB,MAAI,MAAM;AACR,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA,eAAe,KAAK,MAAM;AAAA,MAC1B,CAAC,QAAQ,SAAS;AAChB,eAAQ,CAAC,QAAQ,OAAQ,KAAK,YAAa,aACvC,KAAK,UACL,KAAK,QAAQ,MAAM,MAAM,IAAI;AAAA,MACnC;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEA,eAAe,mBAAmB,YAAoB,UAA8C;AAClG,SAAO,IAAI,QAAQ,OAAO,SAAS,WAAW;AAC5C,UAAM,iBAAiB,yBAAyB,UAAU;AAC1D,UAAM,cAAc,MAAM,SAAS,KAAK,cAAc,IAAI;AAQ1D,UAAM,qBAAqB,KAAK,IAAI,cAAc,KAAK,GAAG;AAE1D,QAAI,cAAc,GAAG;AACnB;AAAA,QACE;AAAA,QACA;AAAA,QAAa;AAAA,QAAY;AAAA,MAC3B;AAAA,IACF;AAEA,eAAW,YAAY;AACrB,UAAI;AACF,cAAM,SAAS,MAAM,SAAS;AAC9B,gBAAQ,MAAM;AAAA,MAEhB,SAAS,GAAP;AACA,eAAO,CAAC;AAAA,MAEV,UAAE;AACA,cAAM,SAAS,KAAK,cAAc;AAAA,MACpC;AAAA,IACF,GAAG,kBAAkB;AAAA,EACvB,CAAC;AACH;AAEA,SAAS,iBAAiB,MAAY,QAAgB;AAEpD,QAAM,MAAM;AACZ,QAAM,QAAQ;AAEd,WAAS,KAAK,UAAU,KAAK,QAAQ,MAAM,MAAM;AACnD;AAEA,SAAS,kBAAkB,MAAY,QAAgB,aAAsB;AAE3E,QAAM,MAAM;AACZ,QAAM,QAAQ;AAEd,WAAS,KAAK,UAAU,KAAK,SAAS,MAAM,QAAQ,WAAW;AACjE;AAEA,SAAS,SAAS,MAAkB;AAElC,WAAS,KAAK,UAAU,KAAK,QAAQ,IAAI;AAC3C;AAEA,eAAe,WAAW,MAAY;AACpC,MAAI,MAAM,qBAAqB,IAAI,GAAG;AAEpC,aAAS,KAAK,UAAU,KAAK,UAAU,IAAI;AAAA,EAC7C;AACF;AAEA,SAAS,mBAAmB,MAAY,aAA4B;AAClE,WAAS,KAAK,UAAU,KAAK,qBAAqB,MAAM,WAAW;AACrE;AAEA,eAAe,YAAY,UAAkB,MAAY;AACvD,mBAAiB,yCAA6C,UAAU,KAAK,QAAQ,SAAS;AAG9F,MAAI,CAAC,0BAA0B;AAC7B,UAAM,MAAM;AACZ,UAAM,QAAQ;AAGd,QAAI,WAAW;AACb,YAAM,SAAS,KAAK,sBAAsB,GAAG,KAAK,MAAM;AAAA,IAC1D;AAAA,EACF;AAGA,WAAS,UAAU,KAAK,WAAW,IAAI;AAGvC,WAAS,IAAI,yBAAyB,QAAQ,CAAC;AAG/C,WAAS,YAAY,eAAe,KAAK,MAAM,CAAC;AAGhD,SAAO,MAAM,KAAK;AACpB;AAKA,SAAS,eAAe,QAAgB;AACtC,SAAO,IAAI;AACb;AAEA,SAAS,yBAAyB,MAAc;AAC9C,SAAO,KAAK;AACd;AAEA,SAAS,kBAAkB,KAAa,WAAW;AACjD,SAAO,KAAK;AACd;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/build/Room.d.ts
CHANGED
|
@@ -83,7 +83,7 @@ export declare abstract class Room<State extends object = any, Metadata = any> {
|
|
|
83
83
|
_events: EventEmitter;
|
|
84
84
|
protected seatReservationTime: number;
|
|
85
85
|
protected reservedSeats: {
|
|
86
|
-
[sessionId: string]: any;
|
|
86
|
+
[sessionId: string]: [any, any];
|
|
87
87
|
};
|
|
88
88
|
protected reservedSeatTimeouts: {
|
|
89
89
|
[sessionId: string]: NodeJS.Timer;
|