@colyseus/core 0.18.0 → 0.18.2
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 +3 -7
- package/build/MatchMaker.cjs.map +2 -2
- package/build/MatchMaker.mjs +4 -8
- package/build/MatchMaker.mjs.map +2 -2
- package/build/Protocol.cjs +3 -3
- package/build/Protocol.cjs.map +1 -1
- package/build/Protocol.d.ts +3 -3
- package/build/Protocol.mjs +3 -3
- package/build/Protocol.mjs.map +1 -1
- 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 +280 -281
- package/build/Room.cjs.map +3 -3
- package/build/Room.d.ts +185 -81
- package/build/Room.mjs +286 -286
- package/build/Room.mjs.map +2 -2
- 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/Server.cjs +88 -31
- package/build/Server.cjs.map +2 -2
- package/build/Server.d.ts +36 -0
- package/build/Server.mjs +88 -31
- package/build/Server.mjs.map +2 -2
- package/build/Transport.cjs +26 -0
- package/build/Transport.cjs.map +2 -2
- package/build/Transport.d.ts +70 -2
- package/build/Transport.mjs +25 -0
- 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 +29 -17
- package/build/errors/RoomExceptions.mjs +5 -3
- package/build/errors/RoomExceptions.mjs.map +2 -2
- package/build/index.cjs +19 -2
- package/build/index.cjs.map +2 -2
- package/build/index.d.ts +7 -4
- package/build/index.mjs +17 -4
- package/build/index.mjs.map +2 -2
- package/build/input/InputBuffer.cjs +465 -16
- package/build/input/InputBuffer.cjs.map +2 -2
- package/build/input/InputBuffer.d.ts +195 -95
- package/build/input/InputBuffer.mjs +461 -15
- package/build/input/InputBuffer.mjs.map +2 -2
- package/build/input/RoomInput.cjs +307 -0
- package/build/input/RoomInput.cjs.map +7 -0
- package/build/input/RoomInput.d.ts +91 -0
- package/build/input/RoomInput.mjs +289 -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/presence/LocalPresence.cjs.map +2 -2
- package/build/presence/LocalPresence.d.ts +1 -1
- package/build/presence/LocalPresence.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/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 +19 -7
- package/build/router/index.cjs.map +2 -2
- package/build/router/index.mjs +19 -7
- package/build/router/index.mjs.map +2 -2
- package/build/router/node.cjs +37 -2
- package/build/router/node.cjs.map +2 -2
- package/build/router/node.d.ts +10 -0
- package/build/router/node.mjs +35 -1
- package/build/router/node.mjs.map +2 -2
- 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/Env.cjs +8 -4
- package/build/utils/Env.cjs.map +3 -3
- package/build/utils/Env.mjs +8 -4
- package/build/utils/Env.mjs.map +2 -2
- 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 +15 -12
- package/src/MatchMaker.ts +7 -9
- package/src/Protocol.ts +3 -3
- package/src/Rewind.ts +572 -0
- package/src/Room.ts +409 -448
- package/src/RoomMessages.ts +342 -0
- package/src/Server.ts +112 -38
- package/src/Transport.ts +98 -2
- package/src/errors/RoomExceptions.ts +36 -22
- package/src/index.ts +13 -2
- package/src/input/InputBuffer.ts +494 -105
- package/src/input/RoomInput.ts +342 -0
- package/src/input/types.ts +503 -0
- package/src/presence/LocalPresence.ts +1 -1
- package/src/rooms/QueueRoom.ts +1 -1
- package/src/router/default_routes.ts +6 -1
- package/src/router/index.ts +32 -14
- package/src/router/node.ts +44 -0
- 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/Env.ts +12 -4
- package/src/utils/Utils.ts +9 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/serializer/SchemaSerializer.ts"],
|
|
4
|
-
"sourcesContent": ["import { Protocol } from '@colyseus/shared-types';\n\nimport type { Serializer } from './Serializer.ts';\nimport { type Client, ClientState } from '../Transport.ts';\n\nimport { type Iterator, Encoder, dumpChanges, Reflection, Schema, StateView } from '@colyseus/schema';\nimport { debugPatch } from '../Debug.ts';\n\nconst SHARED_VIEW = {};\n\nexport class SchemaSerializer<T extends Schema> implements Serializer<T> {\n public id = 'schema';\n\n protected encoder: Encoder<T>;\n protected hasFilters: boolean = false;\n\n protected handshakeCache: Uint8Array;\n\n // flag to avoid re-encoding full state if no changes were made\n protected needFullEncode: boolean = true;\n\n // TODO: make this optional. allocating a new buffer for each room may not be always necessary.\n protected fullEncodeBuffer: Uint8Array = new Uint8Array(Encoder.BUFFER_SIZE);\n protected fullEncodeCache: Uint8Array;\n protected sharedOffsetCache: Iterator = { offset: 0 };\n\n protected encodedViews: Map<StateView | typeof SHARED_VIEW, Uint8Array>;\n\n public reset(newState: T & Schema) {\n this.encoder = new Encoder(newState);\n this.hasFilters = this.encoder.context.hasFilters;\n\n // cache ROOM_STATE byte as part of the encoded buffer\n this.fullEncodeBuffer[0] = Protocol.ROOM_STATE;\n\n if (this.hasFilters) {\n this.encodedViews = new Map();\n }\n }\n\n public getFullState(client?: Client) {\n if (this.needFullEncode || this.encoder.root.changes.next !== undefined) {\n this.sharedOffsetCache = { offset: 1 };\n this.fullEncodeCache = this.encoder.encodeAll(this.sharedOffsetCache, this.fullEncodeBuffer);\n this.needFullEncode = false;\n }\n\n if (this.hasFilters && client?.view) {\n return this.encoder.encodeAllView(\n client.view,\n this.sharedOffsetCache.offset,\n { ...this.sharedOffsetCache },\n this.fullEncodeBuffer\n );\n\n } else {\n return this.fullEncodeCache;\n }\n }\n\n public applyPatches(clients: Client[]) {\n let numClients = clients.length;\n\n if (numClients === 0) {\n if (this.encoder.hasChanges) {\n // if there are changes but no clients, we need to encode full state on next patch\n this.needFullEncode = true;\n }\n // skip patching and clear changes\n this.encoder.discardChanges();\n return false;\n }\n\n if (!this.encoder.hasChanges) {\n\n // check if views have changes (manual add() or remove() items)\n if (this.hasFilters) {\n //\n // FIXME: refactor this to avoid duplicating code.\n //\n // it's probably better to have 2 different 'applyPatches' methods.\n // (one for handling state with filters, and another for handling state without filters)\n //\n const clientsWithViewChange = clients.filter((client) => {\n return client.state === ClientState.JOINED && client.view?.changes.size > 0\n });\n\n if (clientsWithViewChange.length > 0) {\n const it: Iterator = { offset: 1 };\n\n const sharedOffset = it.offset;\n this.encoder.sharedBuffer[0] = Protocol.ROOM_STATE_PATCH;\n\n clientsWithViewChange.forEach((client) => {\n client.raw(this.encoder.encodeView(client.view, sharedOffset, it));\n });\n }\n }\n\n // skip patching state if:\n // - no clients are connected\n // - no changes were made\n // - no \"filtered changes\" were made when using filters\n return false;\n }\n\n this.needFullEncode = true;\n\n // dump changes for patch debugging\n if (debugPatch.enabled) {\n (debugPatch as any).dumpChanges = dumpChanges(this.encoder.state);\n }\n\n // get patch bytes\n const it: Iterator = { offset: 1 };\n this.encoder.sharedBuffer[0] = Protocol.ROOM_STATE_PATCH;\n\n // encode changes once, for all clients\n const encodedChanges = this.encoder.encode(it);\n\n if (!this.hasFilters) {\n while (numClients--) {\n const client = clients[numClients];\n\n //\n // FIXME: avoid this check for each client\n //\n if (client.state !== ClientState.JOINED) {\n continue;\n }\n\n client.raw(encodedChanges);\n }\n\n } else {\n // cache shared offset\n const sharedOffset = it.offset;\n\n // encode state multiple times, for each client\n while (numClients--) {\n const client = clients[numClients];\n\n //\n // FIXME: avoid this check for each client\n //\n if (client.state !== ClientState.JOINED) {\n continue;\n }\n\n const view = client.view || SHARED_VIEW;\n\n let encodedView = this.encodedViews.get(view);\n\n // allow to pass the same encoded view for multiple clients\n if (encodedView === undefined) {\n encodedView = (view === SHARED_VIEW)\n ? encodedChanges\n : this.encoder.encodeView(client.view, sharedOffset, it);\n this.encodedViews.set(view, encodedView);\n }\n\n client.raw(encodedView);\n }\n\n // clear views\n this.encodedViews.clear();\n }\n\n // discard changes after sending\n this.encoder.discardChanges();\n\n // debug patches\n if (debugPatch.enabled) {\n debugPatch(\n '%d bytes sent to %d clients, %j',\n encodedChanges.length,\n clients.length,\n (debugPatch as any).dumpChanges,\n );\n }\n\n return true;\n }\n\n public handshake() {\n /**\n * Cache handshake to avoid encoding it for each client joining\n */\n if (!this.handshakeCache) {\n //\n // TODO: re-use handshake buffer for all rooms of same type (?)\n //\n this.handshakeCache = (this.encoder.state && Reflection.encode(this.encoder));\n }\n\n return this.handshakeCache;\n }\n\n}\n"],
|
|
5
|
-
"mappings": ";AAAA,SAAS,
|
|
4
|
+
"sourcesContent": ["import { Protocol, ProtocolModifier } from '@colyseus/shared-types';\n\nimport type { PatchTimingContext, Serializer } from './Serializer.ts';\nimport { type Client, type ClientPrivate, ClientState } from '../Transport.ts';\n\nimport { type Iterator, encode, Encoder, dumpChanges, Reflection, Schema, StateView } from '@colyseus/schema';\nimport { debugPatch } from '../Debug.ts';\n\n/**\n * Size of the {@link ProtocolModifier.TIMED} prefix that follows the\n * protocol byte:\n * `[uint32 sNow][uint32 inputSeq]`\n *\n * - `sNow` \u2014 server clock as **milliseconds since room start**\n * (`room.clock.elapsedTime`), NOT raw `performance.now()`. A portable,\n * language-agnostic integer (C/C#/Lua have no `performance.now()`); the\n * server's own time-keyed logic uses the SAME `clock.elapsedTime` so the\n * client's reconstructed `serverNow()` stays in phase. Wraps at u32\n * (~49.7 days uptime) \u2014 irrelevant per session. Drives the client clock\n * offset / `serverNow()`.\n * - `inputSeq` \u2014 seq VALUE of the last input CONSUMED into the authoritative\n * state (the input buffer's `ackSeq`, i.e. last-PROCESSED input). Reliable:\n * equals the consumed count (inputs are sequenced implicitly by receive\n * order). Unreliable: the framework wire seq, so a fully-dropped input\n * doesn't make the ack lag the client's sent seq by the lost count. This\n * single number is the canonical reconciliation ack (\"the sequence number\n * of the last input the server processed\"); the client prunes its\n * pending-input buffer against it AND derives RTT from its round-trip\n * (`now \u2212 sendTime(inputSeq)`). No separate received-seq / received-time \u2014\n * RTT-from-the-processed-ack is round-trip-inclusive, which is the standard.\n */\nconst TIMED_PREFIX_SIZE = 8;\n\n/**\n * Construct a per-recipient TIMED buffer:\n * `[code|TIMED][sNow][inputSeq][...schema bytes]`\n *\n * `encodedBody` is the shared encoded buffer whose byte 0 already holds the\n * base protocol code (ROOM_STATE or ROOM_STATE_PATCH); bytes 1.. are the\n * schema payload. We construct a fresh per-client Buffer so the transport\n * never sees a buffer that might mutate between queue and send.\n *\n * Uses the schema `encode.*` helpers to keep the wire layout symmetric with\n * the SDK's `decode.*`-driven reader.\n */\nfunction buildTimedFrame(\n encodedBody: Uint8Array,\n sNow: number,\n inputSeq: number,\n): Buffer {\n const schemaPayload = encodedBody.subarray(1);\n const out = Buffer.allocUnsafe(1 + TIMED_PREFIX_SIZE + schemaPayload.length);\n out[0] = encodedBody[0] | ProtocolModifier.TIMED;\n const it: Iterator = { offset: 1 };\n encode.uint32(out, sNow >>> 0, it); // ms since room start (clock.elapsedTime)\n encode.uint32(out, inputSeq >>> 0, it);\n out.set(schemaPayload, it.offset);\n return out;\n}\n\n/** Seq VALUE of the last input consumed from the client's buffer \u2014 the\n * reconciliation ack (and RTT correlation key). Reliable: equals the consumed\n * count; unreliable: the framework wire seq (so packet loss doesn't skew it).\n * 0 when the room doesn't buffer input. */\nfunction clientProcessedInputSeq(client: Client): number {\n return (client as Client & ClientPrivate)._inputBuffer?.ackSeq ?? 0;\n}\n\n/**\n * Send a per-recipient patch frame: a TIMED-prefixed buffer when `timing` is set,\n * else the shared `encodedBody` passed straight through (no per-client copy). Any\n * `afterNextPatch` frames staged on `client._pendingFrames` are NOT coalesced here\n * \u2014 they go out standalone right after the patch via `Room._flushPendingClientFrames`.\n */\nfunction sendClientFrame(client: Client, encodedBody: Uint8Array, timing?: PatchTimingContext): void {\n if (timing) {\n client.raw(buildTimedFrame(encodedBody, timing.sNow, clientProcessedInputSeq(client)));\n } else {\n client.raw(encodedBody);\n }\n}\n\nconst SHARED_VIEW = {};\n\nexport class SchemaSerializer<T extends Schema> implements Serializer<T> {\n public id = 'schema';\n\n protected encoder: Encoder<T>;\n protected hasFilters: boolean = false;\n\n protected handshakeCache: Uint8Array;\n\n // flag to avoid re-encoding full state if no changes were made\n protected needFullEncode: boolean = true;\n\n // TODO: make this optional. allocating a new buffer for each room may not be always necessary.\n protected fullEncodeBuffer: Uint8Array = new Uint8Array(Encoder.BUFFER_SIZE);\n protected fullEncodeCache: Uint8Array;\n protected sharedOffsetCache: Iterator = { offset: 0 };\n\n protected encodedViews: Map<StateView | typeof SHARED_VIEW, Uint8Array>;\n\n public reset(newState: T & Schema) {\n this.encoder = new Encoder(newState);\n this.hasFilters = this.encoder.context.hasFilters;\n\n // cache ROOM_STATE byte as part of the encoded buffer\n this.fullEncodeBuffer[0] = Protocol.ROOM_STATE;\n\n if (this.hasFilters) {\n this.encodedViews = new Map();\n }\n }\n\n public getFullState(client?: Client, timing?: PatchTimingContext) {\n if (this.needFullEncode || this.encoder.root.changes.next !== undefined) {\n this.sharedOffsetCache = { offset: 1 };\n this.fullEncodeCache = this.encoder.encodeAll(this.sharedOffsetCache, this.fullEncodeBuffer);\n this.needFullEncode = false;\n }\n\n const body = (this.hasFilters && client?.view)\n ? this.encoder.encodeAllView(\n client.view,\n this.sharedOffsetCache.offset,\n { ...this.sharedOffsetCache },\n this.fullEncodeBuffer\n )\n : this.fullEncodeCache;\n\n if (timing && client) {\n return buildTimedFrame(body, timing.sNow, clientProcessedInputSeq(client));\n }\n return body;\n }\n\n public applyPatches(clients: Client[], _state?: unknown, timing?: PatchTimingContext) {\n let numClients = clients.length;\n\n if (numClients === 0) {\n if (this.encoder.hasChanges) {\n // if there are changes but no clients, we need to encode full state on next patch\n this.needFullEncode = true;\n }\n // skip patching and clear changes\n this.encoder.discardChanges();\n return false;\n }\n\n if (!this.encoder.hasChanges) {\n\n // check if views have changes (manual add() or remove() items)\n if (this.hasFilters) {\n //\n // FIXME: refactor this to avoid duplicating code.\n //\n // it's probably better to have 2 different 'applyPatches' methods.\n // (one for handling state with filters, and another for handling state without filters)\n //\n const clientsWithViewChange = clients.filter((client) => {\n return client.state === ClientState.JOINED && client.view?.changes.size > 0\n });\n\n if (clientsWithViewChange.length > 0) {\n const it: Iterator = { offset: 1 };\n\n const sharedOffset = it.offset;\n this.encoder.sharedBuffer[0] = Protocol.ROOM_STATE_PATCH;\n\n clientsWithViewChange.forEach((client) => {\n const encodedView = this.encoder.encodeView(client.view, sharedOffset, it);\n sendClientFrame(client, encodedView, timing);\n });\n }\n }\n\n // Heartbeat: `defineInput()` rooms still need periodic timing samples\n // to keep client clock offset / RTT estimates fresh during quiet\n // ticks. Emit a TIMED-prefixed ROOM_STATE_PATCH with an empty schema\n // body \u2014 1 byte protocol + 8 bytes prefix.\n // No-op for rooms without `defineInput()` (timing is undefined).\n if (timing) {\n const heartbeatBody = Buffer.from([Protocol.ROOM_STATE_PATCH]);\n for (let i = 0; i < clients.length; i++) {\n const client = clients[i];\n if (client.state !== ClientState.JOINED) continue;\n sendClientFrame(client, heartbeatBody, timing);\n }\n }\n\n // skip patching state if:\n // - no clients are connected\n // - no changes were made\n // - no \"filtered changes\" were made when using filters\n return false;\n }\n\n this.needFullEncode = true;\n\n // dump changes for patch debugging\n if (debugPatch.enabled) {\n (debugPatch as any).dumpChanges = dumpChanges(this.encoder.state);\n }\n\n // get patch bytes\n const it: Iterator = { offset: 1 };\n this.encoder.sharedBuffer[0] = Protocol.ROOM_STATE_PATCH;\n\n // encode changes once, for all clients\n const encodedChanges = this.encoder.encode(it);\n\n if (!this.hasFilters) {\n while (numClients--) {\n const client = clients[numClients];\n\n //\n // FIXME: avoid this check for each client\n //\n if (client.state !== ClientState.JOINED) {\n continue;\n }\n\n sendClientFrame(client, encodedChanges, timing);\n }\n\n } else {\n // cache shared offset\n const sharedOffset = it.offset;\n\n // encode state multiple times, for each client\n while (numClients--) {\n const client = clients[numClients];\n\n //\n // FIXME: avoid this check for each client\n //\n if (client.state !== ClientState.JOINED) {\n continue;\n }\n\n const view = client.view || SHARED_VIEW;\n\n let encodedView = this.encodedViews.get(view);\n\n // allow to pass the same encoded view for multiple clients\n if (encodedView === undefined) {\n encodedView = (view === SHARED_VIEW)\n ? encodedChanges\n : this.encoder.encodeView(client.view, sharedOffset, it);\n this.encodedViews.set(view, encodedView);\n }\n\n sendClientFrame(client, encodedView, timing);\n }\n\n // clear views\n this.encodedViews.clear();\n }\n\n // discard changes after sending\n this.encoder.discardChanges();\n\n // debug patches\n if (debugPatch.enabled) {\n debugPatch(\n '%d bytes sent to %d clients, %j',\n encodedChanges.length,\n clients.length,\n (debugPatch as any).dumpChanges,\n );\n }\n\n return true;\n }\n\n public handshake() {\n /**\n * Cache handshake to avoid encoding it for each client joining\n */\n if (!this.handshakeCache) {\n //\n // TODO: re-use handshake buffer for all rooms of same type (?)\n //\n this.handshakeCache = (this.encoder.state && Reflection.encode(this.encoder));\n }\n\n return this.handshakeCache;\n }\n\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["it"]
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/serializer/Serializer.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Client } from '../Transport.ts';\n\nexport interface Serializer<T> {\n id: string;\n reset(data: any): void;\n getFullState(client?: Client): Uint8Array;\n applyPatches(clients: Client[], state: T): boolean;\n handshake?(): Uint8Array;\n}"],
|
|
4
|
+
"sourcesContent": ["import type { Client } from '../Transport.ts';\n\n/**\n * Per-tick timing context, populated by the Room when\n * {@link Room.defineInput} was called. Its presence makes the serializer emit\n * a {@link ProtocolModifier.TIMED} prefix on every state/patch frame \u2014 the\n * gate is per-room (defineInput), not a per-client capability.\n */\nexport interface PatchTimingContext {\n /** Server clock as ms since room start (`clock.elapsedTime`) at patch time. */\n sNow: number;\n}\n\nexport interface Serializer<T> {\n id: string;\n reset(data: any): void;\n getFullState(client?: Client, timing?: PatchTimingContext): Uint8Array;\n applyPatches(clients: Client[], state: T, timing?: PatchTimingContext): boolean;\n handshake?(): Uint8Array;\n}"],
|
|
5
5
|
"mappings": ";;;;;;;;;;;;;;;;AAAA;AAAA;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
import type { Client } from '../Transport.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Per-tick timing context, populated by the Room when
|
|
4
|
+
* {@link Room.defineInput} was called. Its presence makes the serializer emit
|
|
5
|
+
* a {@link ProtocolModifier.TIMED} prefix on every state/patch frame — the
|
|
6
|
+
* gate is per-room (defineInput), not a per-client capability.
|
|
7
|
+
*/
|
|
8
|
+
export interface PatchTimingContext {
|
|
9
|
+
/** Server clock as ms since room start (`clock.elapsedTime`) at patch time. */
|
|
10
|
+
sNow: number;
|
|
11
|
+
}
|
|
2
12
|
export interface Serializer<T> {
|
|
3
13
|
id: string;
|
|
4
14
|
reset(data: any): void;
|
|
5
|
-
getFullState(client?: Client): Uint8Array;
|
|
6
|
-
applyPatches(clients: Client[], state: T): boolean;
|
|
15
|
+
getFullState(client?: Client, timing?: PatchTimingContext): Uint8Array;
|
|
16
|
+
applyPatches(clients: Client[], state: T, timing?: PatchTimingContext): boolean;
|
|
7
17
|
handshake?(): Uint8Array;
|
|
8
18
|
}
|
package/build/utils/DevMode.cjs
CHANGED
|
@@ -32,8 +32,6 @@ var DevMode_exports = {};
|
|
|
32
32
|
__export(DevMode_exports, {
|
|
33
33
|
cacheRoomHistory: () => cacheRoomHistory,
|
|
34
34
|
getDevModeCache: () => getDevModeCache,
|
|
35
|
-
getPreviousProcessId: () => getPreviousProcessId,
|
|
36
|
-
getProcessRestoreKey: () => getProcessRestoreKey,
|
|
37
35
|
getRoomRestoreListKey: () => getRoomRestoreListKey,
|
|
38
36
|
hasDevModeCache: () => hasDevModeCache,
|
|
39
37
|
isDevMode: () => isDevMode,
|
|
@@ -66,10 +64,20 @@ async function reloadFromCache() {
|
|
|
66
64
|
const roomHistoryList = Object.entries(await import_MatchMaker.presence.hgetall(getRoomRestoreListKey()));
|
|
67
65
|
(0, import_Debug.debugDevMode)("rooms to restore: %i", roomHistoryList.length);
|
|
68
66
|
for (const [roomId, value] of roomHistoryList) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
67
|
+
let roomHistory;
|
|
68
|
+
let recreatedRoom;
|
|
69
|
+
let recreatedRoomListing;
|
|
70
|
+
try {
|
|
71
|
+
roomHistory = JSON.parse(value);
|
|
72
|
+
(0, import_Debug.debugDevMode)("restoring room %s (%s)", roomHistory.roomName, roomId);
|
|
73
|
+
recreatedRoomListing = await (0, import_MatchMaker.handleCreateRoom)(roomHistory.roomName, roomHistory.clientOptions, roomId);
|
|
74
|
+
recreatedRoom = (0, import_MatchMaker.getLocalRoomById)(recreatedRoomListing.roomId);
|
|
75
|
+
} catch (e) {
|
|
76
|
+
(0, import_Debug.debugAndPrintError)(`\u274C couldn't restore room '${roomId}':
|
|
77
|
+
${e.stack}`);
|
|
78
|
+
await import_MatchMaker.presence.hdel(getRoomRestoreListKey(), roomId);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
73
81
|
if (roomHistory.hasOwnProperty("state")) {
|
|
74
82
|
try {
|
|
75
83
|
const rawState = JSON.parse(roomHistory.state);
|
|
@@ -136,21 +144,13 @@ ${e.stack}`);
|
|
|
136
144
|
}
|
|
137
145
|
}
|
|
138
146
|
}
|
|
139
|
-
async function getPreviousProcessId(hostname = "") {
|
|
140
|
-
return await import_MatchMaker.presence.hget(getProcessRestoreKey(), hostname);
|
|
141
|
-
}
|
|
142
147
|
function getRoomRestoreListKey() {
|
|
143
148
|
return "roomhistory";
|
|
144
149
|
}
|
|
145
|
-
function getProcessRestoreKey() {
|
|
146
|
-
return "processhistory";
|
|
147
|
-
}
|
|
148
150
|
// Annotate the CommonJS export names for ESM import in node:
|
|
149
151
|
0 && (module.exports = {
|
|
150
152
|
cacheRoomHistory,
|
|
151
153
|
getDevModeCache,
|
|
152
|
-
getPreviousProcessId,
|
|
153
|
-
getProcessRestoreKey,
|
|
154
154
|
getRoomRestoreListKey,
|
|
155
155
|
hasDevModeCache,
|
|
156
156
|
isDevMode,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/utils/DevMode.ts"],
|
|
4
|
-
"sourcesContent": ["import fs from 'fs';\nimport path from 'path';\nimport { type Schema, MapSchema, ArraySchema, SetSchema, CollectionSchema, $childType, $changes } from '@colyseus/schema';\nimport { logger } from '../Logger.ts';\nimport { debugAndPrintError, debugDevMode } from '../Debug.ts';\nimport { getLocalRoomById, handleCreateRoom, presence, remoteRoomCall } from '../MatchMaker.ts';\nimport type { Room } from '../Room.ts';\n\nconst DEVMODE_CACHE_FILE_PATH = path.resolve(\".devmode.json\");\n\nexport let isDevMode: boolean = false;\n\nexport function hasDevModeCache() {\n return fs.existsSync(DEVMODE_CACHE_FILE_PATH);\n}\n\nexport function getDevModeCache() {\n return JSON.parse(fs.readFileSync(DEVMODE_CACHE_FILE_PATH, 'utf8')) || {};\n}\n\nexport function writeDevModeCache(cache: any) {\n fs.writeFileSync(DEVMODE_CACHE_FILE_PATH, JSON.stringify(cache, null, 2), 'utf8');\n}\n\nexport function setDevMode(bool: boolean) {\n isDevMode = bool;\n}\n\nexport async function reloadFromCache() {\n const roomHistoryList = Object.entries(await presence.hgetall(getRoomRestoreListKey()));\n debugDevMode(\"rooms to restore: %i\", roomHistoryList.length);\n\n for (const [roomId, value] of roomHistoryList) {\n
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA
|
|
4
|
+
"sourcesContent": ["import fs from 'fs';\nimport path from 'path';\nimport { type Schema, MapSchema, ArraySchema, SetSchema, CollectionSchema, $childType, $changes } from '@colyseus/schema';\nimport { logger } from '../Logger.ts';\nimport { debugAndPrintError, debugDevMode } from '../Debug.ts';\nimport { getLocalRoomById, handleCreateRoom, presence, remoteRoomCall } from '../MatchMaker.ts';\nimport type { Room } from '../Room.ts';\n\nconst DEVMODE_CACHE_FILE_PATH = path.resolve(\".devmode.json\");\n\nexport let isDevMode: boolean = false;\n\nexport function hasDevModeCache() {\n return fs.existsSync(DEVMODE_CACHE_FILE_PATH);\n}\n\nexport function getDevModeCache() {\n return JSON.parse(fs.readFileSync(DEVMODE_CACHE_FILE_PATH, 'utf8')) || {};\n}\n\nexport function writeDevModeCache(cache: any) {\n fs.writeFileSync(DEVMODE_CACHE_FILE_PATH, JSON.stringify(cache, null, 2), 'utf8');\n}\n\nexport function setDevMode(bool: boolean) {\n isDevMode = bool;\n}\n\nexport async function reloadFromCache() {\n const roomHistoryList = Object.entries(await presence.hgetall(getRoomRestoreListKey()));\n debugDevMode(\"rooms to restore: %i\", roomHistoryList.length);\n\n for (const [roomId, value] of roomHistoryList) {\n let roomHistory: any;\n let recreatedRoom: Room;\n let recreatedRoomListing: any;\n\n try {\n roomHistory = JSON.parse(value);\n debugDevMode(\"restoring room %s (%s)\", roomHistory.roomName, roomId);\n\n recreatedRoomListing = await handleCreateRoom(roomHistory.roomName, roomHistory.clientOptions, roomId);\n recreatedRoom = getLocalRoomById(recreatedRoomListing.roomId);\n\n } catch (e: any) {\n // unrestorable entry (e.g. room type renamed/removed) \u2014 drop it\n // instead of wedging the boot\n debugAndPrintError(`\u274C couldn't restore room '${roomId}':\\n${e.stack}`);\n await presence.hdel(getRoomRestoreListKey(), roomId);\n continue;\n }\n\n // Restore previous state\n if (roomHistory.hasOwnProperty(\"state\")) {\n try {\n const rawState = JSON.parse(roomHistory.state);\n logger.debug(`\uD83D\uDCCB room '${roomId}' state =>`, rawState);\n\n (recreatedRoom.state as Schema).restore(rawState);\n\n // Restore the encoder's nextUniqueId so refIds increase\n // monotonically across HMR cycles. Without this, restore()\n // always produces the same refIds (0,1,2,3...) and onJoin()\n // always assigns the same next refIds (4,5...), causing the\n // client decoder to reuse stale instances on the 2nd+ cycle.\n if (roomHistory.nextRefId !== undefined) {\n const encoderRoot = recreatedRoom.state[$changes]?.root;\n if (encoderRoot && roomHistory.nextRefId > encoderRoot['nextUniqueId']) {\n encoderRoot['nextUniqueId'] = roomHistory.nextRefId;\n }\n }\n } catch (e: any) {\n debugAndPrintError(`\u274C couldn't restore room '${roomId}' state:\\n${e.stack}`);\n }\n }\n\n // Reserve seats for clients from cached history.\n // Skip entries without a reconnectionToken \u2014 these are stale\n // seats from allowReconnection() where the client already left\n // (e.g. page refresh). Restoring them would block room disposal.\n if (roomHistory.clients) {\n for (const clientData of roomHistory.clients) {\n const { sessionId, reconnectionToken } = clientData;\n if (!reconnectionToken) { continue; }\n // TODO: need to restore each client's StateView as well\n await remoteRoomCall(recreatedRoomListing.roomId, '_reserveSeat', [sessionId, {}, {}, recreatedRoom.seatReservationTimeout, false, reconnectionToken]);\n }\n }\n\n // call `onRestoreRoom` with custom 'cache'd property.\n recreatedRoom.onRestoreRoom?.(roomHistory[\"cache\"]);\n\n logger.debug(`\uD83D\uDD04 room '${roomId}' has been restored with ${roomHistory.clients?.length || 0} reserved seats: ${roomHistory.clients?.map((c: any) => c.sessionId).join(\", \")}`);\n }\n\n if (roomHistoryList.length > 0) {\n logger.debug(\"\u2705\", roomHistoryList.length, \"room(s) have been restored.\");\n }\n}\n\nexport async function cacheRoomHistory(rooms: { [roomId: string]: Room }) {\n for (const room of Object.values(rooms)) {\n const roomHistoryResult = await presence.hget(getRoomRestoreListKey(), room.roomId);\n if (roomHistoryResult) {\n try {\n const roomHistory = JSON.parse(roomHistoryResult);\n\n // custom cache method\n roomHistory[\"cache\"] = room.onCacheRoom?.();\n\n // encode state\n debugDevMode(\"caching room %s (%s)\", room.roomName, room.roomId);\n\n if (room.state) {\n roomHistory[\"state\"] = JSON.stringify(room.state);\n\n // Cache the encoder's nextUniqueId so it can be restored.\n // This ensures refIds increase monotonically across HMR cycles,\n // preventing the client decoder from reusing stale refs that\n // happen to have the same refId as newly created instances.\n const encoderRoot = room.state[$changes]?.root;\n if (encoderRoot) {\n roomHistory[\"nextRefId\"] = encoderRoot['nextUniqueId'];\n }\n }\n\n // cache active clients with their reconnection tokens\n // TODO: need to cache each client's StateView as well\n const activeClients = room.clients.map((client) => ({\n sessionId: client.sessionId,\n reconnectionToken: client.reconnectionToken,\n }));\n\n // collect active client sessionIds to avoid duplicates\n const activeSessionIds = new Set(activeClients.map((c) => c.sessionId));\n\n // also cache reserved seats (they don't have reconnectionTokens yet)\n // filter out reserved seats that are already active clients (from devMode reconnection)\n const reservedSeats = Object.keys(room['_reservedSeats'])\n .filter((sessionId) => !activeSessionIds.has(sessionId))\n .map((sessionId) => ({\n sessionId,\n reconnectionToken: undefined,\n }));\n\n roomHistory[\"clients\"] = activeClients.concat(reservedSeats);\n\n await presence.hset(getRoomRestoreListKey(), room.roomId, JSON.stringify(roomHistory));\n\n // Rewrite updated room history\n logger.debug(`\uD83D\uDCBE caching room '${room.roomId}' (clients: ${room.clients.length}, has state: ${roomHistory[\"state\"] !== undefined ? \"yes\" : \"no\"})`);\n\n } catch (e: any) {\n debugAndPrintError(`\u274C couldn't cache room '${room.roomId}', due to:\\n${e.stack}`);\n }\n }\n }\n}\n\nexport function getRoomRestoreListKey() {\n return 'roomhistory';\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["path", "fs"]
|
|
7
7
|
}
|
package/build/utils/DevMode.d.ts
CHANGED
|
@@ -8,6 +8,4 @@ export declare function reloadFromCache(): Promise<void>;
|
|
|
8
8
|
export declare function cacheRoomHistory(rooms: {
|
|
9
9
|
[roomId: string]: Room;
|
|
10
10
|
}): Promise<void>;
|
|
11
|
-
export declare function getPreviousProcessId(hostname?: string): Promise<string>;
|
|
12
11
|
export declare function getRoomRestoreListKey(): string;
|
|
13
|
-
export declare function getProcessRestoreKey(): string;
|
package/build/utils/DevMode.mjs
CHANGED
|
@@ -23,10 +23,20 @@ async function reloadFromCache() {
|
|
|
23
23
|
const roomHistoryList = Object.entries(await presence.hgetall(getRoomRestoreListKey()));
|
|
24
24
|
debugDevMode("rooms to restore: %i", roomHistoryList.length);
|
|
25
25
|
for (const [roomId, value] of roomHistoryList) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
let roomHistory;
|
|
27
|
+
let recreatedRoom;
|
|
28
|
+
let recreatedRoomListing;
|
|
29
|
+
try {
|
|
30
|
+
roomHistory = JSON.parse(value);
|
|
31
|
+
debugDevMode("restoring room %s (%s)", roomHistory.roomName, roomId);
|
|
32
|
+
recreatedRoomListing = await handleCreateRoom(roomHistory.roomName, roomHistory.clientOptions, roomId);
|
|
33
|
+
recreatedRoom = getLocalRoomById(recreatedRoomListing.roomId);
|
|
34
|
+
} catch (e) {
|
|
35
|
+
debugAndPrintError(`\u274C couldn't restore room '${roomId}':
|
|
36
|
+
${e.stack}`);
|
|
37
|
+
await presence.hdel(getRoomRestoreListKey(), roomId);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
30
40
|
if (roomHistory.hasOwnProperty("state")) {
|
|
31
41
|
try {
|
|
32
42
|
const rawState = JSON.parse(roomHistory.state);
|
|
@@ -93,20 +103,12 @@ ${e.stack}`);
|
|
|
93
103
|
}
|
|
94
104
|
}
|
|
95
105
|
}
|
|
96
|
-
async function getPreviousProcessId(hostname = "") {
|
|
97
|
-
return await presence.hget(getProcessRestoreKey(), hostname);
|
|
98
|
-
}
|
|
99
106
|
function getRoomRestoreListKey() {
|
|
100
107
|
return "roomhistory";
|
|
101
108
|
}
|
|
102
|
-
function getProcessRestoreKey() {
|
|
103
|
-
return "processhistory";
|
|
104
|
-
}
|
|
105
109
|
export {
|
|
106
110
|
cacheRoomHistory,
|
|
107
111
|
getDevModeCache,
|
|
108
|
-
getPreviousProcessId,
|
|
109
|
-
getProcessRestoreKey,
|
|
110
112
|
getRoomRestoreListKey,
|
|
111
113
|
hasDevModeCache,
|
|
112
114
|
isDevMode,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/utils/DevMode.ts"],
|
|
4
|
-
"sourcesContent": ["import fs from 'fs';\nimport path from 'path';\nimport { type Schema, MapSchema, ArraySchema, SetSchema, CollectionSchema, $childType, $changes } from '@colyseus/schema';\nimport { logger } from '../Logger.ts';\nimport { debugAndPrintError, debugDevMode } from '../Debug.ts';\nimport { getLocalRoomById, handleCreateRoom, presence, remoteRoomCall } from '../MatchMaker.ts';\nimport type { Room } from '../Room.ts';\n\nconst DEVMODE_CACHE_FILE_PATH = path.resolve(\".devmode.json\");\n\nexport let isDevMode: boolean = false;\n\nexport function hasDevModeCache() {\n return fs.existsSync(DEVMODE_CACHE_FILE_PATH);\n}\n\nexport function getDevModeCache() {\n return JSON.parse(fs.readFileSync(DEVMODE_CACHE_FILE_PATH, 'utf8')) || {};\n}\n\nexport function writeDevModeCache(cache: any) {\n fs.writeFileSync(DEVMODE_CACHE_FILE_PATH, JSON.stringify(cache, null, 2), 'utf8');\n}\n\nexport function setDevMode(bool: boolean) {\n isDevMode = bool;\n}\n\nexport async function reloadFromCache() {\n const roomHistoryList = Object.entries(await presence.hgetall(getRoomRestoreListKey()));\n debugDevMode(\"rooms to restore: %i\", roomHistoryList.length);\n\n for (const [roomId, value] of roomHistoryList) {\n
|
|
5
|
-
"mappings": ";AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAuF,gBAAgB;AACvG,SAAS,cAAc;AACvB,SAAS,oBAAoB,oBAAoB;AACjD,SAAS,kBAAkB,kBAAkB,UAAU,sBAAsB;AAG7E,IAAM,0BAA0B,KAAK,QAAQ,eAAe;AAErD,IAAI,YAAqB;AAEzB,SAAS,kBAAkB;AAChC,SAAO,GAAG,WAAW,uBAAuB;AAC9C;AAEO,SAAS,kBAAkB;AAChC,SAAO,KAAK,MAAM,GAAG,aAAa,yBAAyB,MAAM,CAAC,KAAK,CAAC;AAC1E;AAEO,SAAS,kBAAkB,OAAY;AAC5C,KAAG,cAAc,yBAAyB,KAAK,UAAU,OAAO,MAAM,CAAC,GAAG,MAAM;AAClF;AAEO,SAAS,WAAW,MAAe;AACxC,cAAY;AACd;AAEA,eAAsB,kBAAkB;AACtC,QAAM,kBAAkB,OAAO,QAAQ,MAAM,SAAS,QAAQ,sBAAsB,CAAC,CAAC;AACtF,eAAa,wBAAwB,gBAAgB,MAAM;AAE3D,aAAW,CAAC,QAAQ,KAAK,KAAK,iBAAiB;AAC7C,
|
|
4
|
+
"sourcesContent": ["import fs from 'fs';\nimport path from 'path';\nimport { type Schema, MapSchema, ArraySchema, SetSchema, CollectionSchema, $childType, $changes } from '@colyseus/schema';\nimport { logger } from '../Logger.ts';\nimport { debugAndPrintError, debugDevMode } from '../Debug.ts';\nimport { getLocalRoomById, handleCreateRoom, presence, remoteRoomCall } from '../MatchMaker.ts';\nimport type { Room } from '../Room.ts';\n\nconst DEVMODE_CACHE_FILE_PATH = path.resolve(\".devmode.json\");\n\nexport let isDevMode: boolean = false;\n\nexport function hasDevModeCache() {\n return fs.existsSync(DEVMODE_CACHE_FILE_PATH);\n}\n\nexport function getDevModeCache() {\n return JSON.parse(fs.readFileSync(DEVMODE_CACHE_FILE_PATH, 'utf8')) || {};\n}\n\nexport function writeDevModeCache(cache: any) {\n fs.writeFileSync(DEVMODE_CACHE_FILE_PATH, JSON.stringify(cache, null, 2), 'utf8');\n}\n\nexport function setDevMode(bool: boolean) {\n isDevMode = bool;\n}\n\nexport async function reloadFromCache() {\n const roomHistoryList = Object.entries(await presence.hgetall(getRoomRestoreListKey()));\n debugDevMode(\"rooms to restore: %i\", roomHistoryList.length);\n\n for (const [roomId, value] of roomHistoryList) {\n let roomHistory: any;\n let recreatedRoom: Room;\n let recreatedRoomListing: any;\n\n try {\n roomHistory = JSON.parse(value);\n debugDevMode(\"restoring room %s (%s)\", roomHistory.roomName, roomId);\n\n recreatedRoomListing = await handleCreateRoom(roomHistory.roomName, roomHistory.clientOptions, roomId);\n recreatedRoom = getLocalRoomById(recreatedRoomListing.roomId);\n\n } catch (e: any) {\n // unrestorable entry (e.g. room type renamed/removed) \u2014 drop it\n // instead of wedging the boot\n debugAndPrintError(`\u274C couldn't restore room '${roomId}':\\n${e.stack}`);\n await presence.hdel(getRoomRestoreListKey(), roomId);\n continue;\n }\n\n // Restore previous state\n if (roomHistory.hasOwnProperty(\"state\")) {\n try {\n const rawState = JSON.parse(roomHistory.state);\n logger.debug(`\uD83D\uDCCB room '${roomId}' state =>`, rawState);\n\n (recreatedRoom.state as Schema).restore(rawState);\n\n // Restore the encoder's nextUniqueId so refIds increase\n // monotonically across HMR cycles. Without this, restore()\n // always produces the same refIds (0,1,2,3...) and onJoin()\n // always assigns the same next refIds (4,5...), causing the\n // client decoder to reuse stale instances on the 2nd+ cycle.\n if (roomHistory.nextRefId !== undefined) {\n const encoderRoot = recreatedRoom.state[$changes]?.root;\n if (encoderRoot && roomHistory.nextRefId > encoderRoot['nextUniqueId']) {\n encoderRoot['nextUniqueId'] = roomHistory.nextRefId;\n }\n }\n } catch (e: any) {\n debugAndPrintError(`\u274C couldn't restore room '${roomId}' state:\\n${e.stack}`);\n }\n }\n\n // Reserve seats for clients from cached history.\n // Skip entries without a reconnectionToken \u2014 these are stale\n // seats from allowReconnection() where the client already left\n // (e.g. page refresh). Restoring them would block room disposal.\n if (roomHistory.clients) {\n for (const clientData of roomHistory.clients) {\n const { sessionId, reconnectionToken } = clientData;\n if (!reconnectionToken) { continue; }\n // TODO: need to restore each client's StateView as well\n await remoteRoomCall(recreatedRoomListing.roomId, '_reserveSeat', [sessionId, {}, {}, recreatedRoom.seatReservationTimeout, false, reconnectionToken]);\n }\n }\n\n // call `onRestoreRoom` with custom 'cache'd property.\n recreatedRoom.onRestoreRoom?.(roomHistory[\"cache\"]);\n\n logger.debug(`\uD83D\uDD04 room '${roomId}' has been restored with ${roomHistory.clients?.length || 0} reserved seats: ${roomHistory.clients?.map((c: any) => c.sessionId).join(\", \")}`);\n }\n\n if (roomHistoryList.length > 0) {\n logger.debug(\"\u2705\", roomHistoryList.length, \"room(s) have been restored.\");\n }\n}\n\nexport async function cacheRoomHistory(rooms: { [roomId: string]: Room }) {\n for (const room of Object.values(rooms)) {\n const roomHistoryResult = await presence.hget(getRoomRestoreListKey(), room.roomId);\n if (roomHistoryResult) {\n try {\n const roomHistory = JSON.parse(roomHistoryResult);\n\n // custom cache method\n roomHistory[\"cache\"] = room.onCacheRoom?.();\n\n // encode state\n debugDevMode(\"caching room %s (%s)\", room.roomName, room.roomId);\n\n if (room.state) {\n roomHistory[\"state\"] = JSON.stringify(room.state);\n\n // Cache the encoder's nextUniqueId so it can be restored.\n // This ensures refIds increase monotonically across HMR cycles,\n // preventing the client decoder from reusing stale refs that\n // happen to have the same refId as newly created instances.\n const encoderRoot = room.state[$changes]?.root;\n if (encoderRoot) {\n roomHistory[\"nextRefId\"] = encoderRoot['nextUniqueId'];\n }\n }\n\n // cache active clients with their reconnection tokens\n // TODO: need to cache each client's StateView as well\n const activeClients = room.clients.map((client) => ({\n sessionId: client.sessionId,\n reconnectionToken: client.reconnectionToken,\n }));\n\n // collect active client sessionIds to avoid duplicates\n const activeSessionIds = new Set(activeClients.map((c) => c.sessionId));\n\n // also cache reserved seats (they don't have reconnectionTokens yet)\n // filter out reserved seats that are already active clients (from devMode reconnection)\n const reservedSeats = Object.keys(room['_reservedSeats'])\n .filter((sessionId) => !activeSessionIds.has(sessionId))\n .map((sessionId) => ({\n sessionId,\n reconnectionToken: undefined,\n }));\n\n roomHistory[\"clients\"] = activeClients.concat(reservedSeats);\n\n await presence.hset(getRoomRestoreListKey(), room.roomId, JSON.stringify(roomHistory));\n\n // Rewrite updated room history\n logger.debug(`\uD83D\uDCBE caching room '${room.roomId}' (clients: ${room.clients.length}, has state: ${roomHistory[\"state\"] !== undefined ? \"yes\" : \"no\"})`);\n\n } catch (e: any) {\n debugAndPrintError(`\u274C couldn't cache room '${room.roomId}', due to:\\n${e.stack}`);\n }\n }\n }\n}\n\nexport function getRoomRestoreListKey() {\n return 'roomhistory';\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/build/utils/Env.cjs
CHANGED
|
@@ -36,29 +36,33 @@ __export(Env_exports, {
|
|
|
36
36
|
isColyseusCloud: () => isColyseusCloud
|
|
37
37
|
});
|
|
38
38
|
module.exports = __toCommonJS(Env_exports);
|
|
39
|
+
var import_os = __toESM(require("os"), 1);
|
|
39
40
|
var import_LocalPresence = require("../presence/LocalPresence.cjs");
|
|
40
41
|
var import_LocalDriver = require("../matchmaker/LocalDriver/LocalDriver.cjs");
|
|
41
42
|
var import_Logger = require("../Logger.cjs");
|
|
42
43
|
function isColyseusCloud() {
|
|
43
44
|
return process.env.COLYSEUS_CLOUD !== void 0;
|
|
44
45
|
}
|
|
46
|
+
function shouldUseRedisOnCloud() {
|
|
47
|
+
return import_os.default.cpus().length > 1 || process.env.REDIS_URI !== void 0;
|
|
48
|
+
}
|
|
45
49
|
async function getDefaultPresence() {
|
|
46
|
-
if (isColyseusCloud()) {
|
|
50
|
+
if (isColyseusCloud() && shouldUseRedisOnCloud()) {
|
|
47
51
|
try {
|
|
48
52
|
const RedisPresence = await import("@colyseus/redis-presence");
|
|
49
53
|
return new RedisPresence.RedisPresence(process.env.REDIS_URI);
|
|
50
54
|
} catch (e) {
|
|
51
55
|
console.error(e);
|
|
52
56
|
import_Logger.logger.warn("");
|
|
53
|
-
import_Logger.logger.warn("\u274C could not initialize
|
|
54
|
-
import_Logger.logger.warn("\u{1F449} npm install --save @colyseus/redis-
|
|
57
|
+
import_Logger.logger.warn("\u274C could not initialize RedisPresence.");
|
|
58
|
+
import_Logger.logger.warn("\u{1F449} npm install --save @colyseus/redis-presence");
|
|
55
59
|
import_Logger.logger.warn("");
|
|
56
60
|
}
|
|
57
61
|
}
|
|
58
62
|
return new import_LocalPresence.LocalPresence();
|
|
59
63
|
}
|
|
60
64
|
async function getDefaultDriver() {
|
|
61
|
-
if (isColyseusCloud()) {
|
|
65
|
+
if (isColyseusCloud() && shouldUseRedisOnCloud()) {
|
|
62
66
|
try {
|
|
63
67
|
const RedisDriver = await import("@colyseus/redis-driver");
|
|
64
68
|
return new RedisDriver.RedisDriver(process.env.REDIS_URI);
|
package/build/utils/Env.cjs.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/utils/Env.ts"],
|
|
4
|
-
"sourcesContent": ["import { LocalPresence } from '../presence/LocalPresence.ts';\nimport { LocalDriver, type MatchMakerDriver } from '../matchmaker/LocalDriver/LocalDriver.ts';\nimport { logger } from '../Logger.ts';\nimport type { Presence } from '../presence/Presence.ts';\n\nexport function isColyseusCloud(): boolean {\n return process.env.COLYSEUS_CLOUD !== undefined;\n}\n\nexport async function getDefaultPresence(): Promise<Presence> {\n if (isColyseusCloud()) {\n try {\n const RedisPresence = await import('@colyseus/redis-presence');\n return new RedisPresence.RedisPresence(process.env.REDIS_URI);\n } catch (e) {\n console.error(e);\n logger.warn(\"\");\n logger.warn(\"\u274C could not initialize
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,2BAA8B;AAC9B,yBAAmD;AACnD,oBAAuB;AAGhB,SAAS,kBAA2B;AACzC,SAAO,QAAQ,IAAI,mBAAmB;AACxC;AAEA,eAAsB,qBAAwC;AAC5D,MAAI,gBAAgB,GAAG;
|
|
6
|
-
"names": []
|
|
4
|
+
"sourcesContent": ["import os from 'os';\nimport { LocalPresence } from '../presence/LocalPresence.ts';\nimport { LocalDriver, type MatchMakerDriver } from '../matchmaker/LocalDriver/LocalDriver.ts';\nimport { logger } from '../Logger.ts';\nimport type { Presence } from '../presence/Presence.ts';\n\nexport function isColyseusCloud(): boolean {\n return process.env.COLYSEUS_CLOUD !== undefined;\n}\n\n// On Colyseus Cloud, only opt into Redis when there's more than one CPU\n// (multi-process deploys) or when REDIS_URI is explicitly provided.\n// Single-CPU instances without REDIS_URI keep using Local presence/driver.\nfunction shouldUseRedisOnCloud(): boolean {\n return os.cpus().length > 1 || process.env.REDIS_URI !== undefined;\n}\n\nexport async function getDefaultPresence(): Promise<Presence> {\n if (isColyseusCloud() && shouldUseRedisOnCloud()) {\n try {\n const RedisPresence = await import('@colyseus/redis-presence');\n return new RedisPresence.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 return new LocalPresence();\n}\n\nexport async function getDefaultDriver(): Promise<MatchMakerDriver> {\n if (isColyseusCloud() && shouldUseRedisOnCloud()) {\n try {\n const RedisDriver = await import('@colyseus/redis-driver');\n return new RedisDriver.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 return new LocalDriver();\n}\n\nexport function getDefaultPublicAddress(): string | undefined {\n if (isColyseusCloud()) {\n let port = 2567;\n\n //\n // Multiple processes: Use NODE_APP_INSTANCE to play nicely with pm2\n //\n port += Number(process.env.NODE_APP_INSTANCE || \"0\");\n\n return process.env.SUBDOMAIN + \".\" + process.env.SERVER_NAME + \"/\" + port;\n\n } else {\n return undefined;\n }\n}"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,gBAAe;AACf,2BAA8B;AAC9B,yBAAmD;AACnD,oBAAuB;AAGhB,SAAS,kBAA2B;AACzC,SAAO,QAAQ,IAAI,mBAAmB;AACxC;AAKA,SAAS,wBAAiC;AACxC,SAAO,UAAAA,QAAG,KAAK,EAAE,SAAS,KAAK,QAAQ,IAAI,cAAc;AAC3D;AAEA,eAAsB,qBAAwC;AAC5D,MAAI,gBAAgB,KAAK,sBAAsB,GAAG;AAChD,QAAI;AACF,YAAM,gBAAgB,MAAM,OAAO,0BAA0B;AAC7D,aAAO,IAAI,cAAc,cAAc,QAAQ,IAAI,SAAS;AAAA,IAC9D,SAAS,GAAG;AACV,cAAQ,MAAM,CAAC;AACf,2BAAO,KAAK,EAAE;AACd,2BAAO,KAAK,4CAAuC;AACnD,2BAAO,KAAK,uDAAgD;AAC5D,2BAAO,KAAK,EAAE;AAAA,IAChB;AAAA,EACF;AACA,SAAO,IAAI,mCAAc;AAC3B;AAEA,eAAsB,mBAA8C;AAClE,MAAI,gBAAgB,KAAK,sBAAsB,GAAG;AAChD,QAAI;AACF,YAAM,cAAc,MAAM,OAAO,wBAAwB;AACzD,aAAO,IAAI,YAAY,YAAY,QAAQ,IAAI,SAAS;AAAA,IAC1D,SAAS,GAAG;AACV,cAAQ,MAAM,CAAC;AACf,2BAAO,KAAK,EAAE;AACd,2BAAO,KAAK,0CAAqC;AACjD,2BAAO,KAAK,qDAA8C;AAC1D,2BAAO,KAAK,EAAE;AAAA,IAChB;AAAA,EACF;AACA,SAAO,IAAI,+BAAY;AACzB;AAEO,SAAS,0BAA8C;AAC5D,MAAI,gBAAgB,GAAG;AACrB,QAAI,OAAO;AAKX,YAAQ,OAAO,QAAQ,IAAI,qBAAqB,GAAG;AAEnD,WAAO,QAAQ,IAAI,YAAY,MAAM,QAAQ,IAAI,cAAc,MAAM;AAAA,EAEvE,OAAO;AACL,WAAO;AAAA,EACT;AACF;",
|
|
6
|
+
"names": ["os"]
|
|
7
7
|
}
|
package/build/utils/Env.mjs
CHANGED
|
@@ -1,27 +1,31 @@
|
|
|
1
1
|
// packages/core/src/utils/Env.ts
|
|
2
|
+
import os from "os";
|
|
2
3
|
import { LocalPresence } from "../presence/LocalPresence.mjs";
|
|
3
4
|
import { LocalDriver } from "../matchmaker/LocalDriver/LocalDriver.mjs";
|
|
4
5
|
import { logger } from "../Logger.mjs";
|
|
5
6
|
function isColyseusCloud() {
|
|
6
7
|
return process.env.COLYSEUS_CLOUD !== void 0;
|
|
7
8
|
}
|
|
9
|
+
function shouldUseRedisOnCloud() {
|
|
10
|
+
return os.cpus().length > 1 || process.env.REDIS_URI !== void 0;
|
|
11
|
+
}
|
|
8
12
|
async function getDefaultPresence() {
|
|
9
|
-
if (isColyseusCloud()) {
|
|
13
|
+
if (isColyseusCloud() && shouldUseRedisOnCloud()) {
|
|
10
14
|
try {
|
|
11
15
|
const RedisPresence = await import("@colyseus/redis-presence");
|
|
12
16
|
return new RedisPresence.RedisPresence(process.env.REDIS_URI);
|
|
13
17
|
} catch (e) {
|
|
14
18
|
console.error(e);
|
|
15
19
|
logger.warn("");
|
|
16
|
-
logger.warn("\u274C could not initialize
|
|
17
|
-
logger.warn("\u{1F449} npm install --save @colyseus/redis-
|
|
20
|
+
logger.warn("\u274C could not initialize RedisPresence.");
|
|
21
|
+
logger.warn("\u{1F449} npm install --save @colyseus/redis-presence");
|
|
18
22
|
logger.warn("");
|
|
19
23
|
}
|
|
20
24
|
}
|
|
21
25
|
return new LocalPresence();
|
|
22
26
|
}
|
|
23
27
|
async function getDefaultDriver() {
|
|
24
|
-
if (isColyseusCloud()) {
|
|
28
|
+
if (isColyseusCloud() && shouldUseRedisOnCloud()) {
|
|
25
29
|
try {
|
|
26
30
|
const RedisDriver = await import("@colyseus/redis-driver");
|
|
27
31
|
return new RedisDriver.RedisDriver(process.env.REDIS_URI);
|
package/build/utils/Env.mjs.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/utils/Env.ts"],
|
|
4
|
-
"sourcesContent": ["import { LocalPresence } from '../presence/LocalPresence.ts';\nimport { LocalDriver, type MatchMakerDriver } from '../matchmaker/LocalDriver/LocalDriver.ts';\nimport { logger } from '../Logger.ts';\nimport type { Presence } from '../presence/Presence.ts';\n\nexport function isColyseusCloud(): boolean {\n return process.env.COLYSEUS_CLOUD !== undefined;\n}\n\nexport async function getDefaultPresence(): Promise<Presence> {\n if (isColyseusCloud()) {\n try {\n const RedisPresence = await import('@colyseus/redis-presence');\n return new RedisPresence.RedisPresence(process.env.REDIS_URI);\n } catch (e) {\n console.error(e);\n logger.warn(\"\");\n logger.warn(\"\u274C could not initialize
|
|
5
|
-
"mappings": ";AAAA,SAAS,qBAAqB;AAC9B,SAAS,mBAA0C;AACnD,SAAS,cAAc;AAGhB,SAAS,kBAA2B;AACzC,SAAO,QAAQ,IAAI,mBAAmB;AACxC;AAEA,eAAsB,qBAAwC;AAC5D,MAAI,gBAAgB,GAAG;
|
|
4
|
+
"sourcesContent": ["import os from 'os';\nimport { LocalPresence } from '../presence/LocalPresence.ts';\nimport { LocalDriver, type MatchMakerDriver } from '../matchmaker/LocalDriver/LocalDriver.ts';\nimport { logger } from '../Logger.ts';\nimport type { Presence } from '../presence/Presence.ts';\n\nexport function isColyseusCloud(): boolean {\n return process.env.COLYSEUS_CLOUD !== undefined;\n}\n\n// On Colyseus Cloud, only opt into Redis when there's more than one CPU\n// (multi-process deploys) or when REDIS_URI is explicitly provided.\n// Single-CPU instances without REDIS_URI keep using Local presence/driver.\nfunction shouldUseRedisOnCloud(): boolean {\n return os.cpus().length > 1 || process.env.REDIS_URI !== undefined;\n}\n\nexport async function getDefaultPresence(): Promise<Presence> {\n if (isColyseusCloud() && shouldUseRedisOnCloud()) {\n try {\n const RedisPresence = await import('@colyseus/redis-presence');\n return new RedisPresence.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 return new LocalPresence();\n}\n\nexport async function getDefaultDriver(): Promise<MatchMakerDriver> {\n if (isColyseusCloud() && shouldUseRedisOnCloud()) {\n try {\n const RedisDriver = await import('@colyseus/redis-driver');\n return new RedisDriver.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 return new LocalDriver();\n}\n\nexport function getDefaultPublicAddress(): string | undefined {\n if (isColyseusCloud()) {\n let port = 2567;\n\n //\n // Multiple processes: Use NODE_APP_INSTANCE to play nicely with pm2\n //\n port += Number(process.env.NODE_APP_INSTANCE || \"0\");\n\n return process.env.SUBDOMAIN + \".\" + process.env.SERVER_NAME + \"/\" + port;\n\n } else {\n return undefined;\n }\n}"],
|
|
5
|
+
"mappings": ";AAAA,OAAO,QAAQ;AACf,SAAS,qBAAqB;AAC9B,SAAS,mBAA0C;AACnD,SAAS,cAAc;AAGhB,SAAS,kBAA2B;AACzC,SAAO,QAAQ,IAAI,mBAAmB;AACxC;AAKA,SAAS,wBAAiC;AACxC,SAAO,GAAG,KAAK,EAAE,SAAS,KAAK,QAAQ,IAAI,cAAc;AAC3D;AAEA,eAAsB,qBAAwC;AAC5D,MAAI,gBAAgB,KAAK,sBAAsB,GAAG;AAChD,QAAI;AACF,YAAM,gBAAgB,MAAM,OAAO,0BAA0B;AAC7D,aAAO,IAAI,cAAc,cAAc,QAAQ,IAAI,SAAS;AAAA,IAC9D,SAAS,GAAG;AACV,cAAQ,MAAM,CAAC;AACf,aAAO,KAAK,EAAE;AACd,aAAO,KAAK,4CAAuC;AACnD,aAAO,KAAK,uDAAgD;AAC5D,aAAO,KAAK,EAAE;AAAA,IAChB;AAAA,EACF;AACA,SAAO,IAAI,cAAc;AAC3B;AAEA,eAAsB,mBAA8C;AAClE,MAAI,gBAAgB,KAAK,sBAAsB,GAAG;AAChD,QAAI;AACF,YAAM,cAAc,MAAM,OAAO,wBAAwB;AACzD,aAAO,IAAI,YAAY,YAAY,QAAQ,IAAI,SAAS;AAAA,IAC1D,SAAS,GAAG;AACV,cAAQ,MAAM,CAAC;AACf,aAAO,KAAK,EAAE;AACd,aAAO,KAAK,0CAAqC;AACjD,aAAO,KAAK,qDAA8C;AAC1D,aAAO,KAAK,EAAE;AAAA,IAChB;AAAA,EACF;AACA,SAAO,IAAI,YAAY;AACzB;AAEO,SAAS,0BAA8C;AAC5D,MAAI,gBAAgB,GAAG;AACrB,QAAI,OAAO;AAKX,YAAQ,OAAO,QAAQ,IAAI,qBAAqB,GAAG;AAEnD,WAAO,QAAQ,IAAI,YAAY,MAAM,QAAQ,IAAI,cAAc,MAAM;AAAA,EAEvE,OAAO;AACL,WAAO;AAAA,EACT;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/build/utils/Utils.cjs
CHANGED
|
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// packages/core/src/utils/Utils.ts
|
|
21
21
|
var Utils_exports = {};
|
|
22
22
|
__export(Utils_exports, {
|
|
23
|
+
$METADATA: () => $METADATA,
|
|
23
24
|
Deferred: () => Deferred,
|
|
24
25
|
MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME: () => MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME,
|
|
25
26
|
REMOTE_ROOM_SHORT_TIMEOUT: () => REMOTE_ROOM_SHORT_TIMEOUT,
|
|
@@ -38,6 +39,7 @@ var import_RoomExceptions = require("../errors/RoomExceptions.cjs");
|
|
|
38
39
|
var import_Debug = require("../Debug.cjs");
|
|
39
40
|
var REMOTE_ROOM_SHORT_TIMEOUT = Number(process.env.COLYSEUS_PRESENCE_SHORT_TIMEOUT || 2e3);
|
|
40
41
|
var MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME = Number(process.env.COLYSEUS_MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME || 0.5);
|
|
42
|
+
var $METADATA = Symbol.metadata ?? /* @__PURE__ */ Symbol.for("Symbol.metadata");
|
|
41
43
|
function generateId(length = 9) {
|
|
42
44
|
return (0, import_nanoid.nanoid)(length);
|
|
43
45
|
}
|
|
@@ -152,6 +154,7 @@ function dynamicImport(moduleName) {
|
|
|
152
154
|
}
|
|
153
155
|
// Annotate the CommonJS export names for ESM import in node:
|
|
154
156
|
0 && (module.exports = {
|
|
157
|
+
$METADATA,
|
|
155
158
|
Deferred,
|
|
156
159
|
MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME,
|
|
157
160
|
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;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
|
}
|