@colyseus/core 0.15.1 → 0.15.3

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/Room.d.ts CHANGED
@@ -7,9 +7,9 @@ import Clock from '@gamestdio/timer';
7
7
  import { EventEmitter } from 'events';
8
8
  import { Presence } from './presence/Presence';
9
9
  import { Serializer } from './serializer/Serializer';
10
- import { Deferred, HybridArray } from './utils/Utils';
10
+ import { Deferred } from './utils/Utils';
11
11
  import { RoomListingData } from './matchmaker/driver';
12
- import { Client, ISendOptions } from './Transport';
12
+ import { Client, ClientArray, ISendOptions } from './Transport';
13
13
  export declare const DEFAULT_SEAT_RESERVATION_TIME: number;
14
14
  export type SimulationCallback = (deltaTime: number) => void;
15
15
  export type RoomConstructor<T extends object = any> = new (presence?: Presence) => Room<T>;
@@ -77,7 +77,7 @@ export declare abstract class Room<State extends object = any, Metadata = any> {
77
77
  *
78
78
  * @see {@link https://docs.colyseus.io/colyseus/server/room/#client|Client instance}
79
79
  */
80
- clients: HybridArray<Client>;
80
+ clients: ClientArray<any>;
81
81
  internalState: RoomInternalState;
82
82
  /** @internal */
83
83
  _events: EventEmitter;
package/build/Room.js CHANGED
@@ -67,7 +67,7 @@ class Room {
67
67
  autoDispose = true;
68
68
  state;
69
69
  presence;
70
- clients = new import_Utils.HybridArray("sessionId");
70
+ clients = new import_Transport.ClientArray();
71
71
  internalState = 0 /* CREATING */;
72
72
  _events = new import_events.EventEmitter();
73
73
  seatReservationTime = DEFAULT_SEAT_RESERVATION_TIME;
@@ -251,7 +251,7 @@ class Room {
251
251
  if (!this.state) {
252
252
  return false;
253
253
  }
254
- const hasChanges = this._serializer.applyPatches(this.clients.array, this.state);
254
+ const hasChanges = this._serializer.applyPatches(this.clients, this.state);
255
255
  this._dequeueAfterPatchMessages();
256
256
  return hasChanges;
257
257
  }
@@ -270,7 +270,7 @@ class Room {
270
270
  let numClients = this.clients.length;
271
271
  if (numClients > 0) {
272
272
  while (numClients--) {
273
- this._forciblyCloseClient(this.clients.array[numClients], closeCode);
273
+ this._forciblyCloseClient(this.clients[numClients], closeCode);
274
274
  }
275
275
  } else {
276
276
  this._events.emit("dispose");
@@ -293,7 +293,7 @@ class Room {
293
293
  client._afterNextPatchQueue = this._afterNextPatchQueue;
294
294
  client.ref["onleave"] = this._onLeave.bind(this, client);
295
295
  client.ref.once("close", client.ref["onleave"]);
296
- this.clients.add(client);
296
+ this.clients.push(client);
297
297
  const previousReconnectionToken = this._reconnectingSessionId.get(sessionId);
298
298
  if (previousReconnectionToken) {
299
299
  this._reconnections[previousReconnectionToken]?.[1].resolve(client);
@@ -379,7 +379,7 @@ class Room {
379
379
  const except = typeof options.except !== "undefined" ? Array.isArray(options.except) ? options.except : [options.except] : void 0;
380
380
  let numClients = this.clients.length;
381
381
  while (numClients--) {
382
- const client = this.clients.array[numClients];
382
+ const client = this.clients[numClients];
383
383
  if (!except || !except.includes(client)) {
384
384
  client.enqueueRaw(encodedMessage);
385
385
  }
@@ -391,7 +391,7 @@ class Room {
391
391
  const except = typeof options.except !== "undefined" ? Array.isArray(options.except) ? options.except : [options.except] : void 0;
392
392
  let numClients = this.clients.length;
393
393
  while (numClients--) {
394
- const client = this.clients.array[numClients];
394
+ const client = this.clients[numClients];
395
395
  if (!except || !except.includes(client)) {
396
396
  client.enqueueRaw(encodedMessage);
397
397
  }
package/build/Room.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/Room.ts"],
4
- "sourcesContent": ["import http from 'http';\n\nimport { unpack } from 'msgpackr';\nimport { decode, Iterator, Schema } from '@colyseus/schema';\n\nimport Clock from '@gamestdio/timer';\nimport { EventEmitter } from 'events';\nimport { logger } from './Logger';\n\nimport { Presence } from './presence/Presence';\n\nimport { NoneSerializer } from './serializer/NoneSerializer';\nimport { SchemaSerializer } from './serializer/SchemaSerializer';\nimport { Serializer } from './serializer/Serializer';\n\nimport { ErrorCode, getMessageBytes, Protocol } from './Protocol';\nimport { Deferred, HybridArray, generateId } from './utils/Utils';\nimport { isDevMode } from './utils/DevMode';\n\nimport { debugAndPrintError, debugMessage } from './Debug';\nimport { ServerError } from './errors/ServerError';\nimport { RoomListingData } from './matchmaker/driver';\nimport { Client, ClientState, ISendOptions } from './Transport';\n\nconst DEFAULT_PATCH_RATE = 1000 / 20; // 20fps (50ms)\nconst DEFAULT_SIMULATION_INTERVAL = 1000 / 60; // 60fps (16.66ms)\nconst noneSerializer = new NoneSerializer();\n\nexport const DEFAULT_SEAT_RESERVATION_TIME = Number(process.env.COLYSEUS_SEAT_RESERVATION_TIME || 15);\n\nexport type SimulationCallback = (deltaTime: number) => void;\n\nexport type RoomConstructor<T extends object= any> = new (presence?: Presence) => Room<T>;\n\nexport interface IBroadcastOptions extends ISendOptions {\n except?: Client | Client[];\n}\n\nexport enum RoomInternalState {\n CREATING = 0,\n CREATED = 1,\n DISCONNECTING = 2,\n}\n\n/**\n * A Room class is meant to implement a game session, and/or serve as the communication channel\n * between a group of clients.\n *\n * - Rooms are created on demand during matchmaking by default\n * - Room classes must be exposed using `.define()`\n */\nexport abstract class Room<State extends object= any, Metadata= any> {\n\n /**\n * This property will change on these situations:\n * - The maximum number of allowed clients has been reached (`maxClients`)\n * - You manually locked, or unlocked the room using lock() or `unlock()`.\n *\n * @readonly\n */\n public get locked() {\n return this._locked;\n }\n\n public get metadata() {\n return this.listing.metadata;\n }\n\n public listing: RoomListingData<Metadata>;\n\n /**\n * A ClockTimer instance, used for timing events.\n */\n public clock: Clock = new Clock();\n\n #_roomId: string;\n #_roomName: string;\n\n /**\n * Maximum number of clients allowed to connect into the room. When room reaches this limit,\n * it is locked automatically. Unless the room was explicitly locked by you via `lock()` method,\n * the room will be unlocked as soon as a client disconnects from it.\n */\n public maxClients: number = Infinity;\n /**\n * Frequency to send the room state to connected clients, in milliseconds.\n *\n * @default 50ms (20fps)\n */\n public patchRate: number = DEFAULT_PATCH_RATE;\n /**\n * Automatically dispose the room when last client disconnects.\n *\n * @default true\n */\n public autoDispose: boolean = true;\n\n /**\n * The state instance you provided to `setState()`.\n */\n public state: State;\n /**\n * The presence instance. Check Presence API for more details.\n *\n * @see {@link https://docs.colyseus.io/colyseus/server/presence/|Presence API}\n */\n public presence: Presence;\n\n /**\n * The array of connected clients.\n *\n * @see {@link https://docs.colyseus.io/colyseus/server/room/#client|Client instance}\n */\n public clients: HybridArray<Client> = new HybridArray<Client>(\"sessionId\");\n\n public internalState: RoomInternalState = RoomInternalState.CREATING;\n\n /** @internal */\n public _events = new EventEmitter();\n\n // seat reservation & reconnection\n protected seatReservationTime: number = DEFAULT_SEAT_RESERVATION_TIME;\n protected reservedSeats: { [sessionId: string]: any } = {};\n protected reservedSeatTimeouts: { [sessionId: string]: NodeJS.Timer } = {};\n\n protected _reconnections: { [reconnectionToken: string]: [string, Deferred] } = {};\n private _reconnectingSessionId = new Map<string, string>();\n\n private onMessageHandlers: {[id: string]: (client: Client, message: any) => void} = {};\n\n private _serializer: Serializer<State> = noneSerializer;\n private _afterNextPatchQueue: Array<[string | Client, IArguments]> = [];\n\n private _simulationInterval: NodeJS.Timer;\n private _patchInterval: NodeJS.Timer;\n\n private _locked: boolean = false;\n private _lockedExplicitly: boolean = false;\n private _maxClientsReached: boolean = false;\n\n // this timeout prevents rooms that are created by one process, but no client\n // ever had success joining into it on the specified interval.\n private _autoDisposeTimeout: NodeJS.Timer;\n\n constructor(presence?: Presence) {\n this.presence = presence;\n\n this._events.once('dispose', async () => {\n try {\n await this._dispose();\n\n } catch (e) {\n debugAndPrintError(`onDispose error: ${(e && e.message || e || 'promise rejected')}`);\n }\n this._events.emit('disconnect');\n });\n\n this.setPatchRate(this.patchRate);\n // set default _autoDisposeTimeout\n this.resetAutoDisposeTimeout(this.seatReservationTime);\n }\n\n /**\n * The name of the room you provided as first argument for `gameServer.define()`.\n *\n * @returns roomName string\n */\n public get roomName() { return this.#_roomName; }\n /**\n * Setting the name of the room. Overwriting this property is restricted.\n *\n * @param roomName\n */\n public set roomName(roomName: string) {\n if (this.#_roomName) {\n // prevent user from setting roomName after it has been defined.\n throw new ServerError(ErrorCode.APPLICATION_ERROR, \"'roomName' cannot be overwritten.\");\n }\n this.#_roomName = roomName;\n }\n\n /**\n * A unique, auto-generated, 9-character-long id of the room.\n * You may replace `this.roomId` during `onCreate()`.\n *\n * @returns roomId string\n */\n public get roomId() { return this.#_roomId; }\n /**\n * Setting the roomId, is restricted in room lifetime except upon room creation.\n *\n * @param roomId\n * @returns roomId string\n */\n public set roomId(roomId: string) {\n if (this.internalState !== RoomInternalState.CREATING && !isDevMode) {\n // prevent user from setting roomId after room has been created.\n throw new ServerError(ErrorCode.APPLICATION_ERROR, \"'roomId' can only be overridden upon room creation.\");\n }\n this.#_roomId = roomId;\n }\n\n // Optional abstract methods\n public onBeforePatch?(state: State): void | Promise<any>;\n public onCreate?(options: any): void | Promise<any>;\n public onJoin?(client: Client, options?: any, auth?: any): void | Promise<any>;\n public onLeave?(client: Client, consented?: boolean): void | Promise<any>;\n public onDispose?(): void | Promise<any>;\n public onAuth(client: Client, options: any, request?: http.IncomingMessage): any | Promise<any> {\n return true;\n }\n\n /**\n * devMode: When `devMode` is enabled, `onCacheRoom` method is called during\n * graceful shutdown.\n *\n * Implement this method to return custom data to be cached. `onRestoreRoom`\n * will be called with the data returned by `onCacheRoom`\n */\n public onCacheRoom?(): any;\n\n /**\n * devMode: When `devMode` is enabled, `onRestoreRoom` method is called during\n * process startup, with the data returned by the `onCacheRoom` method.\n */\n public onRestoreRoom?(cached?: any): void;\n\n /**\n * Returns whether the sum of connected clients and reserved seats exceeds maximum number of clients.\n *\n * @returns boolean\n */\n public hasReachedMaxClients(): boolean {\n return (this.clients.length + Object.keys(this.reservedSeats).length) >= this.maxClients;\n }\n\n /**\n * Set the number of seconds a room can wait for a client to effectively join the room.\n * You should consider how long your `onAuth()` will have to wait for setting a different seat reservation time.\n * The default value is 15 seconds. You may set the `COLYSEUS_SEAT_RESERVATION_TIME`\n * environment variable if you'd like to change the seat reservation time globally.\n *\n * @default 15 seconds\n *\n * @param seconds - number of seconds.\n * @returns The modified Room object.\n */\n public setSeatReservationTime(seconds: number) {\n this.seatReservationTime = seconds;\n return this;\n }\n\n public hasReservedSeat(sessionId: string, reconnectionToken?: string): boolean {\n if (reconnectionToken) {\n const reconnection = this._reconnections[reconnectionToken];\n return (\n reconnection &&\n reconnection[0] === sessionId &&\n this.reservedSeats[sessionId] !== undefined &&\n this._reconnectingSessionId.has(sessionId)\n );\n\n } else {\n return (\n this.reservedSeats[sessionId] !== undefined &&\n (\n !this._reconnectingSessionId.has(sessionId) || // prevent possible \"reconnect\" requests without a reconnection token\n (this._reconnectingSessionId.get(sessionId) === sessionId) // devMode reconnection\n )\n );\n }\n }\n\n public checkReconnectionToken(reconnectionToken: string) {\n const reconnection = this._reconnections[reconnectionToken];\n const sessionId = (reconnection && reconnection[0]);\n\n if (this.hasReservedSeat(sessionId)) {\n this._reconnectingSessionId.set(sessionId, reconnectionToken);\n return sessionId;\n\n } else {\n return undefined;\n }\n }\n\n /**\n * (Optional) Set a simulation interval that can change the state of the game.\n * The simulation interval is your game loop.\n *\n * @default 16.6ms (60fps)\n *\n * @param onTickCallback - You can implement your physics or world updates here!\n * This is a good place to update the room state.\n * @param delay - Interval delay on executing `onTickCallback` in milliseconds.\n */\n public setSimulationInterval(onTickCallback?: SimulationCallback, delay: number = DEFAULT_SIMULATION_INTERVAL): void {\n // clear previous interval in case called setSimulationInterval more than once\n if (this._simulationInterval) { clearInterval(this._simulationInterval); }\n\n if (onTickCallback) {\n this._simulationInterval = setInterval(() => {\n this.clock.tick();\n onTickCallback(this.clock.deltaTime);\n }, delay);\n }\n }\n\n public setPatchRate(milliseconds: number): void {\n this.patchRate = milliseconds;\n\n // clear previous interval in case called setPatchRate more than once\n if (this._patchInterval) {\n clearInterval(this._patchInterval);\n this._patchInterval = undefined;\n }\n\n if (milliseconds !== null && milliseconds !== 0) {\n this._patchInterval = setInterval(() => this.broadcastPatch(), milliseconds);\n }\n }\n\n public setState(newState: State) {\n this.clock.start();\n\n if ('_definition' in newState) {\n this.setSerializer(new SchemaSerializer());\n }\n\n this._serializer.reset(newState);\n\n this.state = newState;\n }\n\n public setSerializer(serializer: Serializer<State>) {\n this._serializer = serializer;\n }\n\n public async setMetadata(meta: Partial<Metadata>) {\n if (!this.listing.metadata) {\n this.listing.metadata = meta as Metadata;\n\n } else {\n for (const field in meta) {\n if (!meta.hasOwnProperty(field)) { continue; }\n this.listing.metadata[field] = meta[field];\n }\n\n // `MongooseDriver` workaround: persit metadata mutations\n if ('markModified' in this.listing) {\n (this.listing as any).markModified('metadata');\n }\n }\n\n if (this.internalState === RoomInternalState.CREATED) {\n await this.listing.save();\n }\n }\n\n public async setPrivate(bool: boolean = true) {\n this.listing.private = bool;\n\n if (this.internalState === RoomInternalState.CREATED) {\n await this.listing.save();\n }\n }\n\n /**\n * Locking the room will remove it from the pool of available rooms for new clients to connect to.\n */\n public async lock() {\n // rooms locked internally aren't explicit locks.\n this._lockedExplicitly = (arguments[0] === undefined);\n\n // skip if already locked.\n if (this._locked) { return; }\n\n this._locked = true;\n\n await this.listing.updateOne({\n $set: { locked: this._locked },\n });\n\n this._events.emit('lock');\n }\n\n /**\n * Unlocking the room returns it to the pool of available rooms for new clients to connect to.\n */\n public async unlock() {\n // only internal usage passes arguments to this function.\n if (arguments[0] === undefined) {\n this._lockedExplicitly = false;\n }\n\n // skip if already locked\n if (!this._locked) { return; }\n\n this._locked = false;\n\n await this.listing.updateOne({\n $set: { locked: this._locked },\n });\n\n this._events.emit('unlock');\n }\n\n public send(client: Client, type: string | number, message: any, options?: ISendOptions): void;\n public send(client: Client, message: Schema, options?: ISendOptions): void;\n public send(client: Client, messageOrType: any, messageOrOptions?: any | ISendOptions, options?: ISendOptions): void {\n logger.warn('DEPRECATION WARNING: use client.send(...) instead of this.send(client, ...)');\n client.send(messageOrType, messageOrOptions, options);\n }\n\n public broadcast(type: string | number, message?: any, options?: IBroadcastOptions);\n public broadcast<T extends Schema>(message: T, options?: IBroadcastOptions);\n public broadcast(\n typeOrSchema: string | number | Schema,\n messageOrOptions?: any | IBroadcastOptions,\n options?: IBroadcastOptions,\n ) {\n const isSchema = (typeof(typeOrSchema) === 'object');\n const opts: IBroadcastOptions = ((isSchema) ? messageOrOptions : options);\n\n if (opts && opts.afterNextPatch) {\n delete opts.afterNextPatch;\n this._afterNextPatchQueue.push(['broadcast', arguments]);\n return;\n }\n\n if (isSchema) {\n this.broadcastMessageSchema(typeOrSchema as Schema, opts);\n\n } else {\n\n this.broadcastMessageType(typeOrSchema as string, messageOrOptions, opts);\n }\n }\n\n /**\n * Checks whether mutations have occurred in the state, and broadcast them to all connected clients.\n */\n public broadcastPatch() {\n if (this.onBeforePatch) {\n this.onBeforePatch(this.state);\n }\n\n if (!this._simulationInterval) {\n this.clock.tick();\n }\n\n if (!this.state) {\n return false;\n }\n\n const hasChanges = this._serializer.applyPatches(this.clients.array, this.state);\n\n // broadcast messages enqueued for \"after patch\"\n this._dequeueAfterPatchMessages();\n\n return hasChanges;\n }\n\n public onMessage<T = any>(messageType: '*', callback: (client: Client, type: string | number, message: T) => void);\n public onMessage<T = any>(messageType: string | number, callback: (client: Client, message: T) => void);\n public onMessage<T = any>(messageType: '*' | string | number, callback: (...args: any[]) => void) {\n this.onMessageHandlers[messageType] = callback;\n // returns a method to unbind the callback\n return () => delete this.onMessageHandlers[messageType];\n }\n\n /**\n * Disconnect all connected clients, and then dispose the room.\n *\n * @param closeCode WebSocket close code (default = 4000, which is a \"consented leave\")\n * @returns Promise<void>\n */\n public async disconnect(closeCode: number = Protocol.WS_CLOSE_CONSENTED): Promise<any> {\n this.internalState = RoomInternalState.DISCONNECTING;\n await this.listing.remove();\n\n this.autoDispose = true;\n\n const delayedDisconnection = new Promise<void>((resolve) =>\n this._events.once('disconnect', () => resolve()));\n\n for (const [_, reconnection] of Object.values(this._reconnections)) {\n reconnection.reject();\n }\n\n let numClients = this.clients.length;\n if (numClients > 0) {\n // clients may have `async onLeave`, room will be disposed after they're fulfilled\n while (numClients--) {\n this._forciblyCloseClient(this.clients.array[numClients], closeCode);\n }\n } else {\n // no clients connected, dispose immediately.\n this._events.emit('dispose');\n }\n\n return await delayedDisconnection;\n }\n\n public async ['_onJoin'](client: Client, req?: http.IncomingMessage) {\n const sessionId = client.sessionId;\n\n // generate unique private reconnection token\n client._reconnectionToken = generateId();\n\n if (this.reservedSeatTimeouts[sessionId]) {\n clearTimeout(this.reservedSeatTimeouts[sessionId]);\n delete this.reservedSeatTimeouts[sessionId];\n }\n\n // clear auto-dispose timeout.\n if (this._autoDisposeTimeout) {\n clearTimeout(this._autoDisposeTimeout);\n this._autoDisposeTimeout = undefined;\n }\n\n // get seat reservation options and clear it\n const options = this.reservedSeats[sessionId];\n delete this.reservedSeats[sessionId];\n\n // share \"after next patch queue\" reference with every client.\n client._afterNextPatchQueue = this._afterNextPatchQueue;\n\n // bind clean-up callback when client connection closes\n client.ref['onleave'] = this._onLeave.bind(this, client);\n client.ref.once('close', client.ref['onleave']);\n\n this.clients.add(client);\n\n const previousReconnectionToken = this._reconnectingSessionId.get(sessionId);\n if (previousReconnectionToken) {\n this._reconnections[previousReconnectionToken]?.[1].resolve(client);\n\n } else {\n try {\n client.auth = await this.onAuth(client, options, req);\n\n if (!client.auth) {\n throw new ServerError(ErrorCode.AUTH_FAILED, 'onAuth failed');\n }\n\n if (this.onJoin) {\n await this.onJoin(client, options, client.auth);\n }\n } catch (e) {\n this.clients.delete(client);\n\n // make sure an error code is provided.\n if (!e.code) {\n e.code = ErrorCode.APPLICATION_ERROR;\n }\n\n throw e;\n\n } finally {\n // remove seat reservation\n delete this.reservedSeats[sessionId];\n }\n }\n\n // emit 'join' to room handler\n this._events.emit('join', client);\n\n // allow client to send messages after onJoin has succeeded.\n client.ref.on('message', this._onMessage.bind(this, client));\n\n // confirm room id that matches the room name requested to join\n client.raw(getMessageBytes[Protocol.JOIN_ROOM](\n client._reconnectionToken,\n this._serializer.id,\n this._serializer.handshake && this._serializer.handshake(),\n ));\n }\n\n /**\n * Allow the specified client to reconnect into the room. Must be used inside `onLeave()` method.\n * If seconds is provided, the reconnection is going to be cancelled after the provided amount of seconds.\n *\n * @param previousClient - The client which is to be waiting until re-connection happens.\n * @param seconds - Timeout period on re-connection in seconds.\n *\n * @returns Deferred<Client> - The differed is a promise like type.\n * This type can forcibly reject the promise by calling `.reject()`.\n */\n public allowReconnection(previousClient: Client, seconds: number | \"manual\"): Deferred<Client> {\n if (seconds === undefined) { // TODO: remove this check\n console.warn(\"DEPRECATED: allowReconnection() requires a second argument. Using \\\"manual\\\" mode.\");\n seconds = \"manual\";\n }\n\n if (seconds === \"manual\") {\n seconds = Infinity;\n }\n\n if (this.internalState === RoomInternalState.DISCONNECTING) {\n this._disposeIfEmpty(); // gracefully shutting down\n throw new Error('disconnecting');\n }\n\n const sessionId = previousClient.sessionId;\n const reconnectionToken = previousClient._reconnectionToken;\n\n this._reserveSeat(sessionId, true, seconds, true);\n\n // keep reconnection reference in case the user reconnects into this room.\n const reconnection = new Deferred<Client>();\n this._reconnections[reconnectionToken] = [sessionId, reconnection];\n\n if (seconds !== Infinity) {\n // expire seat reservation after timeout\n this.reservedSeatTimeouts[sessionId] = setTimeout(() =>\n reconnection.reject(false), seconds * 1000);\n }\n\n const cleanup = () => {\n delete this._reconnections[reconnectionToken];\n delete this.reservedSeats[sessionId];\n delete this.reservedSeatTimeouts[sessionId];\n this._reconnectingSessionId.delete(sessionId);\n };\n\n reconnection.\n then((newClient) => {\n newClient.auth = previousClient.auth;\n newClient.userData = previousClient.userData;\n previousClient.ref = newClient.ref; // swap \"ref\" for convenience\n previousClient.state = ClientState.RECONNECTED;\n clearTimeout(this.reservedSeatTimeouts[sessionId]);\n cleanup();\n }).\n catch(() => {\n cleanup();\n this.resetAutoDisposeTimeout();\n });\n\n return reconnection;\n }\n\n protected resetAutoDisposeTimeout(timeoutInSeconds: number = 1) {\n clearTimeout(this._autoDisposeTimeout);\n\n if (!this.autoDispose) {\n return;\n }\n\n this._autoDisposeTimeout = setTimeout(() => {\n this._autoDisposeTimeout = undefined;\n this._disposeIfEmpty();\n }, timeoutInSeconds * 1000);\n }\n\n private broadcastMessageSchema<T extends Schema>(message: T, options: IBroadcastOptions = {}) {\n debugMessage(\"broadcast: %O\", message);\n const encodedMessage = getMessageBytes[Protocol.ROOM_DATA_SCHEMA](message);\n const except = (typeof (options.except) !== \"undefined\")\n ? Array.isArray(options.except)\n ? options.except\n : [options.except]\n : undefined;\n\n let numClients = this.clients.length;\n while (numClients--) {\n const client = this.clients.array[numClients];\n\n if (!except || !except.includes(client)) {\n client.enqueueRaw(encodedMessage);\n }\n }\n }\n\n private broadcastMessageType(type: string, message?: any, options: IBroadcastOptions = {}) {\n debugMessage(\"broadcast: %O\", message);\n const encodedMessage = getMessageBytes.raw(Protocol.ROOM_DATA, type, message);\n const except = (typeof (options.except) !== \"undefined\")\n ? Array.isArray(options.except)\n ? options.except\n : [options.except]\n : undefined;\n\n let numClients = this.clients.length;\n while (numClients--) {\n const client = this.clients.array[numClients];\n\n if (!except || !except.includes(client)) {\n client.enqueueRaw(encodedMessage);\n }\n }\n }\n\n private sendFullState(client: Client): void {\n client.enqueueRaw(getMessageBytes[Protocol.ROOM_STATE](this._serializer.getFullState(client)));\n }\n\n private _dequeueAfterPatchMessages() {\n const length = this._afterNextPatchQueue.length;\n\n if (length > 0) {\n for (let i = 0; i < length; i++) {\n const [target, args] = this._afterNextPatchQueue[i];\n\n if (target === \"broadcast\") {\n this.broadcast.apply(this, args);\n\n } else {\n (target as Client).raw.apply(target, args);\n }\n }\n\n // new messages may have been added in the meantime,\n // let's splice the ones that have been processed\n this._afterNextPatchQueue.splice(0, length);\n }\n }\n\n private async _reserveSeat(\n sessionId: string,\n joinOptions: any = true,\n seconds: number = this.seatReservationTime,\n allowReconnection: boolean = false,\n devModeReconnection?: boolean,\n ) {\n if (!allowReconnection && this.hasReachedMaxClients()) {\n return false;\n }\n\n this.reservedSeats[sessionId] = joinOptions;\n\n if (!allowReconnection) {\n await this._incrementClientCount();\n\n this.reservedSeatTimeouts[sessionId] = setTimeout(async () => {\n delete this.reservedSeats[sessionId];\n delete this.reservedSeatTimeouts[sessionId];\n await this._decrementClientCount();\n }, seconds * 1000);\n\n this.resetAutoDisposeTimeout(seconds);\n }\n\n //\n // isDevMode workaround to allow players to reconnect on devMode\n //\n if (devModeReconnection) {\n this._reconnectingSessionId.set(sessionId, sessionId);\n }\n\n return true;\n }\n\n private _disposeIfEmpty() {\n const willDispose = (\n this.autoDispose &&\n this._autoDisposeTimeout === undefined &&\n this.clients.length === 0 &&\n Object.keys(this.reservedSeats).length === 0\n );\n\n if (willDispose) {\n this._events.emit('dispose');\n }\n\n return willDispose;\n }\n\n private async _dispose(): Promise<any> {\n let userReturnData;\n\n if (this.onDispose) {\n userReturnData = this.onDispose();\n }\n\n if (this._patchInterval) {\n clearInterval(this._patchInterval);\n this._patchInterval = undefined;\n }\n\n if (this._simulationInterval) {\n clearInterval(this._simulationInterval);\n this._simulationInterval = undefined;\n }\n\n if (this._autoDisposeTimeout) {\n clearInterval(this._autoDisposeTimeout);\n this._autoDisposeTimeout = undefined;\n }\n\n // clear all timeouts/intervals + force to stop ticking\n this.clock.clear();\n this.clock.stop();\n\n return await (userReturnData || Promise.resolve());\n }\n\n private _onMessage(client: Client, bytes: number[]) {\n // skip if client is on LEAVING state.\n if (client.state === ClientState.LEAVING) { return; }\n\n const it: Iterator = { offset: 0 };\n const code = decode.uint8(bytes, it);\n\n if (!bytes) {\n debugAndPrintError(`${this.roomName} (${this.roomId}), couldn't decode message: ${bytes}`);\n return;\n }\n\n if (code === Protocol.ROOM_DATA) {\n const messageType = (decode.stringCheck(bytes, it))\n ? decode.string(bytes, it)\n : decode.number(bytes, it);\n\n let message;\n try {\n message = (bytes.length > it.offset)\n ? unpack(new Uint8Array(bytes.slice(it.offset, bytes.length)))\n : undefined;\n debugMessage(\"received: '%s' -> %j\", messageType, message);\n } catch (e) {\n debugAndPrintError(e);\n return;\n }\n\n if (this.onMessageHandlers[messageType]) {\n this.onMessageHandlers[messageType](client, message);\n\n } else if (this.onMessageHandlers['*']) {\n (this.onMessageHandlers['*'] as any)(client, messageType, message);\n\n } else {\n debugAndPrintError(`onMessage for \"${messageType}\" not registered.`);\n }\n\n } else if (code === Protocol.ROOM_DATA_BYTES) {\n const messageType = (decode.stringCheck(bytes, it))\n ? decode.string(bytes, it)\n : decode.number(bytes, it);\n\n const message = bytes.slice(it.offset, bytes.length);\n debugMessage(\"received: '%s' -> %j\", messageType, message);\n\n if (this.onMessageHandlers[messageType]) {\n this.onMessageHandlers[messageType](client, message);\n\n } else if (this.onMessageHandlers['*']) {\n (this.onMessageHandlers['*'] as any)(client, messageType, message);\n\n } else {\n debugAndPrintError(`onMessage for \"${messageType}\" not registered.`);\n }\n\n } else if (code === Protocol.JOIN_ROOM && client.state === ClientState.JOINING) {\n // join room has been acknowledged by the client\n client.state = ClientState.JOINED;\n\n // send current state when new client joins the room\n if (this.state) {\n this.sendFullState(client);\n }\n\n // dequeue messages sent before client has joined effectively (on user-defined `onJoin`)\n if (client._enqueuedMessages.length > 0) {\n client._enqueuedMessages.forEach((enqueued) => client.raw(enqueued));\n }\n delete client._enqueuedMessages;\n\n } else if (code === Protocol.LEAVE_ROOM) {\n this._forciblyCloseClient(client, Protocol.WS_CLOSE_CONSENTED);\n }\n\n }\n\n private _forciblyCloseClient(client: Client, closeCode: number) {\n // stop receiving messages from this client\n client.ref.removeAllListeners('message');\n\n // prevent \"onLeave\" from being called twice if player asks to leave\n client.ref.removeListener('close', client.ref['onleave']);\n\n // only effectively close connection when \"onLeave\" is fulfilled\n this._onLeave(client, closeCode).then(() => client.leave(closeCode));\n }\n\n private async _onLeave(client: Client, code?: number): Promise<any> {\n const success = this.clients.delete(client);\n\n // call 'onLeave' method only if the client has been successfully accepted.\n if (success && this.onLeave) {\n try {\n client.state = ClientState.LEAVING;\n await this.onLeave(client, (code === Protocol.WS_CLOSE_CONSENTED));\n\n } catch (e) {\n debugAndPrintError(`onLeave error: ${(e && e.message || e || 'promise rejected')}`);\n }\n }\n\n if (client.state !== ClientState.RECONNECTED) {\n // try to dispose immediately if client reconnection isn't set up.\n const willDispose = await this._decrementClientCount();\n\n this._events.emit('leave', client, willDispose);\n }\n }\n\n private async _incrementClientCount() {\n // lock automatically when maxClients is reached\n if (!this._locked && this.hasReachedMaxClients()) {\n this._maxClientsReached = true;\n this.lock.call(this, true);\n }\n\n await this.listing.updateOne({\n $inc: { clients: 1 },\n $set: { locked: this._locked },\n });\n }\n\n private async _decrementClientCount() {\n const willDispose = this._disposeIfEmpty();\n\n if (this.internalState === RoomInternalState.DISCONNECTING) {\n return;\n }\n\n // unlock if room is available for new connections\n if (!willDispose) {\n if (this._maxClientsReached && !this._lockedExplicitly) {\n this._maxClientsReached = false;\n this.unlock.call(this, true);\n }\n\n // update room listing cache\n await this.listing.updateOne({\n $inc: { clients: -1 },\n $set: { locked: this._locked },\n });\n }\n\n return willDispose;\n }\n\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import http from 'http';\n\nimport { unpack } from 'msgpackr';\nimport { decode, Iterator, Schema } from '@colyseus/schema';\n\nimport Clock from '@gamestdio/timer';\nimport { EventEmitter } from 'events';\nimport { logger } from './Logger';\n\nimport { Presence } from './presence/Presence';\n\nimport { NoneSerializer } from './serializer/NoneSerializer';\nimport { SchemaSerializer } from './serializer/SchemaSerializer';\nimport { Serializer } from './serializer/Serializer';\n\nimport { ErrorCode, getMessageBytes, Protocol } from './Protocol';\nimport { Deferred, generateId } from './utils/Utils';\nimport { isDevMode } from './utils/DevMode';\n\nimport { debugAndPrintError, debugMessage } from './Debug';\nimport { ServerError } from './errors/ServerError';\nimport { RoomListingData } from './matchmaker/driver';\nimport { Client, ClientArray, ClientState, ISendOptions } from './Transport';\n\nconst DEFAULT_PATCH_RATE = 1000 / 20; // 20fps (50ms)\nconst DEFAULT_SIMULATION_INTERVAL = 1000 / 60; // 60fps (16.66ms)\nconst noneSerializer = new NoneSerializer();\n\nexport const DEFAULT_SEAT_RESERVATION_TIME = Number(process.env.COLYSEUS_SEAT_RESERVATION_TIME || 15);\n\nexport type SimulationCallback = (deltaTime: number) => void;\n\nexport type RoomConstructor<T extends object= any> = new (presence?: Presence) => Room<T>;\n\nexport interface IBroadcastOptions extends ISendOptions {\n except?: Client | Client[];\n}\n\nexport enum RoomInternalState {\n CREATING = 0,\n CREATED = 1,\n DISCONNECTING = 2,\n}\n\n/**\n * A Room class is meant to implement a game session, and/or serve as the communication channel\n * between a group of clients.\n *\n * - Rooms are created on demand during matchmaking by default\n * - Room classes must be exposed using `.define()`\n */\nexport abstract class Room<State extends object= any, Metadata= any> {\n\n /**\n * This property will change on these situations:\n * - The maximum number of allowed clients has been reached (`maxClients`)\n * - You manually locked, or unlocked the room using lock() or `unlock()`.\n *\n * @readonly\n */\n public get locked() {\n return this._locked;\n }\n\n public get metadata() {\n return this.listing.metadata;\n }\n\n public listing: RoomListingData<Metadata>;\n\n /**\n * A ClockTimer instance, used for timing events.\n */\n public clock: Clock = new Clock();\n\n #_roomId: string;\n #_roomName: string;\n\n /**\n * Maximum number of clients allowed to connect into the room. When room reaches this limit,\n * it is locked automatically. Unless the room was explicitly locked by you via `lock()` method,\n * the room will be unlocked as soon as a client disconnects from it.\n */\n public maxClients: number = Infinity;\n /**\n * Frequency to send the room state to connected clients, in milliseconds.\n *\n * @default 50ms (20fps)\n */\n public patchRate: number = DEFAULT_PATCH_RATE;\n /**\n * Automatically dispose the room when last client disconnects.\n *\n * @default true\n */\n public autoDispose: boolean = true;\n\n /**\n * The state instance you provided to `setState()`.\n */\n public state: State;\n /**\n * The presence instance. Check Presence API for more details.\n *\n * @see {@link https://docs.colyseus.io/colyseus/server/presence/|Presence API}\n */\n public presence: Presence;\n\n /**\n * The array of connected clients.\n *\n * @see {@link https://docs.colyseus.io/colyseus/server/room/#client|Client instance}\n */\n public clients: ClientArray<any> = new ClientArray();\n\n public internalState: RoomInternalState = RoomInternalState.CREATING;\n\n /** @internal */\n public _events = new EventEmitter();\n\n // seat reservation & reconnection\n protected seatReservationTime: number = DEFAULT_SEAT_RESERVATION_TIME;\n protected reservedSeats: { [sessionId: string]: any } = {};\n protected reservedSeatTimeouts: { [sessionId: string]: NodeJS.Timer } = {};\n\n protected _reconnections: { [reconnectionToken: string]: [string, Deferred] } = {};\n private _reconnectingSessionId = new Map<string, string>();\n\n private onMessageHandlers: {[id: string]: (client: Client, message: any) => void} = {};\n\n private _serializer: Serializer<State> = noneSerializer;\n private _afterNextPatchQueue: Array<[string | Client, IArguments]> = [];\n\n private _simulationInterval: NodeJS.Timer;\n private _patchInterval: NodeJS.Timer;\n\n private _locked: boolean = false;\n private _lockedExplicitly: boolean = false;\n private _maxClientsReached: boolean = false;\n\n // this timeout prevents rooms that are created by one process, but no client\n // ever had success joining into it on the specified interval.\n private _autoDisposeTimeout: NodeJS.Timer;\n\n constructor(presence?: Presence) {\n this.presence = presence;\n\n this._events.once('dispose', async () => {\n try {\n await this._dispose();\n\n } catch (e) {\n debugAndPrintError(`onDispose error: ${(e && e.message || e || 'promise rejected')}`);\n }\n this._events.emit('disconnect');\n });\n\n this.setPatchRate(this.patchRate);\n // set default _autoDisposeTimeout\n this.resetAutoDisposeTimeout(this.seatReservationTime);\n }\n\n /**\n * The name of the room you provided as first argument for `gameServer.define()`.\n *\n * @returns roomName string\n */\n public get roomName() { return this.#_roomName; }\n /**\n * Setting the name of the room. Overwriting this property is restricted.\n *\n * @param roomName\n */\n public set roomName(roomName: string) {\n if (this.#_roomName) {\n // prevent user from setting roomName after it has been defined.\n throw new ServerError(ErrorCode.APPLICATION_ERROR, \"'roomName' cannot be overwritten.\");\n }\n this.#_roomName = roomName;\n }\n\n /**\n * A unique, auto-generated, 9-character-long id of the room.\n * You may replace `this.roomId` during `onCreate()`.\n *\n * @returns roomId string\n */\n public get roomId() { return this.#_roomId; }\n /**\n * Setting the roomId, is restricted in room lifetime except upon room creation.\n *\n * @param roomId\n * @returns roomId string\n */\n public set roomId(roomId: string) {\n if (this.internalState !== RoomInternalState.CREATING && !isDevMode) {\n // prevent user from setting roomId after room has been created.\n throw new ServerError(ErrorCode.APPLICATION_ERROR, \"'roomId' can only be overridden upon room creation.\");\n }\n this.#_roomId = roomId;\n }\n\n // Optional abstract methods\n public onBeforePatch?(state: State): void | Promise<any>;\n public onCreate?(options: any): void | Promise<any>;\n public onJoin?(client: Client, options?: any, auth?: any): void | Promise<any>;\n public onLeave?(client: Client, consented?: boolean): void | Promise<any>;\n public onDispose?(): void | Promise<any>;\n public onAuth(client: Client, options: any, request?: http.IncomingMessage): any | Promise<any> {\n return true;\n }\n\n /**\n * devMode: When `devMode` is enabled, `onCacheRoom` method is called during\n * graceful shutdown.\n *\n * Implement this method to return custom data to be cached. `onRestoreRoom`\n * will be called with the data returned by `onCacheRoom`\n */\n public onCacheRoom?(): any;\n\n /**\n * devMode: When `devMode` is enabled, `onRestoreRoom` method is called during\n * process startup, with the data returned by the `onCacheRoom` method.\n */\n public onRestoreRoom?(cached?: any): void;\n\n /**\n * Returns whether the sum of connected clients and reserved seats exceeds maximum number of clients.\n *\n * @returns boolean\n */\n public hasReachedMaxClients(): boolean {\n return (this.clients.length + Object.keys(this.reservedSeats).length) >= this.maxClients;\n }\n\n /**\n * Set the number of seconds a room can wait for a client to effectively join the room.\n * You should consider how long your `onAuth()` will have to wait for setting a different seat reservation time.\n * The default value is 15 seconds. You may set the `COLYSEUS_SEAT_RESERVATION_TIME`\n * environment variable if you'd like to change the seat reservation time globally.\n *\n * @default 15 seconds\n *\n * @param seconds - number of seconds.\n * @returns The modified Room object.\n */\n public setSeatReservationTime(seconds: number) {\n this.seatReservationTime = seconds;\n return this;\n }\n\n public hasReservedSeat(sessionId: string, reconnectionToken?: string): boolean {\n if (reconnectionToken) {\n const reconnection = this._reconnections[reconnectionToken];\n return (\n reconnection &&\n reconnection[0] === sessionId &&\n this.reservedSeats[sessionId] !== undefined &&\n this._reconnectingSessionId.has(sessionId)\n );\n\n } else {\n return (\n this.reservedSeats[sessionId] !== undefined &&\n (\n !this._reconnectingSessionId.has(sessionId) || // prevent possible \"reconnect\" requests without a reconnection token\n (this._reconnectingSessionId.get(sessionId) === sessionId) // devMode reconnection\n )\n );\n }\n }\n\n public checkReconnectionToken(reconnectionToken: string) {\n const reconnection = this._reconnections[reconnectionToken];\n const sessionId = (reconnection && reconnection[0]);\n\n if (this.hasReservedSeat(sessionId)) {\n this._reconnectingSessionId.set(sessionId, reconnectionToken);\n return sessionId;\n\n } else {\n return undefined;\n }\n }\n\n /**\n * (Optional) Set a simulation interval that can change the state of the game.\n * The simulation interval is your game loop.\n *\n * @default 16.6ms (60fps)\n *\n * @param onTickCallback - You can implement your physics or world updates here!\n * This is a good place to update the room state.\n * @param delay - Interval delay on executing `onTickCallback` in milliseconds.\n */\n public setSimulationInterval(onTickCallback?: SimulationCallback, delay: number = DEFAULT_SIMULATION_INTERVAL): void {\n // clear previous interval in case called setSimulationInterval more than once\n if (this._simulationInterval) { clearInterval(this._simulationInterval); }\n\n if (onTickCallback) {\n this._simulationInterval = setInterval(() => {\n this.clock.tick();\n onTickCallback(this.clock.deltaTime);\n }, delay);\n }\n }\n\n public setPatchRate(milliseconds: number): void {\n this.patchRate = milliseconds;\n\n // clear previous interval in case called setPatchRate more than once\n if (this._patchInterval) {\n clearInterval(this._patchInterval);\n this._patchInterval = undefined;\n }\n\n if (milliseconds !== null && milliseconds !== 0) {\n this._patchInterval = setInterval(() => this.broadcastPatch(), milliseconds);\n }\n }\n\n public setState(newState: State) {\n this.clock.start();\n\n if ('_definition' in newState) {\n this.setSerializer(new SchemaSerializer());\n }\n\n this._serializer.reset(newState);\n\n this.state = newState;\n }\n\n public setSerializer(serializer: Serializer<State>) {\n this._serializer = serializer;\n }\n\n public async setMetadata(meta: Partial<Metadata>) {\n if (!this.listing.metadata) {\n this.listing.metadata = meta as Metadata;\n\n } else {\n for (const field in meta) {\n if (!meta.hasOwnProperty(field)) { continue; }\n this.listing.metadata[field] = meta[field];\n }\n\n // `MongooseDriver` workaround: persit metadata mutations\n if ('markModified' in this.listing) {\n (this.listing as any).markModified('metadata');\n }\n }\n\n if (this.internalState === RoomInternalState.CREATED) {\n await this.listing.save();\n }\n }\n\n public async setPrivate(bool: boolean = true) {\n this.listing.private = bool;\n\n if (this.internalState === RoomInternalState.CREATED) {\n await this.listing.save();\n }\n }\n\n /**\n * Locking the room will remove it from the pool of available rooms for new clients to connect to.\n */\n public async lock() {\n // rooms locked internally aren't explicit locks.\n this._lockedExplicitly = (arguments[0] === undefined);\n\n // skip if already locked.\n if (this._locked) { return; }\n\n this._locked = true;\n\n await this.listing.updateOne({\n $set: { locked: this._locked },\n });\n\n this._events.emit('lock');\n }\n\n /**\n * Unlocking the room returns it to the pool of available rooms for new clients to connect to.\n */\n public async unlock() {\n // only internal usage passes arguments to this function.\n if (arguments[0] === undefined) {\n this._lockedExplicitly = false;\n }\n\n // skip if already locked\n if (!this._locked) { return; }\n\n this._locked = false;\n\n await this.listing.updateOne({\n $set: { locked: this._locked },\n });\n\n this._events.emit('unlock');\n }\n\n public send(client: Client, type: string | number, message: any, options?: ISendOptions): void;\n public send(client: Client, message: Schema, options?: ISendOptions): void;\n public send(client: Client, messageOrType: any, messageOrOptions?: any | ISendOptions, options?: ISendOptions): void {\n logger.warn('DEPRECATION WARNING: use client.send(...) instead of this.send(client, ...)');\n client.send(messageOrType, messageOrOptions, options);\n }\n\n public broadcast(type: string | number, message?: any, options?: IBroadcastOptions);\n public broadcast<T extends Schema>(message: T, options?: IBroadcastOptions);\n public broadcast(\n typeOrSchema: string | number | Schema,\n messageOrOptions?: any | IBroadcastOptions,\n options?: IBroadcastOptions,\n ) {\n const isSchema = (typeof(typeOrSchema) === 'object');\n const opts: IBroadcastOptions = ((isSchema) ? messageOrOptions : options);\n\n if (opts && opts.afterNextPatch) {\n delete opts.afterNextPatch;\n this._afterNextPatchQueue.push(['broadcast', arguments]);\n return;\n }\n\n if (isSchema) {\n this.broadcastMessageSchema(typeOrSchema as Schema, opts);\n\n } else {\n\n this.broadcastMessageType(typeOrSchema as string, messageOrOptions, opts);\n }\n }\n\n /**\n * Checks whether mutations have occurred in the state, and broadcast them to all connected clients.\n */\n public broadcastPatch() {\n if (this.onBeforePatch) {\n this.onBeforePatch(this.state);\n }\n\n if (!this._simulationInterval) {\n this.clock.tick();\n }\n\n if (!this.state) {\n return false;\n }\n\n const hasChanges = this._serializer.applyPatches(this.clients, this.state);\n\n // broadcast messages enqueued for \"after patch\"\n this._dequeueAfterPatchMessages();\n\n return hasChanges;\n }\n\n public onMessage<T = any>(messageType: '*', callback: (client: Client, type: string | number, message: T) => void);\n public onMessage<T = any>(messageType: string | number, callback: (client: Client, message: T) => void);\n public onMessage<T = any>(messageType: '*' | string | number, callback: (...args: any[]) => void) {\n this.onMessageHandlers[messageType] = callback;\n // returns a method to unbind the callback\n return () => delete this.onMessageHandlers[messageType];\n }\n\n /**\n * Disconnect all connected clients, and then dispose the room.\n *\n * @param closeCode WebSocket close code (default = 4000, which is a \"consented leave\")\n * @returns Promise<void>\n */\n public async disconnect(closeCode: number = Protocol.WS_CLOSE_CONSENTED): Promise<any> {\n this.internalState = RoomInternalState.DISCONNECTING;\n await this.listing.remove();\n\n this.autoDispose = true;\n\n const delayedDisconnection = new Promise<void>((resolve) =>\n this._events.once('disconnect', () => resolve()));\n\n for (const [_, reconnection] of Object.values(this._reconnections)) {\n reconnection.reject();\n }\n\n let numClients = this.clients.length;\n if (numClients > 0) {\n // clients may have `async onLeave`, room will be disposed after they're fulfilled\n while (numClients--) {\n this._forciblyCloseClient(this.clients[numClients], closeCode);\n }\n } else {\n // no clients connected, dispose immediately.\n this._events.emit('dispose');\n }\n\n return await delayedDisconnection;\n }\n\n public async ['_onJoin'](client: Client, req?: http.IncomingMessage) {\n const sessionId = client.sessionId;\n\n // generate unique private reconnection token\n client._reconnectionToken = generateId();\n\n if (this.reservedSeatTimeouts[sessionId]) {\n clearTimeout(this.reservedSeatTimeouts[sessionId]);\n delete this.reservedSeatTimeouts[sessionId];\n }\n\n // clear auto-dispose timeout.\n if (this._autoDisposeTimeout) {\n clearTimeout(this._autoDisposeTimeout);\n this._autoDisposeTimeout = undefined;\n }\n\n // get seat reservation options and clear it\n const options = this.reservedSeats[sessionId];\n delete this.reservedSeats[sessionId];\n\n // share \"after next patch queue\" reference with every client.\n client._afterNextPatchQueue = this._afterNextPatchQueue;\n\n // bind clean-up callback when client connection closes\n client.ref['onleave'] = this._onLeave.bind(this, client);\n client.ref.once('close', client.ref['onleave']);\n\n this.clients.push(client);\n\n const previousReconnectionToken = this._reconnectingSessionId.get(sessionId);\n if (previousReconnectionToken) {\n this._reconnections[previousReconnectionToken]?.[1].resolve(client);\n\n } else {\n try {\n client.auth = await this.onAuth(client, options, req);\n\n if (!client.auth) {\n throw new ServerError(ErrorCode.AUTH_FAILED, 'onAuth failed');\n }\n\n if (this.onJoin) {\n await this.onJoin(client, options, client.auth);\n }\n } catch (e) {\n this.clients.delete(client);\n\n // make sure an error code is provided.\n if (!e.code) {\n e.code = ErrorCode.APPLICATION_ERROR;\n }\n\n throw e;\n\n } finally {\n // remove seat reservation\n delete this.reservedSeats[sessionId];\n }\n }\n\n // emit 'join' to room handler\n this._events.emit('join', client);\n\n // allow client to send messages after onJoin has succeeded.\n client.ref.on('message', this._onMessage.bind(this, client));\n\n // confirm room id that matches the room name requested to join\n client.raw(getMessageBytes[Protocol.JOIN_ROOM](\n client._reconnectionToken,\n this._serializer.id,\n this._serializer.handshake && this._serializer.handshake(),\n ));\n }\n\n /**\n * Allow the specified client to reconnect into the room. Must be used inside `onLeave()` method.\n * If seconds is provided, the reconnection is going to be cancelled after the provided amount of seconds.\n *\n * @param previousClient - The client which is to be waiting until re-connection happens.\n * @param seconds - Timeout period on re-connection in seconds.\n *\n * @returns Deferred<Client> - The differed is a promise like type.\n * This type can forcibly reject the promise by calling `.reject()`.\n */\n public allowReconnection(previousClient: Client, seconds: number | \"manual\"): Deferred<Client> {\n if (seconds === undefined) { // TODO: remove this check\n console.warn(\"DEPRECATED: allowReconnection() requires a second argument. Using \\\"manual\\\" mode.\");\n seconds = \"manual\";\n }\n\n if (seconds === \"manual\") {\n seconds = Infinity;\n }\n\n if (this.internalState === RoomInternalState.DISCONNECTING) {\n this._disposeIfEmpty(); // gracefully shutting down\n throw new Error('disconnecting');\n }\n\n const sessionId = previousClient.sessionId;\n const reconnectionToken = previousClient._reconnectionToken;\n\n this._reserveSeat(sessionId, true, seconds, true);\n\n // keep reconnection reference in case the user reconnects into this room.\n const reconnection = new Deferred<Client>();\n this._reconnections[reconnectionToken] = [sessionId, reconnection];\n\n if (seconds !== Infinity) {\n // expire seat reservation after timeout\n this.reservedSeatTimeouts[sessionId] = setTimeout(() =>\n reconnection.reject(false), seconds * 1000);\n }\n\n const cleanup = () => {\n delete this._reconnections[reconnectionToken];\n delete this.reservedSeats[sessionId];\n delete this.reservedSeatTimeouts[sessionId];\n this._reconnectingSessionId.delete(sessionId);\n };\n\n reconnection.\n then((newClient) => {\n newClient.auth = previousClient.auth;\n newClient.userData = previousClient.userData;\n previousClient.ref = newClient.ref; // swap \"ref\" for convenience\n previousClient.state = ClientState.RECONNECTED;\n clearTimeout(this.reservedSeatTimeouts[sessionId]);\n cleanup();\n }).\n catch(() => {\n cleanup();\n this.resetAutoDisposeTimeout();\n });\n\n return reconnection;\n }\n\n protected resetAutoDisposeTimeout(timeoutInSeconds: number = 1) {\n clearTimeout(this._autoDisposeTimeout);\n\n if (!this.autoDispose) {\n return;\n }\n\n this._autoDisposeTimeout = setTimeout(() => {\n this._autoDisposeTimeout = undefined;\n this._disposeIfEmpty();\n }, timeoutInSeconds * 1000);\n }\n\n private broadcastMessageSchema<T extends Schema>(message: T, options: IBroadcastOptions = {}) {\n debugMessage(\"broadcast: %O\", message);\n const encodedMessage = getMessageBytes[Protocol.ROOM_DATA_SCHEMA](message);\n const except = (typeof (options.except) !== \"undefined\")\n ? Array.isArray(options.except)\n ? options.except\n : [options.except]\n : undefined;\n\n let numClients = this.clients.length;\n while (numClients--) {\n const client = this.clients[numClients];\n\n if (!except || !except.includes(client)) {\n client.enqueueRaw(encodedMessage);\n }\n }\n }\n\n private broadcastMessageType(type: string, message?: any, options: IBroadcastOptions = {}) {\n debugMessage(\"broadcast: %O\", message);\n const encodedMessage = getMessageBytes.raw(Protocol.ROOM_DATA, type, message);\n const except = (typeof (options.except) !== \"undefined\")\n ? Array.isArray(options.except)\n ? options.except\n : [options.except]\n : undefined;\n\n let numClients = this.clients.length;\n while (numClients--) {\n const client = this.clients[numClients];\n\n if (!except || !except.includes(client)) {\n client.enqueueRaw(encodedMessage);\n }\n }\n }\n\n private sendFullState(client: Client): void {\n client.enqueueRaw(getMessageBytes[Protocol.ROOM_STATE](this._serializer.getFullState(client)));\n }\n\n private _dequeueAfterPatchMessages() {\n const length = this._afterNextPatchQueue.length;\n\n if (length > 0) {\n for (let i = 0; i < length; i++) {\n const [target, args] = this._afterNextPatchQueue[i];\n\n if (target === \"broadcast\") {\n this.broadcast.apply(this, args);\n\n } else {\n (target as Client).raw.apply(target, args);\n }\n }\n\n // new messages may have been added in the meantime,\n // let's splice the ones that have been processed\n this._afterNextPatchQueue.splice(0, length);\n }\n }\n\n private async _reserveSeat(\n sessionId: string,\n joinOptions: any = true,\n seconds: number = this.seatReservationTime,\n allowReconnection: boolean = false,\n devModeReconnection?: boolean,\n ) {\n if (!allowReconnection && this.hasReachedMaxClients()) {\n return false;\n }\n\n this.reservedSeats[sessionId] = joinOptions;\n\n if (!allowReconnection) {\n await this._incrementClientCount();\n\n this.reservedSeatTimeouts[sessionId] = setTimeout(async () => {\n delete this.reservedSeats[sessionId];\n delete this.reservedSeatTimeouts[sessionId];\n await this._decrementClientCount();\n }, seconds * 1000);\n\n this.resetAutoDisposeTimeout(seconds);\n }\n\n //\n // isDevMode workaround to allow players to reconnect on devMode\n //\n if (devModeReconnection) {\n this._reconnectingSessionId.set(sessionId, sessionId);\n }\n\n return true;\n }\n\n private _disposeIfEmpty() {\n const willDispose = (\n this.autoDispose &&\n this._autoDisposeTimeout === undefined &&\n this.clients.length === 0 &&\n Object.keys(this.reservedSeats).length === 0\n );\n\n if (willDispose) {\n this._events.emit('dispose');\n }\n\n return willDispose;\n }\n\n private async _dispose(): Promise<any> {\n let userReturnData;\n\n if (this.onDispose) {\n userReturnData = this.onDispose();\n }\n\n if (this._patchInterval) {\n clearInterval(this._patchInterval);\n this._patchInterval = undefined;\n }\n\n if (this._simulationInterval) {\n clearInterval(this._simulationInterval);\n this._simulationInterval = undefined;\n }\n\n if (this._autoDisposeTimeout) {\n clearInterval(this._autoDisposeTimeout);\n this._autoDisposeTimeout = undefined;\n }\n\n // clear all timeouts/intervals + force to stop ticking\n this.clock.clear();\n this.clock.stop();\n\n return await (userReturnData || Promise.resolve());\n }\n\n private _onMessage(client: Client, bytes: number[]) {\n // skip if client is on LEAVING state.\n if (client.state === ClientState.LEAVING) { return; }\n\n const it: Iterator = { offset: 0 };\n const code = decode.uint8(bytes, it);\n\n if (!bytes) {\n debugAndPrintError(`${this.roomName} (${this.roomId}), couldn't decode message: ${bytes}`);\n return;\n }\n\n if (code === Protocol.ROOM_DATA) {\n const messageType = (decode.stringCheck(bytes, it))\n ? decode.string(bytes, it)\n : decode.number(bytes, it);\n\n let message;\n try {\n message = (bytes.length > it.offset)\n ? unpack(new Uint8Array(bytes.slice(it.offset, bytes.length)))\n : undefined;\n debugMessage(\"received: '%s' -> %j\", messageType, message);\n } catch (e) {\n debugAndPrintError(e);\n return;\n }\n\n if (this.onMessageHandlers[messageType]) {\n this.onMessageHandlers[messageType](client, message);\n\n } else if (this.onMessageHandlers['*']) {\n (this.onMessageHandlers['*'] as any)(client, messageType, message);\n\n } else {\n debugAndPrintError(`onMessage for \"${messageType}\" not registered.`);\n }\n\n } else if (code === Protocol.ROOM_DATA_BYTES) {\n const messageType = (decode.stringCheck(bytes, it))\n ? decode.string(bytes, it)\n : decode.number(bytes, it);\n\n const message = bytes.slice(it.offset, bytes.length);\n debugMessage(\"received: '%s' -> %j\", messageType, message);\n\n if (this.onMessageHandlers[messageType]) {\n this.onMessageHandlers[messageType](client, message);\n\n } else if (this.onMessageHandlers['*']) {\n (this.onMessageHandlers['*'] as any)(client, messageType, message);\n\n } else {\n debugAndPrintError(`onMessage for \"${messageType}\" not registered.`);\n }\n\n } else if (code === Protocol.JOIN_ROOM && client.state === ClientState.JOINING) {\n // join room has been acknowledged by the client\n client.state = ClientState.JOINED;\n\n // send current state when new client joins the room\n if (this.state) {\n this.sendFullState(client);\n }\n\n // dequeue messages sent before client has joined effectively (on user-defined `onJoin`)\n if (client._enqueuedMessages.length > 0) {\n client._enqueuedMessages.forEach((enqueued) => client.raw(enqueued));\n }\n delete client._enqueuedMessages;\n\n } else if (code === Protocol.LEAVE_ROOM) {\n this._forciblyCloseClient(client, Protocol.WS_CLOSE_CONSENTED);\n }\n\n }\n\n private _forciblyCloseClient(client: Client, closeCode: number) {\n // stop receiving messages from this client\n client.ref.removeAllListeners('message');\n\n // prevent \"onLeave\" from being called twice if player asks to leave\n client.ref.removeListener('close', client.ref['onleave']);\n\n // only effectively close connection when \"onLeave\" is fulfilled\n this._onLeave(client, closeCode).then(() => client.leave(closeCode));\n }\n\n private async _onLeave(client: Client, code?: number): Promise<any> {\n const success = this.clients.delete(client);\n\n // call 'onLeave' method only if the client has been successfully accepted.\n if (success && this.onLeave) {\n try {\n client.state = ClientState.LEAVING;\n await this.onLeave(client, (code === Protocol.WS_CLOSE_CONSENTED));\n\n } catch (e) {\n debugAndPrintError(`onLeave error: ${(e && e.message || e || 'promise rejected')}`);\n }\n }\n\n if (client.state !== ClientState.RECONNECTED) {\n // try to dispose immediately if client reconnection isn't set up.\n const willDispose = await this._decrementClientCount();\n\n this._events.emit('leave', client, willDispose);\n }\n }\n\n private async _incrementClientCount() {\n // lock automatically when maxClients is reached\n if (!this._locked && this.hasReachedMaxClients()) {\n this._maxClientsReached = true;\n this.lock.call(this, true);\n }\n\n await this.listing.updateOne({\n $inc: { clients: 1 },\n $set: { locked: this._locked },\n });\n }\n\n private async _decrementClientCount() {\n const willDispose = this._disposeIfEmpty();\n\n if (this.internalState === RoomInternalState.DISCONNECTING) {\n return;\n }\n\n // unlock if room is available for new connections\n if (!willDispose) {\n if (this._maxClientsReached && !this._lockedExplicitly) {\n this._maxClientsReached = false;\n this.unlock.call(this, true);\n }\n\n // update room listing cache\n await this.listing.updateOne({\n $inc: { clients: -1 },\n $set: { locked: this._locked },\n });\n }\n\n return willDispose;\n }\n\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["RoomInternalState", "Clock"]
7
7
  }
package/build/Room.mjs CHANGED
@@ -6,11 +6,11 @@ import { logger } from "./Logger";
6
6
  import { NoneSerializer } from "./serializer/NoneSerializer";
7
7
  import { SchemaSerializer } from "./serializer/SchemaSerializer";
8
8
  import { ErrorCode, getMessageBytes, Protocol } from "./Protocol";
9
- import { Deferred, HybridArray, generateId } from "./utils/Utils";
9
+ import { Deferred, generateId } from "./utils/Utils";
10
10
  import { isDevMode } from "./utils/DevMode";
11
11
  import { debugAndPrintError, debugMessage } from "./Debug";
12
12
  import { ServerError } from "./errors/ServerError";
13
- import { ClientState } from "./Transport";
13
+ import { ClientArray, ClientState } from "./Transport";
14
14
  const DEFAULT_PATCH_RATE = 1e3 / 20;
15
15
  const DEFAULT_SIMULATION_INTERVAL = 1e3 / 60;
16
16
  const noneSerializer = new NoneSerializer();
@@ -37,7 +37,7 @@ class Room {
37
37
  autoDispose = true;
38
38
  state;
39
39
  presence;
40
- clients = new HybridArray("sessionId");
40
+ clients = new ClientArray();
41
41
  internalState = 0 /* CREATING */;
42
42
  _events = new EventEmitter();
43
43
  seatReservationTime = DEFAULT_SEAT_RESERVATION_TIME;
@@ -221,7 +221,7 @@ class Room {
221
221
  if (!this.state) {
222
222
  return false;
223
223
  }
224
- const hasChanges = this._serializer.applyPatches(this.clients.array, this.state);
224
+ const hasChanges = this._serializer.applyPatches(this.clients, this.state);
225
225
  this._dequeueAfterPatchMessages();
226
226
  return hasChanges;
227
227
  }
@@ -240,7 +240,7 @@ class Room {
240
240
  let numClients = this.clients.length;
241
241
  if (numClients > 0) {
242
242
  while (numClients--) {
243
- this._forciblyCloseClient(this.clients.array[numClients], closeCode);
243
+ this._forciblyCloseClient(this.clients[numClients], closeCode);
244
244
  }
245
245
  } else {
246
246
  this._events.emit("dispose");
@@ -263,7 +263,7 @@ class Room {
263
263
  client._afterNextPatchQueue = this._afterNextPatchQueue;
264
264
  client.ref["onleave"] = this._onLeave.bind(this, client);
265
265
  client.ref.once("close", client.ref["onleave"]);
266
- this.clients.add(client);
266
+ this.clients.push(client);
267
267
  const previousReconnectionToken = this._reconnectingSessionId.get(sessionId);
268
268
  if (previousReconnectionToken) {
269
269
  this._reconnections[previousReconnectionToken]?.[1].resolve(client);
@@ -349,7 +349,7 @@ class Room {
349
349
  const except = typeof options.except !== "undefined" ? Array.isArray(options.except) ? options.except : [options.except] : void 0;
350
350
  let numClients = this.clients.length;
351
351
  while (numClients--) {
352
- const client = this.clients.array[numClients];
352
+ const client = this.clients[numClients];
353
353
  if (!except || !except.includes(client)) {
354
354
  client.enqueueRaw(encodedMessage);
355
355
  }
@@ -361,7 +361,7 @@ class Room {
361
361
  const except = typeof options.except !== "undefined" ? Array.isArray(options.except) ? options.except : [options.except] : void 0;
362
362
  let numClients = this.clients.length;
363
363
  while (numClients--) {
364
- const client = this.clients.array[numClients];
364
+ const client = this.clients[numClients];
365
365
  if (!except || !except.includes(client)) {
366
366
  client.enqueueRaw(encodedMessage);
367
367
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/Room.ts"],
4
- "sourcesContent": ["import http from 'http';\n\nimport { unpack } from 'msgpackr';\nimport { decode, Iterator, Schema } from '@colyseus/schema';\n\nimport Clock from '@gamestdio/timer';\nimport { EventEmitter } from 'events';\nimport { logger } from './Logger';\n\nimport { Presence } from './presence/Presence';\n\nimport { NoneSerializer } from './serializer/NoneSerializer';\nimport { SchemaSerializer } from './serializer/SchemaSerializer';\nimport { Serializer } from './serializer/Serializer';\n\nimport { ErrorCode, getMessageBytes, Protocol } from './Protocol';\nimport { Deferred, HybridArray, generateId } from './utils/Utils';\nimport { isDevMode } from './utils/DevMode';\n\nimport { debugAndPrintError, debugMessage } from './Debug';\nimport { ServerError } from './errors/ServerError';\nimport { RoomListingData } from './matchmaker/driver';\nimport { Client, ClientState, ISendOptions } from './Transport';\n\nconst DEFAULT_PATCH_RATE = 1000 / 20; // 20fps (50ms)\nconst DEFAULT_SIMULATION_INTERVAL = 1000 / 60; // 60fps (16.66ms)\nconst noneSerializer = new NoneSerializer();\n\nexport const DEFAULT_SEAT_RESERVATION_TIME = Number(process.env.COLYSEUS_SEAT_RESERVATION_TIME || 15);\n\nexport type SimulationCallback = (deltaTime: number) => void;\n\nexport type RoomConstructor<T extends object= any> = new (presence?: Presence) => Room<T>;\n\nexport interface IBroadcastOptions extends ISendOptions {\n except?: Client | Client[];\n}\n\nexport enum RoomInternalState {\n CREATING = 0,\n CREATED = 1,\n DISCONNECTING = 2,\n}\n\n/**\n * A Room class is meant to implement a game session, and/or serve as the communication channel\n * between a group of clients.\n *\n * - Rooms are created on demand during matchmaking by default\n * - Room classes must be exposed using `.define()`\n */\nexport abstract class Room<State extends object= any, Metadata= any> {\n\n /**\n * This property will change on these situations:\n * - The maximum number of allowed clients has been reached (`maxClients`)\n * - You manually locked, or unlocked the room using lock() or `unlock()`.\n *\n * @readonly\n */\n public get locked() {\n return this._locked;\n }\n\n public get metadata() {\n return this.listing.metadata;\n }\n\n public listing: RoomListingData<Metadata>;\n\n /**\n * A ClockTimer instance, used for timing events.\n */\n public clock: Clock = new Clock();\n\n #_roomId: string;\n #_roomName: string;\n\n /**\n * Maximum number of clients allowed to connect into the room. When room reaches this limit,\n * it is locked automatically. Unless the room was explicitly locked by you via `lock()` method,\n * the room will be unlocked as soon as a client disconnects from it.\n */\n public maxClients: number = Infinity;\n /**\n * Frequency to send the room state to connected clients, in milliseconds.\n *\n * @default 50ms (20fps)\n */\n public patchRate: number = DEFAULT_PATCH_RATE;\n /**\n * Automatically dispose the room when last client disconnects.\n *\n * @default true\n */\n public autoDispose: boolean = true;\n\n /**\n * The state instance you provided to `setState()`.\n */\n public state: State;\n /**\n * The presence instance. Check Presence API for more details.\n *\n * @see {@link https://docs.colyseus.io/colyseus/server/presence/|Presence API}\n */\n public presence: Presence;\n\n /**\n * The array of connected clients.\n *\n * @see {@link https://docs.colyseus.io/colyseus/server/room/#client|Client instance}\n */\n public clients: HybridArray<Client> = new HybridArray<Client>(\"sessionId\");\n\n public internalState: RoomInternalState = RoomInternalState.CREATING;\n\n /** @internal */\n public _events = new EventEmitter();\n\n // seat reservation & reconnection\n protected seatReservationTime: number = DEFAULT_SEAT_RESERVATION_TIME;\n protected reservedSeats: { [sessionId: string]: any } = {};\n protected reservedSeatTimeouts: { [sessionId: string]: NodeJS.Timer } = {};\n\n protected _reconnections: { [reconnectionToken: string]: [string, Deferred] } = {};\n private _reconnectingSessionId = new Map<string, string>();\n\n private onMessageHandlers: {[id: string]: (client: Client, message: any) => void} = {};\n\n private _serializer: Serializer<State> = noneSerializer;\n private _afterNextPatchQueue: Array<[string | Client, IArguments]> = [];\n\n private _simulationInterval: NodeJS.Timer;\n private _patchInterval: NodeJS.Timer;\n\n private _locked: boolean = false;\n private _lockedExplicitly: boolean = false;\n private _maxClientsReached: boolean = false;\n\n // this timeout prevents rooms that are created by one process, but no client\n // ever had success joining into it on the specified interval.\n private _autoDisposeTimeout: NodeJS.Timer;\n\n constructor(presence?: Presence) {\n this.presence = presence;\n\n this._events.once('dispose', async () => {\n try {\n await this._dispose();\n\n } catch (e) {\n debugAndPrintError(`onDispose error: ${(e && e.message || e || 'promise rejected')}`);\n }\n this._events.emit('disconnect');\n });\n\n this.setPatchRate(this.patchRate);\n // set default _autoDisposeTimeout\n this.resetAutoDisposeTimeout(this.seatReservationTime);\n }\n\n /**\n * The name of the room you provided as first argument for `gameServer.define()`.\n *\n * @returns roomName string\n */\n public get roomName() { return this.#_roomName; }\n /**\n * Setting the name of the room. Overwriting this property is restricted.\n *\n * @param roomName\n */\n public set roomName(roomName: string) {\n if (this.#_roomName) {\n // prevent user from setting roomName after it has been defined.\n throw new ServerError(ErrorCode.APPLICATION_ERROR, \"'roomName' cannot be overwritten.\");\n }\n this.#_roomName = roomName;\n }\n\n /**\n * A unique, auto-generated, 9-character-long id of the room.\n * You may replace `this.roomId` during `onCreate()`.\n *\n * @returns roomId string\n */\n public get roomId() { return this.#_roomId; }\n /**\n * Setting the roomId, is restricted in room lifetime except upon room creation.\n *\n * @param roomId\n * @returns roomId string\n */\n public set roomId(roomId: string) {\n if (this.internalState !== RoomInternalState.CREATING && !isDevMode) {\n // prevent user from setting roomId after room has been created.\n throw new ServerError(ErrorCode.APPLICATION_ERROR, \"'roomId' can only be overridden upon room creation.\");\n }\n this.#_roomId = roomId;\n }\n\n // Optional abstract methods\n public onBeforePatch?(state: State): void | Promise<any>;\n public onCreate?(options: any): void | Promise<any>;\n public onJoin?(client: Client, options?: any, auth?: any): void | Promise<any>;\n public onLeave?(client: Client, consented?: boolean): void | Promise<any>;\n public onDispose?(): void | Promise<any>;\n public onAuth(client: Client, options: any, request?: http.IncomingMessage): any | Promise<any> {\n return true;\n }\n\n /**\n * devMode: When `devMode` is enabled, `onCacheRoom` method is called during\n * graceful shutdown.\n *\n * Implement this method to return custom data to be cached. `onRestoreRoom`\n * will be called with the data returned by `onCacheRoom`\n */\n public onCacheRoom?(): any;\n\n /**\n * devMode: When `devMode` is enabled, `onRestoreRoom` method is called during\n * process startup, with the data returned by the `onCacheRoom` method.\n */\n public onRestoreRoom?(cached?: any): void;\n\n /**\n * Returns whether the sum of connected clients and reserved seats exceeds maximum number of clients.\n *\n * @returns boolean\n */\n public hasReachedMaxClients(): boolean {\n return (this.clients.length + Object.keys(this.reservedSeats).length) >= this.maxClients;\n }\n\n /**\n * Set the number of seconds a room can wait for a client to effectively join the room.\n * You should consider how long your `onAuth()` will have to wait for setting a different seat reservation time.\n * The default value is 15 seconds. You may set the `COLYSEUS_SEAT_RESERVATION_TIME`\n * environment variable if you'd like to change the seat reservation time globally.\n *\n * @default 15 seconds\n *\n * @param seconds - number of seconds.\n * @returns The modified Room object.\n */\n public setSeatReservationTime(seconds: number) {\n this.seatReservationTime = seconds;\n return this;\n }\n\n public hasReservedSeat(sessionId: string, reconnectionToken?: string): boolean {\n if (reconnectionToken) {\n const reconnection = this._reconnections[reconnectionToken];\n return (\n reconnection &&\n reconnection[0] === sessionId &&\n this.reservedSeats[sessionId] !== undefined &&\n this._reconnectingSessionId.has(sessionId)\n );\n\n } else {\n return (\n this.reservedSeats[sessionId] !== undefined &&\n (\n !this._reconnectingSessionId.has(sessionId) || // prevent possible \"reconnect\" requests without a reconnection token\n (this._reconnectingSessionId.get(sessionId) === sessionId) // devMode reconnection\n )\n );\n }\n }\n\n public checkReconnectionToken(reconnectionToken: string) {\n const reconnection = this._reconnections[reconnectionToken];\n const sessionId = (reconnection && reconnection[0]);\n\n if (this.hasReservedSeat(sessionId)) {\n this._reconnectingSessionId.set(sessionId, reconnectionToken);\n return sessionId;\n\n } else {\n return undefined;\n }\n }\n\n /**\n * (Optional) Set a simulation interval that can change the state of the game.\n * The simulation interval is your game loop.\n *\n * @default 16.6ms (60fps)\n *\n * @param onTickCallback - You can implement your physics or world updates here!\n * This is a good place to update the room state.\n * @param delay - Interval delay on executing `onTickCallback` in milliseconds.\n */\n public setSimulationInterval(onTickCallback?: SimulationCallback, delay: number = DEFAULT_SIMULATION_INTERVAL): void {\n // clear previous interval in case called setSimulationInterval more than once\n if (this._simulationInterval) { clearInterval(this._simulationInterval); }\n\n if (onTickCallback) {\n this._simulationInterval = setInterval(() => {\n this.clock.tick();\n onTickCallback(this.clock.deltaTime);\n }, delay);\n }\n }\n\n public setPatchRate(milliseconds: number): void {\n this.patchRate = milliseconds;\n\n // clear previous interval in case called setPatchRate more than once\n if (this._patchInterval) {\n clearInterval(this._patchInterval);\n this._patchInterval = undefined;\n }\n\n if (milliseconds !== null && milliseconds !== 0) {\n this._patchInterval = setInterval(() => this.broadcastPatch(), milliseconds);\n }\n }\n\n public setState(newState: State) {\n this.clock.start();\n\n if ('_definition' in newState) {\n this.setSerializer(new SchemaSerializer());\n }\n\n this._serializer.reset(newState);\n\n this.state = newState;\n }\n\n public setSerializer(serializer: Serializer<State>) {\n this._serializer = serializer;\n }\n\n public async setMetadata(meta: Partial<Metadata>) {\n if (!this.listing.metadata) {\n this.listing.metadata = meta as Metadata;\n\n } else {\n for (const field in meta) {\n if (!meta.hasOwnProperty(field)) { continue; }\n this.listing.metadata[field] = meta[field];\n }\n\n // `MongooseDriver` workaround: persit metadata mutations\n if ('markModified' in this.listing) {\n (this.listing as any).markModified('metadata');\n }\n }\n\n if (this.internalState === RoomInternalState.CREATED) {\n await this.listing.save();\n }\n }\n\n public async setPrivate(bool: boolean = true) {\n this.listing.private = bool;\n\n if (this.internalState === RoomInternalState.CREATED) {\n await this.listing.save();\n }\n }\n\n /**\n * Locking the room will remove it from the pool of available rooms for new clients to connect to.\n */\n public async lock() {\n // rooms locked internally aren't explicit locks.\n this._lockedExplicitly = (arguments[0] === undefined);\n\n // skip if already locked.\n if (this._locked) { return; }\n\n this._locked = true;\n\n await this.listing.updateOne({\n $set: { locked: this._locked },\n });\n\n this._events.emit('lock');\n }\n\n /**\n * Unlocking the room returns it to the pool of available rooms for new clients to connect to.\n */\n public async unlock() {\n // only internal usage passes arguments to this function.\n if (arguments[0] === undefined) {\n this._lockedExplicitly = false;\n }\n\n // skip if already locked\n if (!this._locked) { return; }\n\n this._locked = false;\n\n await this.listing.updateOne({\n $set: { locked: this._locked },\n });\n\n this._events.emit('unlock');\n }\n\n public send(client: Client, type: string | number, message: any, options?: ISendOptions): void;\n public send(client: Client, message: Schema, options?: ISendOptions): void;\n public send(client: Client, messageOrType: any, messageOrOptions?: any | ISendOptions, options?: ISendOptions): void {\n logger.warn('DEPRECATION WARNING: use client.send(...) instead of this.send(client, ...)');\n client.send(messageOrType, messageOrOptions, options);\n }\n\n public broadcast(type: string | number, message?: any, options?: IBroadcastOptions);\n public broadcast<T extends Schema>(message: T, options?: IBroadcastOptions);\n public broadcast(\n typeOrSchema: string | number | Schema,\n messageOrOptions?: any | IBroadcastOptions,\n options?: IBroadcastOptions,\n ) {\n const isSchema = (typeof(typeOrSchema) === 'object');\n const opts: IBroadcastOptions = ((isSchema) ? messageOrOptions : options);\n\n if (opts && opts.afterNextPatch) {\n delete opts.afterNextPatch;\n this._afterNextPatchQueue.push(['broadcast', arguments]);\n return;\n }\n\n if (isSchema) {\n this.broadcastMessageSchema(typeOrSchema as Schema, opts);\n\n } else {\n\n this.broadcastMessageType(typeOrSchema as string, messageOrOptions, opts);\n }\n }\n\n /**\n * Checks whether mutations have occurred in the state, and broadcast them to all connected clients.\n */\n public broadcastPatch() {\n if (this.onBeforePatch) {\n this.onBeforePatch(this.state);\n }\n\n if (!this._simulationInterval) {\n this.clock.tick();\n }\n\n if (!this.state) {\n return false;\n }\n\n const hasChanges = this._serializer.applyPatches(this.clients.array, this.state);\n\n // broadcast messages enqueued for \"after patch\"\n this._dequeueAfterPatchMessages();\n\n return hasChanges;\n }\n\n public onMessage<T = any>(messageType: '*', callback: (client: Client, type: string | number, message: T) => void);\n public onMessage<T = any>(messageType: string | number, callback: (client: Client, message: T) => void);\n public onMessage<T = any>(messageType: '*' | string | number, callback: (...args: any[]) => void) {\n this.onMessageHandlers[messageType] = callback;\n // returns a method to unbind the callback\n return () => delete this.onMessageHandlers[messageType];\n }\n\n /**\n * Disconnect all connected clients, and then dispose the room.\n *\n * @param closeCode WebSocket close code (default = 4000, which is a \"consented leave\")\n * @returns Promise<void>\n */\n public async disconnect(closeCode: number = Protocol.WS_CLOSE_CONSENTED): Promise<any> {\n this.internalState = RoomInternalState.DISCONNECTING;\n await this.listing.remove();\n\n this.autoDispose = true;\n\n const delayedDisconnection = new Promise<void>((resolve) =>\n this._events.once('disconnect', () => resolve()));\n\n for (const [_, reconnection] of Object.values(this._reconnections)) {\n reconnection.reject();\n }\n\n let numClients = this.clients.length;\n if (numClients > 0) {\n // clients may have `async onLeave`, room will be disposed after they're fulfilled\n while (numClients--) {\n this._forciblyCloseClient(this.clients.array[numClients], closeCode);\n }\n } else {\n // no clients connected, dispose immediately.\n this._events.emit('dispose');\n }\n\n return await delayedDisconnection;\n }\n\n public async ['_onJoin'](client: Client, req?: http.IncomingMessage) {\n const sessionId = client.sessionId;\n\n // generate unique private reconnection token\n client._reconnectionToken = generateId();\n\n if (this.reservedSeatTimeouts[sessionId]) {\n clearTimeout(this.reservedSeatTimeouts[sessionId]);\n delete this.reservedSeatTimeouts[sessionId];\n }\n\n // clear auto-dispose timeout.\n if (this._autoDisposeTimeout) {\n clearTimeout(this._autoDisposeTimeout);\n this._autoDisposeTimeout = undefined;\n }\n\n // get seat reservation options and clear it\n const options = this.reservedSeats[sessionId];\n delete this.reservedSeats[sessionId];\n\n // share \"after next patch queue\" reference with every client.\n client._afterNextPatchQueue = this._afterNextPatchQueue;\n\n // bind clean-up callback when client connection closes\n client.ref['onleave'] = this._onLeave.bind(this, client);\n client.ref.once('close', client.ref['onleave']);\n\n this.clients.add(client);\n\n const previousReconnectionToken = this._reconnectingSessionId.get(sessionId);\n if (previousReconnectionToken) {\n this._reconnections[previousReconnectionToken]?.[1].resolve(client);\n\n } else {\n try {\n client.auth = await this.onAuth(client, options, req);\n\n if (!client.auth) {\n throw new ServerError(ErrorCode.AUTH_FAILED, 'onAuth failed');\n }\n\n if (this.onJoin) {\n await this.onJoin(client, options, client.auth);\n }\n } catch (e) {\n this.clients.delete(client);\n\n // make sure an error code is provided.\n if (!e.code) {\n e.code = ErrorCode.APPLICATION_ERROR;\n }\n\n throw e;\n\n } finally {\n // remove seat reservation\n delete this.reservedSeats[sessionId];\n }\n }\n\n // emit 'join' to room handler\n this._events.emit('join', client);\n\n // allow client to send messages after onJoin has succeeded.\n client.ref.on('message', this._onMessage.bind(this, client));\n\n // confirm room id that matches the room name requested to join\n client.raw(getMessageBytes[Protocol.JOIN_ROOM](\n client._reconnectionToken,\n this._serializer.id,\n this._serializer.handshake && this._serializer.handshake(),\n ));\n }\n\n /**\n * Allow the specified client to reconnect into the room. Must be used inside `onLeave()` method.\n * If seconds is provided, the reconnection is going to be cancelled after the provided amount of seconds.\n *\n * @param previousClient - The client which is to be waiting until re-connection happens.\n * @param seconds - Timeout period on re-connection in seconds.\n *\n * @returns Deferred<Client> - The differed is a promise like type.\n * This type can forcibly reject the promise by calling `.reject()`.\n */\n public allowReconnection(previousClient: Client, seconds: number | \"manual\"): Deferred<Client> {\n if (seconds === undefined) { // TODO: remove this check\n console.warn(\"DEPRECATED: allowReconnection() requires a second argument. Using \\\"manual\\\" mode.\");\n seconds = \"manual\";\n }\n\n if (seconds === \"manual\") {\n seconds = Infinity;\n }\n\n if (this.internalState === RoomInternalState.DISCONNECTING) {\n this._disposeIfEmpty(); // gracefully shutting down\n throw new Error('disconnecting');\n }\n\n const sessionId = previousClient.sessionId;\n const reconnectionToken = previousClient._reconnectionToken;\n\n this._reserveSeat(sessionId, true, seconds, true);\n\n // keep reconnection reference in case the user reconnects into this room.\n const reconnection = new Deferred<Client>();\n this._reconnections[reconnectionToken] = [sessionId, reconnection];\n\n if (seconds !== Infinity) {\n // expire seat reservation after timeout\n this.reservedSeatTimeouts[sessionId] = setTimeout(() =>\n reconnection.reject(false), seconds * 1000);\n }\n\n const cleanup = () => {\n delete this._reconnections[reconnectionToken];\n delete this.reservedSeats[sessionId];\n delete this.reservedSeatTimeouts[sessionId];\n this._reconnectingSessionId.delete(sessionId);\n };\n\n reconnection.\n then((newClient) => {\n newClient.auth = previousClient.auth;\n newClient.userData = previousClient.userData;\n previousClient.ref = newClient.ref; // swap \"ref\" for convenience\n previousClient.state = ClientState.RECONNECTED;\n clearTimeout(this.reservedSeatTimeouts[sessionId]);\n cleanup();\n }).\n catch(() => {\n cleanup();\n this.resetAutoDisposeTimeout();\n });\n\n return reconnection;\n }\n\n protected resetAutoDisposeTimeout(timeoutInSeconds: number = 1) {\n clearTimeout(this._autoDisposeTimeout);\n\n if (!this.autoDispose) {\n return;\n }\n\n this._autoDisposeTimeout = setTimeout(() => {\n this._autoDisposeTimeout = undefined;\n this._disposeIfEmpty();\n }, timeoutInSeconds * 1000);\n }\n\n private broadcastMessageSchema<T extends Schema>(message: T, options: IBroadcastOptions = {}) {\n debugMessage(\"broadcast: %O\", message);\n const encodedMessage = getMessageBytes[Protocol.ROOM_DATA_SCHEMA](message);\n const except = (typeof (options.except) !== \"undefined\")\n ? Array.isArray(options.except)\n ? options.except\n : [options.except]\n : undefined;\n\n let numClients = this.clients.length;\n while (numClients--) {\n const client = this.clients.array[numClients];\n\n if (!except || !except.includes(client)) {\n client.enqueueRaw(encodedMessage);\n }\n }\n }\n\n private broadcastMessageType(type: string, message?: any, options: IBroadcastOptions = {}) {\n debugMessage(\"broadcast: %O\", message);\n const encodedMessage = getMessageBytes.raw(Protocol.ROOM_DATA, type, message);\n const except = (typeof (options.except) !== \"undefined\")\n ? Array.isArray(options.except)\n ? options.except\n : [options.except]\n : undefined;\n\n let numClients = this.clients.length;\n while (numClients--) {\n const client = this.clients.array[numClients];\n\n if (!except || !except.includes(client)) {\n client.enqueueRaw(encodedMessage);\n }\n }\n }\n\n private sendFullState(client: Client): void {\n client.enqueueRaw(getMessageBytes[Protocol.ROOM_STATE](this._serializer.getFullState(client)));\n }\n\n private _dequeueAfterPatchMessages() {\n const length = this._afterNextPatchQueue.length;\n\n if (length > 0) {\n for (let i = 0; i < length; i++) {\n const [target, args] = this._afterNextPatchQueue[i];\n\n if (target === \"broadcast\") {\n this.broadcast.apply(this, args);\n\n } else {\n (target as Client).raw.apply(target, args);\n }\n }\n\n // new messages may have been added in the meantime,\n // let's splice the ones that have been processed\n this._afterNextPatchQueue.splice(0, length);\n }\n }\n\n private async _reserveSeat(\n sessionId: string,\n joinOptions: any = true,\n seconds: number = this.seatReservationTime,\n allowReconnection: boolean = false,\n devModeReconnection?: boolean,\n ) {\n if (!allowReconnection && this.hasReachedMaxClients()) {\n return false;\n }\n\n this.reservedSeats[sessionId] = joinOptions;\n\n if (!allowReconnection) {\n await this._incrementClientCount();\n\n this.reservedSeatTimeouts[sessionId] = setTimeout(async () => {\n delete this.reservedSeats[sessionId];\n delete this.reservedSeatTimeouts[sessionId];\n await this._decrementClientCount();\n }, seconds * 1000);\n\n this.resetAutoDisposeTimeout(seconds);\n }\n\n //\n // isDevMode workaround to allow players to reconnect on devMode\n //\n if (devModeReconnection) {\n this._reconnectingSessionId.set(sessionId, sessionId);\n }\n\n return true;\n }\n\n private _disposeIfEmpty() {\n const willDispose = (\n this.autoDispose &&\n this._autoDisposeTimeout === undefined &&\n this.clients.length === 0 &&\n Object.keys(this.reservedSeats).length === 0\n );\n\n if (willDispose) {\n this._events.emit('dispose');\n }\n\n return willDispose;\n }\n\n private async _dispose(): Promise<any> {\n let userReturnData;\n\n if (this.onDispose) {\n userReturnData = this.onDispose();\n }\n\n if (this._patchInterval) {\n clearInterval(this._patchInterval);\n this._patchInterval = undefined;\n }\n\n if (this._simulationInterval) {\n clearInterval(this._simulationInterval);\n this._simulationInterval = undefined;\n }\n\n if (this._autoDisposeTimeout) {\n clearInterval(this._autoDisposeTimeout);\n this._autoDisposeTimeout = undefined;\n }\n\n // clear all timeouts/intervals + force to stop ticking\n this.clock.clear();\n this.clock.stop();\n\n return await (userReturnData || Promise.resolve());\n }\n\n private _onMessage(client: Client, bytes: number[]) {\n // skip if client is on LEAVING state.\n if (client.state === ClientState.LEAVING) { return; }\n\n const it: Iterator = { offset: 0 };\n const code = decode.uint8(bytes, it);\n\n if (!bytes) {\n debugAndPrintError(`${this.roomName} (${this.roomId}), couldn't decode message: ${bytes}`);\n return;\n }\n\n if (code === Protocol.ROOM_DATA) {\n const messageType = (decode.stringCheck(bytes, it))\n ? decode.string(bytes, it)\n : decode.number(bytes, it);\n\n let message;\n try {\n message = (bytes.length > it.offset)\n ? unpack(new Uint8Array(bytes.slice(it.offset, bytes.length)))\n : undefined;\n debugMessage(\"received: '%s' -> %j\", messageType, message);\n } catch (e) {\n debugAndPrintError(e);\n return;\n }\n\n if (this.onMessageHandlers[messageType]) {\n this.onMessageHandlers[messageType](client, message);\n\n } else if (this.onMessageHandlers['*']) {\n (this.onMessageHandlers['*'] as any)(client, messageType, message);\n\n } else {\n debugAndPrintError(`onMessage for \"${messageType}\" not registered.`);\n }\n\n } else if (code === Protocol.ROOM_DATA_BYTES) {\n const messageType = (decode.stringCheck(bytes, it))\n ? decode.string(bytes, it)\n : decode.number(bytes, it);\n\n const message = bytes.slice(it.offset, bytes.length);\n debugMessage(\"received: '%s' -> %j\", messageType, message);\n\n if (this.onMessageHandlers[messageType]) {\n this.onMessageHandlers[messageType](client, message);\n\n } else if (this.onMessageHandlers['*']) {\n (this.onMessageHandlers['*'] as any)(client, messageType, message);\n\n } else {\n debugAndPrintError(`onMessage for \"${messageType}\" not registered.`);\n }\n\n } else if (code === Protocol.JOIN_ROOM && client.state === ClientState.JOINING) {\n // join room has been acknowledged by the client\n client.state = ClientState.JOINED;\n\n // send current state when new client joins the room\n if (this.state) {\n this.sendFullState(client);\n }\n\n // dequeue messages sent before client has joined effectively (on user-defined `onJoin`)\n if (client._enqueuedMessages.length > 0) {\n client._enqueuedMessages.forEach((enqueued) => client.raw(enqueued));\n }\n delete client._enqueuedMessages;\n\n } else if (code === Protocol.LEAVE_ROOM) {\n this._forciblyCloseClient(client, Protocol.WS_CLOSE_CONSENTED);\n }\n\n }\n\n private _forciblyCloseClient(client: Client, closeCode: number) {\n // stop receiving messages from this client\n client.ref.removeAllListeners('message');\n\n // prevent \"onLeave\" from being called twice if player asks to leave\n client.ref.removeListener('close', client.ref['onleave']);\n\n // only effectively close connection when \"onLeave\" is fulfilled\n this._onLeave(client, closeCode).then(() => client.leave(closeCode));\n }\n\n private async _onLeave(client: Client, code?: number): Promise<any> {\n const success = this.clients.delete(client);\n\n // call 'onLeave' method only if the client has been successfully accepted.\n if (success && this.onLeave) {\n try {\n client.state = ClientState.LEAVING;\n await this.onLeave(client, (code === Protocol.WS_CLOSE_CONSENTED));\n\n } catch (e) {\n debugAndPrintError(`onLeave error: ${(e && e.message || e || 'promise rejected')}`);\n }\n }\n\n if (client.state !== ClientState.RECONNECTED) {\n // try to dispose immediately if client reconnection isn't set up.\n const willDispose = await this._decrementClientCount();\n\n this._events.emit('leave', client, willDispose);\n }\n }\n\n private async _incrementClientCount() {\n // lock automatically when maxClients is reached\n if (!this._locked && this.hasReachedMaxClients()) {\n this._maxClientsReached = true;\n this.lock.call(this, true);\n }\n\n await this.listing.updateOne({\n $inc: { clients: 1 },\n $set: { locked: this._locked },\n });\n }\n\n private async _decrementClientCount() {\n const willDispose = this._disposeIfEmpty();\n\n if (this.internalState === RoomInternalState.DISCONNECTING) {\n return;\n }\n\n // unlock if room is available for new connections\n if (!willDispose) {\n if (this._maxClientsReached && !this._lockedExplicitly) {\n this._maxClientsReached = false;\n this.unlock.call(this, true);\n }\n\n // update room listing cache\n await this.listing.updateOne({\n $inc: { clients: -1 },\n $set: { locked: this._locked },\n });\n }\n\n return willDispose;\n }\n\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import http from 'http';\n\nimport { unpack } from 'msgpackr';\nimport { decode, Iterator, Schema } from '@colyseus/schema';\n\nimport Clock from '@gamestdio/timer';\nimport { EventEmitter } from 'events';\nimport { logger } from './Logger';\n\nimport { Presence } from './presence/Presence';\n\nimport { NoneSerializer } from './serializer/NoneSerializer';\nimport { SchemaSerializer } from './serializer/SchemaSerializer';\nimport { Serializer } from './serializer/Serializer';\n\nimport { ErrorCode, getMessageBytes, Protocol } from './Protocol';\nimport { Deferred, generateId } from './utils/Utils';\nimport { isDevMode } from './utils/DevMode';\n\nimport { debugAndPrintError, debugMessage } from './Debug';\nimport { ServerError } from './errors/ServerError';\nimport { RoomListingData } from './matchmaker/driver';\nimport { Client, ClientArray, ClientState, ISendOptions } from './Transport';\n\nconst DEFAULT_PATCH_RATE = 1000 / 20; // 20fps (50ms)\nconst DEFAULT_SIMULATION_INTERVAL = 1000 / 60; // 60fps (16.66ms)\nconst noneSerializer = new NoneSerializer();\n\nexport const DEFAULT_SEAT_RESERVATION_TIME = Number(process.env.COLYSEUS_SEAT_RESERVATION_TIME || 15);\n\nexport type SimulationCallback = (deltaTime: number) => void;\n\nexport type RoomConstructor<T extends object= any> = new (presence?: Presence) => Room<T>;\n\nexport interface IBroadcastOptions extends ISendOptions {\n except?: Client | Client[];\n}\n\nexport enum RoomInternalState {\n CREATING = 0,\n CREATED = 1,\n DISCONNECTING = 2,\n}\n\n/**\n * A Room class is meant to implement a game session, and/or serve as the communication channel\n * between a group of clients.\n *\n * - Rooms are created on demand during matchmaking by default\n * - Room classes must be exposed using `.define()`\n */\nexport abstract class Room<State extends object= any, Metadata= any> {\n\n /**\n * This property will change on these situations:\n * - The maximum number of allowed clients has been reached (`maxClients`)\n * - You manually locked, or unlocked the room using lock() or `unlock()`.\n *\n * @readonly\n */\n public get locked() {\n return this._locked;\n }\n\n public get metadata() {\n return this.listing.metadata;\n }\n\n public listing: RoomListingData<Metadata>;\n\n /**\n * A ClockTimer instance, used for timing events.\n */\n public clock: Clock = new Clock();\n\n #_roomId: string;\n #_roomName: string;\n\n /**\n * Maximum number of clients allowed to connect into the room. When room reaches this limit,\n * it is locked automatically. Unless the room was explicitly locked by you via `lock()` method,\n * the room will be unlocked as soon as a client disconnects from it.\n */\n public maxClients: number = Infinity;\n /**\n * Frequency to send the room state to connected clients, in milliseconds.\n *\n * @default 50ms (20fps)\n */\n public patchRate: number = DEFAULT_PATCH_RATE;\n /**\n * Automatically dispose the room when last client disconnects.\n *\n * @default true\n */\n public autoDispose: boolean = true;\n\n /**\n * The state instance you provided to `setState()`.\n */\n public state: State;\n /**\n * The presence instance. Check Presence API for more details.\n *\n * @see {@link https://docs.colyseus.io/colyseus/server/presence/|Presence API}\n */\n public presence: Presence;\n\n /**\n * The array of connected clients.\n *\n * @see {@link https://docs.colyseus.io/colyseus/server/room/#client|Client instance}\n */\n public clients: ClientArray<any> = new ClientArray();\n\n public internalState: RoomInternalState = RoomInternalState.CREATING;\n\n /** @internal */\n public _events = new EventEmitter();\n\n // seat reservation & reconnection\n protected seatReservationTime: number = DEFAULT_SEAT_RESERVATION_TIME;\n protected reservedSeats: { [sessionId: string]: any } = {};\n protected reservedSeatTimeouts: { [sessionId: string]: NodeJS.Timer } = {};\n\n protected _reconnections: { [reconnectionToken: string]: [string, Deferred] } = {};\n private _reconnectingSessionId = new Map<string, string>();\n\n private onMessageHandlers: {[id: string]: (client: Client, message: any) => void} = {};\n\n private _serializer: Serializer<State> = noneSerializer;\n private _afterNextPatchQueue: Array<[string | Client, IArguments]> = [];\n\n private _simulationInterval: NodeJS.Timer;\n private _patchInterval: NodeJS.Timer;\n\n private _locked: boolean = false;\n private _lockedExplicitly: boolean = false;\n private _maxClientsReached: boolean = false;\n\n // this timeout prevents rooms that are created by one process, but no client\n // ever had success joining into it on the specified interval.\n private _autoDisposeTimeout: NodeJS.Timer;\n\n constructor(presence?: Presence) {\n this.presence = presence;\n\n this._events.once('dispose', async () => {\n try {\n await this._dispose();\n\n } catch (e) {\n debugAndPrintError(`onDispose error: ${(e && e.message || e || 'promise rejected')}`);\n }\n this._events.emit('disconnect');\n });\n\n this.setPatchRate(this.patchRate);\n // set default _autoDisposeTimeout\n this.resetAutoDisposeTimeout(this.seatReservationTime);\n }\n\n /**\n * The name of the room you provided as first argument for `gameServer.define()`.\n *\n * @returns roomName string\n */\n public get roomName() { return this.#_roomName; }\n /**\n * Setting the name of the room. Overwriting this property is restricted.\n *\n * @param roomName\n */\n public set roomName(roomName: string) {\n if (this.#_roomName) {\n // prevent user from setting roomName after it has been defined.\n throw new ServerError(ErrorCode.APPLICATION_ERROR, \"'roomName' cannot be overwritten.\");\n }\n this.#_roomName = roomName;\n }\n\n /**\n * A unique, auto-generated, 9-character-long id of the room.\n * You may replace `this.roomId` during `onCreate()`.\n *\n * @returns roomId string\n */\n public get roomId() { return this.#_roomId; }\n /**\n * Setting the roomId, is restricted in room lifetime except upon room creation.\n *\n * @param roomId\n * @returns roomId string\n */\n public set roomId(roomId: string) {\n if (this.internalState !== RoomInternalState.CREATING && !isDevMode) {\n // prevent user from setting roomId after room has been created.\n throw new ServerError(ErrorCode.APPLICATION_ERROR, \"'roomId' can only be overridden upon room creation.\");\n }\n this.#_roomId = roomId;\n }\n\n // Optional abstract methods\n public onBeforePatch?(state: State): void | Promise<any>;\n public onCreate?(options: any): void | Promise<any>;\n public onJoin?(client: Client, options?: any, auth?: any): void | Promise<any>;\n public onLeave?(client: Client, consented?: boolean): void | Promise<any>;\n public onDispose?(): void | Promise<any>;\n public onAuth(client: Client, options: any, request?: http.IncomingMessage): any | Promise<any> {\n return true;\n }\n\n /**\n * devMode: When `devMode` is enabled, `onCacheRoom` method is called during\n * graceful shutdown.\n *\n * Implement this method to return custom data to be cached. `onRestoreRoom`\n * will be called with the data returned by `onCacheRoom`\n */\n public onCacheRoom?(): any;\n\n /**\n * devMode: When `devMode` is enabled, `onRestoreRoom` method is called during\n * process startup, with the data returned by the `onCacheRoom` method.\n */\n public onRestoreRoom?(cached?: any): void;\n\n /**\n * Returns whether the sum of connected clients and reserved seats exceeds maximum number of clients.\n *\n * @returns boolean\n */\n public hasReachedMaxClients(): boolean {\n return (this.clients.length + Object.keys(this.reservedSeats).length) >= this.maxClients;\n }\n\n /**\n * Set the number of seconds a room can wait for a client to effectively join the room.\n * You should consider how long your `onAuth()` will have to wait for setting a different seat reservation time.\n * The default value is 15 seconds. You may set the `COLYSEUS_SEAT_RESERVATION_TIME`\n * environment variable if you'd like to change the seat reservation time globally.\n *\n * @default 15 seconds\n *\n * @param seconds - number of seconds.\n * @returns The modified Room object.\n */\n public setSeatReservationTime(seconds: number) {\n this.seatReservationTime = seconds;\n return this;\n }\n\n public hasReservedSeat(sessionId: string, reconnectionToken?: string): boolean {\n if (reconnectionToken) {\n const reconnection = this._reconnections[reconnectionToken];\n return (\n reconnection &&\n reconnection[0] === sessionId &&\n this.reservedSeats[sessionId] !== undefined &&\n this._reconnectingSessionId.has(sessionId)\n );\n\n } else {\n return (\n this.reservedSeats[sessionId] !== undefined &&\n (\n !this._reconnectingSessionId.has(sessionId) || // prevent possible \"reconnect\" requests without a reconnection token\n (this._reconnectingSessionId.get(sessionId) === sessionId) // devMode reconnection\n )\n );\n }\n }\n\n public checkReconnectionToken(reconnectionToken: string) {\n const reconnection = this._reconnections[reconnectionToken];\n const sessionId = (reconnection && reconnection[0]);\n\n if (this.hasReservedSeat(sessionId)) {\n this._reconnectingSessionId.set(sessionId, reconnectionToken);\n return sessionId;\n\n } else {\n return undefined;\n }\n }\n\n /**\n * (Optional) Set a simulation interval that can change the state of the game.\n * The simulation interval is your game loop.\n *\n * @default 16.6ms (60fps)\n *\n * @param onTickCallback - You can implement your physics or world updates here!\n * This is a good place to update the room state.\n * @param delay - Interval delay on executing `onTickCallback` in milliseconds.\n */\n public setSimulationInterval(onTickCallback?: SimulationCallback, delay: number = DEFAULT_SIMULATION_INTERVAL): void {\n // clear previous interval in case called setSimulationInterval more than once\n if (this._simulationInterval) { clearInterval(this._simulationInterval); }\n\n if (onTickCallback) {\n this._simulationInterval = setInterval(() => {\n this.clock.tick();\n onTickCallback(this.clock.deltaTime);\n }, delay);\n }\n }\n\n public setPatchRate(milliseconds: number): void {\n this.patchRate = milliseconds;\n\n // clear previous interval in case called setPatchRate more than once\n if (this._patchInterval) {\n clearInterval(this._patchInterval);\n this._patchInterval = undefined;\n }\n\n if (milliseconds !== null && milliseconds !== 0) {\n this._patchInterval = setInterval(() => this.broadcastPatch(), milliseconds);\n }\n }\n\n public setState(newState: State) {\n this.clock.start();\n\n if ('_definition' in newState) {\n this.setSerializer(new SchemaSerializer());\n }\n\n this._serializer.reset(newState);\n\n this.state = newState;\n }\n\n public setSerializer(serializer: Serializer<State>) {\n this._serializer = serializer;\n }\n\n public async setMetadata(meta: Partial<Metadata>) {\n if (!this.listing.metadata) {\n this.listing.metadata = meta as Metadata;\n\n } else {\n for (const field in meta) {\n if (!meta.hasOwnProperty(field)) { continue; }\n this.listing.metadata[field] = meta[field];\n }\n\n // `MongooseDriver` workaround: persit metadata mutations\n if ('markModified' in this.listing) {\n (this.listing as any).markModified('metadata');\n }\n }\n\n if (this.internalState === RoomInternalState.CREATED) {\n await this.listing.save();\n }\n }\n\n public async setPrivate(bool: boolean = true) {\n this.listing.private = bool;\n\n if (this.internalState === RoomInternalState.CREATED) {\n await this.listing.save();\n }\n }\n\n /**\n * Locking the room will remove it from the pool of available rooms for new clients to connect to.\n */\n public async lock() {\n // rooms locked internally aren't explicit locks.\n this._lockedExplicitly = (arguments[0] === undefined);\n\n // skip if already locked.\n if (this._locked) { return; }\n\n this._locked = true;\n\n await this.listing.updateOne({\n $set: { locked: this._locked },\n });\n\n this._events.emit('lock');\n }\n\n /**\n * Unlocking the room returns it to the pool of available rooms for new clients to connect to.\n */\n public async unlock() {\n // only internal usage passes arguments to this function.\n if (arguments[0] === undefined) {\n this._lockedExplicitly = false;\n }\n\n // skip if already locked\n if (!this._locked) { return; }\n\n this._locked = false;\n\n await this.listing.updateOne({\n $set: { locked: this._locked },\n });\n\n this._events.emit('unlock');\n }\n\n public send(client: Client, type: string | number, message: any, options?: ISendOptions): void;\n public send(client: Client, message: Schema, options?: ISendOptions): void;\n public send(client: Client, messageOrType: any, messageOrOptions?: any | ISendOptions, options?: ISendOptions): void {\n logger.warn('DEPRECATION WARNING: use client.send(...) instead of this.send(client, ...)');\n client.send(messageOrType, messageOrOptions, options);\n }\n\n public broadcast(type: string | number, message?: any, options?: IBroadcastOptions);\n public broadcast<T extends Schema>(message: T, options?: IBroadcastOptions);\n public broadcast(\n typeOrSchema: string | number | Schema,\n messageOrOptions?: any | IBroadcastOptions,\n options?: IBroadcastOptions,\n ) {\n const isSchema = (typeof(typeOrSchema) === 'object');\n const opts: IBroadcastOptions = ((isSchema) ? messageOrOptions : options);\n\n if (opts && opts.afterNextPatch) {\n delete opts.afterNextPatch;\n this._afterNextPatchQueue.push(['broadcast', arguments]);\n return;\n }\n\n if (isSchema) {\n this.broadcastMessageSchema(typeOrSchema as Schema, opts);\n\n } else {\n\n this.broadcastMessageType(typeOrSchema as string, messageOrOptions, opts);\n }\n }\n\n /**\n * Checks whether mutations have occurred in the state, and broadcast them to all connected clients.\n */\n public broadcastPatch() {\n if (this.onBeforePatch) {\n this.onBeforePatch(this.state);\n }\n\n if (!this._simulationInterval) {\n this.clock.tick();\n }\n\n if (!this.state) {\n return false;\n }\n\n const hasChanges = this._serializer.applyPatches(this.clients, this.state);\n\n // broadcast messages enqueued for \"after patch\"\n this._dequeueAfterPatchMessages();\n\n return hasChanges;\n }\n\n public onMessage<T = any>(messageType: '*', callback: (client: Client, type: string | number, message: T) => void);\n public onMessage<T = any>(messageType: string | number, callback: (client: Client, message: T) => void);\n public onMessage<T = any>(messageType: '*' | string | number, callback: (...args: any[]) => void) {\n this.onMessageHandlers[messageType] = callback;\n // returns a method to unbind the callback\n return () => delete this.onMessageHandlers[messageType];\n }\n\n /**\n * Disconnect all connected clients, and then dispose the room.\n *\n * @param closeCode WebSocket close code (default = 4000, which is a \"consented leave\")\n * @returns Promise<void>\n */\n public async disconnect(closeCode: number = Protocol.WS_CLOSE_CONSENTED): Promise<any> {\n this.internalState = RoomInternalState.DISCONNECTING;\n await this.listing.remove();\n\n this.autoDispose = true;\n\n const delayedDisconnection = new Promise<void>((resolve) =>\n this._events.once('disconnect', () => resolve()));\n\n for (const [_, reconnection] of Object.values(this._reconnections)) {\n reconnection.reject();\n }\n\n let numClients = this.clients.length;\n if (numClients > 0) {\n // clients may have `async onLeave`, room will be disposed after they're fulfilled\n while (numClients--) {\n this._forciblyCloseClient(this.clients[numClients], closeCode);\n }\n } else {\n // no clients connected, dispose immediately.\n this._events.emit('dispose');\n }\n\n return await delayedDisconnection;\n }\n\n public async ['_onJoin'](client: Client, req?: http.IncomingMessage) {\n const sessionId = client.sessionId;\n\n // generate unique private reconnection token\n client._reconnectionToken = generateId();\n\n if (this.reservedSeatTimeouts[sessionId]) {\n clearTimeout(this.reservedSeatTimeouts[sessionId]);\n delete this.reservedSeatTimeouts[sessionId];\n }\n\n // clear auto-dispose timeout.\n if (this._autoDisposeTimeout) {\n clearTimeout(this._autoDisposeTimeout);\n this._autoDisposeTimeout = undefined;\n }\n\n // get seat reservation options and clear it\n const options = this.reservedSeats[sessionId];\n delete this.reservedSeats[sessionId];\n\n // share \"after next patch queue\" reference with every client.\n client._afterNextPatchQueue = this._afterNextPatchQueue;\n\n // bind clean-up callback when client connection closes\n client.ref['onleave'] = this._onLeave.bind(this, client);\n client.ref.once('close', client.ref['onleave']);\n\n this.clients.push(client);\n\n const previousReconnectionToken = this._reconnectingSessionId.get(sessionId);\n if (previousReconnectionToken) {\n this._reconnections[previousReconnectionToken]?.[1].resolve(client);\n\n } else {\n try {\n client.auth = await this.onAuth(client, options, req);\n\n if (!client.auth) {\n throw new ServerError(ErrorCode.AUTH_FAILED, 'onAuth failed');\n }\n\n if (this.onJoin) {\n await this.onJoin(client, options, client.auth);\n }\n } catch (e) {\n this.clients.delete(client);\n\n // make sure an error code is provided.\n if (!e.code) {\n e.code = ErrorCode.APPLICATION_ERROR;\n }\n\n throw e;\n\n } finally {\n // remove seat reservation\n delete this.reservedSeats[sessionId];\n }\n }\n\n // emit 'join' to room handler\n this._events.emit('join', client);\n\n // allow client to send messages after onJoin has succeeded.\n client.ref.on('message', this._onMessage.bind(this, client));\n\n // confirm room id that matches the room name requested to join\n client.raw(getMessageBytes[Protocol.JOIN_ROOM](\n client._reconnectionToken,\n this._serializer.id,\n this._serializer.handshake && this._serializer.handshake(),\n ));\n }\n\n /**\n * Allow the specified client to reconnect into the room. Must be used inside `onLeave()` method.\n * If seconds is provided, the reconnection is going to be cancelled after the provided amount of seconds.\n *\n * @param previousClient - The client which is to be waiting until re-connection happens.\n * @param seconds - Timeout period on re-connection in seconds.\n *\n * @returns Deferred<Client> - The differed is a promise like type.\n * This type can forcibly reject the promise by calling `.reject()`.\n */\n public allowReconnection(previousClient: Client, seconds: number | \"manual\"): Deferred<Client> {\n if (seconds === undefined) { // TODO: remove this check\n console.warn(\"DEPRECATED: allowReconnection() requires a second argument. Using \\\"manual\\\" mode.\");\n seconds = \"manual\";\n }\n\n if (seconds === \"manual\") {\n seconds = Infinity;\n }\n\n if (this.internalState === RoomInternalState.DISCONNECTING) {\n this._disposeIfEmpty(); // gracefully shutting down\n throw new Error('disconnecting');\n }\n\n const sessionId = previousClient.sessionId;\n const reconnectionToken = previousClient._reconnectionToken;\n\n this._reserveSeat(sessionId, true, seconds, true);\n\n // keep reconnection reference in case the user reconnects into this room.\n const reconnection = new Deferred<Client>();\n this._reconnections[reconnectionToken] = [sessionId, reconnection];\n\n if (seconds !== Infinity) {\n // expire seat reservation after timeout\n this.reservedSeatTimeouts[sessionId] = setTimeout(() =>\n reconnection.reject(false), seconds * 1000);\n }\n\n const cleanup = () => {\n delete this._reconnections[reconnectionToken];\n delete this.reservedSeats[sessionId];\n delete this.reservedSeatTimeouts[sessionId];\n this._reconnectingSessionId.delete(sessionId);\n };\n\n reconnection.\n then((newClient) => {\n newClient.auth = previousClient.auth;\n newClient.userData = previousClient.userData;\n previousClient.ref = newClient.ref; // swap \"ref\" for convenience\n previousClient.state = ClientState.RECONNECTED;\n clearTimeout(this.reservedSeatTimeouts[sessionId]);\n cleanup();\n }).\n catch(() => {\n cleanup();\n this.resetAutoDisposeTimeout();\n });\n\n return reconnection;\n }\n\n protected resetAutoDisposeTimeout(timeoutInSeconds: number = 1) {\n clearTimeout(this._autoDisposeTimeout);\n\n if (!this.autoDispose) {\n return;\n }\n\n this._autoDisposeTimeout = setTimeout(() => {\n this._autoDisposeTimeout = undefined;\n this._disposeIfEmpty();\n }, timeoutInSeconds * 1000);\n }\n\n private broadcastMessageSchema<T extends Schema>(message: T, options: IBroadcastOptions = {}) {\n debugMessage(\"broadcast: %O\", message);\n const encodedMessage = getMessageBytes[Protocol.ROOM_DATA_SCHEMA](message);\n const except = (typeof (options.except) !== \"undefined\")\n ? Array.isArray(options.except)\n ? options.except\n : [options.except]\n : undefined;\n\n let numClients = this.clients.length;\n while (numClients--) {\n const client = this.clients[numClients];\n\n if (!except || !except.includes(client)) {\n client.enqueueRaw(encodedMessage);\n }\n }\n }\n\n private broadcastMessageType(type: string, message?: any, options: IBroadcastOptions = {}) {\n debugMessage(\"broadcast: %O\", message);\n const encodedMessage = getMessageBytes.raw(Protocol.ROOM_DATA, type, message);\n const except = (typeof (options.except) !== \"undefined\")\n ? Array.isArray(options.except)\n ? options.except\n : [options.except]\n : undefined;\n\n let numClients = this.clients.length;\n while (numClients--) {\n const client = this.clients[numClients];\n\n if (!except || !except.includes(client)) {\n client.enqueueRaw(encodedMessage);\n }\n }\n }\n\n private sendFullState(client: Client): void {\n client.enqueueRaw(getMessageBytes[Protocol.ROOM_STATE](this._serializer.getFullState(client)));\n }\n\n private _dequeueAfterPatchMessages() {\n const length = this._afterNextPatchQueue.length;\n\n if (length > 0) {\n for (let i = 0; i < length; i++) {\n const [target, args] = this._afterNextPatchQueue[i];\n\n if (target === \"broadcast\") {\n this.broadcast.apply(this, args);\n\n } else {\n (target as Client).raw.apply(target, args);\n }\n }\n\n // new messages may have been added in the meantime,\n // let's splice the ones that have been processed\n this._afterNextPatchQueue.splice(0, length);\n }\n }\n\n private async _reserveSeat(\n sessionId: string,\n joinOptions: any = true,\n seconds: number = this.seatReservationTime,\n allowReconnection: boolean = false,\n devModeReconnection?: boolean,\n ) {\n if (!allowReconnection && this.hasReachedMaxClients()) {\n return false;\n }\n\n this.reservedSeats[sessionId] = joinOptions;\n\n if (!allowReconnection) {\n await this._incrementClientCount();\n\n this.reservedSeatTimeouts[sessionId] = setTimeout(async () => {\n delete this.reservedSeats[sessionId];\n delete this.reservedSeatTimeouts[sessionId];\n await this._decrementClientCount();\n }, seconds * 1000);\n\n this.resetAutoDisposeTimeout(seconds);\n }\n\n //\n // isDevMode workaround to allow players to reconnect on devMode\n //\n if (devModeReconnection) {\n this._reconnectingSessionId.set(sessionId, sessionId);\n }\n\n return true;\n }\n\n private _disposeIfEmpty() {\n const willDispose = (\n this.autoDispose &&\n this._autoDisposeTimeout === undefined &&\n this.clients.length === 0 &&\n Object.keys(this.reservedSeats).length === 0\n );\n\n if (willDispose) {\n this._events.emit('dispose');\n }\n\n return willDispose;\n }\n\n private async _dispose(): Promise<any> {\n let userReturnData;\n\n if (this.onDispose) {\n userReturnData = this.onDispose();\n }\n\n if (this._patchInterval) {\n clearInterval(this._patchInterval);\n this._patchInterval = undefined;\n }\n\n if (this._simulationInterval) {\n clearInterval(this._simulationInterval);\n this._simulationInterval = undefined;\n }\n\n if (this._autoDisposeTimeout) {\n clearInterval(this._autoDisposeTimeout);\n this._autoDisposeTimeout = undefined;\n }\n\n // clear all timeouts/intervals + force to stop ticking\n this.clock.clear();\n this.clock.stop();\n\n return await (userReturnData || Promise.resolve());\n }\n\n private _onMessage(client: Client, bytes: number[]) {\n // skip if client is on LEAVING state.\n if (client.state === ClientState.LEAVING) { return; }\n\n const it: Iterator = { offset: 0 };\n const code = decode.uint8(bytes, it);\n\n if (!bytes) {\n debugAndPrintError(`${this.roomName} (${this.roomId}), couldn't decode message: ${bytes}`);\n return;\n }\n\n if (code === Protocol.ROOM_DATA) {\n const messageType = (decode.stringCheck(bytes, it))\n ? decode.string(bytes, it)\n : decode.number(bytes, it);\n\n let message;\n try {\n message = (bytes.length > it.offset)\n ? unpack(new Uint8Array(bytes.slice(it.offset, bytes.length)))\n : undefined;\n debugMessage(\"received: '%s' -> %j\", messageType, message);\n } catch (e) {\n debugAndPrintError(e);\n return;\n }\n\n if (this.onMessageHandlers[messageType]) {\n this.onMessageHandlers[messageType](client, message);\n\n } else if (this.onMessageHandlers['*']) {\n (this.onMessageHandlers['*'] as any)(client, messageType, message);\n\n } else {\n debugAndPrintError(`onMessage for \"${messageType}\" not registered.`);\n }\n\n } else if (code === Protocol.ROOM_DATA_BYTES) {\n const messageType = (decode.stringCheck(bytes, it))\n ? decode.string(bytes, it)\n : decode.number(bytes, it);\n\n const message = bytes.slice(it.offset, bytes.length);\n debugMessage(\"received: '%s' -> %j\", messageType, message);\n\n if (this.onMessageHandlers[messageType]) {\n this.onMessageHandlers[messageType](client, message);\n\n } else if (this.onMessageHandlers['*']) {\n (this.onMessageHandlers['*'] as any)(client, messageType, message);\n\n } else {\n debugAndPrintError(`onMessage for \"${messageType}\" not registered.`);\n }\n\n } else if (code === Protocol.JOIN_ROOM && client.state === ClientState.JOINING) {\n // join room has been acknowledged by the client\n client.state = ClientState.JOINED;\n\n // send current state when new client joins the room\n if (this.state) {\n this.sendFullState(client);\n }\n\n // dequeue messages sent before client has joined effectively (on user-defined `onJoin`)\n if (client._enqueuedMessages.length > 0) {\n client._enqueuedMessages.forEach((enqueued) => client.raw(enqueued));\n }\n delete client._enqueuedMessages;\n\n } else if (code === Protocol.LEAVE_ROOM) {\n this._forciblyCloseClient(client, Protocol.WS_CLOSE_CONSENTED);\n }\n\n }\n\n private _forciblyCloseClient(client: Client, closeCode: number) {\n // stop receiving messages from this client\n client.ref.removeAllListeners('message');\n\n // prevent \"onLeave\" from being called twice if player asks to leave\n client.ref.removeListener('close', client.ref['onleave']);\n\n // only effectively close connection when \"onLeave\" is fulfilled\n this._onLeave(client, closeCode).then(() => client.leave(closeCode));\n }\n\n private async _onLeave(client: Client, code?: number): Promise<any> {\n const success = this.clients.delete(client);\n\n // call 'onLeave' method only if the client has been successfully accepted.\n if (success && this.onLeave) {\n try {\n client.state = ClientState.LEAVING;\n await this.onLeave(client, (code === Protocol.WS_CLOSE_CONSENTED));\n\n } catch (e) {\n debugAndPrintError(`onLeave error: ${(e && e.message || e || 'promise rejected')}`);\n }\n }\n\n if (client.state !== ClientState.RECONNECTED) {\n // try to dispose immediately if client reconnection isn't set up.\n const willDispose = await this._decrementClientCount();\n\n this._events.emit('leave', client, willDispose);\n }\n }\n\n private async _incrementClientCount() {\n // lock automatically when maxClients is reached\n if (!this._locked && this.hasReachedMaxClients()) {\n this._maxClientsReached = true;\n this.lock.call(this, true);\n }\n\n await this.listing.updateOne({\n $inc: { clients: 1 },\n $set: { locked: this._locked },\n });\n }\n\n private async _decrementClientCount() {\n const willDispose = this._disposeIfEmpty();\n\n if (this.internalState === RoomInternalState.DISCONNECTING) {\n return;\n }\n\n // unlock if room is available for new connections\n if (!willDispose) {\n if (this._maxClientsReached && !this._lockedExplicitly) {\n this._maxClientsReached = false;\n this.unlock.call(this, true);\n }\n\n // update room listing cache\n await this.listing.updateOne({\n $inc: { clients: -1 },\n $set: { locked: this._locked },\n });\n }\n\n return willDispose;\n }\n\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["RoomInternalState"]
7
7
  }
@@ -31,7 +31,7 @@ export declare enum ClientState {
31
31
  * - This is the raw WebSocket connection coming from the `ws` package. There are more methods available which aren't
32
32
  * encouraged to use along with Colyseus.
33
33
  */
34
- export interface Client {
34
+ export interface Client<UserData = any> {
35
35
  readyState: number;
36
36
  id: string;
37
37
  /**
@@ -46,7 +46,7 @@ export interface Client {
46
46
  * - Can be used to store custom data about the client's connection. userData is not synchronized with the client,
47
47
  * and should be used only to keep player-specific with its connection.
48
48
  */
49
- userData?: any;
49
+ userData?: UserData;
50
50
  /**
51
51
  * auth data provided by your `onAuth`
52
52
  */
@@ -95,3 +95,7 @@ export interface Client {
95
95
  */
96
96
  error(code: number, message?: string): void;
97
97
  }
98
+ export declare class ClientArray<UserData> extends Array<Client<UserData>> {
99
+ getById(sessionId: string): Client<UserData> | undefined;
100
+ delete(client: Client<UserData>): boolean;
101
+ }