@colyseus/tools 0.16.8 → 0.16.10

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/index.js CHANGED
@@ -42,7 +42,13 @@ var import_express = __toESM(require("express"));
42
42
  var import_core = require("@colyseus/core");
43
43
  var import_ws_transport = require("@colyseus/ws-transport");
44
44
  let BunWebSockets = void 0;
45
- import("@colyseus/bun-websockets").then((module2) => BunWebSockets = module2).catch(() => {
45
+ import("@colyseus/bun-websockets").then((module2) => BunWebSockets = module2.BunWebSockets).catch(() => {
46
+ });
47
+ let RedisDriver;
48
+ import("@colyseus/redis-driver").then((module2) => RedisDriver = module2.RedisDriver).catch(() => {
49
+ });
50
+ let RedisPresence;
51
+ import("@colyseus/redis-presence").then((module2) => RedisPresence = module2.RedisPresence).catch(() => {
46
52
  });
47
53
  const ALLOWED_KEYS = {
48
54
  "displayLogs": "boolean",
@@ -102,11 +108,10 @@ async function buildServerFromOptions(options, port) {
102
108
  if (process.env.COLYSEUS_CLOUD !== void 0) {
103
109
  const useRedisConfig = import_os.default.cpus().length > 1 || process.env.REDIS_URI !== void 0;
104
110
  if (!serverOptions.driver && useRedisConfig) {
105
- let RedisDriver = void 0;
106
111
  try {
107
- RedisDriver = require("@colyseus/redis-driver").RedisDriver;
108
112
  serverOptions.driver = new RedisDriver(process.env.REDIS_URI);
109
113
  } catch (e) {
114
+ console.error(e);
110
115
  import_core.logger.warn("");
111
116
  import_core.logger.warn("\u274C could not initialize RedisDriver.");
112
117
  import_core.logger.warn("\u{1F449} npm install --save @colyseus/redis-driver");
@@ -114,11 +119,10 @@ async function buildServerFromOptions(options, port) {
114
119
  }
115
120
  }
116
121
  if (!serverOptions.presence && useRedisConfig) {
117
- let RedisPresence = void 0;
118
122
  try {
119
- RedisPresence = require("@colyseus/redis-presence").RedisPresence;
120
123
  serverOptions.presence = new RedisPresence(process.env.REDIS_URI);
121
124
  } catch (e) {
125
+ console.error(e);
122
126
  import_core.logger.warn("");
123
127
  import_core.logger.warn("\u274C could not initialize RedisPresence.");
124
128
  import_core.logger.warn("\u{1F449} npm install --save @colyseus/redis-presence");
@@ -140,7 +144,7 @@ async function getTransport(options) {
140
144
  let transport;
141
145
  if (!options.initializeTransport) {
142
146
  if (BunWebSockets !== void 0) {
143
- options.initializeTransport = (options2) => new BunWebSockets.BunWebSockets(options2);
147
+ options.initializeTransport = (options2) => new BunWebSockets(options2);
144
148
  } else {
145
149
  options.initializeTransport = (options2) => new import_ws_transport.WebSocketTransport(options2);
146
150
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/index.ts"],
4
- "sourcesContent": ["import './loadenv.js';\nimport os from 'os';\nimport fs from \"fs\";\nimport net from \"net\";\nimport http from 'http';\nimport cors from 'cors';\nimport express from 'express';\nimport { logger, Server, ServerOptions, Transport, matchMaker } from '@colyseus/core';\nimport { WebSocketTransport } from '@colyseus/ws-transport';\n\nlet BunWebSockets: any = undefined;\n\n// @ts-ignore\nimport('@colyseus/bun-websockets')\n .then((module) => BunWebSockets = module)\n .catch(() => { });\n\nexport interface ConfigOptions {\n options?: ServerOptions,\n displayLogs?: boolean,\n getId?: () => string,\n initializeTransport?: (options: any) => Transport,\n initializeExpress?: (app: express.Express) => void,\n initializeGameServer?: (app: Server) => void,\n beforeListen?: () => void,\n}\n\nconst ALLOWED_KEYS: { [key in keyof ConfigOptions]: string } = {\n 'displayLogs': \"boolean\",\n 'options': \"object\",\n 'getId': \"function\",\n 'initializeTransport': \"function\",\n 'initializeExpress': \"function\",\n 'initializeGameServer': \"function\",\n 'beforeListen': \"function\"\n};\n\nexport default function (options: ConfigOptions) {\n for (const option in options) {\n if (!ALLOWED_KEYS[option]) {\n throw new Error(`\u274C Invalid option '${option}'. Allowed options are: ${Object.keys(ALLOWED_KEYS).join(\", \")}`);\n }\n if(options[option] !== undefined && typeof(options[option]) !== ALLOWED_KEYS[option]) {\n throw new Error(`\u274C Invalid type for ${option}: please provide a ${ALLOWED_KEYS[option]} value.`);\n }\n }\n\n return options;\n}\n\n/**\n * Listen on your development environment\n * @param options Application options\n * @param port Port number to bind Colyseus + Express\n */\nexport async function listen(\n options: ConfigOptions | Server,\n port: number = Number(process.env.PORT || 2567),\n) {\n // Force 2567 port on Colyseus Cloud\n if (process.env.COLYSEUS_CLOUD !== undefined) {\n port = 2567;\n }\n\n //\n // Handling multiple processes\n // Use NODE_APP_INSTANCE to play nicely with pm2\n //\n const processNumber = Number(process.env.NODE_APP_INSTANCE || \"0\");\n port += processNumber;\n\n let gameServer: Server;\n let displayLogs = true;\n\n if (options instanceof Server) {\n gameServer = options;\n\n } else {\n gameServer = await buildServerFromOptions(options, port);\n displayLogs = options.displayLogs;\n\n await options.initializeGameServer?.(gameServer);\n await matchMaker.onReady;\n await options.beforeListen?.();\n }\n\n if (process.env.COLYSEUS_CLOUD !== undefined) {\n // listening on socket\n const socketPath: any = `/run/colyseus/${port}.sock`;\n\n // check if .sock file is active\n // (fixes \"ADDRINUSE\" issue when restarting the server)\n await checkInactiveSocketFile(socketPath);\n\n await gameServer.listen(socketPath);\n\n } else {\n // listening on port\n await gameServer.listen(port);\n }\n\n // notify process manager (production)\n if (typeof(process.send) === \"function\") {\n process.send('ready');\n }\n\n if (displayLogs) {\n logger.info(`\u2694\uFE0F Listening on http://localhost:${port}`);\n }\n\n return gameServer;\n}\n\nasync function buildServerFromOptions(options: ConfigOptions, port: number) {\n const serverOptions = options.options || {};\n options.displayLogs = options.displayLogs ?? true;\n\n // automatically configure for production under Colyseus Cloud\n if (process.env.COLYSEUS_CLOUD !== undefined) {\n\n // special configuration is required when using multiple processes\n const useRedisConfig = (os.cpus().length > 1) || (process.env.REDIS_URI !== undefined);\n\n if (!serverOptions.driver && useRedisConfig) {\n let RedisDriver: any = undefined;\n try {\n RedisDriver = require('@colyseus/redis-driver').RedisDriver;\n serverOptions.driver = new RedisDriver(process.env.REDIS_URI);\n } catch (e) {\n logger.warn(\"\");\n logger.warn(\"\u274C could not initialize RedisDriver.\");\n logger.warn(\"\uD83D\uDC49 npm install --save @colyseus/redis-driver\");\n logger.warn(\"\");\n }\n }\n\n if (!serverOptions.presence && useRedisConfig) {\n let RedisPresence: any = undefined;\n try {\n RedisPresence = require('@colyseus/redis-presence').RedisPresence;\n serverOptions.presence = new RedisPresence(process.env.REDIS_URI);\n } catch (e) {\n logger.warn(\"\");\n logger.warn(\"\u274C could not initialize RedisPresence.\");\n logger.warn(\"\uD83D\uDC49 npm install --save @colyseus/redis-presence\");\n logger.warn(\"\");\n }\n }\n\n if (useRedisConfig) {\n // force \"publicAddress\" when more than 1 process is available\n serverOptions.publicAddress = process.env.SUBDOMAIN + \".\" + process.env.SERVER_NAME;\n\n // nginx is responsible for forwarding /{port}/ to this process\n serverOptions.publicAddress += \"/\" + port;\n }\n }\n\n const transport = await getTransport(options);\n return new Server({\n ...serverOptions,\n transport,\n });\n}\n\nexport async function getTransport(options: ConfigOptions) {\n let transport: Transport;\n\n if (!options.initializeTransport) {\n if (BunWebSockets !== undefined) {\n // @colyseus/bun-websockets\n options.initializeTransport = (options: any) => new BunWebSockets.BunWebSockets(options);\n\n } else {\n // use WebSocketTransport by default\n options.initializeTransport = (options: any) => new WebSocketTransport(options);\n }\n }\n\n let app: express.Express | undefined = express();\n let server = http.createServer(app);\n\n transport = await options.initializeTransport({ server, app });\n\n //\n // TODO: refactor me!\n // BunWebSockets: There's no need to instantiate \"app\" and \"server\" above\n //\n if (transport['expressApp']) {\n app = transport['expressApp'];\n }\n\n if (app) {\n // Enable CORS\n app.use(cors({ origin: true, credentials: true, }));\n\n // Enable JSON parsing.\n app.use(express.json());\n\n if (options.initializeExpress) {\n await options.initializeExpress(app);\n }\n\n // health check for load balancers\n app.get(\"/__healthcheck\", (req, res) => {\n res.status(200).end();\n });\n\n if (options.displayLogs) {\n logger.info(\"\u2705 Express initialized\");\n }\n }\n\n return transport;\n}\n\n/**\n * Check if a socket file is active and remove it if it's not.\n */\nfunction checkInactiveSocketFile(sockFilePath: string) {\n return new Promise((resolve, reject) => {\n const client = net.createConnection({ path: sockFilePath })\n .on('connect', () => {\n // socket file is active, close the connection\n client.end();\n throw new Error(`EADDRINUSE: Already listening on '${sockFilePath}'`);\n })\n .on('error', () => {\n // socket file is inactive, remove it\n fs.unlink(sockFilePath, () => resolve(true));\n });\n });\n}"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import './loadenv.js';\nimport os from 'os';\nimport fs from \"fs\";\nimport net from \"net\";\nimport http from 'http';\nimport cors from 'cors';\nimport express from 'express';\nimport { logger, Server, ServerOptions, Transport, matchMaker } from '@colyseus/core';\nimport { WebSocketTransport } from '@colyseus/ws-transport';\n\nimport { type BunWebSockets as TBunWebSockets } from '@colyseus/bun-websockets';\nimport { type RedisDriver as TRedisDriver } from '@colyseus/redis-driver';\nimport { type RedisPresence as TRedisPresence } from '@colyseus/redis-presence';\n\nlet BunWebSockets: typeof TBunWebSockets = undefined;\nimport('@colyseus/bun-websockets').then((module) => BunWebSockets = module.BunWebSockets).catch(() => { });\n\nlet RedisDriver: typeof TRedisDriver;\nimport('@colyseus/redis-driver').then((module) => RedisDriver = module.RedisDriver).catch(() => { });\n\nlet RedisPresence: typeof TRedisPresence;\nimport('@colyseus/redis-presence').then((module) => RedisPresence = module.RedisPresence).catch(() => { });\n\nexport interface ConfigOptions {\n options?: ServerOptions,\n displayLogs?: boolean,\n getId?: () => string,\n initializeTransport?: (options: any) => Transport,\n initializeExpress?: (app: express.Express) => void,\n initializeGameServer?: (app: Server) => void,\n beforeListen?: () => void,\n}\n\nconst ALLOWED_KEYS: { [key in keyof ConfigOptions]: string } = {\n 'displayLogs': \"boolean\",\n 'options': \"object\",\n 'getId': \"function\",\n 'initializeTransport': \"function\",\n 'initializeExpress': \"function\",\n 'initializeGameServer': \"function\",\n 'beforeListen': \"function\"\n};\n\nexport default function (options: ConfigOptions) {\n for (const option in options) {\n if (!ALLOWED_KEYS[option]) {\n throw new Error(`\u274C Invalid option '${option}'. Allowed options are: ${Object.keys(ALLOWED_KEYS).join(\", \")}`);\n }\n if(options[option] !== undefined && typeof(options[option]) !== ALLOWED_KEYS[option]) {\n throw new Error(`\u274C Invalid type for ${option}: please provide a ${ALLOWED_KEYS[option]} value.`);\n }\n }\n\n return options;\n}\n\n/**\n * Listen on your development environment\n * @param options Application options\n * @param port Port number to bind Colyseus + Express\n */\nexport async function listen(\n options: ConfigOptions | Server,\n port: number = Number(process.env.PORT || 2567),\n) {\n // Force 2567 port on Colyseus Cloud\n if (process.env.COLYSEUS_CLOUD !== undefined) {\n port = 2567;\n }\n\n //\n // Handling multiple processes\n // Use NODE_APP_INSTANCE to play nicely with pm2\n //\n const processNumber = Number(process.env.NODE_APP_INSTANCE || \"0\");\n port += processNumber;\n\n let gameServer: Server;\n let displayLogs = true;\n\n if (options instanceof Server) {\n gameServer = options;\n\n } else {\n gameServer = await buildServerFromOptions(options, port);\n displayLogs = options.displayLogs;\n\n await options.initializeGameServer?.(gameServer);\n await matchMaker.onReady;\n await options.beforeListen?.();\n }\n\n if (process.env.COLYSEUS_CLOUD !== undefined) {\n // listening on socket\n const socketPath: any = `/run/colyseus/${port}.sock`;\n\n // check if .sock file is active\n // (fixes \"ADDRINUSE\" issue when restarting the server)\n await checkInactiveSocketFile(socketPath);\n\n await gameServer.listen(socketPath);\n\n } else {\n // listening on port\n await gameServer.listen(port);\n }\n\n // notify process manager (production)\n if (typeof(process.send) === \"function\") {\n process.send('ready');\n }\n\n if (displayLogs) {\n logger.info(`\u2694\uFE0F Listening on http://localhost:${port}`);\n }\n\n return gameServer;\n}\n\nasync function buildServerFromOptions(options: ConfigOptions, port: number) {\n const serverOptions = options.options || {};\n options.displayLogs = options.displayLogs ?? true;\n\n // automatically configure for production under Colyseus Cloud\n if (process.env.COLYSEUS_CLOUD !== undefined) {\n\n // special configuration is required when using multiple processes\n const useRedisConfig = (os.cpus().length > 1) || (process.env.REDIS_URI !== undefined);\n\n if (!serverOptions.driver && useRedisConfig) {\n try {\n serverOptions.driver = new RedisDriver(process.env.REDIS_URI);\n } catch (e) {\n console.error(e);\n logger.warn(\"\");\n logger.warn(\"\u274C could not initialize RedisDriver.\");\n logger.warn(\"\uD83D\uDC49 npm install --save @colyseus/redis-driver\");\n logger.warn(\"\");\n }\n }\n\n if (!serverOptions.presence && useRedisConfig) {\n try {\n serverOptions.presence = new RedisPresence(process.env.REDIS_URI);\n } catch (e) {\n console.error(e);\n logger.warn(\"\");\n logger.warn(\"\u274C could not initialize RedisPresence.\");\n logger.warn(\"\uD83D\uDC49 npm install --save @colyseus/redis-presence\");\n logger.warn(\"\");\n }\n }\n\n if (useRedisConfig) {\n // force \"publicAddress\" when more than 1 process is available\n serverOptions.publicAddress = process.env.SUBDOMAIN + \".\" + process.env.SERVER_NAME;\n\n // nginx is responsible for forwarding /{port}/ to this process\n serverOptions.publicAddress += \"/\" + port;\n }\n }\n\n const transport = await getTransport(options);\n return new Server({\n ...serverOptions,\n transport,\n });\n}\n\nexport async function getTransport(options: ConfigOptions) {\n let transport: Transport;\n\n if (!options.initializeTransport) {\n if (BunWebSockets !== undefined) {\n // @colyseus/bun-websockets\n options.initializeTransport = (options: any) => new BunWebSockets(options);\n\n } else {\n // use WebSocketTransport by default\n options.initializeTransport = (options: any) => new WebSocketTransport(options);\n }\n }\n\n let app: express.Express | undefined = express();\n let server = http.createServer(app);\n\n transport = await options.initializeTransport({ server, app });\n\n //\n // TODO: refactor me!\n // BunWebSockets: There's no need to instantiate \"app\" and \"server\" above\n //\n if (transport['expressApp']) {\n app = transport['expressApp'];\n }\n\n if (app) {\n // Enable CORS\n app.use(cors({ origin: true, credentials: true, }));\n\n // Enable JSON parsing.\n app.use(express.json());\n\n if (options.initializeExpress) {\n await options.initializeExpress(app);\n }\n\n // health check for load balancers\n app.get(\"/__healthcheck\", (req, res) => {\n res.status(200).end();\n });\n\n if (options.displayLogs) {\n logger.info(\"\u2705 Express initialized\");\n }\n }\n\n return transport;\n}\n\n/**\n * Check if a socket file is active and remove it if it's not.\n */\nfunction checkInactiveSocketFile(sockFilePath: string) {\n return new Promise((resolve, reject) => {\n const client = net.createConnection({ path: sockFilePath })\n .on('connect', () => {\n // socket file is active, close the connection\n client.end();\n throw new Error(`EADDRINUSE: Already listening on '${sockFilePath}'`);\n })\n .on('error', () => {\n // socket file is inactive, remove it\n fs.unlink(sockFilePath, () => resolve(true));\n });\n });\n}"],
5
+ "mappings": "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",
6
6
  "names": ["module", "os", "options", "express", "http", "cors", "net", "fs"]
7
7
  }
package/build/index.mjs CHANGED
@@ -1,10 +1,3 @@
1
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
- }) : x)(function(x) {
4
- if (typeof require !== "undefined") return require.apply(this, arguments);
5
- throw Error('Dynamic require of "' + x + '" is not supported');
6
- });
7
-
8
1
  // packages/tools/src/index.ts
9
2
  import "./loadenv.mjs";
10
3
  import os from "os";
@@ -16,7 +9,13 @@ import express from "express";
16
9
  import { logger, Server, matchMaker } from "@colyseus/core";
17
10
  import { WebSocketTransport } from "@colyseus/ws-transport";
18
11
  var BunWebSockets = void 0;
19
- import("@colyseus/bun-websockets").then((module) => BunWebSockets = module).catch(() => {
12
+ import("@colyseus/bun-websockets").then((module) => BunWebSockets = module.BunWebSockets).catch(() => {
13
+ });
14
+ var RedisDriver;
15
+ import("@colyseus/redis-driver").then((module) => RedisDriver = module.RedisDriver).catch(() => {
16
+ });
17
+ var RedisPresence;
18
+ import("@colyseus/redis-presence").then((module) => RedisPresence = module.RedisPresence).catch(() => {
20
19
  });
21
20
  var ALLOWED_KEYS = {
22
21
  "displayLogs": "boolean",
@@ -76,11 +75,10 @@ async function buildServerFromOptions(options, port) {
76
75
  if (process.env.COLYSEUS_CLOUD !== void 0) {
77
76
  const useRedisConfig = os.cpus().length > 1 || process.env.REDIS_URI !== void 0;
78
77
  if (!serverOptions.driver && useRedisConfig) {
79
- let RedisDriver = void 0;
80
78
  try {
81
- RedisDriver = __require("@colyseus/redis-driver").RedisDriver;
82
79
  serverOptions.driver = new RedisDriver(process.env.REDIS_URI);
83
80
  } catch (e) {
81
+ console.error(e);
84
82
  logger.warn("");
85
83
  logger.warn("\u274C could not initialize RedisDriver.");
86
84
  logger.warn("\u{1F449} npm install --save @colyseus/redis-driver");
@@ -88,11 +86,10 @@ async function buildServerFromOptions(options, port) {
88
86
  }
89
87
  }
90
88
  if (!serverOptions.presence && useRedisConfig) {
91
- let RedisPresence = void 0;
92
89
  try {
93
- RedisPresence = __require("@colyseus/redis-presence").RedisPresence;
94
90
  serverOptions.presence = new RedisPresence(process.env.REDIS_URI);
95
91
  } catch (e) {
92
+ console.error(e);
96
93
  logger.warn("");
97
94
  logger.warn("\u274C could not initialize RedisPresence.");
98
95
  logger.warn("\u{1F449} npm install --save @colyseus/redis-presence");
@@ -114,7 +111,7 @@ async function getTransport(options) {
114
111
  let transport;
115
112
  if (!options.initializeTransport) {
116
113
  if (BunWebSockets !== void 0) {
117
- options.initializeTransport = (options2) => new BunWebSockets.BunWebSockets(options2);
114
+ options.initializeTransport = (options2) => new BunWebSockets(options2);
118
115
  } else {
119
116
  options.initializeTransport = (options2) => new WebSocketTransport(options2);
120
117
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/index.ts"],
4
- "sourcesContent": ["import './loadenv.js';\nimport os from 'os';\nimport fs from \"fs\";\nimport net from \"net\";\nimport http from 'http';\nimport cors from 'cors';\nimport express from 'express';\nimport { logger, Server, ServerOptions, Transport, matchMaker } from '@colyseus/core';\nimport { WebSocketTransport } from '@colyseus/ws-transport';\n\nlet BunWebSockets: any = undefined;\n\n// @ts-ignore\nimport('@colyseus/bun-websockets')\n .then((module) => BunWebSockets = module)\n .catch(() => { });\n\nexport interface ConfigOptions {\n options?: ServerOptions,\n displayLogs?: boolean,\n getId?: () => string,\n initializeTransport?: (options: any) => Transport,\n initializeExpress?: (app: express.Express) => void,\n initializeGameServer?: (app: Server) => void,\n beforeListen?: () => void,\n}\n\nconst ALLOWED_KEYS: { [key in keyof ConfigOptions]: string } = {\n 'displayLogs': \"boolean\",\n 'options': \"object\",\n 'getId': \"function\",\n 'initializeTransport': \"function\",\n 'initializeExpress': \"function\",\n 'initializeGameServer': \"function\",\n 'beforeListen': \"function\"\n};\n\nexport default function (options: ConfigOptions) {\n for (const option in options) {\n if (!ALLOWED_KEYS[option]) {\n throw new Error(`\u274C Invalid option '${option}'. Allowed options are: ${Object.keys(ALLOWED_KEYS).join(\", \")}`);\n }\n if(options[option] !== undefined && typeof(options[option]) !== ALLOWED_KEYS[option]) {\n throw new Error(`\u274C Invalid type for ${option}: please provide a ${ALLOWED_KEYS[option]} value.`);\n }\n }\n\n return options;\n}\n\n/**\n * Listen on your development environment\n * @param options Application options\n * @param port Port number to bind Colyseus + Express\n */\nexport async function listen(\n options: ConfigOptions | Server,\n port: number = Number(process.env.PORT || 2567),\n) {\n // Force 2567 port on Colyseus Cloud\n if (process.env.COLYSEUS_CLOUD !== undefined) {\n port = 2567;\n }\n\n //\n // Handling multiple processes\n // Use NODE_APP_INSTANCE to play nicely with pm2\n //\n const processNumber = Number(process.env.NODE_APP_INSTANCE || \"0\");\n port += processNumber;\n\n let gameServer: Server;\n let displayLogs = true;\n\n if (options instanceof Server) {\n gameServer = options;\n\n } else {\n gameServer = await buildServerFromOptions(options, port);\n displayLogs = options.displayLogs;\n\n await options.initializeGameServer?.(gameServer);\n await matchMaker.onReady;\n await options.beforeListen?.();\n }\n\n if (process.env.COLYSEUS_CLOUD !== undefined) {\n // listening on socket\n const socketPath: any = `/run/colyseus/${port}.sock`;\n\n // check if .sock file is active\n // (fixes \"ADDRINUSE\" issue when restarting the server)\n await checkInactiveSocketFile(socketPath);\n\n await gameServer.listen(socketPath);\n\n } else {\n // listening on port\n await gameServer.listen(port);\n }\n\n // notify process manager (production)\n if (typeof(process.send) === \"function\") {\n process.send('ready');\n }\n\n if (displayLogs) {\n logger.info(`\u2694\uFE0F Listening on http://localhost:${port}`);\n }\n\n return gameServer;\n}\n\nasync function buildServerFromOptions(options: ConfigOptions, port: number) {\n const serverOptions = options.options || {};\n options.displayLogs = options.displayLogs ?? true;\n\n // automatically configure for production under Colyseus Cloud\n if (process.env.COLYSEUS_CLOUD !== undefined) {\n\n // special configuration is required when using multiple processes\n const useRedisConfig = (os.cpus().length > 1) || (process.env.REDIS_URI !== undefined);\n\n if (!serverOptions.driver && useRedisConfig) {\n let RedisDriver: any = undefined;\n try {\n RedisDriver = require('@colyseus/redis-driver').RedisDriver;\n serverOptions.driver = new RedisDriver(process.env.REDIS_URI);\n } catch (e) {\n logger.warn(\"\");\n logger.warn(\"\u274C could not initialize RedisDriver.\");\n logger.warn(\"\uD83D\uDC49 npm install --save @colyseus/redis-driver\");\n logger.warn(\"\");\n }\n }\n\n if (!serverOptions.presence && useRedisConfig) {\n let RedisPresence: any = undefined;\n try {\n RedisPresence = require('@colyseus/redis-presence').RedisPresence;\n serverOptions.presence = new RedisPresence(process.env.REDIS_URI);\n } catch (e) {\n logger.warn(\"\");\n logger.warn(\"\u274C could not initialize RedisPresence.\");\n logger.warn(\"\uD83D\uDC49 npm install --save @colyseus/redis-presence\");\n logger.warn(\"\");\n }\n }\n\n if (useRedisConfig) {\n // force \"publicAddress\" when more than 1 process is available\n serverOptions.publicAddress = process.env.SUBDOMAIN + \".\" + process.env.SERVER_NAME;\n\n // nginx is responsible for forwarding /{port}/ to this process\n serverOptions.publicAddress += \"/\" + port;\n }\n }\n\n const transport = await getTransport(options);\n return new Server({\n ...serverOptions,\n transport,\n });\n}\n\nexport async function getTransport(options: ConfigOptions) {\n let transport: Transport;\n\n if (!options.initializeTransport) {\n if (BunWebSockets !== undefined) {\n // @colyseus/bun-websockets\n options.initializeTransport = (options: any) => new BunWebSockets.BunWebSockets(options);\n\n } else {\n // use WebSocketTransport by default\n options.initializeTransport = (options: any) => new WebSocketTransport(options);\n }\n }\n\n let app: express.Express | undefined = express();\n let server = http.createServer(app);\n\n transport = await options.initializeTransport({ server, app });\n\n //\n // TODO: refactor me!\n // BunWebSockets: There's no need to instantiate \"app\" and \"server\" above\n //\n if (transport['expressApp']) {\n app = transport['expressApp'];\n }\n\n if (app) {\n // Enable CORS\n app.use(cors({ origin: true, credentials: true, }));\n\n // Enable JSON parsing.\n app.use(express.json());\n\n if (options.initializeExpress) {\n await options.initializeExpress(app);\n }\n\n // health check for load balancers\n app.get(\"/__healthcheck\", (req, res) => {\n res.status(200).end();\n });\n\n if (options.displayLogs) {\n logger.info(\"\u2705 Express initialized\");\n }\n }\n\n return transport;\n}\n\n/**\n * Check if a socket file is active and remove it if it's not.\n */\nfunction checkInactiveSocketFile(sockFilePath: string) {\n return new Promise((resolve, reject) => {\n const client = net.createConnection({ path: sockFilePath })\n .on('connect', () => {\n // socket file is active, close the connection\n client.end();\n throw new Error(`EADDRINUSE: Already listening on '${sockFilePath}'`);\n })\n .on('error', () => {\n // socket file is inactive, remove it\n fs.unlink(sockFilePath, () => resolve(true));\n });\n });\n}"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import './loadenv.js';\nimport os from 'os';\nimport fs from \"fs\";\nimport net from \"net\";\nimport http from 'http';\nimport cors from 'cors';\nimport express from 'express';\nimport { logger, Server, ServerOptions, Transport, matchMaker } from '@colyseus/core';\nimport { WebSocketTransport } from '@colyseus/ws-transport';\n\nimport { type BunWebSockets as TBunWebSockets } from '@colyseus/bun-websockets';\nimport { type RedisDriver as TRedisDriver } from '@colyseus/redis-driver';\nimport { type RedisPresence as TRedisPresence } from '@colyseus/redis-presence';\n\nlet BunWebSockets: typeof TBunWebSockets = undefined;\nimport('@colyseus/bun-websockets').then((module) => BunWebSockets = module.BunWebSockets).catch(() => { });\n\nlet RedisDriver: typeof TRedisDriver;\nimport('@colyseus/redis-driver').then((module) => RedisDriver = module.RedisDriver).catch(() => { });\n\nlet RedisPresence: typeof TRedisPresence;\nimport('@colyseus/redis-presence').then((module) => RedisPresence = module.RedisPresence).catch(() => { });\n\nexport interface ConfigOptions {\n options?: ServerOptions,\n displayLogs?: boolean,\n getId?: () => string,\n initializeTransport?: (options: any) => Transport,\n initializeExpress?: (app: express.Express) => void,\n initializeGameServer?: (app: Server) => void,\n beforeListen?: () => void,\n}\n\nconst ALLOWED_KEYS: { [key in keyof ConfigOptions]: string } = {\n 'displayLogs': \"boolean\",\n 'options': \"object\",\n 'getId': \"function\",\n 'initializeTransport': \"function\",\n 'initializeExpress': \"function\",\n 'initializeGameServer': \"function\",\n 'beforeListen': \"function\"\n};\n\nexport default function (options: ConfigOptions) {\n for (const option in options) {\n if (!ALLOWED_KEYS[option]) {\n throw new Error(`\u274C Invalid option '${option}'. Allowed options are: ${Object.keys(ALLOWED_KEYS).join(\", \")}`);\n }\n if(options[option] !== undefined && typeof(options[option]) !== ALLOWED_KEYS[option]) {\n throw new Error(`\u274C Invalid type for ${option}: please provide a ${ALLOWED_KEYS[option]} value.`);\n }\n }\n\n return options;\n}\n\n/**\n * Listen on your development environment\n * @param options Application options\n * @param port Port number to bind Colyseus + Express\n */\nexport async function listen(\n options: ConfigOptions | Server,\n port: number = Number(process.env.PORT || 2567),\n) {\n // Force 2567 port on Colyseus Cloud\n if (process.env.COLYSEUS_CLOUD !== undefined) {\n port = 2567;\n }\n\n //\n // Handling multiple processes\n // Use NODE_APP_INSTANCE to play nicely with pm2\n //\n const processNumber = Number(process.env.NODE_APP_INSTANCE || \"0\");\n port += processNumber;\n\n let gameServer: Server;\n let displayLogs = true;\n\n if (options instanceof Server) {\n gameServer = options;\n\n } else {\n gameServer = await buildServerFromOptions(options, port);\n displayLogs = options.displayLogs;\n\n await options.initializeGameServer?.(gameServer);\n await matchMaker.onReady;\n await options.beforeListen?.();\n }\n\n if (process.env.COLYSEUS_CLOUD !== undefined) {\n // listening on socket\n const socketPath: any = `/run/colyseus/${port}.sock`;\n\n // check if .sock file is active\n // (fixes \"ADDRINUSE\" issue when restarting the server)\n await checkInactiveSocketFile(socketPath);\n\n await gameServer.listen(socketPath);\n\n } else {\n // listening on port\n await gameServer.listen(port);\n }\n\n // notify process manager (production)\n if (typeof(process.send) === \"function\") {\n process.send('ready');\n }\n\n if (displayLogs) {\n logger.info(`\u2694\uFE0F Listening on http://localhost:${port}`);\n }\n\n return gameServer;\n}\n\nasync function buildServerFromOptions(options: ConfigOptions, port: number) {\n const serverOptions = options.options || {};\n options.displayLogs = options.displayLogs ?? true;\n\n // automatically configure for production under Colyseus Cloud\n if (process.env.COLYSEUS_CLOUD !== undefined) {\n\n // special configuration is required when using multiple processes\n const useRedisConfig = (os.cpus().length > 1) || (process.env.REDIS_URI !== undefined);\n\n if (!serverOptions.driver && useRedisConfig) {\n try {\n serverOptions.driver = new RedisDriver(process.env.REDIS_URI);\n } catch (e) {\n console.error(e);\n logger.warn(\"\");\n logger.warn(\"\u274C could not initialize RedisDriver.\");\n logger.warn(\"\uD83D\uDC49 npm install --save @colyseus/redis-driver\");\n logger.warn(\"\");\n }\n }\n\n if (!serverOptions.presence && useRedisConfig) {\n try {\n serverOptions.presence = new RedisPresence(process.env.REDIS_URI);\n } catch (e) {\n console.error(e);\n logger.warn(\"\");\n logger.warn(\"\u274C could not initialize RedisPresence.\");\n logger.warn(\"\uD83D\uDC49 npm install --save @colyseus/redis-presence\");\n logger.warn(\"\");\n }\n }\n\n if (useRedisConfig) {\n // force \"publicAddress\" when more than 1 process is available\n serverOptions.publicAddress = process.env.SUBDOMAIN + \".\" + process.env.SERVER_NAME;\n\n // nginx is responsible for forwarding /{port}/ to this process\n serverOptions.publicAddress += \"/\" + port;\n }\n }\n\n const transport = await getTransport(options);\n return new Server({\n ...serverOptions,\n transport,\n });\n}\n\nexport async function getTransport(options: ConfigOptions) {\n let transport: Transport;\n\n if (!options.initializeTransport) {\n if (BunWebSockets !== undefined) {\n // @colyseus/bun-websockets\n options.initializeTransport = (options: any) => new BunWebSockets(options);\n\n } else {\n // use WebSocketTransport by default\n options.initializeTransport = (options: any) => new WebSocketTransport(options);\n }\n }\n\n let app: express.Express | undefined = express();\n let server = http.createServer(app);\n\n transport = await options.initializeTransport({ server, app });\n\n //\n // TODO: refactor me!\n // BunWebSockets: There's no need to instantiate \"app\" and \"server\" above\n //\n if (transport['expressApp']) {\n app = transport['expressApp'];\n }\n\n if (app) {\n // Enable CORS\n app.use(cors({ origin: true, credentials: true, }));\n\n // Enable JSON parsing.\n app.use(express.json());\n\n if (options.initializeExpress) {\n await options.initializeExpress(app);\n }\n\n // health check for load balancers\n app.get(\"/__healthcheck\", (req, res) => {\n res.status(200).end();\n });\n\n if (options.displayLogs) {\n logger.info(\"\u2705 Express initialized\");\n }\n }\n\n return transport;\n}\n\n/**\n * Check if a socket file is active and remove it if it's not.\n */\nfunction checkInactiveSocketFile(sockFilePath: string) {\n return new Promise((resolve, reject) => {\n const client = net.createConnection({ path: sockFilePath })\n .on('connect', () => {\n // socket file is active, close the connection\n client.end();\n throw new Error(`EADDRINUSE: Already listening on '${sockFilePath}'`);\n })\n .on('error', () => {\n // socket file is inactive, remove it\n fs.unlink(sockFilePath, () => resolve(true));\n });\n });\n}"],
5
+ "mappings": "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",
6
6
  "names": ["options"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@colyseus/tools",
3
- "version": "0.16.8",
3
+ "version": "0.16.10",
4
4
  "description": "Simplify the development and production settings for your Colyseus project.",
5
5
  "input": "./src/index.ts",
6
6
  "main": "./build/index.js",
@@ -50,9 +50,12 @@
50
50
  "@types/cors": "^2.8.10",
51
51
  "@types/dotenv": "^8.2.0",
52
52
  "uwebsockets-express": "^1.1.10",
53
- "@colyseus/core": "^0.16.17",
53
+ "@colyseus/bun-websockets": "^0.16.4",
54
+ "@colyseus/redis-driver": "^0.16.1",
54
55
  "@colyseus/ws-transport": "^0.16.5",
55
- "@colyseus/uwebsockets-transport": "^0.16.9"
56
+ "@colyseus/core": "^0.16.18",
57
+ "@colyseus/uwebsockets-transport": "^0.16.9",
58
+ "@colyseus/redis-presence": "^0.16.3"
56
59
  },
57
60
  "peerDependencies": {
58
61
  "@colyseus/core": "0.16.x",
package/system-boot.js CHANGED
@@ -6,57 +6,63 @@ const { execSync } = require('child_process');
6
6
 
7
7
  const NGINX_LIMITS_CONFIG_FILE = '/etc/nginx/colyseus_limits.conf';
8
8
  const LIMITS_CONF_FILE = '/etc/security/limits.d/colyseus.conf';
9
- const SYSCTL_FILE = '/etc/sysctl.d/local.conf'
9
+ const SYSCTL_FILE = '/etc/sysctl.d/local.conf';
10
10
 
11
- const MAX_CCU_PER_CPU = 8000;
11
+ const MAX_CCU_PER_CPU = 5000;
12
12
 
13
13
  /**
14
14
  * System-wide limits configuration for high-CCU environments
15
15
  * @param {Object} options
16
16
  * @param {number} options.maxCCUPerCPU - Maximum concurrent users per CPU core
17
- * @param {number} options.connectionsMultiplier - Multiplier for worker connections (default: 3)
18
- * @param {number} options.fileDescriptorMultiplier - Multiplier for max file descriptors (default: 4)
17
+ * @param {number} options.connectionsMultiplier - Multiplier for worker connections (default: 2)
18
+ * @param {number} options.fileDescriptorMultiplier - Multiplier for max file descriptors (default: 6)
19
19
  */
20
20
  function configureSystemLimits(options = {}) {
21
21
  const {
22
- connectionsMultiplier = 3,
23
- fileDescriptorMultiplier = 4
22
+ connectionsMultiplier = 2,
23
+ fileDescriptorMultiplier = 6
24
24
  } = options;
25
25
 
26
26
  const numCPU = os.cpus().length;
27
27
  const maxCCU = numCPU * MAX_CCU_PER_CPU;
28
28
 
29
29
  // Calculate limits
30
- const workerConnections = maxCCU * connectionsMultiplier;
31
- const maxFileDescriptors = maxCCU * fileDescriptorMultiplier;
30
+ const workerConnections = Math.min(maxCCU * connectionsMultiplier, 65535); // Cap at max_socket_backlog (65535)
31
+ let maxFileDescriptors = maxCCU * fileDescriptorMultiplier;
32
+ const workerRlimitNofile = Math.ceil(workerConnections * 3.5); // Assume 3.5 file descriptors per connection for safety
32
33
 
33
- // Nginx-specific calculations
34
- const workerRlimitNofile = (workerConnections / numCPU) * 2;
34
+ // Calculate total file descriptors needed
35
+ const totalFileDescriptors = workerRlimitNofile * numCPU;
35
36
 
36
- // Validation checks
37
- if (workerConnections > 65535) {
38
- console.warn(`Warning: worker_connections (${workerConnections}) exceeds typical max_socket_backlog (65535)`);
37
+ if (totalFileDescriptors > maxFileDescriptors) {
38
+ console.warn(`Warning: Total file descriptors (${totalFileDescriptors}) exceeds maxFileDescriptors (${maxFileDescriptors}). Increasing maxFileDescriptors.`);
39
+ maxFileDescriptors = totalFileDescriptors * 1.5; // 50% safety margin
39
40
  }
40
41
 
41
- if (maxFileDescriptors > 1048576) {
42
- console.warn(`Warning: Very high file descriptor limit (${maxFileDescriptors}). Verify system capabilities.`);
42
+ // Check memory (rough estimate: 150 KB per connection)
43
+ const estimatedMemoryMB = (workerConnections * numCPU * 150) / 1024;
44
+ const totalMemoryMB = Number(execSync('free -m | awk \'/Mem:/ {print $2}\'').toString().trim());
45
+ if (estimatedMemoryMB > totalMemoryMB * 0.8) {
46
+ console.warn(`Warning: Estimated memory usage (${estimatedMemoryMB} MB) exceeds 80% of available memory (${totalMemoryMB} MB). Consider reducing MAX_CCU_PER_CPU.`);
43
47
  }
44
48
 
45
49
  if (process.argv.includes('--dry-run')) {
46
50
  console.log({
51
+ numCPU,
47
52
  maxCCU,
48
53
  workerConnections,
49
54
  maxFileDescriptors,
50
- workerRlimitNofile
51
- })
55
+ workerRlimitNofile,
56
+ totalFileDescriptors,
57
+ estimatedMemoryMB
58
+ });
52
59
  process.exit();
53
60
  }
54
61
 
55
62
  // Configuration updates
56
63
  try {
57
64
  // Update Nginx limits
58
- if (fs.existsSync(NGINX_LIMITS_CONFIG_FILE)) {
59
- fs.writeFileSync(NGINX_LIMITS_CONFIG_FILE, `
65
+ fs.writeFileSync(NGINX_LIMITS_CONFIG_FILE, `
60
66
  worker_rlimit_nofile ${workerRlimitNofile};
61
67
  events {
62
68
  use epoll;
@@ -64,7 +70,6 @@ events {
64
70
  multi_accept on;
65
71
  }
66
72
  `);
67
- }
68
73
 
69
74
  // Update system-wide limits
70
75
  fs.writeFileSync(LIMITS_CONF_FILE, `
@@ -74,7 +79,7 @@ nginx soft nofile ${maxFileDescriptors}
74
79
  nginx hard nofile ${maxFileDescriptors}
75
80
  `);
76
81
 
77
- // Update sysctl with doubled file-max for safety margin
82
+ // Update sysctl
78
83
  fs.writeFileSync(SYSCTL_FILE, `
79
84
  # System-wide file descriptor limit
80
85
  fs.file-max = ${maxFileDescriptors * 2}
@@ -99,7 +104,11 @@ net.ipv4.tcp_keepalive_probes = 3
99
104
  `);
100
105
 
101
106
  // Apply sysctl changes
102
- execSync("sysctl -p", { stdio: 'inherit' });
107
+ execSync('sysctl -p', { stdio: 'inherit' });
108
+
109
+ // Reload systemd
110
+ execSync('systemctl daemon-reload', { stdio: 'inherit' });
111
+
103
112
  console.log(`System limits configured successfully for ${maxCCU} CCU (${MAX_CCU_PER_CPU}/CPU)`);
104
113
 
105
114
  } catch (error) {