@colyseus/core 0.18.6 → 0.18.8

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/Server.cjs CHANGED
@@ -32,8 +32,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
32
32
  var Server_exports = {};
33
33
  __export(Server_exports, {
34
34
  Server: () => Server,
35
+ applySimulatedLatency: () => applySimulatedLatency,
35
36
  defineRoom: () => defineRoom,
36
37
  defineServer: () => defineServer,
38
+ parseLatencyEnv: () => parseLatencyEnv,
37
39
  registerRoomDefinitions: () => registerRoomDefinitions,
38
40
  unregisterRoomDefinitions: () => unregisterRoomDefinitions
39
41
  });
@@ -54,7 +56,6 @@ var import_default_routes = require("./router/default_routes.cjs");
54
56
  var Server = class _Server {
55
57
  constructor(options = {}) {
56
58
  this._onTransportReady = new import_Utils.Deferred();
57
- this._originalRoomOnMessage = null;
58
59
  this.onShutdownCallback = () => Promise.resolve();
59
60
  this.onBeforeShutdownCallback = () => Promise.resolve();
60
61
  const {
@@ -208,7 +209,7 @@ var Server = class _Server {
208
209
  try {
209
210
  await this.onBeforeShutdownCallback();
210
211
  await matchMaker.gracefullyShutdown();
211
- this.transport.shutdown();
212
+ this.transport?.shutdown();
212
213
  this.presence?.shutdown();
213
214
  await this.driver?.shutdown();
214
215
  await this.onShutdownCallback();
@@ -222,24 +223,19 @@ var Server = class _Server {
222
223
  }
223
224
  /**
224
225
  * Add simulated latency between client and server.
226
+ *
227
+ * May be called at any time — before or after `listen()`. Also available
228
+ * via the `COLYSEUS_LATENCY` environment variable (round-trip ms), which
229
+ * overrides calls made before the server boots.
230
+ *
225
231
  * @param milliseconds round trip latency in milliseconds.
226
232
  */
227
233
  simulateLatency(milliseconds) {
228
- if (milliseconds > 0) {
229
- import_Logger.logger.warn(`\u{1F4F6}\uFE0F\u2757 Colyseus latency simulation enabled \u2192 ${milliseconds}ms latency for round trip.`);
230
- } else {
231
- import_Logger.logger.warn(`\u{1F4F6}\uFE0F\u2757 Colyseus latency simulation disabled.`);
232
- }
233
- const halfwayMS = milliseconds / 2;
234
- this.transport.simulateLatency(halfwayMS);
235
- if (this._originalRoomOnMessage == null) {
236
- this._originalRoomOnMessage = import_Room.Room.prototype["_onMessage"];
237
- }
238
- const originalOnMessage = this._originalRoomOnMessage;
239
- import_Room.Room.prototype["_onMessage"] = milliseconds <= Number.EPSILON ? originalOnMessage : function(client, buffer) {
240
- const cachedBuffer = Buffer.from(buffer);
241
- setTimeout(() => originalOnMessage.call(this, client, cachedBuffer), halfwayMS);
242
- };
234
+ this._onTransportReady.then(
235
+ (transport) => applySimulatedLatency(transport, milliseconds),
236
+ () => {
237
+ }
238
+ );
243
239
  }
244
240
  /**
245
241
  * Register a callback that is going to be executed before the server shuts down.
@@ -257,6 +253,10 @@ var Server = class _Server {
257
253
  // serves the express callback and auth/database routes.
258
254
  async _bootServices() {
259
255
  const { beforeListen, database, express } = this.options;
256
+ const envLatency = parseLatencyEnv();
257
+ if (envLatency !== void 0) {
258
+ this.simulateLatency(envLatency);
259
+ }
260
260
  if (beforeListen) {
261
261
  await beforeListen();
262
262
  }
@@ -307,6 +307,36 @@ var Server = class _Server {
307
307
  }
308
308
  }
309
309
  };
310
+ var _originalRoomOnMessage = null;
311
+ function applySimulatedLatency(transport, milliseconds) {
312
+ if (milliseconds > 0) {
313
+ import_Logger.logger.warn(`\u{1F4F6}\uFE0F\u2757 Colyseus latency simulation enabled \u2192 ${milliseconds}ms latency for round trip.`);
314
+ } else {
315
+ import_Logger.logger.warn(`\u{1F4F6}\uFE0F\u2757 Colyseus latency simulation disabled.`);
316
+ }
317
+ const halfwayMS = milliseconds / 2;
318
+ transport?.simulateLatency(halfwayMS);
319
+ if (_originalRoomOnMessage == null) {
320
+ _originalRoomOnMessage = import_Room.Room.prototype["_onMessage"];
321
+ }
322
+ const originalOnMessage = _originalRoomOnMessage;
323
+ import_Room.Room.prototype["_onMessage"] = milliseconds <= Number.EPSILON ? originalOnMessage : function(client, buffer) {
324
+ const cachedBuffer = Buffer.from(buffer);
325
+ setTimeout(() => originalOnMessage.call(this, client, cachedBuffer), halfwayMS);
326
+ };
327
+ }
328
+ function parseLatencyEnv() {
329
+ const value = process.env.COLYSEUS_LATENCY;
330
+ if (value === void 0 || value === "") {
331
+ return void 0;
332
+ }
333
+ const milliseconds = Number(value);
334
+ if (isNaN(milliseconds)) {
335
+ import_Logger.logger.warn(`\u{1F4F6}\uFE0F\u2757 Ignoring COLYSEUS_LATENCY: expected a number of milliseconds, got ${JSON.stringify(value)}.`);
336
+ return void 0;
337
+ }
338
+ return milliseconds;
339
+ }
310
340
  function isRegisteredHandler(value) {
311
341
  return value instanceof import_RegisteredHandler.RegisteredHandler || typeof value === "object" && value !== null && "klass" in value;
312
342
  }
@@ -334,7 +364,9 @@ function defineServer(options) {
334
364
  return {
335
365
  options: serverOptions,
336
366
  router: routes,
337
- "~rooms": rooms
367
+ "~rooms": rooms,
368
+ // the vite plugin registers the transport before importing user code
369
+ simulateLatency: (milliseconds) => applySimulatedLatency((0, import_Transport.getTransport)(), milliseconds)
338
370
  };
339
371
  }
340
372
  const server = new Server(serverOptions);
@@ -348,8 +380,10 @@ function defineRoom(roomKlass, defaultOptions) {
348
380
  // Annotate the CommonJS export names for ESM import in node:
349
381
  0 && (module.exports = {
350
382
  Server,
383
+ applySimulatedLatency,
351
384
  defineRoom,
352
385
  defineServer,
386
+ parseLatencyEnv,
353
387
  registerRoomDefinitions,
354
388
  unregisterRoomDefinitions
355
389
  });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/Server.ts"],
4
- "sourcesContent": ["import { greet } from \"@colyseus/greeting-banner\";\nimport type express from 'express';\nimport type { Server as HttpServer } from 'node:http';\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\";\n\nimport { setTransport, Transport } from './Transport.ts';\nimport { logger, setLogger } from './Logger.ts';\nimport { setDevMode, isDevMode } from './utils/DevMode.ts';\nimport { type Router, bindRouterToTransport, createRouter } from './router/index.ts';\nimport { prereadRequestBodies } from './router/node.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 * Booted before `matchMaker.accept()`. Structural so `@colyseus/core` keeps\n * no runtime dep on `@colyseus/database` \u2014 any `{ boot(): Promise<void> }`\n * works. The optional `applyRouterDefaults` is invoked after boot with the\n * user's router so the database can contribute endpoints (e.g. auth routes).\n */\n database?: {\n boot(): Promise<void>;\n applyRouterDefaults?(router: Router): Router | Promise<Router>;\n },\n\n /**\n * Mount `@colyseus/auth` routes into the router. Pass an options object\n * (forwarded to `auth.endpoints(...)`) to wire it explicitly \u2014 useful when\n * `database` is not in use. `false` disables auto-mounting even if a\n * `database` is present and would have provided defaults.\n */\n auth?: false | {\n settings?: Record<string, any>;\n oauth?: boolean | { cookieSecret?: string };\n prefix?: string;\n },\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 // Implicit default for callers that omit explicit Server reference \u2014 e.g.\n // `playground()` reads `Server.current.router.endpoints` at request time.\n // Last-construction wins; multi-server setups should reference instances\n // explicitly.\n static current: Server<any, any> | undefined;\n\n constructor(options: ServerOptions = {}) {\n const {\n gracefullyShutdown = true,\n greet = true\n } = options;\n\n setDevMode(options.devMode === true);\n\n this.options = options;\n this.greet = greet;\n\n (Server as { current: Server<any, any> | undefined }).current = this as Server<any, any>;\n\n this.attach(options);\n\n // Pass options.presence/driver through as-is (possibly undefined).\n // matchMaker.setup() falls back to getDefaultPresence/getDefaultDriver,\n // which auto-select RedisPresence/RedisDriver on Colyseus Cloud.\n matchMaker.setup(\n options.presence,\n options.driver,\n options.publicAddress,\n options.selectProcessIdToCreateRoom,\n ).then(() => {\n this.presence = matchMaker.presence;\n this.driver = matchMaker.driver;\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 // `options.express` runs in `listen()` after `database.boot()` so user\n // code reading `database.auth.settings` etc. finds services instantiated.\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 await this._bootServices();\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 this.transport.listen(port, hostname, backlog, (err) => {\n if (this.transport.server) {\n this.transport.server.on('error', (err) => reject(err));\n }\n\n this.bindRoutes();\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 * Prepare matchmaking and HTTP routes, then return the underlying\n * `http.Server` *without* binding to a port.\n *\n * Use this on serverless platforms that consume an exported HTTP server\n * instead of a listening one. Vercel, for example, drives the request handler\n * of a default-exported server (its Express/Hono WebSocket examples use\n * `export default server`), whereas calling `listen()` there selects Vercel's\n * \"captured server\" path, which does not invoke Express-style app handlers.\n *\n * The transport must be created with an `http.Server` so it can be exported.\n * Vercel additionally needs `package.json` `\"main\"` pointing at the entrypoint:\n *\n * ```ts\n * import { createServer } from \"node:http\";\n * const httpServer = createServer();\n * const server = new Server({\n * transport: new WebSocketTransport({ server: httpServer }),\n * express: (app) => { app.get(\"/hello\", (req, res) => res.json({ ok: true })); },\n * });\n * server.define(\"my_room\", MyRoom);\n * export default await server.serverless(); // no listen()\n * ```\n */\n public async serverless(): Promise<HttpServer> {\n await this._bootServices();\n\n // transport (and its HTTP server) must be ready before binding routes\n await this._onTransportReady;\n\n const server = this.transport.server as HttpServer | undefined;\n if (!server) {\n throw new Error(\"serverless() requires a transport backed by an http.Server (pass `{ server }` to the transport).\");\n }\n\n this.bindRoutes();\n\n // When the server is consumed via `export default` (rather than listen()),\n // the request body must be available synchronously \u2014 buffer it up-front so\n // `req.body` is set before the router runs.\n prereadRequestBodies(server);\n\n await matchMaker.accept(this.options.isStandaloneMatchMaker);\n\n if (this.greet) {\n greet();\n }\n\n return server;\n }\n\n /**\n * Set the active transport globally and bind the matchmaking/HTTP routes.\n * Shared by {@link listen} and {@link serverless}.\n *\n * `setTransport()` runs here rather than before `transport.listen()`: the\n * global transport is only read by the matchmaking route handler, which can't\n * run until `bindRouterToTransport()` below registers the route.\n */\n private bindRoutes() {\n // set transport globally, to be used by the matchmaking route\n setTransport(this.transport);\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\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 // Boot sequence shared by listen() and serverless(): user `beforeListen`\n // hook, database boot, framework router defaults (auth/db endpoints), then\n // the express app. serverless() needs this too \u2014 its exported server still\n // serves the express callback and auth/database routes.\n private async _bootServices(): Promise<void> {\n const { beforeListen, database, express } = this.options;\n\n if (beforeListen) { await beforeListen(); }\n if (database) { await database.boot(); }\n\n await this._applyRouterDefaults();\n\n if (express) {\n await this._onTransportReady;\n if (this.transport.getExpressApp) {\n const expressApp = await this.transport.getExpressApp();\n await express(expressApp);\n }\n }\n }\n\n // Extend the user's router with framework-contributed endpoints. An explicit\n // `auth` option wins over the database's `applyRouterDefaults` so a non-DB\n // setup can still auto-mount @colyseus/auth, and a DB user can opt out.\n private async _applyRouterDefaults(): Promise<void> {\n const { auth, database } = this.options;\n const wantsAuth = auth !== undefined && auth !== false;\n const wantsDatabase = auth === undefined && !!database?.applyRouterDefaults;\n if (!wantsAuth && !wantsDatabase) { return; }\n\n // Boot an empty router if the user didn't pass `routes` \u2014 the framework's\n // default routes get layered on later in the `transport.listen()` callback.\n this.router ??= createRouter({}) as unknown as Routes;\n\n if (wantsAuth) {\n const authMod: any = await dynamicImport('@colyseus/auth').catch(() => undefined);\n const endpointsFn = authMod?.auth?.endpoints ?? authMod?.default?.auth?.endpoints;\n if (typeof endpointsFn === 'function') {\n this.router = (this.router as Router).extend(endpointsFn(auth)) as Routes;\n }\n } else {\n const updated = await database!.applyRouterDefaults!(this.router as Router);\n if (updated) { this.router = updated as Routes; }\n }\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"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { greet } from \"@colyseus/greeting-banner\";\nimport type express from 'express';\nimport type { Server as HttpServer } from 'node:http';\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\";\n\nimport { getTransport, setTransport, Transport } from './Transport.ts';\nimport { logger, setLogger } from './Logger.ts';\nimport { setDevMode, isDevMode } from './utils/DevMode.ts';\nimport { type Router, bindRouterToTransport, createRouter } from './router/index.ts';\nimport { prereadRequestBodies } from './router/node.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 * Booted before `matchMaker.accept()`. Structural so `@colyseus/core` keeps\n * no runtime dep on `@colyseus/database` \u2014 any `{ boot(): Promise<void> }`\n * works. The optional `applyRouterDefaults` is invoked after boot with the\n * user's router so the database can contribute endpoints (e.g. auth routes).\n */\n database?: {\n boot(): Promise<void>;\n applyRouterDefaults?(router: Router): Router | Promise<Router>;\n },\n\n /**\n * Mount `@colyseus/auth` routes into the router. Pass an options object\n * (forwarded to `auth.endpoints(...)`) to wire it explicitly \u2014 useful when\n * `database` is not in use. `false` disables auto-mounting even if a\n * `database` is present and would have provided defaults.\n */\n auth?: false | {\n settings?: Record<string, any>;\n oauth?: boolean | { cookieSecret?: string };\n prefix?: string;\n },\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 // Implicit default for callers that omit explicit Server reference \u2014 e.g.\n // `playground()` reads `Server.current.router.endpoints` at request time.\n // Last-construction wins; multi-server setups should reference instances\n // explicitly.\n static current: Server<any, any> | undefined;\n\n constructor(options: ServerOptions = {}) {\n const {\n gracefullyShutdown = true,\n greet = true\n } = options;\n\n setDevMode(options.devMode === true);\n\n this.options = options;\n this.greet = greet;\n\n (Server as { current: Server<any, any> | undefined }).current = this as Server<any, any>;\n\n this.attach(options);\n\n // Pass options.presence/driver through as-is (possibly undefined).\n // matchMaker.setup() falls back to getDefaultPresence/getDefaultDriver,\n // which auto-select RedisPresence/RedisDriver on Colyseus Cloud.\n matchMaker.setup(\n options.presence,\n options.driver,\n options.publicAddress,\n options.selectProcessIdToCreateRoom,\n ).then(() => {\n this.presence = matchMaker.presence;\n this.driver = matchMaker.driver;\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 // `options.express` runs in `listen()` after `database.boot()` so user\n // code reading `database.auth.settings` etc. finds services instantiated.\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 await this._bootServices();\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 this.transport.listen(port, hostname, backlog, (err) => {\n if (this.transport.server) {\n this.transport.server.on('error', (err) => reject(err));\n }\n\n this.bindRoutes();\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 * Prepare matchmaking and HTTP routes, then return the underlying\n * `http.Server` *without* binding to a port.\n *\n * Use this on serverless platforms that consume an exported HTTP server\n * instead of a listening one. Vercel, for example, drives the request handler\n * of a default-exported server (its Express/Hono WebSocket examples use\n * `export default server`), whereas calling `listen()` there selects Vercel's\n * \"captured server\" path, which does not invoke Express-style app handlers.\n *\n * The transport must be created with an `http.Server` so it can be exported.\n * Vercel additionally needs `package.json` `\"main\"` pointing at the entrypoint:\n *\n * ```ts\n * import { createServer } from \"node:http\";\n * const httpServer = createServer();\n * const server = new Server({\n * transport: new WebSocketTransport({ server: httpServer }),\n * express: (app) => { app.get(\"/hello\", (req, res) => res.json({ ok: true })); },\n * });\n * server.define(\"my_room\", MyRoom);\n * export default await server.serverless(); // no listen()\n * ```\n */\n public async serverless(): Promise<HttpServer> {\n await this._bootServices();\n\n // transport (and its HTTP server) must be ready before binding routes\n await this._onTransportReady;\n\n const server = this.transport.server as HttpServer | undefined;\n if (!server) {\n throw new Error(\"serverless() requires a transport backed by an http.Server (pass `{ server }` to the transport).\");\n }\n\n this.bindRoutes();\n\n // When the server is consumed via `export default` (rather than listen()),\n // the request body must be available synchronously \u2014 buffer it up-front so\n // `req.body` is set before the router runs.\n prereadRequestBodies(server);\n\n await matchMaker.accept(this.options.isStandaloneMatchMaker);\n\n if (this.greet) {\n greet();\n }\n\n return server;\n }\n\n /**\n * Set the active transport globally and bind the matchmaking/HTTP routes.\n * Shared by {@link listen} and {@link serverless}.\n *\n * `setTransport()` runs here rather than before `transport.listen()`: the\n * global transport is only read by the matchmaking route handler, which can't\n * run until `bindRouterToTransport()` below registers the route.\n */\n private bindRoutes() {\n // set transport globally, to be used by the matchmaking route\n setTransport(this.transport);\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\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 *\n * May be called at any time \u2014 before or after `listen()`. Also available\n * via the `COLYSEUS_LATENCY` environment variable (round-trip ms), which\n * overrides calls made before the server boots.\n *\n * @param milliseconds round trip latency in milliseconds.\n */\n public simulateLatency(milliseconds: number) {\n // transport may still be resolving (constructor's attach() imports the default one)\n this._onTransportReady.then(\n (transport) => applySimulatedLatency(transport, milliseconds),\n () => {/* missing-transport error already surfaces via attach()/listen() */},\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 // Boot sequence shared by listen() and serverless(): user `beforeListen`\n // hook, database boot, framework router defaults (auth/db endpoints), then\n // the express app. serverless() needs this too \u2014 its exported server still\n // serves the express callback and auth/database routes.\n private async _bootServices(): Promise<void> {\n const { beforeListen, database, express } = this.options;\n\n // boot runs after module evaluation, so the env var wins over\n // simulateLatency() calls made at the top level of user code\n const envLatency = parseLatencyEnv();\n if (envLatency !== undefined) { this.simulateLatency(envLatency); }\n\n if (beforeListen) { await beforeListen(); }\n if (database) { await database.boot(); }\n\n await this._applyRouterDefaults();\n\n if (express) {\n await this._onTransportReady;\n if (this.transport.getExpressApp) {\n const expressApp = await this.transport.getExpressApp();\n await express(expressApp);\n }\n }\n }\n\n // Extend the user's router with framework-contributed endpoints. An explicit\n // `auth` option wins over the database's `applyRouterDefaults` so a non-DB\n // setup can still auto-mount @colyseus/auth, and a DB user can opt out.\n private async _applyRouterDefaults(): Promise<void> {\n const { auth, database } = this.options;\n const wantsAuth = auth !== undefined && auth !== false;\n const wantsDatabase = auth === undefined && !!database?.applyRouterDefaults;\n if (!wantsAuth && !wantsDatabase) { return; }\n\n // Boot an empty router if the user didn't pass `routes` \u2014 the framework's\n // default routes get layered on later in the `transport.listen()` callback.\n this.router ??= createRouter({}) as unknown as Routes;\n\n if (wantsAuth) {\n const authMod: any = await dynamicImport('@colyseus/auth').catch(() => undefined);\n const endpointsFn = authMod?.auth?.endpoints ?? authMod?.default?.auth?.endpoints;\n if (typeof endpointsFn === 'function') {\n this.router = (this.router as Router).extend(endpointsFn(auth)) as Routes;\n }\n } else {\n const updated = await database!.applyRouterDefaults!(this.router as Router);\n if (updated) { this.router = updated as Routes; }\n }\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\nlet _originalRoomOnMessage: typeof Room.prototype['_onMessage'] | null = null;\n\n/**\n * Applies both halves of the round-trip latency simulation: delays the\n * transport's outgoing messages and the Room's incoming message handling.\n *\n * Prefer `server.simulateLatency()` \u2014 this is the shared core behind it, the\n * dev-mode `defineServer()` object and the `COLYSEUS_LATENCY` env var, where\n * no `Server` instance (or transport, yet) may exist.\n */\nexport function applySimulatedLatency(transport: Transport | undefined, 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 transport?.simulateLatency(halfwayMS);\n\n if (_originalRoomOnMessage == null) {\n _originalRoomOnMessage = Room.prototype['_onMessage'];\n }\n\n const originalOnMessage = _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 * Reads the `COLYSEUS_LATENCY` environment variable (round-trip milliseconds).\n * Returns `undefined` when absent or empty; a non-numeric value warns and is ignored.\n */\nexport function parseLatencyEnv(): number | undefined {\n const value = process.env.COLYSEUS_LATENCY;\n if (value === undefined || value === '') { return undefined; }\n\n const milliseconds = Number(value);\n if (isNaN(milliseconds)) {\n logger.warn(`\uD83D\uDCF6\uFE0F\u2757 Ignoring COLYSEUS_LATENCY: expected a number of milliseconds, got ${JSON.stringify(value)}.`);\n return undefined;\n }\n return milliseconds;\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 // the vite plugin registers the transport before importing user code\n simulateLatency: (milliseconds: number) => applySimulatedLatency(getTransport(), milliseconds),\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"],
5
+ "mappings": "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",
6
6
  "names": ["gracefullyShutdown", "greet", "err", "module"]
7
7
  }
package/build/Server.d.ts CHANGED
@@ -96,7 +96,6 @@ export declare class Server<RoomTypes extends Record<string, RegisteredHandler>
96
96
  protected port: number | string;
97
97
  protected greet: boolean;
98
98
  protected _onTransportReady: Deferred<Transport>;
99
- private _originalRoomOnMessage;
100
99
  static current: Server<any, any> | undefined;
101
100
  constructor(options?: ServerOptions);
102
101
  attach(options: ServerOptions): Promise<void>;
@@ -160,6 +159,11 @@ export declare class Server<RoomTypes extends Record<string, RegisteredHandler>
160
159
  gracefullyShutdown(exit?: boolean, err?: Error): Promise<void>;
161
160
  /**
162
161
  * Add simulated latency between client and server.
162
+ *
163
+ * May be called at any time — before or after `listen()`. Also available
164
+ * via the `COLYSEUS_LATENCY` environment variable (round-trip ms), which
165
+ * overrides calls made before the server boots.
166
+ *
163
167
  * @param milliseconds round trip latency in milliseconds.
164
168
  */
165
169
  simulateLatency(milliseconds: number): void;
@@ -175,6 +179,20 @@ export declare class Server<RoomTypes extends Record<string, RegisteredHandler>
175
179
  protected onShutdownCallback: () => void | Promise<any>;
176
180
  protected onBeforeShutdownCallback: () => void | Promise<any>;
177
181
  }
182
+ /**
183
+ * Applies both halves of the round-trip latency simulation: delays the
184
+ * transport's outgoing messages and the Room's incoming message handling.
185
+ *
186
+ * Prefer `server.simulateLatency()` — this is the shared core behind it, the
187
+ * dev-mode `defineServer()` object and the `COLYSEUS_LATENCY` env var, where
188
+ * no `Server` instance (or transport, yet) may exist.
189
+ */
190
+ export declare function applySimulatedLatency(transport: Transport | undefined, milliseconds: number): void;
191
+ /**
192
+ * Reads the `COLYSEUS_LATENCY` environment variable (round-trip milliseconds).
193
+ * Returns `undefined` when absent or empty; a non-numeric value warns and is ignored.
194
+ */
195
+ export declare function parseLatencyEnv(): number | undefined;
178
196
  export type RoomDefinitions = Record<string, RegisteredHandler | Type<Room>>;
179
197
  export declare function registerRoomDefinitions<T extends RoomDefinitions>(rooms: T): string[];
180
198
  export declare function unregisterRoomDefinitions(roomNames: Iterable<string>): void;
package/build/Server.mjs CHANGED
@@ -5,7 +5,7 @@ import * as matchMaker from "./MatchMaker.mjs";
5
5
  import { RegisteredHandler } from "./matchmaker/RegisteredHandler.mjs";
6
6
  import { Room } from "./Room.mjs";
7
7
  import { Deferred, registerGracefulShutdown, dynamicImport } from "./utils/Utils.mjs";
8
- import { setTransport } from "./Transport.mjs";
8
+ import { getTransport, setTransport } from "./Transport.mjs";
9
9
  import { logger, setLogger } from "./Logger.mjs";
10
10
  import { setDevMode, isDevMode } from "./utils/DevMode.mjs";
11
11
  import { bindRouterToTransport, createRouter } from "./router/index.mjs";
@@ -15,7 +15,6 @@ import { getDefaultRouter } from "./router/default_routes.mjs";
15
15
  var Server = class _Server {
16
16
  constructor(options = {}) {
17
17
  this._onTransportReady = new Deferred();
18
- this._originalRoomOnMessage = null;
19
18
  this.onShutdownCallback = () => Promise.resolve();
20
19
  this.onBeforeShutdownCallback = () => Promise.resolve();
21
20
  const {
@@ -169,7 +168,7 @@ var Server = class _Server {
169
168
  try {
170
169
  await this.onBeforeShutdownCallback();
171
170
  await matchMaker.gracefullyShutdown();
172
- this.transport.shutdown();
171
+ this.transport?.shutdown();
173
172
  this.presence?.shutdown();
174
173
  await this.driver?.shutdown();
175
174
  await this.onShutdownCallback();
@@ -183,24 +182,19 @@ var Server = class _Server {
183
182
  }
184
183
  /**
185
184
  * Add simulated latency between client and server.
185
+ *
186
+ * May be called at any time — before or after `listen()`. Also available
187
+ * via the `COLYSEUS_LATENCY` environment variable (round-trip ms), which
188
+ * overrides calls made before the server boots.
189
+ *
186
190
  * @param milliseconds round trip latency in milliseconds.
187
191
  */
188
192
  simulateLatency(milliseconds) {
189
- if (milliseconds > 0) {
190
- logger.warn(`\u{1F4F6}\uFE0F\u2757 Colyseus latency simulation enabled \u2192 ${milliseconds}ms latency for round trip.`);
191
- } else {
192
- logger.warn(`\u{1F4F6}\uFE0F\u2757 Colyseus latency simulation disabled.`);
193
- }
194
- const halfwayMS = milliseconds / 2;
195
- this.transport.simulateLatency(halfwayMS);
196
- if (this._originalRoomOnMessage == null) {
197
- this._originalRoomOnMessage = Room.prototype["_onMessage"];
198
- }
199
- const originalOnMessage = this._originalRoomOnMessage;
200
- Room.prototype["_onMessage"] = milliseconds <= Number.EPSILON ? originalOnMessage : function(client, buffer) {
201
- const cachedBuffer = Buffer.from(buffer);
202
- setTimeout(() => originalOnMessage.call(this, client, cachedBuffer), halfwayMS);
203
- };
193
+ this._onTransportReady.then(
194
+ (transport) => applySimulatedLatency(transport, milliseconds),
195
+ () => {
196
+ }
197
+ );
204
198
  }
205
199
  /**
206
200
  * Register a callback that is going to be executed before the server shuts down.
@@ -218,6 +212,10 @@ var Server = class _Server {
218
212
  // serves the express callback and auth/database routes.
219
213
  async _bootServices() {
220
214
  const { beforeListen, database, express } = this.options;
215
+ const envLatency = parseLatencyEnv();
216
+ if (envLatency !== void 0) {
217
+ this.simulateLatency(envLatency);
218
+ }
221
219
  if (beforeListen) {
222
220
  await beforeListen();
223
221
  }
@@ -268,6 +266,36 @@ var Server = class _Server {
268
266
  }
269
267
  }
270
268
  };
269
+ var _originalRoomOnMessage = null;
270
+ function applySimulatedLatency(transport, milliseconds) {
271
+ if (milliseconds > 0) {
272
+ logger.warn(`\u{1F4F6}\uFE0F\u2757 Colyseus latency simulation enabled \u2192 ${milliseconds}ms latency for round trip.`);
273
+ } else {
274
+ logger.warn(`\u{1F4F6}\uFE0F\u2757 Colyseus latency simulation disabled.`);
275
+ }
276
+ const halfwayMS = milliseconds / 2;
277
+ transport?.simulateLatency(halfwayMS);
278
+ if (_originalRoomOnMessage == null) {
279
+ _originalRoomOnMessage = Room.prototype["_onMessage"];
280
+ }
281
+ const originalOnMessage = _originalRoomOnMessage;
282
+ Room.prototype["_onMessage"] = milliseconds <= Number.EPSILON ? originalOnMessage : function(client, buffer) {
283
+ const cachedBuffer = Buffer.from(buffer);
284
+ setTimeout(() => originalOnMessage.call(this, client, cachedBuffer), halfwayMS);
285
+ };
286
+ }
287
+ function parseLatencyEnv() {
288
+ const value = process.env.COLYSEUS_LATENCY;
289
+ if (value === void 0 || value === "") {
290
+ return void 0;
291
+ }
292
+ const milliseconds = Number(value);
293
+ if (isNaN(milliseconds)) {
294
+ logger.warn(`\u{1F4F6}\uFE0F\u2757 Ignoring COLYSEUS_LATENCY: expected a number of milliseconds, got ${JSON.stringify(value)}.`);
295
+ return void 0;
296
+ }
297
+ return milliseconds;
298
+ }
271
299
  function isRegisteredHandler(value) {
272
300
  return value instanceof RegisteredHandler || typeof value === "object" && value !== null && "klass" in value;
273
301
  }
@@ -295,7 +323,9 @@ function defineServer(options) {
295
323
  return {
296
324
  options: serverOptions,
297
325
  router: routes,
298
- "~rooms": rooms
326
+ "~rooms": rooms,
327
+ // the vite plugin registers the transport before importing user code
328
+ simulateLatency: (milliseconds) => applySimulatedLatency(getTransport(), milliseconds)
299
329
  };
300
330
  }
301
331
  const server = new Server(serverOptions);
@@ -308,8 +338,10 @@ function defineRoom(roomKlass, defaultOptions) {
308
338
  }
309
339
  export {
310
340
  Server,
341
+ applySimulatedLatency,
311
342
  defineRoom,
312
343
  defineServer,
344
+ parseLatencyEnv,
313
345
  registerRoomDefinitions,
314
346
  unregisterRoomDefinitions
315
347
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/Server.ts"],
4
- "sourcesContent": ["import { greet } from \"@colyseus/greeting-banner\";\nimport type express from 'express';\nimport type { Server as HttpServer } from 'node:http';\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\";\n\nimport { setTransport, Transport } from './Transport.ts';\nimport { logger, setLogger } from './Logger.ts';\nimport { setDevMode, isDevMode } from './utils/DevMode.ts';\nimport { type Router, bindRouterToTransport, createRouter } from './router/index.ts';\nimport { prereadRequestBodies } from './router/node.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 * Booted before `matchMaker.accept()`. Structural so `@colyseus/core` keeps\n * no runtime dep on `@colyseus/database` \u2014 any `{ boot(): Promise<void> }`\n * works. The optional `applyRouterDefaults` is invoked after boot with the\n * user's router so the database can contribute endpoints (e.g. auth routes).\n */\n database?: {\n boot(): Promise<void>;\n applyRouterDefaults?(router: Router): Router | Promise<Router>;\n },\n\n /**\n * Mount `@colyseus/auth` routes into the router. Pass an options object\n * (forwarded to `auth.endpoints(...)`) to wire it explicitly \u2014 useful when\n * `database` is not in use. `false` disables auto-mounting even if a\n * `database` is present and would have provided defaults.\n */\n auth?: false | {\n settings?: Record<string, any>;\n oauth?: boolean | { cookieSecret?: string };\n prefix?: string;\n },\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 // Implicit default for callers that omit explicit Server reference \u2014 e.g.\n // `playground()` reads `Server.current.router.endpoints` at request time.\n // Last-construction wins; multi-server setups should reference instances\n // explicitly.\n static current: Server<any, any> | undefined;\n\n constructor(options: ServerOptions = {}) {\n const {\n gracefullyShutdown = true,\n greet = true\n } = options;\n\n setDevMode(options.devMode === true);\n\n this.options = options;\n this.greet = greet;\n\n (Server as { current: Server<any, any> | undefined }).current = this as Server<any, any>;\n\n this.attach(options);\n\n // Pass options.presence/driver through as-is (possibly undefined).\n // matchMaker.setup() falls back to getDefaultPresence/getDefaultDriver,\n // which auto-select RedisPresence/RedisDriver on Colyseus Cloud.\n matchMaker.setup(\n options.presence,\n options.driver,\n options.publicAddress,\n options.selectProcessIdToCreateRoom,\n ).then(() => {\n this.presence = matchMaker.presence;\n this.driver = matchMaker.driver;\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 // `options.express` runs in `listen()` after `database.boot()` so user\n // code reading `database.auth.settings` etc. finds services instantiated.\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 await this._bootServices();\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 this.transport.listen(port, hostname, backlog, (err) => {\n if (this.transport.server) {\n this.transport.server.on('error', (err) => reject(err));\n }\n\n this.bindRoutes();\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 * Prepare matchmaking and HTTP routes, then return the underlying\n * `http.Server` *without* binding to a port.\n *\n * Use this on serverless platforms that consume an exported HTTP server\n * instead of a listening one. Vercel, for example, drives the request handler\n * of a default-exported server (its Express/Hono WebSocket examples use\n * `export default server`), whereas calling `listen()` there selects Vercel's\n * \"captured server\" path, which does not invoke Express-style app handlers.\n *\n * The transport must be created with an `http.Server` so it can be exported.\n * Vercel additionally needs `package.json` `\"main\"` pointing at the entrypoint:\n *\n * ```ts\n * import { createServer } from \"node:http\";\n * const httpServer = createServer();\n * const server = new Server({\n * transport: new WebSocketTransport({ server: httpServer }),\n * express: (app) => { app.get(\"/hello\", (req, res) => res.json({ ok: true })); },\n * });\n * server.define(\"my_room\", MyRoom);\n * export default await server.serverless(); // no listen()\n * ```\n */\n public async serverless(): Promise<HttpServer> {\n await this._bootServices();\n\n // transport (and its HTTP server) must be ready before binding routes\n await this._onTransportReady;\n\n const server = this.transport.server as HttpServer | undefined;\n if (!server) {\n throw new Error(\"serverless() requires a transport backed by an http.Server (pass `{ server }` to the transport).\");\n }\n\n this.bindRoutes();\n\n // When the server is consumed via `export default` (rather than listen()),\n // the request body must be available synchronously \u2014 buffer it up-front so\n // `req.body` is set before the router runs.\n prereadRequestBodies(server);\n\n await matchMaker.accept(this.options.isStandaloneMatchMaker);\n\n if (this.greet) {\n greet();\n }\n\n return server;\n }\n\n /**\n * Set the active transport globally and bind the matchmaking/HTTP routes.\n * Shared by {@link listen} and {@link serverless}.\n *\n * `setTransport()` runs here rather than before `transport.listen()`: the\n * global transport is only read by the matchmaking route handler, which can't\n * run until `bindRouterToTransport()` below registers the route.\n */\n private bindRoutes() {\n // set transport globally, to be used by the matchmaking route\n setTransport(this.transport);\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\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 // Boot sequence shared by listen() and serverless(): user `beforeListen`\n // hook, database boot, framework router defaults (auth/db endpoints), then\n // the express app. serverless() needs this too \u2014 its exported server still\n // serves the express callback and auth/database routes.\n private async _bootServices(): Promise<void> {\n const { beforeListen, database, express } = this.options;\n\n if (beforeListen) { await beforeListen(); }\n if (database) { await database.boot(); }\n\n await this._applyRouterDefaults();\n\n if (express) {\n await this._onTransportReady;\n if (this.transport.getExpressApp) {\n const expressApp = await this.transport.getExpressApp();\n await express(expressApp);\n }\n }\n }\n\n // Extend the user's router with framework-contributed endpoints. An explicit\n // `auth` option wins over the database's `applyRouterDefaults` so a non-DB\n // setup can still auto-mount @colyseus/auth, and a DB user can opt out.\n private async _applyRouterDefaults(): Promise<void> {\n const { auth, database } = this.options;\n const wantsAuth = auth !== undefined && auth !== false;\n const wantsDatabase = auth === undefined && !!database?.applyRouterDefaults;\n if (!wantsAuth && !wantsDatabase) { return; }\n\n // Boot an empty router if the user didn't pass `routes` \u2014 the framework's\n // default routes get layered on later in the `transport.listen()` callback.\n this.router ??= createRouter({}) as unknown as Routes;\n\n if (wantsAuth) {\n const authMod: any = await dynamicImport('@colyseus/auth').catch(() => undefined);\n const endpointsFn = authMod?.auth?.endpoints ?? authMod?.default?.auth?.endpoints;\n if (typeof endpointsFn === 'function') {\n this.router = (this.router as Router).extend(endpointsFn(auth)) as Routes;\n }\n } else {\n const updated = await database!.applyRouterDefaults!(this.router as Router);\n if (updated) { this.router = updated as Routes; }\n }\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"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { greet } from \"@colyseus/greeting-banner\";\nimport type express from 'express';\nimport type { Server as HttpServer } from 'node:http';\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\";\n\nimport { getTransport, setTransport, Transport } from './Transport.ts';\nimport { logger, setLogger } from './Logger.ts';\nimport { setDevMode, isDevMode } from './utils/DevMode.ts';\nimport { type Router, bindRouterToTransport, createRouter } from './router/index.ts';\nimport { prereadRequestBodies } from './router/node.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 * Booted before `matchMaker.accept()`. Structural so `@colyseus/core` keeps\n * no runtime dep on `@colyseus/database` \u2014 any `{ boot(): Promise<void> }`\n * works. The optional `applyRouterDefaults` is invoked after boot with the\n * user's router so the database can contribute endpoints (e.g. auth routes).\n */\n database?: {\n boot(): Promise<void>;\n applyRouterDefaults?(router: Router): Router | Promise<Router>;\n },\n\n /**\n * Mount `@colyseus/auth` routes into the router. Pass an options object\n * (forwarded to `auth.endpoints(...)`) to wire it explicitly \u2014 useful when\n * `database` is not in use. `false` disables auto-mounting even if a\n * `database` is present and would have provided defaults.\n */\n auth?: false | {\n settings?: Record<string, any>;\n oauth?: boolean | { cookieSecret?: string };\n prefix?: string;\n },\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 // Implicit default for callers that omit explicit Server reference \u2014 e.g.\n // `playground()` reads `Server.current.router.endpoints` at request time.\n // Last-construction wins; multi-server setups should reference instances\n // explicitly.\n static current: Server<any, any> | undefined;\n\n constructor(options: ServerOptions = {}) {\n const {\n gracefullyShutdown = true,\n greet = true\n } = options;\n\n setDevMode(options.devMode === true);\n\n this.options = options;\n this.greet = greet;\n\n (Server as { current: Server<any, any> | undefined }).current = this as Server<any, any>;\n\n this.attach(options);\n\n // Pass options.presence/driver through as-is (possibly undefined).\n // matchMaker.setup() falls back to getDefaultPresence/getDefaultDriver,\n // which auto-select RedisPresence/RedisDriver on Colyseus Cloud.\n matchMaker.setup(\n options.presence,\n options.driver,\n options.publicAddress,\n options.selectProcessIdToCreateRoom,\n ).then(() => {\n this.presence = matchMaker.presence;\n this.driver = matchMaker.driver;\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 // `options.express` runs in `listen()` after `database.boot()` so user\n // code reading `database.auth.settings` etc. finds services instantiated.\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 await this._bootServices();\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 this.transport.listen(port, hostname, backlog, (err) => {\n if (this.transport.server) {\n this.transport.server.on('error', (err) => reject(err));\n }\n\n this.bindRoutes();\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 * Prepare matchmaking and HTTP routes, then return the underlying\n * `http.Server` *without* binding to a port.\n *\n * Use this on serverless platforms that consume an exported HTTP server\n * instead of a listening one. Vercel, for example, drives the request handler\n * of a default-exported server (its Express/Hono WebSocket examples use\n * `export default server`), whereas calling `listen()` there selects Vercel's\n * \"captured server\" path, which does not invoke Express-style app handlers.\n *\n * The transport must be created with an `http.Server` so it can be exported.\n * Vercel additionally needs `package.json` `\"main\"` pointing at the entrypoint:\n *\n * ```ts\n * import { createServer } from \"node:http\";\n * const httpServer = createServer();\n * const server = new Server({\n * transport: new WebSocketTransport({ server: httpServer }),\n * express: (app) => { app.get(\"/hello\", (req, res) => res.json({ ok: true })); },\n * });\n * server.define(\"my_room\", MyRoom);\n * export default await server.serverless(); // no listen()\n * ```\n */\n public async serverless(): Promise<HttpServer> {\n await this._bootServices();\n\n // transport (and its HTTP server) must be ready before binding routes\n await this._onTransportReady;\n\n const server = this.transport.server as HttpServer | undefined;\n if (!server) {\n throw new Error(\"serverless() requires a transport backed by an http.Server (pass `{ server }` to the transport).\");\n }\n\n this.bindRoutes();\n\n // When the server is consumed via `export default` (rather than listen()),\n // the request body must be available synchronously \u2014 buffer it up-front so\n // `req.body` is set before the router runs.\n prereadRequestBodies(server);\n\n await matchMaker.accept(this.options.isStandaloneMatchMaker);\n\n if (this.greet) {\n greet();\n }\n\n return server;\n }\n\n /**\n * Set the active transport globally and bind the matchmaking/HTTP routes.\n * Shared by {@link listen} and {@link serverless}.\n *\n * `setTransport()` runs here rather than before `transport.listen()`: the\n * global transport is only read by the matchmaking route handler, which can't\n * run until `bindRouterToTransport()` below registers the route.\n */\n private bindRoutes() {\n // set transport globally, to be used by the matchmaking route\n setTransport(this.transport);\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\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 *\n * May be called at any time \u2014 before or after `listen()`. Also available\n * via the `COLYSEUS_LATENCY` environment variable (round-trip ms), which\n * overrides calls made before the server boots.\n *\n * @param milliseconds round trip latency in milliseconds.\n */\n public simulateLatency(milliseconds: number) {\n // transport may still be resolving (constructor's attach() imports the default one)\n this._onTransportReady.then(\n (transport) => applySimulatedLatency(transport, milliseconds),\n () => {/* missing-transport error already surfaces via attach()/listen() */},\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 // Boot sequence shared by listen() and serverless(): user `beforeListen`\n // hook, database boot, framework router defaults (auth/db endpoints), then\n // the express app. serverless() needs this too \u2014 its exported server still\n // serves the express callback and auth/database routes.\n private async _bootServices(): Promise<void> {\n const { beforeListen, database, express } = this.options;\n\n // boot runs after module evaluation, so the env var wins over\n // simulateLatency() calls made at the top level of user code\n const envLatency = parseLatencyEnv();\n if (envLatency !== undefined) { this.simulateLatency(envLatency); }\n\n if (beforeListen) { await beforeListen(); }\n if (database) { await database.boot(); }\n\n await this._applyRouterDefaults();\n\n if (express) {\n await this._onTransportReady;\n if (this.transport.getExpressApp) {\n const expressApp = await this.transport.getExpressApp();\n await express(expressApp);\n }\n }\n }\n\n // Extend the user's router with framework-contributed endpoints. An explicit\n // `auth` option wins over the database's `applyRouterDefaults` so a non-DB\n // setup can still auto-mount @colyseus/auth, and a DB user can opt out.\n private async _applyRouterDefaults(): Promise<void> {\n const { auth, database } = this.options;\n const wantsAuth = auth !== undefined && auth !== false;\n const wantsDatabase = auth === undefined && !!database?.applyRouterDefaults;\n if (!wantsAuth && !wantsDatabase) { return; }\n\n // Boot an empty router if the user didn't pass `routes` \u2014 the framework's\n // default routes get layered on later in the `transport.listen()` callback.\n this.router ??= createRouter({}) as unknown as Routes;\n\n if (wantsAuth) {\n const authMod: any = await dynamicImport('@colyseus/auth').catch(() => undefined);\n const endpointsFn = authMod?.auth?.endpoints ?? authMod?.default?.auth?.endpoints;\n if (typeof endpointsFn === 'function') {\n this.router = (this.router as Router).extend(endpointsFn(auth)) as Routes;\n }\n } else {\n const updated = await database!.applyRouterDefaults!(this.router as Router);\n if (updated) { this.router = updated as Routes; }\n }\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\nlet _originalRoomOnMessage: typeof Room.prototype['_onMessage'] | null = null;\n\n/**\n * Applies both halves of the round-trip latency simulation: delays the\n * transport's outgoing messages and the Room's incoming message handling.\n *\n * Prefer `server.simulateLatency()` \u2014 this is the shared core behind it, the\n * dev-mode `defineServer()` object and the `COLYSEUS_LATENCY` env var, where\n * no `Server` instance (or transport, yet) may exist.\n */\nexport function applySimulatedLatency(transport: Transport | undefined, 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 transport?.simulateLatency(halfwayMS);\n\n if (_originalRoomOnMessage == null) {\n _originalRoomOnMessage = Room.prototype['_onMessage'];\n }\n\n const originalOnMessage = _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 * Reads the `COLYSEUS_LATENCY` environment variable (round-trip milliseconds).\n * Returns `undefined` when absent or empty; a non-numeric value warns and is ignored.\n */\nexport function parseLatencyEnv(): number | undefined {\n const value = process.env.COLYSEUS_LATENCY;\n if (value === undefined || value === '') { return undefined; }\n\n const milliseconds = Number(value);\n if (isNaN(milliseconds)) {\n logger.warn(`\uD83D\uDCF6\uFE0F\u2757 Ignoring COLYSEUS_LATENCY: expected a number of milliseconds, got ${JSON.stringify(value)}.`);\n return undefined;\n }\n return milliseconds;\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 // the vite plugin registers the transport before importing user code\n simulateLatency: (milliseconds: number) => applySimulatedLatency(getTransport(), milliseconds),\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"],
5
+ "mappings": "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",
6
6
  "names": ["gracefullyShutdown", "greet", "err"]
7
7
  }
package/build/index.cjs CHANGED
@@ -66,6 +66,7 @@ __export(index_exports, {
66
66
  TimedEventException: () => import_RoomExceptions.TimedEventException,
67
67
  TimestepException: () => import_RoomExceptions.TimestepException,
68
68
  Transport: () => import_Transport.Transport,
69
+ applySimulatedLatency: () => import_Server.applySimulatedLatency,
69
70
  attachToTestRoom: () => import_RoomPlugin.attachToTestRoom,
70
71
  basicAuth: () => import_router.basicAuth,
71
72
  compileSanitizer: () => import_InputBuffer.compileSanitizer,
@@ -97,6 +98,7 @@ __export(index_exports, {
97
98
  isDevMode: () => import_DevMode.isDevMode,
98
99
  logger: () => import_Logger.logger,
99
100
  matchMaker: () => matchMaker,
101
+ parseLatencyEnv: () => import_Server.parseLatencyEnv,
100
102
  registerRoomDefinitions: () => import_Server.registerRoomDefinitions,
101
103
  requestFromIPC: () => import_IPC.requestFromIPC,
102
104
  runBeforeUpgrade: () => import_Transport.runBeforeUpgrade,
@@ -177,6 +179,7 @@ var import_Logger = require("./Logger.cjs");
177
179
  TimedEventException,
178
180
  TimestepException,
179
181
  Transport,
182
+ applySimulatedLatency,
180
183
  attachToTestRoom,
181
184
  basicAuth,
182
185
  compileSanitizer,
@@ -208,6 +211,7 @@ var import_Logger = require("./Logger.cjs");
208
211
  isDevMode,
209
212
  logger,
210
213
  matchMaker,
214
+ parseLatencyEnv,
211
215
  registerRoomDefinitions,
212
216
  requestFromIPC,
213
217
  runBeforeUpgrade,