{
  "version": 3,
  "sources": ["../src/Server.ts"],
  "sourcesContent": ["import { greet } from \"@colyseus/greeting-banner\";\nimport type express from 'express';\n\nimport { debugAndPrintError } from './Debug.ts';\nimport * as matchMaker from './MatchMaker.ts';\nimport { RegisteredHandler } from './matchmaker/RegisteredHandler.ts';\n\nimport { type OnCreateOptions, Room } from './Room.ts';\nimport { Deferred, registerGracefulShutdown, dynamicImport, type Type } from './utils/Utils.ts';\n\nimport type { Presence } from \"./presence/Presence.ts\";\nimport { LocalPresence } from './presence/LocalPresence.ts';\nimport { LocalDriver } from './matchmaker/LocalDriver/LocalDriver.ts';\n\nimport { setTransport, Transport } from './Transport.ts';\nimport { logger, setLogger } from './Logger.ts';\nimport { setDevMode, isDevMode } from './utils/DevMode.ts';\nimport { type Router, bindRouterToTransport } from './router/index.ts';\nimport { type SDKTypes as SharedSDKTypes } from '@colyseus/shared-types';\nimport { getDefaultRouter } from './router/default_routes.ts';\n\nexport type ServerOptions = {\n  publicAddress?: string,\n  presence?: Presence,\n  driver?: matchMaker.MatchMakerDriver,\n  transport?: Transport,\n  gracefullyShutdown?: boolean,\n  logger?: any;\n\n  /**\n   * Optional callback to execute before the server listens.\n   * This is useful for example to connect into a database or other services before the server listens.\n   */\n  beforeListen?: () => Promise<void> | void,\n\n  /**\n   * Optional callback to configure Express routes.\n   * When provided, the transport layer will initialize an Express-compatible app\n   * and pass it to this callback for custom route configuration.\n   *\n   * For uWebSockets transport, this uses the uwebsockets-express module.\n   */\n  express?: (app: express.Application) => Promise<void> | void,\n\n  /**\n   * Custom function to determine which process should handle room creation.\n   * Default: assign new rooms the process with least amount of rooms created\n   */\n  selectProcessIdToCreateRoom?: matchMaker.SelectProcessIdCallback;\n\n  /**\n   * Whether this process is running as a standalone match-maker or not. (default: false)\n   * When enabled, this process will not spawn rooms and will only be responsible for matchmaking.\n   */\n  isStandaloneMatchMaker?: boolean; \n\n  /**\n   * If enabled, rooms are going to be restored in the server-side upon restart,\n   * clients are going to automatically re-connect when server reboots.\n   *\n   * Beware of \"schema mismatch\" issues. When updating Schema structures and\n   * reloading existing data, you may see \"schema mismatch\" errors in the\n   * client-side.\n   *\n   * (This operation is costly and should not be used in a production\n   * environment)\n   */\n  devMode?: boolean,\n\n  /**\n   * Display greeting message on server start.\n   * Default: true\n   */\n  greet?: boolean,\n};\n\n/**\n * Exposed types for the client-side SDK.\n * Re-exported from @colyseus/shared-types with specific type constraints.\n */\nexport interface SDKTypes<\n  RoomTypes extends Record<string, RegisteredHandler> = any,\n  Routes extends Router = any\n> extends SharedSDKTypes<RoomTypes, Routes> {}\n\nexport class Server<\n  RoomTypes extends Record<string, RegisteredHandler> = any,\n  Routes extends Router = any\n> implements SDKTypes<RoomTypes, Routes> {\n  '~rooms': RoomTypes;\n  '~routes': Routes;\n\n  public transport: Transport;\n  public router: Routes;\n  public options: ServerOptions;\n\n  protected presence: Presence;\n  protected driver: matchMaker.MatchMakerDriver;\n\n  protected port: number | string;\n  protected greet: boolean;\n\n  protected _onTransportReady = new Deferred<Transport>();\n\n  private _originalRoomOnMessage: typeof Room.prototype['_onMessage'] | null = null;\n\n  constructor(options: ServerOptions = {}) {\n    const {\n      gracefullyShutdown = true,\n      greet = true\n    } = options;\n\n    setDevMode(options.devMode === true);\n\n    this.presence = options.presence || new LocalPresence();\n    this.driver = options.driver || new LocalDriver();\n    this.options = options;\n    this.greet = greet;\n\n    this.attach(options);\n\n    matchMaker.setup(\n      this.presence,\n      this.driver,\n      options.publicAddress,\n      options.selectProcessIdToCreateRoom,\n    );\n\n    if (gracefullyShutdown) {\n      registerGracefulShutdown((err) => this.gracefullyShutdown(true, err));\n    }\n\n    if (options.logger) {\n      setLogger(options.logger);\n    }\n  }\n\n  public async attach(options: ServerOptions) {\n    this.transport = options.transport || await this.getDefaultTransport(options);\n\n    // Initialize Express if callback is provided\n    if (options.express && this.transport.getExpressApp) {\n      const expressApp = await this.transport.getExpressApp();\n      await options.express(expressApp);\n    }\n\n    // Resolve the promise when the transport is ready\n    this._onTransportReady.resolve(this.transport);\n  }\n\n  /**\n   * Bind the server into the port specified.\n   *\n   * @param port - Port number or Unix socket path\n   * @param hostname\n   * @param backlog\n   * @param listeningListener\n   */\n  public async listen(port: number | string, hostname?: string, backlog?: number, listeningListener?: Function) {\n    if (this.options.beforeListen) {\n      await this.options.beforeListen();\n    }\n\n    //\n    // if Colyseus Cloud is detected, use @colyseus/tools to listen\n    //\n    if (process.env.COLYSEUS_CLOUD !== undefined ) {\n      if (typeof(hostname) === \"number\") {\n        //\n        // workaround, @colyseus/tools calls server.listen() again with the port as a string\n        //\n        hostname = undefined;\n\n      } else {\n        try {\n          return (await dynamicImport(\"@colyseus/tools\")).listen(this);\n        } catch (error) {\n          const err = new Error(\"Please install @colyseus/tools to be able to host on Colyseus Cloud.\");\n          err.cause = error;\n          throw err;\n        }\n      }\n    }\n\n    //\n    // otherwise, listen on the port directly\n    //\n    this.port = port;\n\n    //\n    // Make sure matchmaker is ready before accepting connections\n    // (isDevMode: matchmaker may take extra milliseconds to restore the rooms)\n    //\n    await matchMaker.accept(this.options.isStandaloneMatchMaker);\n\n    /**\n     * Greetings!\n     */\n    if (this.greet) {\n      greet();\n    }\n\n    // Wait for the transport to be ready\n    await this._onTransportReady;\n\n    return new Promise<void>((resolve, reject) => {\n      // TODO: refactor me!\n      // set transport globally, to be used by matchmaking route\n      setTransport(this.transport);\n\n      this.transport.listen(port, hostname, backlog, (err) => {\n        if (this.transport.server) {\n          this.transport.server.on('error', (err) => reject(err));\n        }\n\n        // default router is used if no router is provided\n        if (!this.router) {\n          this.router = getDefaultRouter() as unknown as Routes;\n\n        } else {\n          // make sure default routes are included\n          // https://github.com/Bekacru/better-call/pull/67\n          this.router = this.router.extend({ ...getDefaultRouter().endpoints }) as unknown as Routes;\n        }\n\n        bindRouterToTransport(this.transport, this.router, this.options.express !== undefined);\n\n        if (listeningListener) {\n          listeningListener(err);\n        }\n\n        if (err) {\n          reject(err);\n\n        } else {\n          resolve();\n        }\n      });\n    });\n  }\n\n  /**\n   * Define a new type of room for matchmaking.\n   *\n   * @param name public room identifier for match-making.\n   * @param roomClass Room class definition\n   * @param defaultOptions default options for `onCreate`\n   */\n  public define<T extends Type<Room>>(\n    roomClass: T,\n    defaultOptions?: OnCreateOptions<T>,\n  ): RegisteredHandler\n  public define<T extends Type<Room>>(\n    name: string,\n    roomClass: T,\n    defaultOptions?: OnCreateOptions<T>,\n  ): RegisteredHandler\n  public define<T extends Type<Room>>(\n    nameOrHandler: string | T,\n    handlerOrOptions: T | OnCreateOptions<T>,\n    defaultOptions?: OnCreateOptions<T>,\n  ): RegisteredHandler {\n    const name = (typeof(nameOrHandler) === \"string\")\n      ? nameOrHandler\n      : nameOrHandler.name;\n\n    const roomClass = (typeof(nameOrHandler) === \"string\")\n      ? handlerOrOptions\n      : nameOrHandler;\n\n    const options = (typeof(nameOrHandler) === \"string\")\n      ? defaultOptions\n      : handlerOrOptions;\n\n    return matchMaker.defineRoomType(name, roomClass, options);\n  }\n\n  /**\n   * Remove a room definition from matchmaking.\n   * This method does not destroy any room. It only dissallows matchmaking\n   */\n  public removeRoomType(name: string): void {\n    matchMaker.removeRoomType(name);\n  }\n\n  public async gracefullyShutdown(exit: boolean = true, err?: Error) {\n    if (matchMaker.state === matchMaker.MatchMakerState.SHUTTING_DOWN) {\n      return;\n    }\n\n    try {\n      // custom \"before shutdown\" method\n      await this.onBeforeShutdownCallback();\n\n      // this is going to lock all rooms and wait for them to be disposed\n      await matchMaker.gracefullyShutdown();\n\n      this.transport.shutdown();\n      this.presence.shutdown();\n      await this.driver.shutdown();\n\n      // custom \"after shutdown\" method\n      await this.onShutdownCallback();\n\n    } catch (e) {\n      debugAndPrintError(`error during shutdown: ${e}`);\n\n    } finally {\n      if (exit) {\n        process.exit((err && !isDevMode) ? 1 : 0);\n      }\n    }\n  }\n\n  /**\n   * Add simulated latency between client and server.\n   * @param milliseconds round trip latency in milliseconds.\n   */\n  public simulateLatency(milliseconds: number) {\n    if (milliseconds > 0) {\n      logger.warn(`\uD83D\uDCF6\uFE0F\u2757 Colyseus latency simulation enabled \u2192 ${milliseconds}ms latency for round trip.`);\n    } else {\n      logger.warn(`\uD83D\uDCF6\uFE0F\u2757 Colyseus latency simulation disabled.`);\n    }\n\n    const halfwayMS = (milliseconds / 2);\n    this.transport.simulateLatency(halfwayMS);\n\n    if (this._originalRoomOnMessage == null) {\n      this._originalRoomOnMessage = Room.prototype['_onMessage'];\n    }\n\n    const originalOnMessage = this._originalRoomOnMessage;\n\n    Room.prototype['_onMessage'] = milliseconds <= Number.EPSILON ? originalOnMessage : function (this: Room, client, buffer) {\n      // uWebSockets.js: duplicate buffer because it is cleared at native layer before the timeout.\n      const cachedBuffer = Buffer.from(buffer);\n      setTimeout(() => originalOnMessage.call(this, client, cachedBuffer), halfwayMS);\n    };\n  }\n\n  /**\n   * Register a callback that is going to be executed before the server shuts down.\n   * @param callback\n   */\n  public onShutdown(callback: () => void | Promise<any>) {\n    this.onShutdownCallback = callback;\n  }\n\n  public onBeforeShutdown(callback: () => void | Promise<any>) {\n    this.onBeforeShutdownCallback = callback;\n  }\n\n  protected async getDefaultTransport(options: any): Promise<Transport> {\n    try {\n      const module = await dynamicImport('@colyseus/ws-transport');\n      const WebSocketTransport = module.WebSocketTransport;\n      return new WebSocketTransport(options);\n\n    } catch (error) {\n      this._onTransportReady.reject(error);\n      throw new Error(\"Please provide a 'transport' layer. Default transport not set.\");\n    }\n  }\n\n  protected onShutdownCallback: () => void | Promise<any> =\n    () => Promise.resolve()\n\n  protected onBeforeShutdownCallback: () => void | Promise<any> =\n    () => Promise.resolve()\n}\n\nexport type RoomDefinitions = Record<string, RegisteredHandler | Type<Room>>;\n\nfunction isRegisteredHandler(value: RegisteredHandler | Type<Room>): value is RegisteredHandler {\n  return value instanceof RegisteredHandler || (\n    typeof(value) === \"object\" &&\n    value !== null &&\n    'klass' in (value as object)\n  );\n}\n\nexport function registerRoomDefinitions<T extends RoomDefinitions>(rooms: T): string[] {\n  const roomNames: string[] = [];\n\n  for (const [name, value] of Object.entries(rooms)) {\n    if (isRegisteredHandler(value)) {\n      value.name = name;\n      matchMaker.addRoomType(value);\n\n    } else {\n      matchMaker.defineRoomType(name, value);\n    }\n\n    roomNames.push(name);\n  }\n\n  return roomNames;\n}\n\nexport function unregisterRoomDefinitions(roomNames: Iterable<string>) {\n  for (const roomName of roomNames) {\n    matchMaker.removeRoomType(roomName);\n  }\n}\n\nexport type DefineServerOptions<\n  T extends Record<string, RegisteredHandler>,\n  R extends Router\n> = ServerOptions & {\n  rooms: T,\n  routes?: R,\n};\n\nexport function defineServer<\n  T extends Record<string, RegisteredHandler>,\n  R extends Router\n>(\n  options: DefineServerOptions<T, R>,\n): Server<T, R> {\n  const { rooms, routes, ...serverOptions } = options;\n\n  if (isDevMode) {\n    // In dev mode, the Vite plugin manages Server/matchMaker lifecycle.\n    // Return a config-only object \u2014 no Server instance, no matchMaker.setup().\n    return {\n      options: serverOptions,\n      router: routes,\n      '~rooms': rooms,\n    } as unknown as Server<T, R>;\n  }\n\n  const server = new Server<T, R>(serverOptions);\n  server.router = routes;\n\n  registerRoomDefinitions(rooms);\n\n  return server;\n}\n\nexport function defineRoom<T extends Type<Room>>(\n  roomKlass: T,\n  defaultOptions?: Parameters<NonNullable<InstanceType<T>['onCreate']>>[0],\n): RegisteredHandler<InstanceType<T>> {\n  return new RegisteredHandler(roomKlass, defaultOptions) as unknown as RegisteredHandler<InstanceType<T>>;\n}\n"],
  "mappings": "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",
  "names": ["gracefullyShutdown", "greet", "err"]
}
