UNPKG

@colyseus/core

Version:

Multiplayer Framework for Node.js.

266 lines (265 loc) 11.2 kB
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>; }