{
  "version": 3,
  "sources": ["../src/Server.ts"],
  "sourcesContent": ["import http, { IncomingMessage, ServerResponse } from 'http';\nimport greeting from \"@colyseus/greeting-banner\";\n\nimport { debugAndPrintError, debugMatchMaking } from './Debug.js';\nimport * as matchMaker from './MatchMaker.js';\nimport { RegisteredHandler } from './matchmaker/RegisteredHandler.js';\nimport { Presence } from './presence/Presence.js';\n\nimport { Room } from './Room.js';\nimport { Type } from './utils/types.js';\nimport { getBearerToken, registerGracefulShutdown } from './utils/Utils.js';\n\nimport { registerNode, unregisterNode} from './discovery/index.js';\n\nimport { LocalPresence } from './presence/LocalPresence.js';\nimport { LocalDriver } from './matchmaker/driver/local/LocalDriver.js';\n\nimport { Transport } from './Transport.js';\nimport { logger, setLogger } from './Logger.js';\nimport { setDevMode, isDevMode } from './utils/DevMode.js';\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   * 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   * 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   * Options below are now part of WebSocketTransport (@colyseus/ws-transport)\n   * TODO: remove me on 0.15.0\n   */\n  /** @deprecated */\n  pingInterval?: number,\n\n  /** @deprecated */\n  pingMaxRetries?: number,\n\n  /** @deprecated */\n  verifyClient?: any,\n\n  /** @deprecated */\n  server?: http.Server,\n};\n\nexport class Server {\n  public transport: Transport;\n\n  protected presence: Presence;\n  protected driver: matchMaker.MatchMakerDriver;\n\n  protected port: number;\n  protected greet: boolean;\n\n  //@ts-expect-error\n  private _originalRoomOnMessage: typeof Room.prototype._onMessage | null = null;\n\n  constructor(options: ServerOptions = {}) {\n    const { gracefullyShutdown = true, greet = true } = options;\n\n    setDevMode(options.devMode === true);\n\n    this.presence = options.presence || new LocalPresence();\n    this.driver = options.driver || new LocalDriver();\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 attach(options: ServerOptions) {\n    /**\n     * Display deprecation warnings for moved Transport options.\n     * TODO: Remove me on 0.15\n     */\n    if (\n      options.pingInterval !== undefined ||\n      options.pingMaxRetries !== undefined ||\n      options.server !== undefined ||\n      options.verifyClient !== undefined\n    ) {\n      logger.warn(\"DEPRECATION WARNING: 'pingInterval', 'pingMaxRetries', 'server', and 'verifyClient' Server options will be permanently moved to WebSocketTransport on v0.15\");\n      logger.warn(`new Server({\n  transport: new WebSocketTransport({\n    pingInterval: ...,\n    pingMaxRetries: ...,\n    server: ...,\n    verifyClient: ...\n  })\n})`);\n      logger.warn(\"\uD83D\uDC49 Documentation: https://docs.colyseus.io/server/transport/\")\n    }\n\n    const transport = options.transport || this.getDefaultTransport(options);\n    delete options.transport;\n\n    this.transport = transport;\n\n    if (this.transport.server) {\n      // @ts-ignore\n      this.transport.server.once('listening', () => this.registerProcessForDiscovery());\n      this.attachMatchMakingRoutes(this.transport.server as http.Server);\n    }\n  }\n\n  /**\n   * Bind the server into the port specified.\n   *\n   * @param port\n   * @param hostname\n   * @param backlog\n   * @param listeningListener\n   */\n  public async listen(port: number, hostname?: string, backlog?: number, listeningListener?: Function) {\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();\n\n    /**\n     * Greetings!\n     */\n    if (this.greet) {\n      console.log(greeting);\n    }\n\n    return new Promise<void>((resolve, reject) => {\n      // @ts-ignore\n      this.transport.server?.on('error', (err) => reject(err));\n      this.transport.listen(port, hostname, backlog, (err) => {\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  public async registerProcessForDiscovery() {\n    // register node for proxy/service discovery\n    await registerNode(this.presence, {\n      port: this.port,\n      processId: matchMaker.processId,\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?: Parameters<NonNullable<InstanceType<T>['onCreate']>>[0],\n  ): RegisteredHandler\n  public define<T extends Type<Room>>(\n    name: string,\n    roomClass: T,\n    defaultOptions?: Parameters<NonNullable<InstanceType<T>['onCreate']>>[0],\n  ): RegisteredHandler\n  public define<T extends Type<Room>>(\n    nameOrHandler: string | T,\n    handlerOrOptions: T | Parameters<NonNullable<InstanceType<T>['onCreate']>>[0],\n    defaultOptions?: Parameters<NonNullable<InstanceType<T>['onCreate']>>[0],\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    await unregisterNode(this.presence, {\n      port: this.port,\n      processId: matchMaker.processId,\n    });\n\n    try {\n      // custom \"before shutdown\" method\n      await this.onBeforeShutdownCallback();\n\n      await matchMaker.gracefullyShutdown();\n      this.transport.shutdown();\n      this.presence.shutdown();\n      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      /* tslint:disable:no-string-literal */\n      this._originalRoomOnMessage = Room.prototype['_onMessage'];\n    }\n\n    const originalOnMessage = this._originalRoomOnMessage;\n\n    /* tslint:disable:no-string-literal */\n    Room.prototype['_onMessage'] = milliseconds <= Number.EPSILON ? originalOnMessage : function (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 getDefaultTransport(_: any): Transport {\n    throw new Error(\"Please provide a 'transport' layer. Default transport not set.\");\n  }\n\n  protected onShutdownCallback: () => void | Promise<any> =\n    () => Promise.resolve()\n\n  protected onBeforeShutdownCallback: () => void | Promise<any> =\n    () => Promise.resolve()\n\n  protected attachMatchMakingRoutes(server: http.Server) {\n    const listeners = server.listeners('request').slice(0);\n    server.removeAllListeners('request');\n\n    server.on('request', (req, res) => {\n      if (req.url.indexOf(`/${matchMaker.controller.matchmakeRoute}`) !== -1) {\n        debugMatchMaking('received matchmake request: %s', req.url);\n        this.handleMatchMakeRequest(req, res);\n\n      } else {\n        for (let i = 0, l = listeners.length; i < l; i++) {\n          listeners[i].call(server, req, res);\n        }\n      }\n    });\n  }\n\n  protected async handleMatchMakeRequest(req: IncomingMessage, res: ServerResponse) {\n    // do not accept matchmaking requests if already shutting down\n    if (matchMaker.state === matchMaker.MatchMakerState.SHUTTING_DOWN) {\n      res.writeHead(503, {});\n      res.end();\n      return;\n    }\n\n    const headers = Object.assign(\n      {},\n      matchMaker.controller.DEFAULT_CORS_HEADERS,\n      matchMaker.controller.getCorsHeaders.call(undefined, req)\n    );\n\n    if (req.method === 'OPTIONS') {\n      res.writeHead(204, headers);\n      res.end();\n\n    } else if (req.method === 'POST') {\n      const matchedParams = req.url.match(matchMaker.controller.allowedRoomNameChars);\n      const matchmakeIndex = matchedParams.indexOf(matchMaker.controller.matchmakeRoute);\n      const method = matchedParams[matchmakeIndex + 1];\n      const roomName = matchedParams[matchmakeIndex + 2] || '';\n\n      const data = [];\n      req.on('data', (chunk) => data.push(chunk));\n      req.on('end', async () => {\n        headers['Content-Type'] = 'application/json';\n        res.writeHead(200, headers);\n\n        try {\n          const clientOptions = JSON.parse(Buffer.concat(data).toString());\n          const response = await matchMaker.controller.invokeMethod(\n            method,\n            roomName,\n            clientOptions,\n            {\n              token: getBearerToken(req.headers['authorization']),\n              headers: req.headers,\n              ip: req.headers['x-real-ip'] ?? req.headers['x-forwarded-for'] ?? req.socket.remoteAddress,\n              req,\n            },\n          );\n\n          // specify protocol, if available.\n          if (this.transport.protocol !== undefined) {\n            response.protocol = this.transport.protocol;\n          }\n\n          res.write(JSON.stringify(response));\n\n        } catch (e) {\n          res.write(JSON.stringify({ code: e.code, error: e.message, }));\n        }\n\n        res.end();\n      });\n\n    } else if (req.method === 'GET') {\n      res.writeHead(404, headers);\n      res.end();\n    }\n\n  }\n\n\n}\n"],
  "mappings": "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",
  "names": ["gracefullyShutdown"]
}
