@colyseus/tools 0.16.13 → 0.16.15

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
@@ -166,7 +166,6 @@ async function getTransport(options) {
166
166
  }
167
167
  if (app) {
168
168
  app.use((0, import_cors.default)({ origin: true, credentials: true }));
169
- app.use(import_express.default.json());
170
169
  if (options.initializeExpress) {
171
170
  await options.initializeExpress(app);
172
171
  }
@@ -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\nconst BunWebSockets = import('@colyseus/bun-websockets'); BunWebSockets.catch(() => {});\nconst RedisDriver = import('@colyseus/redis-driver'); RedisDriver.catch(() => {});\nconst RedisPresence = import('@colyseus/redis-presence'); 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 const module = await RedisDriver;\n serverOptions.driver = new module.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 const module = await RedisPresence;\n serverOptions.presence = new module.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 // @ts-ignore\n if (typeof Bun !== \"undefined\") {\n // @colyseus/bun-websockets\n BunWebSockets.catch(() => {\n logger.warn(\"\");\n logger.warn(\"\u274C could not initialize BunWebSockets.\");\n logger.warn(\"\uD83D\uDC49 npm install --save @colyseus/bun-websockets\");\n logger.warn(\"\");\n })\n const module = await BunWebSockets;\n options.initializeTransport = (options: any) => new module.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\nconst BunWebSockets = import('@colyseus/bun-websockets'); BunWebSockets.catch(() => {});\nconst RedisDriver = import('@colyseus/redis-driver'); RedisDriver.catch(() => {});\nconst RedisPresence = import('@colyseus/redis-presence'); 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 const module = await RedisDriver;\n serverOptions.driver = new module.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 const module = await RedisPresence;\n serverOptions.presence = new module.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 // @ts-ignore\n if (typeof Bun !== \"undefined\") {\n // @colyseus/bun-websockets\n BunWebSockets.catch(() => {\n logger.warn(\"\");\n logger.warn(\"\u274C could not initialize BunWebSockets.\");\n logger.warn(\"\uD83D\uDC49 npm install --save @colyseus/bun-websockets\");\n logger.warn(\"\");\n })\n const module = await BunWebSockets;\n options.initializeTransport = (options: any) => new module.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 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": ["os", "module", "options", "express", "http", "cors", "net", "fs"]
7
7
  }
package/build/index.mjs CHANGED
@@ -133,7 +133,6 @@ async function getTransport(options) {
133
133
  }
134
134
  if (app) {
135
135
  app.use(cors({ origin: true, credentials: true }));
136
- app.use(express.json());
137
136
  if (options.initializeExpress) {
138
137
  await options.initializeExpress(app);
139
138
  }
@@ -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\nconst BunWebSockets = import('@colyseus/bun-websockets'); BunWebSockets.catch(() => {});\nconst RedisDriver = import('@colyseus/redis-driver'); RedisDriver.catch(() => {});\nconst RedisPresence = import('@colyseus/redis-presence'); 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 const module = await RedisDriver;\n serverOptions.driver = new module.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 const module = await RedisPresence;\n serverOptions.presence = new module.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 // @ts-ignore\n if (typeof Bun !== \"undefined\") {\n // @colyseus/bun-websockets\n BunWebSockets.catch(() => {\n logger.warn(\"\");\n logger.warn(\"\u274C could not initialize BunWebSockets.\");\n logger.warn(\"\uD83D\uDC49 npm install --save @colyseus/bun-websockets\");\n logger.warn(\"\");\n })\n const module = await BunWebSockets;\n options.initializeTransport = (options: any) => new module.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\nconst BunWebSockets = import('@colyseus/bun-websockets'); BunWebSockets.catch(() => {});\nconst RedisDriver = import('@colyseus/redis-driver'); RedisDriver.catch(() => {});\nconst RedisPresence = import('@colyseus/redis-presence'); 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 const module = await RedisDriver;\n serverOptions.driver = new module.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 const module = await RedisPresence;\n serverOptions.presence = new module.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 // @ts-ignore\n if (typeof Bun !== \"undefined\") {\n // @colyseus/bun-websockets\n BunWebSockets.catch(() => {\n logger.warn(\"\");\n logger.warn(\"\u274C could not initialize BunWebSockets.\");\n logger.warn(\"\uD83D\uDC49 npm install --save @colyseus/bun-websockets\");\n logger.warn(\"\");\n })\n const module = await BunWebSockets;\n options.initializeTransport = (options: any) => new module.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 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.13",
3
+ "version": "0.16.15",
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,12 +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/bun-websockets": "^0.16.4",
54
- "@colyseus/core": "^0.16.19",
55
- "@colyseus/redis-presence": "^0.16.4",
56
- "@colyseus/uwebsockets-transport": "^0.16.9",
53
+ "@colyseus/core": "^0.16.24",
57
54
  "@colyseus/redis-driver": "^0.16.1",
58
- "@colyseus/ws-transport": "^0.16.5"
55
+ "@colyseus/ws-transport": "^0.16.5",
56
+ "@colyseus/redis-presence": "^0.16.4",
57
+ "@colyseus/bun-websockets": "^0.16.5",
58
+ "@colyseus/uwebsockets-transport": "^0.16.10"
59
59
  },
60
60
  "peerDependencies": {
61
61
  "@colyseus/core": "0.16.x",
package/report-stats.js CHANGED
@@ -3,12 +3,18 @@
3
3
  const fs = require('fs');
4
4
  const net = require('net');
5
5
  const pm2 = require('pm2');
6
+ const dotenv = require('dotenv');
6
7
 
7
8
  const COLYSEUS_CLOUD_URL = `${process.env.ENDPOINT}/vultr/stats`;
8
9
 
9
10
  const FAILED_ATTEMPS_FILE = "/var/tmp/pm2-stats-attempts.txt";
10
11
  const FETCH_TIMEOUT = 30000;
11
12
 
13
+ // load environment variables (Colyseus Cloud environment variables)
14
+ if (process.env.APP_ROOT_PATH) {
15
+ dotenv.config({ path: `${process.env.APP_ROOT_PATH}/.env.cloud` });
16
+ }
17
+
12
18
  async function retryFailedAttempts() {
13
19
  /**
14
20
  * Retry cached failed attempts
@@ -127,6 +133,9 @@ pm2.Client.executeRemote('getMonitorData', {}, async function(err, list) {
127
133
  version: 1,
128
134
  ip,
129
135
  time: new Date(),
136
+ statuses: {
137
+ driver: await checkDriverIsAccessible()
138
+ },
130
139
  aggregate,
131
140
  apps,
132
141
  };
@@ -165,6 +174,11 @@ pm2.Client.executeRemote('getMonitorData', {}, async function(err, list) {
165
174
  }
166
175
  });
167
176
 
177
+ /**
178
+ * Check if a socket file is active
179
+ * @param {string} sockFilePath - The path to the socket file
180
+ * @returns {Promise<boolean>} true if the socket file is active, false otherwise
181
+ */
168
182
  function checkSocketIsActive(sockFilePath) {
169
183
  return new Promise((resolve, _) => {
170
184
  const client = net.createConnection({ path: sockFilePath, timeout: 5000 })
@@ -176,3 +190,45 @@ function checkSocketIsActive(sockFilePath) {
176
190
  .on('timeout', () => resolve(false));
177
191
  });
178
192
  }
193
+
194
+ /**
195
+ * Check if the driver is accessible
196
+ * @returns {Promise<boolean | string>} returns true if the driver is accessible, false otherwise or the error message
197
+ */
198
+ async function checkDriverIsAccessible() {
199
+ try {
200
+ if (process.env.REDIS_URI) {
201
+ const url = new URL(process.env.REDIS_URI);
202
+ return await isPortOpen({
203
+ host: url.hostname,
204
+ port: url.port
205
+ });
206
+ } else {
207
+ return true;
208
+ }
209
+ } catch (e) {
210
+ return e.message || false;
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Check if a port is open
216
+ * @param {{ host: string, port: number, timeout?: number }} options - The options to check
217
+ * @returns {Promise<boolean | string>}
218
+ */
219
+ function isPortOpen({ host, port, timeout = 2000 }) {
220
+ return new Promise((resolve) => {
221
+ const socket = new net.Socket();
222
+ const onError = (value) => {
223
+ socket.destroy();
224
+ resolve(value || false);
225
+ };
226
+ socket.setTimeout(timeout);
227
+ socket.once("error", (e) => onError(e.message));
228
+ socket.once("timeout", () => onError("timeout"));
229
+ socket.connect(port, host, () => {
230
+ socket.end(); // immediately close
231
+ resolve(true);
232
+ });
233
+ });
234
+ }