@colyseus/core 0.17.44 → 0.18.1
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/MatchMaker.cjs +22 -13
- package/build/MatchMaker.cjs.map +2 -2
- package/build/MatchMaker.d.ts +10 -0
- package/build/MatchMaker.mjs +22 -14
- package/build/MatchMaker.mjs.map +2 -2
- package/build/Protocol.cjs +102 -37
- package/build/Protocol.cjs.map +2 -2
- package/build/Protocol.d.ts +33 -2
- package/build/Protocol.mjs +102 -37
- package/build/Protocol.mjs.map +2 -2
- package/build/Rewind.cjs +385 -0
- package/build/Rewind.cjs.map +7 -0
- package/build/Rewind.d.ts +291 -0
- package/build/Rewind.mjs +359 -0
- package/build/Rewind.mjs.map +7 -0
- package/build/Room.cjs +425 -149
- package/build/Room.cjs.map +3 -3
- package/build/Room.d.ts +314 -27
- package/build/Room.mjs +434 -152
- package/build/Room.mjs.map +3 -3
- package/build/RoomMessages.cjs +265 -0
- package/build/RoomMessages.cjs.map +7 -0
- package/build/RoomMessages.d.ts +49 -0
- package/build/RoomMessages.mjs +240 -0
- package/build/RoomMessages.mjs.map +7 -0
- package/build/RoomPlugin.cjs +252 -0
- package/build/RoomPlugin.cjs.map +7 -0
- package/build/RoomPlugin.d.ts +271 -0
- package/build/RoomPlugin.mjs +220 -0
- package/build/RoomPlugin.mjs.map +7 -0
- package/build/Server.cjs +49 -11
- package/build/Server.cjs.map +2 -2
- package/build/Server.d.ts +26 -0
- package/build/Server.mjs +50 -12
- package/build/Server.mjs.map +2 -2
- package/build/Transport.cjs +64 -2
- package/build/Transport.cjs.map +2 -2
- package/build/Transport.d.ts +108 -5
- package/build/Transport.mjs +63 -2
- package/build/Transport.mjs.map +2 -2
- package/build/errors/RoomExceptions.cjs +7 -4
- package/build/errors/RoomExceptions.cjs.map +2 -2
- package/build/errors/RoomExceptions.d.ts +15 -3
- package/build/errors/RoomExceptions.mjs +5 -3
- package/build/errors/RoomExceptions.mjs.map +2 -2
- package/build/index.cjs +30 -4
- package/build/index.cjs.map +2 -2
- package/build/index.d.ts +9 -5
- package/build/index.mjs +29 -6
- package/build/index.mjs.map +2 -2
- package/build/input/InputBuffer.cjs +536 -0
- package/build/input/InputBuffer.cjs.map +7 -0
- package/build/input/InputBuffer.d.ts +226 -0
- package/build/input/InputBuffer.mjs +507 -0
- package/build/input/InputBuffer.mjs.map +7 -0
- package/build/input/RoomInput.cjs +306 -0
- package/build/input/RoomInput.cjs.map +7 -0
- package/build/input/RoomInput.d.ts +91 -0
- package/build/input/RoomInput.mjs +287 -0
- package/build/input/RoomInput.mjs.map +7 -0
- package/build/input/types.cjs +18 -0
- package/build/input/types.cjs.map +7 -0
- package/build/input/types.d.ts +476 -0
- package/build/input/types.mjs +0 -0
- package/build/input/types.mjs.map +7 -0
- package/build/internal.cjs +61 -0
- package/build/internal.cjs.map +7 -0
- package/build/internal.d.ts +9 -0
- package/build/internal.mjs +29 -0
- package/build/internal.mjs.map +7 -0
- package/build/matchmaker/LocalDriver/LocalDriver.cjs +13 -0
- package/build/matchmaker/LocalDriver/LocalDriver.cjs.map +2 -2
- package/build/matchmaker/LocalDriver/LocalDriver.d.ts +1 -0
- package/build/matchmaker/LocalDriver/LocalDriver.mjs +13 -0
- package/build/matchmaker/LocalDriver/LocalDriver.mjs.map +2 -2
- package/build/matchmaker/driver.cjs.map +1 -1
- package/build/matchmaker/driver.d.ts +12 -0
- package/build/matchmaker/driver.mjs.map +1 -1
- package/build/presence/LocalPresence.d.ts +1 -1
- package/build/rooms/LobbyRoom.cjs +8 -10
- package/build/rooms/LobbyRoom.cjs.map +2 -2
- package/build/rooms/LobbyRoom.d.ts +4 -3
- package/build/rooms/LobbyRoom.mjs +8 -10
- package/build/rooms/LobbyRoom.mjs.map +2 -2
- package/build/rooms/QueueRoom.cjs +1 -1
- package/build/rooms/QueueRoom.cjs.map +2 -2
- package/build/rooms/QueueRoom.mjs +1 -1
- package/build/rooms/QueueRoom.mjs.map +2 -2
- package/build/rooms/RelayRoom.cjs +12 -16
- package/build/rooms/RelayRoom.cjs.map +2 -2
- package/build/rooms/RelayRoom.d.ts +32 -11
- package/build/rooms/RelayRoom.mjs +10 -16
- package/build/rooms/RelayRoom.mjs.map +2 -2
- package/build/router/default_routes.cjs +4 -1
- package/build/router/default_routes.cjs.map +2 -2
- package/build/router/default_routes.mjs +4 -1
- package/build/router/default_routes.mjs.map +2 -2
- package/build/router/index.cjs +71 -5
- package/build/router/index.cjs.map +2 -2
- package/build/router/index.d.ts +30 -6
- package/build/router/index.mjs +72 -7
- package/build/router/index.mjs.map +3 -3
- package/build/serializer/NoneSerializer.cjs +2 -2
- package/build/serializer/NoneSerializer.cjs.map +2 -2
- package/build/serializer/NoneSerializer.d.ts +3 -3
- package/build/serializer/NoneSerializer.mjs +2 -2
- package/build/serializer/NoneSerializer.mjs.map +2 -2
- package/build/serializer/SchemaSerializer.cjs +44 -14
- package/build/serializer/SchemaSerializer.cjs.map +2 -2
- package/build/serializer/SchemaSerializer.d.ts +3 -3
- package/build/serializer/SchemaSerializer.mjs +46 -16
- package/build/serializer/SchemaSerializer.mjs.map +2 -2
- package/build/serializer/Serializer.cjs.map +1 -1
- package/build/serializer/Serializer.d.ts +12 -2
- package/build/utils/DevMode.cjs +14 -14
- package/build/utils/DevMode.cjs.map +2 -2
- package/build/utils/DevMode.d.ts +0 -2
- package/build/utils/DevMode.mjs +14 -12
- package/build/utils/DevMode.mjs.map +2 -2
- package/build/utils/UserSessionIndex.cjs +162 -0
- package/build/utils/UserSessionIndex.cjs.map +7 -0
- package/build/utils/UserSessionIndex.d.ts +166 -0
- package/build/utils/UserSessionIndex.mjs +130 -0
- package/build/utils/UserSessionIndex.mjs.map +7 -0
- package/build/utils/Utils.cjs +3 -0
- package/build/utils/Utils.cjs.map +2 -2
- package/build/utils/Utils.d.ts +8 -0
- package/build/utils/Utils.mjs +2 -0
- package/build/utils/Utils.mjs.map +2 -2
- package/package.json +19 -14
- package/src/MatchMaker.ts +47 -15
- package/src/Protocol.ts +130 -59
- package/src/Rewind.ts +572 -0
- package/src/Room.ts +649 -235
- package/src/RoomMessages.ts +342 -0
- package/src/RoomPlugin.ts +563 -0
- package/src/Server.ts +82 -15
- package/src/Transport.ts +172 -9
- package/src/errors/RoomExceptions.ts +18 -4
- package/src/index.ts +23 -3
- package/src/input/InputBuffer.ts +549 -0
- package/src/input/RoomInput.ts +337 -0
- package/src/input/types.ts +503 -0
- package/src/internal.ts +46 -0
- package/src/matchmaker/LocalDriver/LocalDriver.ts +10 -0
- package/src/matchmaker/driver.ts +13 -0
- package/src/rooms/LobbyRoom.ts +12 -8
- package/src/rooms/QueueRoom.ts +1 -1
- package/src/rooms/RelayRoom.ts +9 -15
- package/src/router/default_routes.ts +6 -1
- package/src/router/index.ts +117 -11
- package/src/serializer/NoneSerializer.ts +3 -3
- package/src/serializer/SchemaSerializer.ts +110 -19
- package/src/serializer/Serializer.ts +13 -2
- package/src/utils/DevMode.ts +18 -13
- package/src/utils/UserSessionIndex.ts +311 -0
- package/src/utils/Utils.ts +9 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/utils/Utils.ts"],
|
|
4
|
-
"sourcesContent": ["import { nanoid } from 'nanoid';\nimport { type RoomException, type RoomMethodName } from '../errors/RoomExceptions.ts';\n\nimport { debugAndPrintError, debugMatchMaking } from '../Debug.ts';\n\nexport type Type<T> = new (...args: any[]) => T;\nexport type MethodName<T> = string & {\n [K in keyof T]: T[K] extends (...args: any[]) => any ? K : never\n}[keyof T];\n\n/**\n * Utility type that extracts the return type of a method or the type of a property\n * from a given class/object type.\n *\n * - If the key is a method, returns the awaited return type of that method\n * - If the key is a property, returns the type of that property\n */\nexport type ExtractMethodOrPropertyType<\n TClass,\n TKey extends keyof TClass\n> = TClass[TKey] extends (...args: any[]) => infer R\n ? Awaited<R>\n : TClass[TKey];\n\n// remote room call timeouts\nexport const REMOTE_ROOM_SHORT_TIMEOUT = Number(process.env.COLYSEUS_PRESENCE_SHORT_TIMEOUT || 2000);\nexport const MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME = Number(process.env.COLYSEUS_MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME || 0.5);\n\nexport function generateId(length: number = 9) {\n return nanoid(length);\n}\n\nexport function getBearerToken(authHeader: string) {\n return (authHeader && authHeader.startsWith(\"Bearer \") && authHeader.substring(7, authHeader.length)) || undefined;\n}\n\n// nodemon sends SIGUSR2 before reloading\n// (https://github.com/remy/nodemon#controlling-shutdown-of-your-script)\n//\nconst signals: NodeJS.Signals[] = ['SIGINT', 'SIGTERM', 'SIGUSR2'];\n\nexport function registerGracefulShutdown(callback: (err?: Error) => void) {\n /**\n * Gracefully shutdown on uncaught errors\n */\n process.on('uncaughtException', (err) => {\n debugAndPrintError(err);\n callback(err);\n });\n\n signals.forEach((signal) =>\n process.once(signal, () => callback()));\n}\n\nexport function retry<T = any>(\n cb: Function,\n maxRetries: number = 3,\n errorWhiteList: any[] = [],\n retries: number = 0,\n) {\n return new Promise<T>((resolve, reject) => {\n cb()\n .then(resolve)\n .catch((e: any) => {\n if (\n errorWhiteList.indexOf(e.constructor) !== -1 &&\n retries++ < maxRetries\n ) {\n setTimeout(() => {\n debugMatchMaking(\"retrying due to error (error: %s, retries: %s, maxRetries: %s)\", e.message, retries, maxRetries);\n retry<T>(cb, maxRetries, errorWhiteList, retries).\n then(resolve).\n catch((e2) => reject(e2));\n }, Math.floor(Math.random() * Math.pow(2, retries) * 400));\n\n } else {\n reject(e);\n }\n });\n });\n}\n\nexport function spliceOne(arr: any[], index: number): boolean {\n // manually splice availableRooms array\n // http://jsperf.com/manual-splice\n if (index === -1 || index >= arr.length) {\n return false;\n }\n\n const len = arr.length - 1;\n for (let i = index; i < len; i++) {\n arr[i] = arr[i + 1];\n }\n\n arr.length = len;\n return true;\n}\n\nexport class Deferred<T = any> {\n public promise: Promise<T>;\n\n public resolve: Function;\n public reject: Function;\n\n constructor(promise?: Promise<T>) {\n this.promise = promise ?? new Promise<T>((resolve, reject) => {\n this.resolve = resolve;\n this.reject = reject;\n });\n }\n\n public then(onFulfilled?: (value: T) => any, onRejected?: (reason: any) => any) {\n return this.promise.then(onFulfilled, onRejected);\n }\n\n public catch(func: (value: any) => any) {\n return this.promise.catch(func);\n }\n\n static reject (reason?: any) {\n return new Deferred(Promise.reject(reason));\n }\n\n static resolve<T = any>(value?: T) {\n return new Deferred<T>(Promise.resolve(value));\n }\n\n}\n\nexport function merge(a: any, ...objs: any[]): any {\n for (let i = 0, len = objs.length; i < len; i++) {\n const b = objs[i];\n for (const key in b) {\n if (b.hasOwnProperty(key)) {\n a[key] = b[key];\n }\n }\n }\n return a;\n}\n\nexport function wrapTryCatch(\n method: Function,\n onError: (error: RoomException, methodName: RoomMethodName) => void,\n exceptionClass: Type<RoomException>,\n methodName: RoomMethodName,\n rethrow: boolean = false,\n ...additionalErrorArgs: any[]\n) {\n return (...args: any[]) => {\n try {\n const result = method(...args);\n if (typeof (result?.catch) === \"function\") {\n return result.catch((e: Error) => {\n onError(new exceptionClass(e, e.message, ...args, ...additionalErrorArgs), methodName);\n if (rethrow) { throw e; }\n });\n }\n return result;\n } catch (e: any) {\n onError(new exceptionClass(e, e.message, ...args, ...additionalErrorArgs), methodName);\n if (rethrow) { throw e; }\n }\n };\n}\n\n/**\n * Dynamically import a module using either require() or import()\n * based on the current module system (CJS vs ESM).\n *\n * This avoids double-loading packages when running in mixed ESM/CJS environments.\n * Errors are silently caught - await the promise and handle errors at usage site.\n */\nexport function dynamicImport<T = any>(moduleName: string): Promise<T> {\n // __dirname exists in CJS but not in ESM\n if (\n typeof __dirname !== 'undefined' &&\n // @ts-ignore\n typeof (Bun) === 'undefined' // prevent bun from loading CJS modules\n ) {\n // CJS context - use require()\n try {\n return Promise.resolve(require(moduleName));\n } catch (e: any) {\n // If the error is not a MODULE_NOT_FOUND error, reject with the error.\n if (e.code !== 'MODULE_NOT_FOUND') {\n return Promise.reject(e);\n }\n return Promise.resolve(undefined);\n }\n } else {\n // ESM context - use import()\n const promise = import(/* @vite-ignore */ moduleName);\n promise.catch(() => {}); // prevent unhandled rejection warnings\n return promise;\n }\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAAuB;AACvB,4BAAwD;AAExD,mBAAqD;AAsB9C,IAAM,4BAA4B,OAAO,QAAQ,IAAI,mCAAmC,GAAI;AAC5F,IAAM,uCAAuC,OAAO,QAAQ,IAAI,iDAAiD,GAAG;
|
|
4
|
+
"sourcesContent": ["import { nanoid } from 'nanoid';\nimport { type RoomException, type RoomMethodName } from '../errors/RoomExceptions.ts';\n\nimport { debugAndPrintError, debugMatchMaking } from '../Debug.ts';\n\nexport type Type<T> = new (...args: any[]) => T;\nexport type MethodName<T> = string & {\n [K in keyof T]: T[K] extends (...args: any[]) => any ? K : never\n}[keyof T];\n\n/**\n * Utility type that extracts the return type of a method or the type of a property\n * from a given class/object type.\n *\n * - If the key is a method, returns the awaited return type of that method\n * - If the key is a property, returns the type of that property\n */\nexport type ExtractMethodOrPropertyType<\n TClass,\n TKey extends keyof TClass\n> = TClass[TKey] extends (...args: any[]) => infer R\n ? Awaited<R>\n : TClass[TKey];\n\n// remote room call timeouts\nexport const REMOTE_ROOM_SHORT_TIMEOUT = Number(process.env.COLYSEUS_PRESENCE_SHORT_TIMEOUT || 2000);\nexport const MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME = Number(process.env.COLYSEUS_MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME || 0.5);\n\n/**\n * The well-known `Symbol.metadata` (TC39 decorator metadata), falling back to the\n * registered symbol on runtimes that don't expose it globally yet. `@colyseus/schema`\n * stores each class's field metadata under this key \u2014 read it via\n * `instance.constructor[$METADATA]`. Shared so every reader resolves the IDENTICAL\n * symbol (InputBuffer's field-name walk, Rewind's field-index lookup).\n */\nexport const $METADATA: symbol = (Symbol as { metadata?: symbol }).metadata ?? Symbol.for(\"Symbol.metadata\");\n\nexport function generateId(length: number = 9) {\n return nanoid(length);\n}\n\nexport function getBearerToken(authHeader: string) {\n return (authHeader && authHeader.startsWith(\"Bearer \") && authHeader.substring(7, authHeader.length)) || undefined;\n}\n\n// nodemon sends SIGUSR2 before reloading\n// (https://github.com/remy/nodemon#controlling-shutdown-of-your-script)\n//\nconst signals: NodeJS.Signals[] = ['SIGINT', 'SIGTERM', 'SIGUSR2'];\n\nexport function registerGracefulShutdown(callback: (err?: Error) => void) {\n /**\n * Gracefully shutdown on uncaught errors\n */\n process.on('uncaughtException', (err) => {\n debugAndPrintError(err);\n callback(err);\n });\n\n signals.forEach((signal) =>\n process.once(signal, () => callback()));\n}\n\nexport function retry<T = any>(\n cb: Function,\n maxRetries: number = 3,\n errorWhiteList: any[] = [],\n retries: number = 0,\n) {\n return new Promise<T>((resolve, reject) => {\n cb()\n .then(resolve)\n .catch((e: any) => {\n if (\n errorWhiteList.indexOf(e.constructor) !== -1 &&\n retries++ < maxRetries\n ) {\n setTimeout(() => {\n debugMatchMaking(\"retrying due to error (error: %s, retries: %s, maxRetries: %s)\", e.message, retries, maxRetries);\n retry<T>(cb, maxRetries, errorWhiteList, retries).\n then(resolve).\n catch((e2) => reject(e2));\n }, Math.floor(Math.random() * Math.pow(2, retries) * 400));\n\n } else {\n reject(e);\n }\n });\n });\n}\n\nexport function spliceOne(arr: any[], index: number): boolean {\n // manually splice availableRooms array\n // http://jsperf.com/manual-splice\n if (index === -1 || index >= arr.length) {\n return false;\n }\n\n const len = arr.length - 1;\n for (let i = index; i < len; i++) {\n arr[i] = arr[i + 1];\n }\n\n arr.length = len;\n return true;\n}\n\nexport class Deferred<T = any> {\n public promise: Promise<T>;\n\n public resolve: Function;\n public reject: Function;\n\n constructor(promise?: Promise<T>) {\n this.promise = promise ?? new Promise<T>((resolve, reject) => {\n this.resolve = resolve;\n this.reject = reject;\n });\n }\n\n public then(onFulfilled?: (value: T) => any, onRejected?: (reason: any) => any) {\n return this.promise.then(onFulfilled, onRejected);\n }\n\n public catch(func: (value: any) => any) {\n return this.promise.catch(func);\n }\n\n static reject (reason?: any) {\n return new Deferred(Promise.reject(reason));\n }\n\n static resolve<T = any>(value?: T) {\n return new Deferred<T>(Promise.resolve(value));\n }\n\n}\n\nexport function merge(a: any, ...objs: any[]): any {\n for (let i = 0, len = objs.length; i < len; i++) {\n const b = objs[i];\n for (const key in b) {\n if (b.hasOwnProperty(key)) {\n a[key] = b[key];\n }\n }\n }\n return a;\n}\n\nexport function wrapTryCatch(\n method: Function,\n onError: (error: RoomException, methodName: RoomMethodName) => void,\n exceptionClass: Type<RoomException>,\n methodName: RoomMethodName,\n rethrow: boolean = false,\n ...additionalErrorArgs: any[]\n) {\n return (...args: any[]) => {\n try {\n const result = method(...args);\n if (typeof (result?.catch) === \"function\") {\n return result.catch((e: Error) => {\n onError(new exceptionClass(e, e.message, ...args, ...additionalErrorArgs), methodName);\n if (rethrow) { throw e; }\n });\n }\n return result;\n } catch (e: any) {\n onError(new exceptionClass(e, e.message, ...args, ...additionalErrorArgs), methodName);\n if (rethrow) { throw e; }\n }\n };\n}\n\n/**\n * Dynamically import a module using either require() or import()\n * based on the current module system (CJS vs ESM).\n *\n * This avoids double-loading packages when running in mixed ESM/CJS environments.\n * Errors are silently caught - await the promise and handle errors at usage site.\n */\nexport function dynamicImport<T = any>(moduleName: string): Promise<T> {\n // __dirname exists in CJS but not in ESM\n if (\n typeof __dirname !== 'undefined' &&\n // @ts-ignore\n typeof (Bun) === 'undefined' // prevent bun from loading CJS modules\n ) {\n // CJS context - use require()\n try {\n return Promise.resolve(require(moduleName));\n } catch (e: any) {\n // If the error is not a MODULE_NOT_FOUND error, reject with the error.\n if (e.code !== 'MODULE_NOT_FOUND') {\n return Promise.reject(e);\n }\n return Promise.resolve(undefined);\n }\n } else {\n // ESM context - use import()\n const promise = import(/* @vite-ignore */ moduleName);\n promise.catch(() => {}); // prevent unhandled rejection warnings\n return promise;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/build/utils/Utils.d.ts
CHANGED
|
@@ -13,6 +13,14 @@ export type MethodName<T> = string & {
|
|
|
13
13
|
export type ExtractMethodOrPropertyType<TClass, TKey extends keyof TClass> = TClass[TKey] extends (...args: any[]) => infer R ? Awaited<R> : TClass[TKey];
|
|
14
14
|
export declare const REMOTE_ROOM_SHORT_TIMEOUT: number;
|
|
15
15
|
export declare const MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME: number;
|
|
16
|
+
/**
|
|
17
|
+
* The well-known `Symbol.metadata` (TC39 decorator metadata), falling back to the
|
|
18
|
+
* registered symbol on runtimes that don't expose it globally yet. `@colyseus/schema`
|
|
19
|
+
* stores each class's field metadata under this key — read it via
|
|
20
|
+
* `instance.constructor[$METADATA]`. Shared so every reader resolves the IDENTICAL
|
|
21
|
+
* symbol (InputBuffer's field-name walk, Rewind's field-index lookup).
|
|
22
|
+
*/
|
|
23
|
+
export declare const $METADATA: symbol;
|
|
16
24
|
export declare function generateId(length?: number): string;
|
|
17
25
|
export declare function getBearerToken(authHeader: string): string;
|
|
18
26
|
export declare function registerGracefulShutdown(callback: (err?: Error) => void): void;
|
package/build/utils/Utils.mjs
CHANGED
|
@@ -11,6 +11,7 @@ import "../errors/RoomExceptions.mjs";
|
|
|
11
11
|
import { debugAndPrintError, debugMatchMaking } from "../Debug.mjs";
|
|
12
12
|
var REMOTE_ROOM_SHORT_TIMEOUT = Number(process.env.COLYSEUS_PRESENCE_SHORT_TIMEOUT || 2e3);
|
|
13
13
|
var MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME = Number(process.env.COLYSEUS_MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME || 0.5);
|
|
14
|
+
var $METADATA = Symbol.metadata ?? /* @__PURE__ */ Symbol.for("Symbol.metadata");
|
|
14
15
|
function generateId(length = 9) {
|
|
15
16
|
return nanoid(length);
|
|
16
17
|
}
|
|
@@ -124,6 +125,7 @@ function dynamicImport(moduleName) {
|
|
|
124
125
|
}
|
|
125
126
|
}
|
|
126
127
|
export {
|
|
128
|
+
$METADATA,
|
|
127
129
|
Deferred,
|
|
128
130
|
MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME,
|
|
129
131
|
REMOTE_ROOM_SHORT_TIMEOUT,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/utils/Utils.ts"],
|
|
4
|
-
"sourcesContent": ["import { nanoid } from 'nanoid';\nimport { type RoomException, type RoomMethodName } from '../errors/RoomExceptions.ts';\n\nimport { debugAndPrintError, debugMatchMaking } from '../Debug.ts';\n\nexport type Type<T> = new (...args: any[]) => T;\nexport type MethodName<T> = string & {\n [K in keyof T]: T[K] extends (...args: any[]) => any ? K : never\n}[keyof T];\n\n/**\n * Utility type that extracts the return type of a method or the type of a property\n * from a given class/object type.\n *\n * - If the key is a method, returns the awaited return type of that method\n * - If the key is a property, returns the type of that property\n */\nexport type ExtractMethodOrPropertyType<\n TClass,\n TKey extends keyof TClass\n> = TClass[TKey] extends (...args: any[]) => infer R\n ? Awaited<R>\n : TClass[TKey];\n\n// remote room call timeouts\nexport const REMOTE_ROOM_SHORT_TIMEOUT = Number(process.env.COLYSEUS_PRESENCE_SHORT_TIMEOUT || 2000);\nexport const MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME = Number(process.env.COLYSEUS_MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME || 0.5);\n\nexport function generateId(length: number = 9) {\n return nanoid(length);\n}\n\nexport function getBearerToken(authHeader: string) {\n return (authHeader && authHeader.startsWith(\"Bearer \") && authHeader.substring(7, authHeader.length)) || undefined;\n}\n\n// nodemon sends SIGUSR2 before reloading\n// (https://github.com/remy/nodemon#controlling-shutdown-of-your-script)\n//\nconst signals: NodeJS.Signals[] = ['SIGINT', 'SIGTERM', 'SIGUSR2'];\n\nexport function registerGracefulShutdown(callback: (err?: Error) => void) {\n /**\n * Gracefully shutdown on uncaught errors\n */\n process.on('uncaughtException', (err) => {\n debugAndPrintError(err);\n callback(err);\n });\n\n signals.forEach((signal) =>\n process.once(signal, () => callback()));\n}\n\nexport function retry<T = any>(\n cb: Function,\n maxRetries: number = 3,\n errorWhiteList: any[] = [],\n retries: number = 0,\n) {\n return new Promise<T>((resolve, reject) => {\n cb()\n .then(resolve)\n .catch((e: any) => {\n if (\n errorWhiteList.indexOf(e.constructor) !== -1 &&\n retries++ < maxRetries\n ) {\n setTimeout(() => {\n debugMatchMaking(\"retrying due to error (error: %s, retries: %s, maxRetries: %s)\", e.message, retries, maxRetries);\n retry<T>(cb, maxRetries, errorWhiteList, retries).\n then(resolve).\n catch((e2) => reject(e2));\n }, Math.floor(Math.random() * Math.pow(2, retries) * 400));\n\n } else {\n reject(e);\n }\n });\n });\n}\n\nexport function spliceOne(arr: any[], index: number): boolean {\n // manually splice availableRooms array\n // http://jsperf.com/manual-splice\n if (index === -1 || index >= arr.length) {\n return false;\n }\n\n const len = arr.length - 1;\n for (let i = index; i < len; i++) {\n arr[i] = arr[i + 1];\n }\n\n arr.length = len;\n return true;\n}\n\nexport class Deferred<T = any> {\n public promise: Promise<T>;\n\n public resolve: Function;\n public reject: Function;\n\n constructor(promise?: Promise<T>) {\n this.promise = promise ?? new Promise<T>((resolve, reject) => {\n this.resolve = resolve;\n this.reject = reject;\n });\n }\n\n public then(onFulfilled?: (value: T) => any, onRejected?: (reason: any) => any) {\n return this.promise.then(onFulfilled, onRejected);\n }\n\n public catch(func: (value: any) => any) {\n return this.promise.catch(func);\n }\n\n static reject (reason?: any) {\n return new Deferred(Promise.reject(reason));\n }\n\n static resolve<T = any>(value?: T) {\n return new Deferred<T>(Promise.resolve(value));\n }\n\n}\n\nexport function merge(a: any, ...objs: any[]): any {\n for (let i = 0, len = objs.length; i < len; i++) {\n const b = objs[i];\n for (const key in b) {\n if (b.hasOwnProperty(key)) {\n a[key] = b[key];\n }\n }\n }\n return a;\n}\n\nexport function wrapTryCatch(\n method: Function,\n onError: (error: RoomException, methodName: RoomMethodName) => void,\n exceptionClass: Type<RoomException>,\n methodName: RoomMethodName,\n rethrow: boolean = false,\n ...additionalErrorArgs: any[]\n) {\n return (...args: any[]) => {\n try {\n const result = method(...args);\n if (typeof (result?.catch) === \"function\") {\n return result.catch((e: Error) => {\n onError(new exceptionClass(e, e.message, ...args, ...additionalErrorArgs), methodName);\n if (rethrow) { throw e; }\n });\n }\n return result;\n } catch (e: any) {\n onError(new exceptionClass(e, e.message, ...args, ...additionalErrorArgs), methodName);\n if (rethrow) { throw e; }\n }\n };\n}\n\n/**\n * Dynamically import a module using either require() or import()\n * based on the current module system (CJS vs ESM).\n *\n * This avoids double-loading packages when running in mixed ESM/CJS environments.\n * Errors are silently caught - await the promise and handle errors at usage site.\n */\nexport function dynamicImport<T = any>(moduleName: string): Promise<T> {\n // __dirname exists in CJS but not in ESM\n if (\n typeof __dirname !== 'undefined' &&\n // @ts-ignore\n typeof (Bun) === 'undefined' // prevent bun from loading CJS modules\n ) {\n // CJS context - use require()\n try {\n return Promise.resolve(require(moduleName));\n } catch (e: any) {\n // If the error is not a MODULE_NOT_FOUND error, reject with the error.\n if (e.code !== 'MODULE_NOT_FOUND') {\n return Promise.reject(e);\n }\n return Promise.resolve(undefined);\n }\n } else {\n // ESM context - use import()\n const promise = import(/* @vite-ignore */ moduleName);\n promise.catch(() => {}); // prevent unhandled rejection warnings\n return promise;\n }\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;AAAA,SAAS,cAAc;AACvB,OAAwD;AAExD,SAAS,oBAAoB,wBAAwB;AAsB9C,IAAM,4BAA4B,OAAO,QAAQ,IAAI,mCAAmC,GAAI;AAC5F,IAAM,uCAAuC,OAAO,QAAQ,IAAI,iDAAiD,GAAG;
|
|
4
|
+
"sourcesContent": ["import { nanoid } from 'nanoid';\nimport { type RoomException, type RoomMethodName } from '../errors/RoomExceptions.ts';\n\nimport { debugAndPrintError, debugMatchMaking } from '../Debug.ts';\n\nexport type Type<T> = new (...args: any[]) => T;\nexport type MethodName<T> = string & {\n [K in keyof T]: T[K] extends (...args: any[]) => any ? K : never\n}[keyof T];\n\n/**\n * Utility type that extracts the return type of a method or the type of a property\n * from a given class/object type.\n *\n * - If the key is a method, returns the awaited return type of that method\n * - If the key is a property, returns the type of that property\n */\nexport type ExtractMethodOrPropertyType<\n TClass,\n TKey extends keyof TClass\n> = TClass[TKey] extends (...args: any[]) => infer R\n ? Awaited<R>\n : TClass[TKey];\n\n// remote room call timeouts\nexport const REMOTE_ROOM_SHORT_TIMEOUT = Number(process.env.COLYSEUS_PRESENCE_SHORT_TIMEOUT || 2000);\nexport const MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME = Number(process.env.COLYSEUS_MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME || 0.5);\n\n/**\n * The well-known `Symbol.metadata` (TC39 decorator metadata), falling back to the\n * registered symbol on runtimes that don't expose it globally yet. `@colyseus/schema`\n * stores each class's field metadata under this key \u2014 read it via\n * `instance.constructor[$METADATA]`. Shared so every reader resolves the IDENTICAL\n * symbol (InputBuffer's field-name walk, Rewind's field-index lookup).\n */\nexport const $METADATA: symbol = (Symbol as { metadata?: symbol }).metadata ?? Symbol.for(\"Symbol.metadata\");\n\nexport function generateId(length: number = 9) {\n return nanoid(length);\n}\n\nexport function getBearerToken(authHeader: string) {\n return (authHeader && authHeader.startsWith(\"Bearer \") && authHeader.substring(7, authHeader.length)) || undefined;\n}\n\n// nodemon sends SIGUSR2 before reloading\n// (https://github.com/remy/nodemon#controlling-shutdown-of-your-script)\n//\nconst signals: NodeJS.Signals[] = ['SIGINT', 'SIGTERM', 'SIGUSR2'];\n\nexport function registerGracefulShutdown(callback: (err?: Error) => void) {\n /**\n * Gracefully shutdown on uncaught errors\n */\n process.on('uncaughtException', (err) => {\n debugAndPrintError(err);\n callback(err);\n });\n\n signals.forEach((signal) =>\n process.once(signal, () => callback()));\n}\n\nexport function retry<T = any>(\n cb: Function,\n maxRetries: number = 3,\n errorWhiteList: any[] = [],\n retries: number = 0,\n) {\n return new Promise<T>((resolve, reject) => {\n cb()\n .then(resolve)\n .catch((e: any) => {\n if (\n errorWhiteList.indexOf(e.constructor) !== -1 &&\n retries++ < maxRetries\n ) {\n setTimeout(() => {\n debugMatchMaking(\"retrying due to error (error: %s, retries: %s, maxRetries: %s)\", e.message, retries, maxRetries);\n retry<T>(cb, maxRetries, errorWhiteList, retries).\n then(resolve).\n catch((e2) => reject(e2));\n }, Math.floor(Math.random() * Math.pow(2, retries) * 400));\n\n } else {\n reject(e);\n }\n });\n });\n}\n\nexport function spliceOne(arr: any[], index: number): boolean {\n // manually splice availableRooms array\n // http://jsperf.com/manual-splice\n if (index === -1 || index >= arr.length) {\n return false;\n }\n\n const len = arr.length - 1;\n for (let i = index; i < len; i++) {\n arr[i] = arr[i + 1];\n }\n\n arr.length = len;\n return true;\n}\n\nexport class Deferred<T = any> {\n public promise: Promise<T>;\n\n public resolve: Function;\n public reject: Function;\n\n constructor(promise?: Promise<T>) {\n this.promise = promise ?? new Promise<T>((resolve, reject) => {\n this.resolve = resolve;\n this.reject = reject;\n });\n }\n\n public then(onFulfilled?: (value: T) => any, onRejected?: (reason: any) => any) {\n return this.promise.then(onFulfilled, onRejected);\n }\n\n public catch(func: (value: any) => any) {\n return this.promise.catch(func);\n }\n\n static reject (reason?: any) {\n return new Deferred(Promise.reject(reason));\n }\n\n static resolve<T = any>(value?: T) {\n return new Deferred<T>(Promise.resolve(value));\n }\n\n}\n\nexport function merge(a: any, ...objs: any[]): any {\n for (let i = 0, len = objs.length; i < len; i++) {\n const b = objs[i];\n for (const key in b) {\n if (b.hasOwnProperty(key)) {\n a[key] = b[key];\n }\n }\n }\n return a;\n}\n\nexport function wrapTryCatch(\n method: Function,\n onError: (error: RoomException, methodName: RoomMethodName) => void,\n exceptionClass: Type<RoomException>,\n methodName: RoomMethodName,\n rethrow: boolean = false,\n ...additionalErrorArgs: any[]\n) {\n return (...args: any[]) => {\n try {\n const result = method(...args);\n if (typeof (result?.catch) === \"function\") {\n return result.catch((e: Error) => {\n onError(new exceptionClass(e, e.message, ...args, ...additionalErrorArgs), methodName);\n if (rethrow) { throw e; }\n });\n }\n return result;\n } catch (e: any) {\n onError(new exceptionClass(e, e.message, ...args, ...additionalErrorArgs), methodName);\n if (rethrow) { throw e; }\n }\n };\n}\n\n/**\n * Dynamically import a module using either require() or import()\n * based on the current module system (CJS vs ESM).\n *\n * This avoids double-loading packages when running in mixed ESM/CJS environments.\n * Errors are silently caught - await the promise and handle errors at usage site.\n */\nexport function dynamicImport<T = any>(moduleName: string): Promise<T> {\n // __dirname exists in CJS but not in ESM\n if (\n typeof __dirname !== 'undefined' &&\n // @ts-ignore\n typeof (Bun) === 'undefined' // prevent bun from loading CJS modules\n ) {\n // CJS context - use require()\n try {\n return Promise.resolve(require(moduleName));\n } catch (e: any) {\n // If the error is not a MODULE_NOT_FOUND error, reject with the error.\n if (e.code !== 'MODULE_NOT_FOUND') {\n return Promise.reject(e);\n }\n return Promise.resolve(undefined);\n }\n } else {\n // ESM context - use import()\n const promise = import(/* @vite-ignore */ moduleName);\n promise.catch(() => {}); // prevent unhandled rejection warnings\n return promise;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@colyseus/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.1",
|
|
4
4
|
"description": "Multiplayer Framework for Node.js.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"input": "./src/index.ts",
|
|
@@ -46,31 +46,35 @@
|
|
|
46
46
|
"node": ">= 22.x"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@colyseus/msgpackr": "^1.11.3",
|
|
50
49
|
"@colyseus/timer": "^2.0.0",
|
|
51
50
|
"@standard-schema/spec": "^1.0.0",
|
|
52
51
|
"debug": "^4.3.4",
|
|
52
|
+
"msgpackr": "^2.0.1",
|
|
53
53
|
"nanoid": "^3.3.11",
|
|
54
54
|
"@colyseus/better-call": "^1.3.1",
|
|
55
|
-
"@colyseus/greeting-banner": "^
|
|
56
|
-
"@colyseus/shared-types": "^0.
|
|
55
|
+
"@colyseus/greeting-banner": "^4.0.1",
|
|
56
|
+
"@colyseus/shared-types": "^0.18.1"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
|
-
"@colyseus/schema": "^
|
|
59
|
+
"@colyseus/schema": "^5.0.8",
|
|
60
60
|
"express": "^5.0.0",
|
|
61
|
-
"@colyseus/redis-
|
|
62
|
-
"@colyseus/redis-
|
|
63
|
-
"@colyseus/tools": "^0.
|
|
61
|
+
"@colyseus/redis-driver": "^0.18.1",
|
|
62
|
+
"@colyseus/redis-presence": "^0.18.1",
|
|
63
|
+
"@colyseus/tools": "^0.18.1"
|
|
64
64
|
},
|
|
65
65
|
"peerDependencies": {
|
|
66
|
-
"@colyseus/schema": "^
|
|
66
|
+
"@colyseus/schema": "^5.0.8",
|
|
67
67
|
"@pm2/io": "^6.1.0",
|
|
68
68
|
"express": "^4.16.0 || ^5.0.0",
|
|
69
69
|
"zod": "^4.1.12",
|
|
70
70
|
"@colyseus/better-call": "^1.3.1",
|
|
71
|
-
"@colyseus/ws-transport": "^0.
|
|
71
|
+
"@colyseus/ws-transport": "^0.18.1",
|
|
72
|
+
"@colyseus/auth": "^0.18.1"
|
|
72
73
|
},
|
|
73
74
|
"peerDependenciesMeta": {
|
|
75
|
+
"@colyseus/auth": {
|
|
76
|
+
"optional": true
|
|
77
|
+
},
|
|
74
78
|
"@colyseus/ws-transport": {
|
|
75
79
|
"optional": true
|
|
76
80
|
},
|
|
@@ -85,12 +89,13 @@
|
|
|
85
89
|
}
|
|
86
90
|
},
|
|
87
91
|
"optionalPeerDependencies": {
|
|
88
|
-
"@colyseus/tools": "0.
|
|
89
|
-
"@colyseus/redis-driver": "0.
|
|
90
|
-
"@colyseus/redis-presence": "0.
|
|
92
|
+
"@colyseus/tools": "0.18.x",
|
|
93
|
+
"@colyseus/redis-driver": "0.18.x",
|
|
94
|
+
"@colyseus/redis-presence": "0.18.x"
|
|
91
95
|
},
|
|
92
96
|
"publishConfig": {
|
|
93
|
-
"access": "public"
|
|
97
|
+
"access": "public",
|
|
98
|
+
"tag": "next"
|
|
94
99
|
},
|
|
95
100
|
"gitHead": "c45b410e99eadffff4b74e701339992e2faa15f8"
|
|
96
101
|
}
|
package/src/MatchMaker.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { EventEmitter } from 'events';
|
|
|
3
3
|
import { requestFromIPC, subscribeIPC, subscribeWithTimeout } from './IPC.ts';
|
|
4
4
|
|
|
5
5
|
import { type Type, Deferred, generateId, merge, retry, MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME, REMOTE_ROOM_SHORT_TIMEOUT, type MethodName, type ExtractMethodOrPropertyType } from './utils/Utils.ts';
|
|
6
|
-
import { isDevMode, cacheRoomHistory,
|
|
6
|
+
import { isDevMode, cacheRoomHistory, getRoomRestoreListKey, reloadFromCache } from './utils/DevMode.ts';
|
|
7
7
|
|
|
8
8
|
import { RegisteredHandler } from './matchmaker/RegisteredHandler.ts';
|
|
9
9
|
import { type OnCreateOptions, Room, RoomInternalState } from './Room.ts';
|
|
@@ -103,9 +103,6 @@ export async function setup(
|
|
|
103
103
|
|
|
104
104
|
stats.reset(false);
|
|
105
105
|
|
|
106
|
-
// devMode: try to retrieve previous processId
|
|
107
|
-
if (isDevMode) { processId = await getPreviousProcessId(); }
|
|
108
|
-
|
|
109
106
|
// ensure processId is set
|
|
110
107
|
if (!processId) { processId = generateId(); }
|
|
111
108
|
|
|
@@ -252,7 +249,7 @@ export async function join(roomName: string, clientOptions: ClientOptions = {},
|
|
|
252
249
|
}
|
|
253
250
|
|
|
254
251
|
return reserveSeatFor(room, clientOptions, authData);
|
|
255
|
-
});
|
|
252
|
+
}, 5, [SeatReservationError]);
|
|
256
253
|
}
|
|
257
254
|
|
|
258
255
|
/**
|
|
@@ -321,6 +318,20 @@ export async function query<T extends Room = any>(
|
|
|
321
318
|
return await driver.query<T>(conditions, sortOptions);
|
|
322
319
|
}
|
|
323
320
|
|
|
321
|
+
/**
|
|
322
|
+
* Batch-resolve room caches by roomId in a single backend round
|
|
323
|
+
* trip. Returns a Map keyed by roomId; missing roomIds are absent.
|
|
324
|
+
*
|
|
325
|
+
* Hot paths (per-join uniqueness checks, by-user reverse-index
|
|
326
|
+
* lookups) reach for this instead of `query({})` — the latter scans
|
|
327
|
+
* the whole room cache and grows linearly with cluster size, while
|
|
328
|
+
* `findRoomsByIds` is O(K) in the caller's input.
|
|
329
|
+
*/
|
|
330
|
+
export async function findRoomsByIds(roomIds: string[]): Promise<Map<string, IRoomCache>> {
|
|
331
|
+
if (roomIds.length === 0) { return new Map(); }
|
|
332
|
+
return await driver.findByIds(roomIds);
|
|
333
|
+
}
|
|
334
|
+
|
|
324
335
|
/**
|
|
325
336
|
* Find for a public and unlocked room available.
|
|
326
337
|
*
|
|
@@ -537,6 +548,8 @@ export async function handleCreateRoom(roomName: string, clientOptions: ClientOp
|
|
|
537
548
|
|
|
538
549
|
// set room public attributes
|
|
539
550
|
if (restoringRoomId && isDevMode) {
|
|
551
|
+
// an ungraceful exit may have left a stale cache row for this roomId
|
|
552
|
+
await driver.remove(restoringRoomId);
|
|
540
553
|
room.roomId = restoringRoomId;
|
|
541
554
|
|
|
542
555
|
} else {
|
|
@@ -939,13 +952,33 @@ export function buildSeatReservation(room: IRoomCache, sessionId: string) {
|
|
|
939
952
|
|
|
940
953
|
async function callOnAuth(roomName: string, clientOptions?: ClientOptions, authContext?: AuthContext) {
|
|
941
954
|
const roomClass = getRoomClass(roomName);
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
955
|
+
const onAuth = roomClass?.['onAuth'];
|
|
956
|
+
if (!onAuth) { return; }
|
|
957
|
+
|
|
958
|
+
// Server-initiated joins (e.g. internal `matchMaker.create()` from
|
|
959
|
+
// bots/tests/cron) skip auth — there's no transport, no headers, no
|
|
960
|
+
// token. Treat the absence of authContext as "no auth performed",
|
|
961
|
+
// which leaves client.auth undefined just like the legacy short-
|
|
962
|
+
// circuit did.
|
|
963
|
+
if (!authContext) { return; }
|
|
964
|
+
|
|
965
|
+
// Always invoke onAuth when an auth context is present — even when
|
|
966
|
+
// the room hasn't overridden it. The base `Room.onAuth` returns
|
|
967
|
+
// `true` (or whatever a side-effect import like @colyseus/auth swapped
|
|
968
|
+
// it for), and we use the return value to populate `client.auth` when
|
|
969
|
+
// it's a payload object. Pre-change this call was skipped when the
|
|
970
|
+
// subclass inherited the default, which made it impossible to install
|
|
971
|
+
// a workspace-wide token decoder by patching `Room.onAuth` once.
|
|
972
|
+
const result = await onAuth(authContext.token, clientOptions, authContext);
|
|
973
|
+
|
|
974
|
+
// Auth-result semantics:
|
|
975
|
+
// false / null / undefined → AUTH_FAILED
|
|
976
|
+
// true → succeeded but no payload (don't set client.auth)
|
|
977
|
+
// anything else (object) → succeeded; the value becomes client.auth
|
|
978
|
+
if (result === false || result === null || result === undefined) {
|
|
979
|
+
throw new ServerError(ErrorCode.AUTH_FAILED, 'onAuth failed');
|
|
948
980
|
}
|
|
981
|
+
return result === true ? undefined : result;
|
|
949
982
|
}
|
|
950
983
|
|
|
951
984
|
/**
|
|
@@ -1005,10 +1038,9 @@ export function healthCheckProcessId(processId: string) {
|
|
|
1005
1038
|
logger.debug(`❌ Process '${processId}' failed to respond. Cleaning it up.`);
|
|
1006
1039
|
await stats.excludeProcess(processId);
|
|
1007
1040
|
|
|
1008
|
-
// clean-up
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
}
|
|
1041
|
+
// clean-up stale room ids — a dead process never comes back;
|
|
1042
|
+
// devMode restore reads 'roomhistory' (presence), never these rows
|
|
1043
|
+
await removeRoomsByProcessId(processId);
|
|
1012
1044
|
|
|
1013
1045
|
resolve(false);
|
|
1014
1046
|
} finally {
|
package/src/Protocol.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Packr } from '
|
|
1
|
+
import { Packr, RESERVE_START_SPACE } from 'msgpackr';
|
|
2
2
|
import { encode, type Iterator } from '@colyseus/schema';
|
|
3
3
|
import { Protocol } from '@colyseus/shared-types';
|
|
4
4
|
|
|
@@ -11,101 +11,172 @@ export const IpcProtocol = {
|
|
|
11
11
|
export type IpcProtocol = typeof IpcProtocol[keyof typeof IpcProtocol];
|
|
12
12
|
|
|
13
13
|
const packr = new Packr({
|
|
14
|
-
useRecords: false, //
|
|
14
|
+
useRecords: false, // interop with non-msgpackr decoders
|
|
15
15
|
});
|
|
16
16
|
|
|
17
|
-
//
|
|
18
|
-
|
|
17
|
+
// Buffer for assembling outgoing frames; keeps us off msgpackr's internal
|
|
18
|
+
// buffer so `core` can use the upstream package directly.
|
|
19
|
+
let frameBuffer = Buffer.allocUnsafe(8192);
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
// Grow to `capacity`, preserving written bytes (`rawMessage` writes the header
|
|
22
|
+
// before the payload size is known).
|
|
23
|
+
function ensureFrameCapacity(capacity: number) {
|
|
24
|
+
if (capacity > frameBuffer.byteLength) {
|
|
25
|
+
const next = Buffer.allocUnsafe(capacity);
|
|
26
|
+
frameBuffer.copy(next);
|
|
27
|
+
frameBuffer = next;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
27
30
|
|
|
28
|
-
|
|
29
|
-
|
|
31
|
+
// Shared across calls to avoid a per-call allocation on the hot send path.
|
|
32
|
+
// Safe: encode.* mutate `.offset` in place and never re-enter this module.
|
|
33
|
+
const it: Iterator = { offset: 0 };
|
|
30
34
|
|
|
35
|
+
export const getMessageBytes = {
|
|
36
|
+
/**
|
|
37
|
+
* Build the JOIN_ROOM payload.
|
|
38
|
+
*
|
|
39
|
+
* Wire layout:
|
|
40
|
+
* [JOIN_ROOM byte][rt-len][rt][sid-len][sid][stateReflectionLen varint][stateReflection][...sections]
|
|
41
|
+
*
|
|
42
|
+
* The varint length prefix on `stateReflection` is required because the
|
|
43
|
+
* schema decoder on the SDK side runs `while (offset < bytes.byteLength)`
|
|
44
|
+
* — without a boundary it consumes past the state reflection into the
|
|
45
|
+
* trailing tagged-section bytes, producing "definition mismatch" warnings.
|
|
46
|
+
* Length is `0` when no state reflection is present.
|
|
47
|
+
*
|
|
48
|
+
* Each trailing section is `[tag (uint8)][length (varint)][payload]`. The
|
|
49
|
+
* SDK skips unknown tags via `length`, so new sections can be added without
|
|
50
|
+
* breaking older clients. See {@link HandshakeSection} in shared-types.
|
|
51
|
+
*/
|
|
52
|
+
[Protocol.JOIN_ROOM]: (
|
|
53
|
+
reconnectionToken: string,
|
|
54
|
+
serializerId: string,
|
|
55
|
+
handshake?: Uint8Array,
|
|
56
|
+
extraSections?: Array<{ tag: number; bytes: Uint8Array }>,
|
|
57
|
+
) => {
|
|
58
|
+
const reconnectionTokenLength = Buffer.byteLength(reconnectionToken, "utf8");
|
|
59
|
+
const serializerIdLength = Buffer.byteLength(serializerId, "utf8");
|
|
31
60
|
let handshakeLength = handshake?.byteLength || 0;
|
|
32
61
|
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
|
|
62
|
+
// per section: 1 tag byte + 9 (max varint) + payload
|
|
63
|
+
let extraLength = 0;
|
|
64
|
+
if (extraSections !== undefined) {
|
|
65
|
+
for (let i = 0; i < extraSections.length; i++) {
|
|
66
|
+
extraLength += 1 + 9 + extraSections[i].bytes.byteLength;
|
|
67
|
+
}
|
|
36
68
|
}
|
|
37
69
|
|
|
70
|
+
// capacity: header + 9 (handshake-len varint) + handshake + sections
|
|
71
|
+
ensureFrameCapacity(1 + 1 + reconnectionTokenLength + 1 + serializerIdLength + 9 + handshakeLength + extraLength);
|
|
72
|
+
|
|
73
|
+
it.offset = 1;
|
|
74
|
+
frameBuffer[0] = Protocol.JOIN_ROOM;
|
|
75
|
+
|
|
76
|
+
frameBuffer[it.offset++] = reconnectionTokenLength;
|
|
77
|
+
encode.utf8Write(frameBuffer, reconnectionToken, it);
|
|
78
|
+
|
|
79
|
+
frameBuffer[it.offset++] = serializerIdLength;
|
|
80
|
+
encode.utf8Write(frameBuffer, serializerId, it);
|
|
81
|
+
|
|
82
|
+
encode.number(frameBuffer, handshakeLength, it);
|
|
38
83
|
if (handshakeLength > 0) {
|
|
39
|
-
|
|
84
|
+
frameBuffer.set(handshake, it.offset);
|
|
85
|
+
it.offset += handshakeLength;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (extraSections !== undefined) {
|
|
89
|
+
for (let i = 0; i < extraSections.length; i++) {
|
|
90
|
+
const section = extraSections[i];
|
|
91
|
+
frameBuffer[it.offset++] = section.tag;
|
|
92
|
+
encode.number(frameBuffer, section.bytes.byteLength, it);
|
|
93
|
+
frameBuffer.set(section.bytes, it.offset);
|
|
94
|
+
it.offset += section.bytes.byteLength;
|
|
95
|
+
}
|
|
40
96
|
}
|
|
41
97
|
|
|
42
|
-
return Buffer.from(
|
|
98
|
+
return Buffer.from(frameBuffer.subarray(0, it.offset));
|
|
43
99
|
},
|
|
44
100
|
|
|
45
101
|
[Protocol.ERROR]: (code: number, message: string = '') => {
|
|
46
|
-
|
|
47
|
-
|
|
102
|
+
// capacity: 1 + code varint + length-prefixed message
|
|
103
|
+
ensureFrameCapacity(1 + 9 + 9 + Buffer.byteLength(message, "utf8"));
|
|
104
|
+
|
|
105
|
+
it.offset = 1;
|
|
106
|
+
frameBuffer[0] = Protocol.ERROR;
|
|
48
107
|
|
|
49
|
-
encode.number(
|
|
50
|
-
encode.string(
|
|
108
|
+
encode.number(frameBuffer, code, it);
|
|
109
|
+
encode.string(frameBuffer, message, it);
|
|
51
110
|
|
|
52
|
-
return Buffer.from(
|
|
111
|
+
return Buffer.from(frameBuffer.subarray(0, it.offset));
|
|
53
112
|
},
|
|
54
113
|
|
|
55
114
|
[Protocol.ROOM_STATE]: (bytes: number[]) => {
|
|
56
115
|
return [Protocol.ROOM_STATE, ...bytes];
|
|
57
116
|
},
|
|
58
117
|
|
|
118
|
+
/**
|
|
119
|
+
* Reply to a client {@link Protocol.ROOM_REQUEST}.
|
|
120
|
+
*
|
|
121
|
+
* Wire layout: `[ROOM_RESPONSE byte][requestId varint][status uint8][msgpack payload?]`
|
|
122
|
+
*
|
|
123
|
+
* `requestId` is opaque — echoed back exactly as the SDK sent it so the
|
|
124
|
+
* pending callback/promise can be correlated. `status` is a
|
|
125
|
+
* {@link ResponseStatus} (0 = OK, 1 = REJECTED, 2 = ERROR). The payload is
|
|
126
|
+
* omitted when a handler resolves with `undefined`. Returns a fresh Buffer copy
|
|
127
|
+
* for the same back-pressure reason documented on `raw` below.
|
|
128
|
+
*/
|
|
129
|
+
[Protocol.ROOM_RESPONSE]: (requestId: number, status: number, message?: any): Buffer => {
|
|
130
|
+
it.offset = 1;
|
|
131
|
+
frameBuffer[0] = Protocol.ROOM_RESPONSE;
|
|
132
|
+
|
|
133
|
+
encode.number(frameBuffer, requestId, it);
|
|
134
|
+
frameBuffer[it.offset++] = status;
|
|
135
|
+
const headerLength = it.offset;
|
|
136
|
+
|
|
137
|
+
if (message !== undefined) {
|
|
138
|
+
// reserve `headerLength` bytes up front in the pack output for the header
|
|
139
|
+
const packed = packr.pack(message, RESERVE_START_SPACE | headerLength);
|
|
140
|
+
packed.set(frameBuffer.subarray(0, headerLength), 0);
|
|
141
|
+
return Buffer.from(packed);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return Buffer.from(frameBuffer.subarray(0, headerLength));
|
|
145
|
+
},
|
|
146
|
+
|
|
59
147
|
[Protocol.PING]: () => {
|
|
60
|
-
|
|
61
|
-
return Buffer.from(
|
|
148
|
+
frameBuffer[0] = Protocol.PING;
|
|
149
|
+
return Buffer.from(frameBuffer.subarray(0, 1));
|
|
62
150
|
},
|
|
63
151
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
152
|
+
// Returns a fresh copy: frameBuffer/packr are reused next call, but callers pass
|
|
153
|
+
// the result to async consumers (Node retains it for libuv writev across ticks).
|
|
154
|
+
// See benchmark/test-buffer-corruption.ts for the back-pressure repro.
|
|
155
|
+
raw: (code: Protocol, type: string | number, message?: any, rawMessage?: Uint8Array | Buffer): Buffer => {
|
|
156
|
+
it.offset = 1;
|
|
157
|
+
frameBuffer[0] = code;
|
|
67
158
|
|
|
68
159
|
if (typeof (type) === 'string') {
|
|
69
|
-
encode.string(
|
|
160
|
+
encode.string(frameBuffer, type, it);
|
|
70
161
|
|
|
71
162
|
} else {
|
|
72
|
-
encode.number(
|
|
163
|
+
encode.number(frameBuffer, type, it);
|
|
73
164
|
}
|
|
165
|
+
const headerLength = it.offset;
|
|
74
166
|
|
|
75
167
|
if (message !== undefined) {
|
|
76
|
-
//
|
|
77
|
-
packr.
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
// TODO: remove this after issue is fixed https://github.com/kriszyp/msgpackr/issues/139
|
|
81
|
-
//
|
|
82
|
-
// - This check is only required when running integration tests.
|
|
83
|
-
// (colyseus.js' usage of msgpackr/buffer is conflicting)
|
|
84
|
-
//
|
|
85
|
-
if (process.env.NODE_ENV !== "production") {
|
|
86
|
-
packr.useBuffer(packr.buffer);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// pack message into the same packr.buffer
|
|
90
|
-
const endOfBufferOffset = packr.pack(message, 2048 + it.offset).byteLength;
|
|
91
|
-
// 2048 = RESERVE_START_SPACE
|
|
92
|
-
return Buffer.from(packr.buffer.subarray(0, endOfBufferOffset));
|
|
168
|
+
// reserve `headerLength` bytes up front in the pack output for the header
|
|
169
|
+
const packed = packr.pack(message, RESERVE_START_SPACE | headerLength);
|
|
170
|
+
packed.set(frameBuffer.subarray(0, headerLength), 0);
|
|
171
|
+
return Buffer.from(packed);
|
|
93
172
|
|
|
94
173
|
} else if (rawMessage !== undefined) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
if (rawMessage.length + it.offset > packr.buffer.byteLength) {
|
|
99
|
-
packr.useBuffer(Buffer.alloc(it.offset + rawMessage.length, packr.buffer));
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// copy raw message into packr.buffer
|
|
103
|
-
packr.buffer.set(rawMessage, it.offset);
|
|
104
|
-
|
|
105
|
-
return Buffer.from(packr.buffer.subarray(0, it.offset + rawMessage.byteLength));
|
|
174
|
+
ensureFrameCapacity(headerLength + rawMessage.byteLength);
|
|
175
|
+
frameBuffer.set(rawMessage, headerLength);
|
|
176
|
+
return Buffer.from(frameBuffer.subarray(0, headerLength + rawMessage.byteLength));
|
|
106
177
|
|
|
107
178
|
} else {
|
|
108
|
-
return Buffer.from(
|
|
179
|
+
return Buffer.from(frameBuffer.subarray(0, headerLength));
|
|
109
180
|
}
|
|
110
181
|
},
|
|
111
182
|
|