@colyseus/core
Version:
Multiplayer Framework for Node.js.
266 lines (265 loc) • 11.2 kB
TypeScript
import Clock from '@colyseus/timer';
import { EventEmitter } from 'events';
import { Presence } from './presence/Presence.js';
import { Serializer } from './serializer/Serializer.js';
import { Deferred } from './utils/Utils.js';
import { RoomCache } from './matchmaker/driver/api.js';
import { AuthContext, Client, ClientPrivate, ClientArray, ISendOptions } from './Transport.js';
import { RoomException } from './errors/RoomExceptions.js';
export declare const DEFAULT_SEAT_RESERVATION_TIME: number;
export type SimulationCallback = (deltaTime: number) => void;
export interface IBroadcastOptions extends ISendOptions {
except?: Client | Client[];
}
export declare enum RoomInternalState {
CREATING = 0,
CREATED = 1,
DISPOSING = 2
}
export type ExtractUserData<T> = T extends ClientArray<infer U> ? U : never;
export type ExtractAuthData<T> = T extends ClientArray<infer _, infer U> ? U : never;
/**
* A Room class is meant to implement a game session, and/or serve as the communication channel
* between a group of clients.
*
* - Rooms are created on demand during matchmaking by default
* - Room classes must be exposed using `.define()`
*/
export declare abstract class Room<State extends object = any, Metadata = any, UserData = any, AuthData = any> {
#private;
/**
* This property will change on these situations:
* - The maximum number of allowed clients has been reached (`maxClients`)
* - You manually locked, or unlocked the room using lock() or `unlock()`.
*
* @readonly
*/
get locked(): boolean;
get metadata(): Metadata;
listing: RoomCache<Metadata>;
/**
* Timing events tied to the room instance.
* Intervals and timeouts are cleared when the room is disposed.
*/
clock: Clock;
/**
* Maximum number of clients allowed to connect into the room. When room reaches this limit,
* it is locked automatically. Unless the room was explicitly locked by you via `lock()` method,
* the room will be unlocked as soon as a client disconnects from it.
*/
maxClients: number;
/**
* Automatically dispose the room when last client disconnects.
*
* @default true
*/
autoDispose: boolean;
/**
* Frequency to send the room state to connected clients, in milliseconds.
*
* @default 50ms (20fps)
*/
patchRate: number;
/**
* The state instance you provided to `setState()`.
*/
state: State;
/**
* The presence instance. Check Presence API for more details.
*
* @see {@link https://docs.colyseus.io/colyseus/server/presence/|Presence API}
*/
presence: Presence;
/**
* The array of connected clients.
*
* @see {@link https://docs.colyseus.io/colyseus/server/room/#client|Client instance}
*/
clients: ClientArray<UserData, AuthData>;
/** @internal */
_events: EventEmitter<[never]>;
protected seatReservationTime: number;
protected reservedSeats: {
[sessionId: string]: [any, any, boolean?, boolean?];
};
protected reservedSeatTimeouts: {
[sessionId: string]: NodeJS.Timeout;
};
protected _reconnections: {
[reconnectionToken: string]: [string, Deferred];
};
private _reconnectingSessionId;
private onMessageHandlers;
private _serializer;
private _afterNextPatchQueue;
private _simulationInterval;
private _internalState;
private _lockedExplicitly;
private _autoDisposeTimeout;
constructor();
/**
* This method is called by the MatchMaker before onCreate()
* @internal
*/
protected __init(): void;
/**
* The name of the room you provided as first argument for `gameServer.define()`.
*
* @returns roomName string
*/
get roomName(): string;
/**
* Setting the name of the room. Overwriting this property is restricted.
*
* @param roomName
*/
set roomName(roomName: string);
/**
* A unique, auto-generated, 9-character-long id of the room.
* You may replace `this.roomId` during `onCreate()`.
*
* @returns roomId string
*/
get roomId(): string;
/**
* Setting the roomId, is restricted in room lifetime except upon room creation.
*
* @param roomId
* @returns roomId string
*/
set roomId(roomId: string);
onBeforePatch?(state: State): void | Promise<any>;
onCreate?(options: any): void | Promise<any>;
onJoin?(client: Client<UserData, AuthData>, options?: any, auth?: AuthData): void | Promise<any>;
onLeave?(client: Client<UserData, AuthData>, consented?: boolean): void | Promise<any>;
onDispose?(): void | Promise<any>;
/**
* Define a custom exception handler.
* If defined, all lifecycle hooks will be wrapped by try/catch, and the exception will be forwarded to this method.
*
* These methods will be wrapped by try/catch:
* - `onMessage`
* - `onAuth` / `onJoin` / `onLeave` / `onCreate` / `onDispose`
* - `clock.setTimeout` / `clock.setInterval`
* - `setSimulationInterval`
*
* (Experimental: this feature is subject to change in the future - we're currently getting feedback to improve it)
*/
onUncaughtException?(error: RoomException<this>, methodName: 'onCreate' | 'onAuth' | 'onJoin' | 'onLeave' | 'onDispose' | 'onMessage' | 'setSimulationInterval' | 'setInterval' | 'setTimeout'): void;
onAuth(client: Client<UserData, AuthData>, options: any, context: AuthContext): any | Promise<any>;
static onAuth(token: string, options: any, context: AuthContext): Promise<unknown>;
/**
* This method is called during graceful shutdown of the server process
* You may override this method to dispose the room in your own way.
*
* Once process reaches room count of 0, the room process will be terminated.
*/
onBeforeShutdown(): void;
/**
* devMode: When `devMode` is enabled, `onCacheRoom` method is called during
* graceful shutdown.
*
* Implement this method to return custom data to be cached. `onRestoreRoom`
* will be called with the data returned by `onCacheRoom`
*/
onCacheRoom?(): any;
/**
* devMode: When `devMode` is enabled, `onRestoreRoom` method is called during
* process startup, with the data returned by the `onCacheRoom` method.
*/
onRestoreRoom?(cached?: any): void;
/**
* Returns whether the sum of connected clients and reserved seats exceeds maximum number of clients.
*
* @returns boolean
*/
hasReachedMaxClients(): boolean;
/**
* Set the number of seconds a room can wait for a client to effectively join the room.
* You should consider how long your `onAuth()` will have to wait for setting a different seat reservation time.
* The default value is 15 seconds. You may set the `COLYSEUS_SEAT_RESERVATION_TIME`
* environment variable if you'd like to change the seat reservation time globally.
*
* @default 15 seconds
*
* @param seconds - number of seconds.
* @returns The modified Room object.
*/
setSeatReservationTime(seconds: number): this;
hasReservedSeat(sessionId: string, reconnectionToken?: string): boolean;
checkReconnectionToken(reconnectionToken: string): string;
/**
* (Optional) Set a simulation interval that can change the state of the game.
* The simulation interval is your game loop.
*
* @default 16.6ms (60fps)
*
* @param onTickCallback - You can implement your physics or world updates here!
* This is a good place to update the room state.
* @param delay - Interval delay on executing `onTickCallback` in milliseconds.
*/
setSimulationInterval(onTickCallback?: SimulationCallback, delay?: number): void;
/**
* @deprecated Use `.patchRate=` instead.
*/
setPatchRate(milliseconds: number | null): void;
/**
* @deprecated Use `.state =` instead.
*/
setState(newState: State): void;
setSerializer(serializer: Serializer<State>): void;
setMetadata(meta: Partial<Metadata>): Promise<void>;
setPrivate(bool?: boolean): Promise<void>;
/**
* Locking the room will remove it from the pool of available rooms for new clients to connect to.
*/
lock(): Promise<void>;
/**
* Unlocking the room returns it to the pool of available rooms for new clients to connect to.
*/
unlock(): Promise<void>;
send(client: Client, type: string | number, message: any, options?: ISendOptions): void;
broadcast(type: string | number, message?: any, options?: IBroadcastOptions): void;
/**
* Broadcast bytes (UInt8Arrays) to a particular room
*/
broadcastBytes(type: string | number, message: Uint8Array, options: IBroadcastOptions): void;
/**
* Checks whether mutations have occurred in the state, and broadcast them to all connected clients.
*/
broadcastPatch(): boolean;
onMessage<T = any>(messageType: '*', callback: (client: Client<UserData, AuthData>, type: string | number, message: T) => void): any;
onMessage<T = any>(messageType: string | number, callback: (client: Client<UserData, AuthData>, message: T) => void, validate?: (message: unknown) => T): any;
/**
* Disconnect all connected clients, and then dispose the room.
*
* @param closeCode WebSocket close code (default = 4000, which is a "consented leave")
* @returns Promise<void>
*/
disconnect(closeCode?: number): Promise<any>;
['_onJoin'](client: Client & ClientPrivate, authContext: AuthContext): Promise<void>;
/**
* Allow the specified client to reconnect into the room. Must be used inside `onLeave()` method.
* If seconds is provided, the reconnection is going to be cancelled after the provided amount of seconds.
*
* @param previousClient - The client which is to be waiting until re-connection happens.
* @param seconds - Timeout period on re-connection in seconds.
*
* @returns Deferred<Client> - The differed is a promise like type.
* This type can forcibly reject the promise by calling `.reject()`.
*/
allowReconnection(previousClient: Client, seconds: number | "manual"): Deferred<Client>;
protected resetAutoDisposeTimeout(timeoutInSeconds?: number): void;
private broadcastMessageType;
protected sendFullState(client: Client): void;
protected _dequeueAfterPatchMessages(): void;
protected _reserveSeat(sessionId: string, joinOptions?: any, authData?: any, seconds?: number, allowReconnection?: boolean, devModeReconnection?: boolean): Promise<boolean>;
protected _disposeIfEmpty(): boolean;
protected _dispose(): Promise<any>;
protected _onMessage(client: Client & ClientPrivate, buffer: Buffer): void;
protected _forciblyCloseClient(client: Client & ClientPrivate, closeCode: number): void;
protected _onLeave(client: Client, code?: number): Promise<any>;
protected _onAfterLeave(client: Client): Promise<void>;
protected _incrementClientCount(): Promise<void>;
protected _decrementClientCount(): Promise<boolean>;
}