@colyseus/core 0.17.42 → 0.18.0
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 +19 -6
- package/build/MatchMaker.cjs.map +2 -2
- package/build/MatchMaker.d.ts +10 -0
- package/build/MatchMaker.mjs +18 -6
- 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/Room.cjs +296 -19
- package/build/Room.cjs.map +3 -3
- package/build/Room.d.ts +186 -3
- package/build/Room.mjs +303 -21
- package/build/Room.mjs.map +3 -3
- 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 +40 -7
- package/build/Server.cjs.map +2 -2
- package/build/Server.d.ts +25 -0
- package/build/Server.mjs +41 -8
- package/build/Server.mjs.map +2 -2
- package/build/Transport.cjs +38 -2
- package/build/Transport.cjs.map +2 -2
- package/build/Transport.d.ts +40 -4
- package/build/Transport.mjs +38 -2
- package/build/Transport.mjs.map +2 -2
- package/build/index.cjs +11 -2
- package/build/index.cjs.map +2 -2
- package/build/index.d.ts +2 -1
- package/build/index.mjs +12 -2
- package/build/index.mjs.map +2 -2
- package/build/input/InputBuffer.cjs +113 -0
- package/build/input/InputBuffer.cjs.map +7 -0
- package/build/input/InputBuffer.d.ts +136 -0
- package/build/input/InputBuffer.mjs +86 -0
- package/build/input/InputBuffer.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/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/index.cjs +65 -4
- package/build/router/index.cjs.map +2 -2
- package/build/router/index.d.ts +30 -6
- package/build/router/index.mjs +66 -6
- package/build/router/index.mjs.map +3 -3
- 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/package.json +19 -14
- package/src/MatchMaker.ts +40 -6
- package/src/Protocol.ts +130 -59
- package/src/Room.ts +475 -22
- package/src/RoomPlugin.ts +563 -0
- package/src/Server.ts +72 -11
- package/src/Transport.ts +76 -8
- package/src/index.ts +10 -1
- package/src/input/InputBuffer.ts +192 -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/RelayRoom.ts +9 -15
- package/src/router/index.ts +112 -11
- package/src/utils/UserSessionIndex.ts +311 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// packages/core/src/input/InputBuffer.ts
|
|
21
|
+
var InputBuffer_exports = {};
|
|
22
|
+
__export(InputBuffer_exports, {
|
|
23
|
+
InputAccessorImpl: () => InputAccessorImpl,
|
|
24
|
+
InputBufferImpl: () => InputBufferImpl,
|
|
25
|
+
NO_OP_INPUT_ACCESSOR: () => NO_OP_INPUT_ACCESSOR
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(InputBuffer_exports);
|
|
28
|
+
var InputBufferImpl = class {
|
|
29
|
+
constructor(maxSize, seqField) {
|
|
30
|
+
this._items = [];
|
|
31
|
+
this._lastSeq = -Infinity;
|
|
32
|
+
this._maxSize = maxSize;
|
|
33
|
+
this._seqField = seqField;
|
|
34
|
+
}
|
|
35
|
+
push(snapshot) {
|
|
36
|
+
this._items.push(snapshot);
|
|
37
|
+
if (this._items.length > this._maxSize) {
|
|
38
|
+
this._items.shift();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/** Returns true if `value` hasn't been seen, and updates the last-seen marker. */
|
|
42
|
+
accept(value) {
|
|
43
|
+
if (value <= this._lastSeq) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
this._lastSeq = value;
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
drain() {
|
|
50
|
+
const out = this._items;
|
|
51
|
+
this._items = [];
|
|
52
|
+
return out;
|
|
53
|
+
}
|
|
54
|
+
peek() {
|
|
55
|
+
return this._items.slice();
|
|
56
|
+
}
|
|
57
|
+
at(value) {
|
|
58
|
+
if (this._seqField === void 0) {
|
|
59
|
+
return void 0;
|
|
60
|
+
}
|
|
61
|
+
for (let i = 0; i < this._items.length; i++) {
|
|
62
|
+
if (this._items[i][this._seqField] === value) {
|
|
63
|
+
return this._items[i];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return void 0;
|
|
67
|
+
}
|
|
68
|
+
get size() {
|
|
69
|
+
return this._items.length;
|
|
70
|
+
}
|
|
71
|
+
clear() {
|
|
72
|
+
this._items.length = 0;
|
|
73
|
+
this._lastSeq = -Infinity;
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
var InputAccessorImpl = class {
|
|
77
|
+
constructor(client) {
|
|
78
|
+
this._client = client;
|
|
79
|
+
}
|
|
80
|
+
get latest() {
|
|
81
|
+
return this._client._input;
|
|
82
|
+
}
|
|
83
|
+
at(value) {
|
|
84
|
+
return this._client._inputBuffer?.at(value);
|
|
85
|
+
}
|
|
86
|
+
drain() {
|
|
87
|
+
return this._client._inputBuffer?.drain() ?? [];
|
|
88
|
+
}
|
|
89
|
+
peek() {
|
|
90
|
+
return this._client._inputBuffer?.peek() ?? [];
|
|
91
|
+
}
|
|
92
|
+
get size() {
|
|
93
|
+
return this._client._inputBuffer?.size ?? 0;
|
|
94
|
+
}
|
|
95
|
+
clear() {
|
|
96
|
+
this._client._inputBuffer?.clear();
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
var NO_OP_INPUT_ACCESSOR = Object.freeze({
|
|
100
|
+
latest: void 0,
|
|
101
|
+
at: () => void 0,
|
|
102
|
+
drain: () => [],
|
|
103
|
+
peek: () => [],
|
|
104
|
+
size: 0,
|
|
105
|
+
clear: () => {
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
109
|
+
0 && (module.exports = {
|
|
110
|
+
InputAccessorImpl,
|
|
111
|
+
InputBufferImpl,
|
|
112
|
+
NO_OP_INPUT_ACCESSOR
|
|
113
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/input/InputBuffer.ts"],
|
|
4
|
+
"sourcesContent": ["import type { ClientPrivate } from '../Transport.ts';\n\n/**\n * Names of fields on `I` whose values are `number` \u2014 used by\n * `Room.defineInput()` to constrain `seqField` to actually-numeric fields\n * on the input schema. Filters out booleans, strings, methods, etc.\n */\nexport type NumericFieldsOf<I> = {\n [K in keyof I]: I[K] extends number ? (K extends string ? K : never) : never;\n}[keyof I];\n\n/**\n * Internal: input configuration captured by `Room.defineInput()`. The schema\n * constructor is stored here so the runtime doesn't need to know it through\n * the public `room.input` (which is now a callable accessor).\n *\n * @internal\n */\nexport interface InputOptions {\n /**\n * Schema constructor used to allocate per-client input instances on join.\n * Captured by `defineInput()` from its `type` argument.\n *\n * Typed loosely (`new () => any`) to sidestep type-identity issues across\n * duplicate `@colyseus/schema` installs; the runtime calls\n * `instance.clone()` and friends, which match by shape.\n */\n ctor: new () => any;\n\n /**\n * Name of a monotonically-increasing numeric field on the input schema used\n * to order and dedupe incoming frames. When set, the framework:\n * - Drops redundant frames (`input[seqField]` \u2264 the last-seen value are\n * discarded before they enter the buffer). Matches the unreliable-mode\n * ring-redundancy pattern out of the box.\n * - Powers `room.input(sessionId).at(value)` lookups.\n *\n * Despite the name, \"seq\" here is broader than an integer counter \u2014 any\n * monotonic numeric field works:\n * - **Sequence counter** (`\"seq\"`, `\"tick\"`, `\"frame\"`) \u2014 typical for\n * lockstep / rollback netcode (Photon Quantum, GGPO).\n * - **Timestamp** (`\"timestamp\"`, milliseconds or seconds) \u2014 useful for\n * variable-rate clients, lag compensation, hit registration (Unreal CMC\n * uses float-seconds timestamps via `FSavedMove_Character.TimeStamp`).\n *\n * Whichever you use, the field must increase monotonically across frames\n * for dedupe to work.\n */\n seqField?: string;\n\n /**\n * > 0 enables per-client buffering of cloned snapshots \u2014 required for\n * `room.input(sessionId).drain() / .peek() / .at()` to return populated\n * data. Oldest drops on overflow. Set to `0` to disable (`.latest` still\n * works).\n */\n bufferMaxSize: number;\n}\n\n/**\n * Per-client input accessor returned by `room.input(sessionId)`. Combines the\n * latest decoded instance with the (optional) snapshot ring buffer.\n *\n * - {@link latest} \u2014 the bound Schema instance, mutated in place by the\n * decoder. Cheapest read; use when only the most recent state matters.\n * - {@link drain} / {@link peek} / {@link at} \u2014 populated when\n * `defineInput()` was called with `bufferMaxSize > 0` (default 32).\n * Use for rollback netcode / lockstep where every frame matters.\n *\n * Returned for unknown sessionIds and rooms without `defineInput()` is a\n * frozen no-op accessor (latest=undefined, drain/peek=[], at=undefined,\n * size=0, clear=no-op).\n */\nexport interface InputAccessor<I = any> {\n /** Latest decoded input. `undefined` when unknown sessionId or no input declared. */\n readonly latest: I | undefined;\n\n /**\n * Find the buffered snapshot whose `[seqField]` equals `value`. The field\n * name is the Room's `defineInput()` `seqField`. Linear scan \u2014 cheap for\n * typical buffer sizes; not intended for very large rings. Returns\n * `undefined` when no match is buffered (or `seqField` isn't configured).\n *\n * Useful for tick-aligned retrieval (lockstep, rollback).\n */\n at(value: number): I | undefined;\n\n /** Take everything buffered (oldest \u2192 newest) and clear. Snapshots are safe to retain. */\n drain(): I[];\n\n /** Read everything buffered without consuming. */\n peek(): I[];\n\n /** Number of snapshots currently buffered. */\n readonly size: number;\n\n /** Drop all buffered snapshots (also resets the dedupe tracker). */\n clear(): void;\n}\n\n/**\n * Callable returned by `Room.defineInput()`. Assign it to `this.input` and\n * call `room.input(sessionId)` per tick to read each client's latest input\n * and/or buffered snapshots.\n */\nexport type InputAPI<I = any> = (sessionId: string) => InputAccessor<I>;\n\n/** @internal */\nexport class InputBufferImpl<I = any> {\n private _items: I[] = [];\n private _lastSeq: number = -Infinity;\n private readonly _maxSize: number;\n private readonly _seqField: string | undefined;\n\n constructor(maxSize: number, seqField: string | undefined) {\n this._maxSize = maxSize;\n this._seqField = seqField;\n }\n\n push(snapshot: I): void {\n this._items.push(snapshot);\n if (this._items.length > this._maxSize) { this._items.shift(); }\n }\n\n /** Returns true if `value` hasn't been seen, and updates the last-seen marker. */\n accept(value: number): boolean {\n if (value <= this._lastSeq) { return false; }\n this._lastSeq = value;\n return true;\n }\n\n drain(): I[] {\n const out = this._items;\n this._items = [];\n return out;\n }\n\n peek(): I[] {\n return this._items.slice();\n }\n\n at(value: number): I | undefined {\n if (this._seqField === undefined) { return undefined; }\n for (let i = 0; i < this._items.length; i++) {\n if ((this._items[i] as any)[this._seqField] === value) { return this._items[i]; }\n }\n return undefined;\n }\n\n get size(): number {\n return this._items.length;\n }\n\n clear(): void {\n this._items.length = 0;\n this._lastSeq = -Infinity;\n }\n}\n\n/**\n * Default per-client accessor. Reads `_input` and `_inputBuffer` off the\n * client at access time \u2014 both are nullable until the room declares input\n * via `defineInput()`. Cached as `client._inputAccessor` at join, so\n * `room.input(sessionId)` is a Map lookup + property read.\n *\n * @internal\n */\nexport class InputAccessorImpl<I = any> implements InputAccessor<I> {\n private _client: ClientPrivate;\n constructor(client: ClientPrivate) { this._client = client; }\n get latest(): I | undefined { return this._client._input as I | undefined; }\n at(value: number): I | undefined { return this._client._inputBuffer?.at(value) as I | undefined; }\n drain(): I[] { return (this._client._inputBuffer?.drain() ?? []) as I[]; }\n peek(): I[] { return (this._client._inputBuffer?.peek() ?? []) as I[]; }\n get size(): number { return this._client._inputBuffer?.size ?? 0; }\n clear(): void { this._client._inputBuffer?.clear(); }\n}\n\n/**\n * Returned by `room.input(sessionId)` for unknown sessions and for rooms\n * that didn't call `defineInput()`.\n *\n * @internal\n */\nexport const NO_OP_INPUT_ACCESSOR: InputAccessor<any> = Object.freeze({\n latest: undefined,\n at: () => undefined,\n drain: () => [],\n peek: () => [],\n size: 0,\n clear: () => {},\n});\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4GO,IAAM,kBAAN,MAA+B;AAAA,EAMpC,YAAY,SAAiB,UAA8B;AAL3D,SAAQ,SAAc,CAAC;AACvB,SAAQ,WAAmB;AAKzB,SAAK,WAAW;AAChB,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,KAAK,UAAmB;AACtB,SAAK,OAAO,KAAK,QAAQ;AACzB,QAAI,KAAK,OAAO,SAAS,KAAK,UAAU;AAAE,WAAK,OAAO,MAAM;AAAA,IAAG;AAAA,EACjE;AAAA;AAAA,EAGA,OAAO,OAAwB;AAC7B,QAAI,SAAS,KAAK,UAAU;AAAE,aAAO;AAAA,IAAO;AAC5C,SAAK,WAAW;AAChB,WAAO;AAAA,EACT;AAAA,EAEA,QAAa;AACX,UAAM,MAAM,KAAK;AACjB,SAAK,SAAS,CAAC;AACf,WAAO;AAAA,EACT;AAAA,EAEA,OAAY;AACV,WAAO,KAAK,OAAO,MAAM;AAAA,EAC3B;AAAA,EAEA,GAAG,OAA8B;AAC/B,QAAI,KAAK,cAAc,QAAW;AAAE,aAAO;AAAA,IAAW;AACtD,aAAS,IAAI,GAAG,IAAI,KAAK,OAAO,QAAQ,KAAK;AAC3C,UAAK,KAAK,OAAO,CAAC,EAAU,KAAK,SAAS,MAAM,OAAO;AAAE,eAAO,KAAK,OAAO,CAAC;AAAA,MAAG;AAAA,IAClF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,IAAI,OAAe;AACjB,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA,EAEA,QAAc;AACZ,SAAK,OAAO,SAAS;AACrB,SAAK,WAAW;AAAA,EAClB;AACF;AAUO,IAAM,oBAAN,MAA6D;AAAA,EAElE,YAAY,QAAuB;AAAE,SAAK,UAAU;AAAA,EAAQ;AAAA,EAC5D,IAAI,SAAwB;AAAE,WAAO,KAAK,QAAQ;AAAA,EAAyB;AAAA,EAC3E,GAAG,OAA8B;AAAE,WAAO,KAAK,QAAQ,cAAc,GAAG,KAAK;AAAA,EAAoB;AAAA,EACjG,QAAa;AAAE,WAAQ,KAAK,QAAQ,cAAc,MAAM,KAAK,CAAC;AAAA,EAAW;AAAA,EACzE,OAAY;AAAE,WAAQ,KAAK,QAAQ,cAAc,KAAK,KAAK,CAAC;AAAA,EAAW;AAAA,EACvE,IAAI,OAAe;AAAE,WAAO,KAAK,QAAQ,cAAc,QAAQ;AAAA,EAAG;AAAA,EAClE,QAAc;AAAE,SAAK,QAAQ,cAAc,MAAM;AAAA,EAAG;AACtD;AAQO,IAAM,uBAA2C,OAAO,OAAO;AAAA,EACpE,QAAQ;AAAA,EACR,IAAI,MAAM;AAAA,EACV,OAAO,MAAM,CAAC;AAAA,EACd,MAAM,MAAM,CAAC;AAAA,EACb,MAAM;AAAA,EACN,OAAO,MAAM;AAAA,EAAC;AAChB,CAAC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import type { ClientPrivate } from '../Transport.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Names of fields on `I` whose values are `number` — used by
|
|
4
|
+
* `Room.defineInput()` to constrain `seqField` to actually-numeric fields
|
|
5
|
+
* on the input schema. Filters out booleans, strings, methods, etc.
|
|
6
|
+
*/
|
|
7
|
+
export type NumericFieldsOf<I> = {
|
|
8
|
+
[K in keyof I]: I[K] extends number ? (K extends string ? K : never) : never;
|
|
9
|
+
}[keyof I];
|
|
10
|
+
/**
|
|
11
|
+
* Internal: input configuration captured by `Room.defineInput()`. The schema
|
|
12
|
+
* constructor is stored here so the runtime doesn't need to know it through
|
|
13
|
+
* the public `room.input` (which is now a callable accessor).
|
|
14
|
+
*
|
|
15
|
+
* @internal
|
|
16
|
+
*/
|
|
17
|
+
export interface InputOptions {
|
|
18
|
+
/**
|
|
19
|
+
* Schema constructor used to allocate per-client input instances on join.
|
|
20
|
+
* Captured by `defineInput()` from its `type` argument.
|
|
21
|
+
*
|
|
22
|
+
* Typed loosely (`new () => any`) to sidestep type-identity issues across
|
|
23
|
+
* duplicate `@colyseus/schema` installs; the runtime calls
|
|
24
|
+
* `instance.clone()` and friends, which match by shape.
|
|
25
|
+
*/
|
|
26
|
+
ctor: new () => any;
|
|
27
|
+
/**
|
|
28
|
+
* Name of a monotonically-increasing numeric field on the input schema used
|
|
29
|
+
* to order and dedupe incoming frames. When set, the framework:
|
|
30
|
+
* - Drops redundant frames (`input[seqField]` ≤ the last-seen value are
|
|
31
|
+
* discarded before they enter the buffer). Matches the unreliable-mode
|
|
32
|
+
* ring-redundancy pattern out of the box.
|
|
33
|
+
* - Powers `room.input(sessionId).at(value)` lookups.
|
|
34
|
+
*
|
|
35
|
+
* Despite the name, "seq" here is broader than an integer counter — any
|
|
36
|
+
* monotonic numeric field works:
|
|
37
|
+
* - **Sequence counter** (`"seq"`, `"tick"`, `"frame"`) — typical for
|
|
38
|
+
* lockstep / rollback netcode (Photon Quantum, GGPO).
|
|
39
|
+
* - **Timestamp** (`"timestamp"`, milliseconds or seconds) — useful for
|
|
40
|
+
* variable-rate clients, lag compensation, hit registration (Unreal CMC
|
|
41
|
+
* uses float-seconds timestamps via `FSavedMove_Character.TimeStamp`).
|
|
42
|
+
*
|
|
43
|
+
* Whichever you use, the field must increase monotonically across frames
|
|
44
|
+
* for dedupe to work.
|
|
45
|
+
*/
|
|
46
|
+
seqField?: string;
|
|
47
|
+
/**
|
|
48
|
+
* > 0 enables per-client buffering of cloned snapshots — required for
|
|
49
|
+
* `room.input(sessionId).drain() / .peek() / .at()` to return populated
|
|
50
|
+
* data. Oldest drops on overflow. Set to `0` to disable (`.latest` still
|
|
51
|
+
* works).
|
|
52
|
+
*/
|
|
53
|
+
bufferMaxSize: number;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Per-client input accessor returned by `room.input(sessionId)`. Combines the
|
|
57
|
+
* latest decoded instance with the (optional) snapshot ring buffer.
|
|
58
|
+
*
|
|
59
|
+
* - {@link latest} — the bound Schema instance, mutated in place by the
|
|
60
|
+
* decoder. Cheapest read; use when only the most recent state matters.
|
|
61
|
+
* - {@link drain} / {@link peek} / {@link at} — populated when
|
|
62
|
+
* `defineInput()` was called with `bufferMaxSize > 0` (default 32).
|
|
63
|
+
* Use for rollback netcode / lockstep where every frame matters.
|
|
64
|
+
*
|
|
65
|
+
* Returned for unknown sessionIds and rooms without `defineInput()` is a
|
|
66
|
+
* frozen no-op accessor (latest=undefined, drain/peek=[], at=undefined,
|
|
67
|
+
* size=0, clear=no-op).
|
|
68
|
+
*/
|
|
69
|
+
export interface InputAccessor<I = any> {
|
|
70
|
+
/** Latest decoded input. `undefined` when unknown sessionId or no input declared. */
|
|
71
|
+
readonly latest: I | undefined;
|
|
72
|
+
/**
|
|
73
|
+
* Find the buffered snapshot whose `[seqField]` equals `value`. The field
|
|
74
|
+
* name is the Room's `defineInput()` `seqField`. Linear scan — cheap for
|
|
75
|
+
* typical buffer sizes; not intended for very large rings. Returns
|
|
76
|
+
* `undefined` when no match is buffered (or `seqField` isn't configured).
|
|
77
|
+
*
|
|
78
|
+
* Useful for tick-aligned retrieval (lockstep, rollback).
|
|
79
|
+
*/
|
|
80
|
+
at(value: number): I | undefined;
|
|
81
|
+
/** Take everything buffered (oldest → newest) and clear. Snapshots are safe to retain. */
|
|
82
|
+
drain(): I[];
|
|
83
|
+
/** Read everything buffered without consuming. */
|
|
84
|
+
peek(): I[];
|
|
85
|
+
/** Number of snapshots currently buffered. */
|
|
86
|
+
readonly size: number;
|
|
87
|
+
/** Drop all buffered snapshots (also resets the dedupe tracker). */
|
|
88
|
+
clear(): void;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Callable returned by `Room.defineInput()`. Assign it to `this.input` and
|
|
92
|
+
* call `room.input(sessionId)` per tick to read each client's latest input
|
|
93
|
+
* and/or buffered snapshots.
|
|
94
|
+
*/
|
|
95
|
+
export type InputAPI<I = any> = (sessionId: string) => InputAccessor<I>;
|
|
96
|
+
/** @internal */
|
|
97
|
+
export declare class InputBufferImpl<I = any> {
|
|
98
|
+
private _items;
|
|
99
|
+
private _lastSeq;
|
|
100
|
+
private readonly _maxSize;
|
|
101
|
+
private readonly _seqField;
|
|
102
|
+
constructor(maxSize: number, seqField: string | undefined);
|
|
103
|
+
push(snapshot: I): void;
|
|
104
|
+
/** Returns true if `value` hasn't been seen, and updates the last-seen marker. */
|
|
105
|
+
accept(value: number): boolean;
|
|
106
|
+
drain(): I[];
|
|
107
|
+
peek(): I[];
|
|
108
|
+
at(value: number): I | undefined;
|
|
109
|
+
get size(): number;
|
|
110
|
+
clear(): void;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Default per-client accessor. Reads `_input` and `_inputBuffer` off the
|
|
114
|
+
* client at access time — both are nullable until the room declares input
|
|
115
|
+
* via `defineInput()`. Cached as `client._inputAccessor` at join, so
|
|
116
|
+
* `room.input(sessionId)` is a Map lookup + property read.
|
|
117
|
+
*
|
|
118
|
+
* @internal
|
|
119
|
+
*/
|
|
120
|
+
export declare class InputAccessorImpl<I = any> implements InputAccessor<I> {
|
|
121
|
+
private _client;
|
|
122
|
+
constructor(client: ClientPrivate);
|
|
123
|
+
get latest(): I | undefined;
|
|
124
|
+
at(value: number): I | undefined;
|
|
125
|
+
drain(): I[];
|
|
126
|
+
peek(): I[];
|
|
127
|
+
get size(): number;
|
|
128
|
+
clear(): void;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Returned by `room.input(sessionId)` for unknown sessions and for rooms
|
|
132
|
+
* that didn't call `defineInput()`.
|
|
133
|
+
*
|
|
134
|
+
* @internal
|
|
135
|
+
*/
|
|
136
|
+
export declare const NO_OP_INPUT_ACCESSOR: InputAccessor<any>;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// packages/core/src/input/InputBuffer.ts
|
|
2
|
+
var InputBufferImpl = class {
|
|
3
|
+
constructor(maxSize, seqField) {
|
|
4
|
+
this._items = [];
|
|
5
|
+
this._lastSeq = -Infinity;
|
|
6
|
+
this._maxSize = maxSize;
|
|
7
|
+
this._seqField = seqField;
|
|
8
|
+
}
|
|
9
|
+
push(snapshot) {
|
|
10
|
+
this._items.push(snapshot);
|
|
11
|
+
if (this._items.length > this._maxSize) {
|
|
12
|
+
this._items.shift();
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/** Returns true if `value` hasn't been seen, and updates the last-seen marker. */
|
|
16
|
+
accept(value) {
|
|
17
|
+
if (value <= this._lastSeq) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
this._lastSeq = value;
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
drain() {
|
|
24
|
+
const out = this._items;
|
|
25
|
+
this._items = [];
|
|
26
|
+
return out;
|
|
27
|
+
}
|
|
28
|
+
peek() {
|
|
29
|
+
return this._items.slice();
|
|
30
|
+
}
|
|
31
|
+
at(value) {
|
|
32
|
+
if (this._seqField === void 0) {
|
|
33
|
+
return void 0;
|
|
34
|
+
}
|
|
35
|
+
for (let i = 0; i < this._items.length; i++) {
|
|
36
|
+
if (this._items[i][this._seqField] === value) {
|
|
37
|
+
return this._items[i];
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return void 0;
|
|
41
|
+
}
|
|
42
|
+
get size() {
|
|
43
|
+
return this._items.length;
|
|
44
|
+
}
|
|
45
|
+
clear() {
|
|
46
|
+
this._items.length = 0;
|
|
47
|
+
this._lastSeq = -Infinity;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
var InputAccessorImpl = class {
|
|
51
|
+
constructor(client) {
|
|
52
|
+
this._client = client;
|
|
53
|
+
}
|
|
54
|
+
get latest() {
|
|
55
|
+
return this._client._input;
|
|
56
|
+
}
|
|
57
|
+
at(value) {
|
|
58
|
+
return this._client._inputBuffer?.at(value);
|
|
59
|
+
}
|
|
60
|
+
drain() {
|
|
61
|
+
return this._client._inputBuffer?.drain() ?? [];
|
|
62
|
+
}
|
|
63
|
+
peek() {
|
|
64
|
+
return this._client._inputBuffer?.peek() ?? [];
|
|
65
|
+
}
|
|
66
|
+
get size() {
|
|
67
|
+
return this._client._inputBuffer?.size ?? 0;
|
|
68
|
+
}
|
|
69
|
+
clear() {
|
|
70
|
+
this._client._inputBuffer?.clear();
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
var NO_OP_INPUT_ACCESSOR = Object.freeze({
|
|
74
|
+
latest: void 0,
|
|
75
|
+
at: () => void 0,
|
|
76
|
+
drain: () => [],
|
|
77
|
+
peek: () => [],
|
|
78
|
+
size: 0,
|
|
79
|
+
clear: () => {
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
export {
|
|
83
|
+
InputAccessorImpl,
|
|
84
|
+
InputBufferImpl,
|
|
85
|
+
NO_OP_INPUT_ACCESSOR
|
|
86
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/input/InputBuffer.ts"],
|
|
4
|
+
"sourcesContent": ["import type { ClientPrivate } from '../Transport.ts';\n\n/**\n * Names of fields on `I` whose values are `number` \u2014 used by\n * `Room.defineInput()` to constrain `seqField` to actually-numeric fields\n * on the input schema. Filters out booleans, strings, methods, etc.\n */\nexport type NumericFieldsOf<I> = {\n [K in keyof I]: I[K] extends number ? (K extends string ? K : never) : never;\n}[keyof I];\n\n/**\n * Internal: input configuration captured by `Room.defineInput()`. The schema\n * constructor is stored here so the runtime doesn't need to know it through\n * the public `room.input` (which is now a callable accessor).\n *\n * @internal\n */\nexport interface InputOptions {\n /**\n * Schema constructor used to allocate per-client input instances on join.\n * Captured by `defineInput()` from its `type` argument.\n *\n * Typed loosely (`new () => any`) to sidestep type-identity issues across\n * duplicate `@colyseus/schema` installs; the runtime calls\n * `instance.clone()` and friends, which match by shape.\n */\n ctor: new () => any;\n\n /**\n * Name of a monotonically-increasing numeric field on the input schema used\n * to order and dedupe incoming frames. When set, the framework:\n * - Drops redundant frames (`input[seqField]` \u2264 the last-seen value are\n * discarded before they enter the buffer). Matches the unreliable-mode\n * ring-redundancy pattern out of the box.\n * - Powers `room.input(sessionId).at(value)` lookups.\n *\n * Despite the name, \"seq\" here is broader than an integer counter \u2014 any\n * monotonic numeric field works:\n * - **Sequence counter** (`\"seq\"`, `\"tick\"`, `\"frame\"`) \u2014 typical for\n * lockstep / rollback netcode (Photon Quantum, GGPO).\n * - **Timestamp** (`\"timestamp\"`, milliseconds or seconds) \u2014 useful for\n * variable-rate clients, lag compensation, hit registration (Unreal CMC\n * uses float-seconds timestamps via `FSavedMove_Character.TimeStamp`).\n *\n * Whichever you use, the field must increase monotonically across frames\n * for dedupe to work.\n */\n seqField?: string;\n\n /**\n * > 0 enables per-client buffering of cloned snapshots \u2014 required for\n * `room.input(sessionId).drain() / .peek() / .at()` to return populated\n * data. Oldest drops on overflow. Set to `0` to disable (`.latest` still\n * works).\n */\n bufferMaxSize: number;\n}\n\n/**\n * Per-client input accessor returned by `room.input(sessionId)`. Combines the\n * latest decoded instance with the (optional) snapshot ring buffer.\n *\n * - {@link latest} \u2014 the bound Schema instance, mutated in place by the\n * decoder. Cheapest read; use when only the most recent state matters.\n * - {@link drain} / {@link peek} / {@link at} \u2014 populated when\n * `defineInput()` was called with `bufferMaxSize > 0` (default 32).\n * Use for rollback netcode / lockstep where every frame matters.\n *\n * Returned for unknown sessionIds and rooms without `defineInput()` is a\n * frozen no-op accessor (latest=undefined, drain/peek=[], at=undefined,\n * size=0, clear=no-op).\n */\nexport interface InputAccessor<I = any> {\n /** Latest decoded input. `undefined` when unknown sessionId or no input declared. */\n readonly latest: I | undefined;\n\n /**\n * Find the buffered snapshot whose `[seqField]` equals `value`. The field\n * name is the Room's `defineInput()` `seqField`. Linear scan \u2014 cheap for\n * typical buffer sizes; not intended for very large rings. Returns\n * `undefined` when no match is buffered (or `seqField` isn't configured).\n *\n * Useful for tick-aligned retrieval (lockstep, rollback).\n */\n at(value: number): I | undefined;\n\n /** Take everything buffered (oldest \u2192 newest) and clear. Snapshots are safe to retain. */\n drain(): I[];\n\n /** Read everything buffered without consuming. */\n peek(): I[];\n\n /** Number of snapshots currently buffered. */\n readonly size: number;\n\n /** Drop all buffered snapshots (also resets the dedupe tracker). */\n clear(): void;\n}\n\n/**\n * Callable returned by `Room.defineInput()`. Assign it to `this.input` and\n * call `room.input(sessionId)` per tick to read each client's latest input\n * and/or buffered snapshots.\n */\nexport type InputAPI<I = any> = (sessionId: string) => InputAccessor<I>;\n\n/** @internal */\nexport class InputBufferImpl<I = any> {\n private _items: I[] = [];\n private _lastSeq: number = -Infinity;\n private readonly _maxSize: number;\n private readonly _seqField: string | undefined;\n\n constructor(maxSize: number, seqField: string | undefined) {\n this._maxSize = maxSize;\n this._seqField = seqField;\n }\n\n push(snapshot: I): void {\n this._items.push(snapshot);\n if (this._items.length > this._maxSize) { this._items.shift(); }\n }\n\n /** Returns true if `value` hasn't been seen, and updates the last-seen marker. */\n accept(value: number): boolean {\n if (value <= this._lastSeq) { return false; }\n this._lastSeq = value;\n return true;\n }\n\n drain(): I[] {\n const out = this._items;\n this._items = [];\n return out;\n }\n\n peek(): I[] {\n return this._items.slice();\n }\n\n at(value: number): I | undefined {\n if (this._seqField === undefined) { return undefined; }\n for (let i = 0; i < this._items.length; i++) {\n if ((this._items[i] as any)[this._seqField] === value) { return this._items[i]; }\n }\n return undefined;\n }\n\n get size(): number {\n return this._items.length;\n }\n\n clear(): void {\n this._items.length = 0;\n this._lastSeq = -Infinity;\n }\n}\n\n/**\n * Default per-client accessor. Reads `_input` and `_inputBuffer` off the\n * client at access time \u2014 both are nullable until the room declares input\n * via `defineInput()`. Cached as `client._inputAccessor` at join, so\n * `room.input(sessionId)` is a Map lookup + property read.\n *\n * @internal\n */\nexport class InputAccessorImpl<I = any> implements InputAccessor<I> {\n private _client: ClientPrivate;\n constructor(client: ClientPrivate) { this._client = client; }\n get latest(): I | undefined { return this._client._input as I | undefined; }\n at(value: number): I | undefined { return this._client._inputBuffer?.at(value) as I | undefined; }\n drain(): I[] { return (this._client._inputBuffer?.drain() ?? []) as I[]; }\n peek(): I[] { return (this._client._inputBuffer?.peek() ?? []) as I[]; }\n get size(): number { return this._client._inputBuffer?.size ?? 0; }\n clear(): void { this._client._inputBuffer?.clear(); }\n}\n\n/**\n * Returned by `room.input(sessionId)` for unknown sessions and for rooms\n * that didn't call `defineInput()`.\n *\n * @internal\n */\nexport const NO_OP_INPUT_ACCESSOR: InputAccessor<any> = Object.freeze({\n latest: undefined,\n at: () => undefined,\n drain: () => [],\n peek: () => [],\n size: 0,\n clear: () => {},\n});\n"],
|
|
5
|
+
"mappings": ";AA4GO,IAAM,kBAAN,MAA+B;AAAA,EAMpC,YAAY,SAAiB,UAA8B;AAL3D,SAAQ,SAAc,CAAC;AACvB,SAAQ,WAAmB;AAKzB,SAAK,WAAW;AAChB,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,KAAK,UAAmB;AACtB,SAAK,OAAO,KAAK,QAAQ;AACzB,QAAI,KAAK,OAAO,SAAS,KAAK,UAAU;AAAE,WAAK,OAAO,MAAM;AAAA,IAAG;AAAA,EACjE;AAAA;AAAA,EAGA,OAAO,OAAwB;AAC7B,QAAI,SAAS,KAAK,UAAU;AAAE,aAAO;AAAA,IAAO;AAC5C,SAAK,WAAW;AAChB,WAAO;AAAA,EACT;AAAA,EAEA,QAAa;AACX,UAAM,MAAM,KAAK;AACjB,SAAK,SAAS,CAAC;AACf,WAAO;AAAA,EACT;AAAA,EAEA,OAAY;AACV,WAAO,KAAK,OAAO,MAAM;AAAA,EAC3B;AAAA,EAEA,GAAG,OAA8B;AAC/B,QAAI,KAAK,cAAc,QAAW;AAAE,aAAO;AAAA,IAAW;AACtD,aAAS,IAAI,GAAG,IAAI,KAAK,OAAO,QAAQ,KAAK;AAC3C,UAAK,KAAK,OAAO,CAAC,EAAU,KAAK,SAAS,MAAM,OAAO;AAAE,eAAO,KAAK,OAAO,CAAC;AAAA,MAAG;AAAA,IAClF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,IAAI,OAAe;AACjB,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA,EAEA,QAAc;AACZ,SAAK,OAAO,SAAS;AACrB,SAAK,WAAW;AAAA,EAClB;AACF;AAUO,IAAM,oBAAN,MAA6D;AAAA,EAElE,YAAY,QAAuB;AAAE,SAAK,UAAU;AAAA,EAAQ;AAAA,EAC5D,IAAI,SAAwB;AAAE,WAAO,KAAK,QAAQ;AAAA,EAAyB;AAAA,EAC3E,GAAG,OAA8B;AAAE,WAAO,KAAK,QAAQ,cAAc,GAAG,KAAK;AAAA,EAAoB;AAAA,EACjG,QAAa;AAAE,WAAQ,KAAK,QAAQ,cAAc,MAAM,KAAK,CAAC;AAAA,EAAW;AAAA,EACzE,OAAY;AAAE,WAAQ,KAAK,QAAQ,cAAc,KAAK,KAAK,CAAC;AAAA,EAAW;AAAA,EACvE,IAAI,OAAe;AAAE,WAAO,KAAK,QAAQ,cAAc,QAAQ;AAAA,EAAG;AAAA,EAClE,QAAc;AAAE,SAAK,QAAQ,cAAc,MAAM;AAAA,EAAG;AACtD;AAQO,IAAM,uBAA2C,OAAO,OAAO;AAAA,EACpE,QAAQ;AAAA,EACR,IAAI,MAAM;AAAA,EACV,OAAO,MAAM,CAAC;AAAA,EACd,MAAM,MAAM,CAAC;AAAA,EACb,MAAM;AAAA,EACN,OAAO,MAAM;AAAA,EAAC;AAChB,CAAC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// packages/core/src/internal.ts
|
|
31
|
+
var internal_exports = {};
|
|
32
|
+
__export(internal_exports, {
|
|
33
|
+
USER_ROOMS_KEY_PREFIX: () => import_UserSessionIndex2.USER_ROOMS_KEY_PREFIX,
|
|
34
|
+
listUserSessions: () => import_UserSessionIndex2.listUserSessions,
|
|
35
|
+
listUserSessionsLive: () => listUserSessionsLive,
|
|
36
|
+
releaseRoomLeave: () => import_UserSessionIndex2.releaseRoomLeave,
|
|
37
|
+
releaseUserSession: () => import_UserSessionIndex2.releaseUserSession,
|
|
38
|
+
sweepRoomDispose: () => import_UserSessionIndex2.sweepRoomDispose,
|
|
39
|
+
trackRoomJoin: () => import_UserSessionIndex2.trackRoomJoin,
|
|
40
|
+
trackUserSession: () => import_UserSessionIndex2.trackUserSession,
|
|
41
|
+
userRoomsKey: () => import_UserSessionIndex2.userRoomsKey
|
|
42
|
+
});
|
|
43
|
+
module.exports = __toCommonJS(internal_exports);
|
|
44
|
+
var matchMaker = __toESM(require("./MatchMaker.cjs"), 1);
|
|
45
|
+
var import_UserSessionIndex = require("./utils/UserSessionIndex.cjs");
|
|
46
|
+
var import_UserSessionIndex2 = require("./utils/UserSessionIndex.cjs");
|
|
47
|
+
function listUserSessionsLive(userId, options) {
|
|
48
|
+
return (0, import_UserSessionIndex.listUserSessions)(matchMaker.presence, matchMaker.findRoomsByIds, userId, options);
|
|
49
|
+
}
|
|
50
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
51
|
+
0 && (module.exports = {
|
|
52
|
+
USER_ROOMS_KEY_PREFIX,
|
|
53
|
+
listUserSessions,
|
|
54
|
+
listUserSessionsLive,
|
|
55
|
+
releaseRoomLeave,
|
|
56
|
+
releaseUserSession,
|
|
57
|
+
sweepRoomDispose,
|
|
58
|
+
trackRoomJoin,
|
|
59
|
+
trackUserSession,
|
|
60
|
+
userRoomsKey
|
|
61
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/internal.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Framework-internal entry \u2014 symbols used by the bundled `colyseus`\n * plugins (track-user-sessions, unique-session) and the admin\n * backend, but NOT part of `@colyseus/core`'s public surface.\n *\n * Third parties should reach for `TrackUserSessionsPlugin` and its\n * `listUserSessions` static instead \u2014 the raw helpers below are\n * implementation details and can change between minor versions.\n *\n * Resolved via the `./*` wildcard in `packages/core/package.json`.\n *\n * @internal\n */\nimport * as matchMaker from './MatchMaker.ts';\nimport {\n listUserSessions,\n type ListUserSessionsOptions,\n type UserSessionInfo,\n} from './utils/UserSessionIndex.ts';\n\nexport {\n userRoomsKey,\n USER_ROOMS_KEY_PREFIX,\n trackUserSession,\n releaseUserSession,\n trackRoomJoin,\n releaseRoomLeave,\n sweepRoomDispose,\n listUserSessions,\n type UserRoomEntry,\n type UserSessionInfo,\n type ListUserSessionsOptions,\n} from './utils/UserSessionIndex.ts';\n\n/**\n * `listUserSessions` wired to the live `matchMaker` \u2014 what\n * `TrackUserSessionsPlugin.listUserSessions` and the admin backend\n * actually call. The pure `listUserSessions` is kept exported above\n * so unit tests can drive it with fake presence + findRooms.\n */\nexport function listUserSessionsLive(\n userId: string,\n options?: ListUserSessionsOptions,\n): Promise<UserSessionInfo[]> {\n return listUserSessions(matchMaker.presence, matchMaker.findRoomsByIds, userId, options);\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAaA,iBAA4B;AAC5B,8BAIO;AAEP,IAAAA,2BAYO;AAQA,SAAS,qBACd,QACA,SAC4B;AAC5B,aAAO,0CAA4B,qBAAqB,2BAAgB,QAAQ,OAAO;AACzF;",
|
|
6
|
+
"names": ["import_UserSessionIndex"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type ListUserSessionsOptions, type UserSessionInfo } from './utils/UserSessionIndex.ts';
|
|
2
|
+
export { userRoomsKey, USER_ROOMS_KEY_PREFIX, trackUserSession, releaseUserSession, trackRoomJoin, releaseRoomLeave, sweepRoomDispose, listUserSessions, type UserRoomEntry, type UserSessionInfo, type ListUserSessionsOptions, } from './utils/UserSessionIndex.ts';
|
|
3
|
+
/**
|
|
4
|
+
* `listUserSessions` wired to the live `matchMaker` — what
|
|
5
|
+
* `TrackUserSessionsPlugin.listUserSessions` and the admin backend
|
|
6
|
+
* actually call. The pure `listUserSessions` is kept exported above
|
|
7
|
+
* so unit tests can drive it with fake presence + findRooms.
|
|
8
|
+
*/
|
|
9
|
+
export declare function listUserSessionsLive(userId: string, options?: ListUserSessionsOptions): Promise<UserSessionInfo[]>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// packages/core/src/internal.ts
|
|
2
|
+
import * as matchMaker from "./MatchMaker.mjs";
|
|
3
|
+
import {
|
|
4
|
+
listUserSessions
|
|
5
|
+
} from "./utils/UserSessionIndex.mjs";
|
|
6
|
+
import {
|
|
7
|
+
userRoomsKey,
|
|
8
|
+
USER_ROOMS_KEY_PREFIX,
|
|
9
|
+
trackUserSession,
|
|
10
|
+
releaseUserSession,
|
|
11
|
+
trackRoomJoin,
|
|
12
|
+
releaseRoomLeave,
|
|
13
|
+
sweepRoomDispose,
|
|
14
|
+
listUserSessions as listUserSessions2
|
|
15
|
+
} from "./utils/UserSessionIndex.mjs";
|
|
16
|
+
function listUserSessionsLive(userId, options) {
|
|
17
|
+
return listUserSessions(matchMaker.presence, matchMaker.findRoomsByIds, userId, options);
|
|
18
|
+
}
|
|
19
|
+
export {
|
|
20
|
+
USER_ROOMS_KEY_PREFIX,
|
|
21
|
+
listUserSessions2 as listUserSessions,
|
|
22
|
+
listUserSessionsLive,
|
|
23
|
+
releaseRoomLeave,
|
|
24
|
+
releaseUserSession,
|
|
25
|
+
sweepRoomDispose,
|
|
26
|
+
trackRoomJoin,
|
|
27
|
+
trackUserSession,
|
|
28
|
+
userRoomsKey
|
|
29
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/internal.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Framework-internal entry \u2014 symbols used by the bundled `colyseus`\n * plugins (track-user-sessions, unique-session) and the admin\n * backend, but NOT part of `@colyseus/core`'s public surface.\n *\n * Third parties should reach for `TrackUserSessionsPlugin` and its\n * `listUserSessions` static instead \u2014 the raw helpers below are\n * implementation details and can change between minor versions.\n *\n * Resolved via the `./*` wildcard in `packages/core/package.json`.\n *\n * @internal\n */\nimport * as matchMaker from './MatchMaker.ts';\nimport {\n listUserSessions,\n type ListUserSessionsOptions,\n type UserSessionInfo,\n} from './utils/UserSessionIndex.ts';\n\nexport {\n userRoomsKey,\n USER_ROOMS_KEY_PREFIX,\n trackUserSession,\n releaseUserSession,\n trackRoomJoin,\n releaseRoomLeave,\n sweepRoomDispose,\n listUserSessions,\n type UserRoomEntry,\n type UserSessionInfo,\n type ListUserSessionsOptions,\n} from './utils/UserSessionIndex.ts';\n\n/**\n * `listUserSessions` wired to the live `matchMaker` \u2014 what\n * `TrackUserSessionsPlugin.listUserSessions` and the admin backend\n * actually call. The pure `listUserSessions` is kept exported above\n * so unit tests can drive it with fake presence + findRooms.\n */\nexport function listUserSessionsLive(\n userId: string,\n options?: ListUserSessionsOptions,\n): Promise<UserSessionInfo[]> {\n return listUserSessions(matchMaker.presence, matchMaker.findRoomsByIds, userId, options);\n}\n"],
|
|
5
|
+
"mappings": ";AAaA,YAAY,gBAAgB;AAC5B;AAAA,EACE;AAAA,OAGK;AAEP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,oBAAAA;AAAA,OAIK;AAQA,SAAS,qBACd,QACA,SAC4B;AAC5B,SAAO,iBAA4B,qBAAqB,2BAAgB,QAAQ,OAAO;AACzF;",
|
|
6
|
+
"names": ["listUserSessions"]
|
|
7
|
+
}
|
|
@@ -52,6 +52,19 @@ var LocalDriver = class {
|
|
|
52
52
|
}
|
|
53
53
|
return query;
|
|
54
54
|
}
|
|
55
|
+
async findByIds(roomIds) {
|
|
56
|
+
const result = /* @__PURE__ */ new Map();
|
|
57
|
+
if (roomIds.length === 0) {
|
|
58
|
+
return result;
|
|
59
|
+
}
|
|
60
|
+
const wanted = new Set(roomIds);
|
|
61
|
+
for (const room of this.rooms) {
|
|
62
|
+
if (wanted.has(room.roomId)) {
|
|
63
|
+
result.set(room.roomId, room);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
55
68
|
update(room, operations) {
|
|
56
69
|
if (operations.$set) {
|
|
57
70
|
for (const field in operations.$set) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/matchmaker/LocalDriver/LocalDriver.ts"],
|
|
4
|
-
"sourcesContent": ["import { debugMatchMaking } from '../../Debug.ts';\nimport type { IRoomCache, SortOptions, MatchMakerDriver } from '../driver.ts';\nimport { Query } from './Query.ts';\n\n// re-export\nexport type { IRoomCache, SortOptions, MatchMakerDriver };\n\nexport class LocalDriver implements MatchMakerDriver {\n public rooms: IRoomCache[] = [];\n\n public has(roomId: string) {\n return this.rooms.some((room) => room.roomId === roomId);\n }\n\n public query(conditions: Partial<IRoomCache>, sortOptions?: SortOptions) {\n const query = new Query<IRoomCache>(this.rooms, conditions);\n\n if (sortOptions) {\n query.sort(sortOptions);\n }\n\n return query.filter(conditions);\n }\n\n public cleanup(processId: string) {\n const cachedRooms = this.query({ processId });\n debugMatchMaking(\"removing stale rooms by processId %s (%s rooms found)\", processId, cachedRooms.length);\n\n cachedRooms.forEach((room) => this.remove(room.roomId));\n return Promise.resolve();\n }\n\n public findOne(conditions: Partial<IRoomCache>, sortOptions?: SortOptions) {\n const query = new Query<IRoomCache>(this.rooms, conditions);\n\n if (sortOptions) {\n query.sort(sortOptions);\n }\n\n return query as unknown as Promise<IRoomCache>;\n }\n\n public update(room: IRoomCache, operations: Partial<{ $set: Partial<IRoomCache>, $inc: Partial<IRoomCache> }>) {\n if (operations.$set) {\n for (const field in operations.$set) {\n if (operations.$set.hasOwnProperty(field)) {\n room[field] = operations.$set[field];\n }\n }\n }\n\n if (operations.$inc) {\n for (const field in operations.$inc) {\n if (operations.$inc.hasOwnProperty(field)) {\n room[field] += operations.$inc[field];\n }\n }\n }\n\n return true;\n }\n\n public persist(room: IRoomCache, create: boolean = false) {\n // if (this.rooms.indexOf(room) !== -1) {\n // // already in the list\n // return true;\n // }\n\n if (!create) { return false; }\n\n // add to the list\n this.rooms.push(room);\n\n return true;\n }\n\n public remove(roomId: string) {\n const roomIndex = this.rooms.findIndex((room) => room.roomId === roomId);\n if (roomIndex !== -1) {\n this.rooms.splice(roomIndex, 1);\n return true;\n }\n return false;\n }\n\n public clear() {\n this.rooms = [];\n }\n\n public shutdown() {\n }\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAiC;AAEjC,mBAAsB;AAKf,IAAM,cAAN,MAA8C;AAAA,EAA9C;AACL,SAAO,QAAsB,CAAC;AAAA;AAAA,EAEvB,IAAI,QAAgB;AACzB,WAAO,KAAK,MAAM,KAAK,CAAC,SAAS,KAAK,WAAW,MAAM;AAAA,EACzD;AAAA,EAEO,MAAM,YAAiC,aAA2B;AACvE,UAAM,QAAQ,IAAI,mBAAkB,KAAK,OAAO,UAAU;AAE1D,QAAI,aAAa;AACf,YAAM,KAAK,WAAW;AAAA,IACxB;AAEA,WAAO,MAAM,OAAO,UAAU;AAAA,EAChC;AAAA,EAEO,QAAQ,WAAmB;AAChC,UAAM,cAAc,KAAK,MAAM,EAAE,UAAU,CAAC;AAC5C,uCAAiB,yDAAyD,WAAW,YAAY,MAAM;AAEvG,gBAAY,QAAQ,CAAC,SAAS,KAAK,OAAO,KAAK,MAAM,CAAC;AACtD,WAAO,QAAQ,QAAQ;AAAA,EACzB;AAAA,EAEO,QAAQ,YAAiC,aAA2B;AACzE,UAAM,QAAQ,IAAI,mBAAkB,KAAK,OAAO,UAAU;AAE1D,QAAI,aAAa;AACf,YAAM,KAAK,WAAW;AAAA,IACxB;AAEA,WAAO;AAAA,EACT;AAAA,EAEO,OAAO,MAAkB,YAA+E;AAC7G,QAAI,WAAW,MAAM;AACnB,iBAAW,SAAS,WAAW,MAAM;AACnC,YAAI,WAAW,KAAK,eAAe,KAAK,GAAG;AACzC,eAAK,KAAK,IAAI,WAAW,KAAK,KAAK;AAAA,QACrC;AAAA,MACF;AAAA,IACF;AAEA,QAAI,WAAW,MAAM;AACnB,iBAAW,SAAS,WAAW,MAAM;AACnC,YAAI,WAAW,KAAK,eAAe,KAAK,GAAG;AACzC,eAAK,KAAK,KAAK,WAAW,KAAK,KAAK;AAAA,QACtC;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEO,QAAQ,MAAkB,SAAkB,OAAO;AAMxD,QAAI,CAAC,QAAQ;AAAE,aAAO;AAAA,IAAO;AAG7B,SAAK,MAAM,KAAK,IAAI;AAEpB,WAAO;AAAA,EACT;AAAA,EAEO,OAAO,QAAgB;AAC5B,UAAM,YAAY,KAAK,MAAM,UAAU,CAAC,SAAS,KAAK,WAAW,MAAM;AACvE,QAAI,cAAc,IAAI;AACpB,WAAK,MAAM,OAAO,WAAW,CAAC;AAC9B,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAAA,EAEO,QAAQ;AACb,SAAK,QAAQ,CAAC;AAAA,EAChB;AAAA,EAEO,WAAW;AAAA,EAClB;AACF;",
|
|
4
|
+
"sourcesContent": ["import { debugMatchMaking } from '../../Debug.ts';\nimport type { IRoomCache, SortOptions, MatchMakerDriver } from '../driver.ts';\nimport { Query } from './Query.ts';\n\n// re-export\nexport type { IRoomCache, SortOptions, MatchMakerDriver };\n\nexport class LocalDriver implements MatchMakerDriver {\n public rooms: IRoomCache[] = [];\n\n public has(roomId: string) {\n return this.rooms.some((room) => room.roomId === roomId);\n }\n\n public query(conditions: Partial<IRoomCache>, sortOptions?: SortOptions) {\n const query = new Query<IRoomCache>(this.rooms, conditions);\n\n if (sortOptions) {\n query.sort(sortOptions);\n }\n\n return query.filter(conditions);\n }\n\n public cleanup(processId: string) {\n const cachedRooms = this.query({ processId });\n debugMatchMaking(\"removing stale rooms by processId %s (%s rooms found)\", processId, cachedRooms.length);\n\n cachedRooms.forEach((room) => this.remove(room.roomId));\n return Promise.resolve();\n }\n\n public findOne(conditions: Partial<IRoomCache>, sortOptions?: SortOptions) {\n const query = new Query<IRoomCache>(this.rooms, conditions);\n\n if (sortOptions) {\n query.sort(sortOptions);\n }\n\n return query as unknown as Promise<IRoomCache>;\n }\n\n public async findByIds(roomIds: string[]): Promise<Map<string, IRoomCache>> {\n const result = new Map<string, IRoomCache>();\n if (roomIds.length === 0) { return result; }\n const wanted = new Set(roomIds);\n for (const room of this.rooms) {\n if (wanted.has(room.roomId)) { result.set(room.roomId, room); }\n }\n return result;\n }\n\n public update(room: IRoomCache, operations: Partial<{ $set: Partial<IRoomCache>, $inc: Partial<IRoomCache> }>) {\n if (operations.$set) {\n for (const field in operations.$set) {\n if (operations.$set.hasOwnProperty(field)) {\n room[field] = operations.$set[field];\n }\n }\n }\n\n if (operations.$inc) {\n for (const field in operations.$inc) {\n if (operations.$inc.hasOwnProperty(field)) {\n room[field] += operations.$inc[field];\n }\n }\n }\n\n return true;\n }\n\n public persist(room: IRoomCache, create: boolean = false) {\n // if (this.rooms.indexOf(room) !== -1) {\n // // already in the list\n // return true;\n // }\n\n if (!create) { return false; }\n\n // add to the list\n this.rooms.push(room);\n\n return true;\n }\n\n public remove(roomId: string) {\n const roomIndex = this.rooms.findIndex((room) => room.roomId === roomId);\n if (roomIndex !== -1) {\n this.rooms.splice(roomIndex, 1);\n return true;\n }\n return false;\n }\n\n public clear() {\n this.rooms = [];\n }\n\n public shutdown() {\n }\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAiC;AAEjC,mBAAsB;AAKf,IAAM,cAAN,MAA8C;AAAA,EAA9C;AACL,SAAO,QAAsB,CAAC;AAAA;AAAA,EAEvB,IAAI,QAAgB;AACzB,WAAO,KAAK,MAAM,KAAK,CAAC,SAAS,KAAK,WAAW,MAAM;AAAA,EACzD;AAAA,EAEO,MAAM,YAAiC,aAA2B;AACvE,UAAM,QAAQ,IAAI,mBAAkB,KAAK,OAAO,UAAU;AAE1D,QAAI,aAAa;AACf,YAAM,KAAK,WAAW;AAAA,IACxB;AAEA,WAAO,MAAM,OAAO,UAAU;AAAA,EAChC;AAAA,EAEO,QAAQ,WAAmB;AAChC,UAAM,cAAc,KAAK,MAAM,EAAE,UAAU,CAAC;AAC5C,uCAAiB,yDAAyD,WAAW,YAAY,MAAM;AAEvG,gBAAY,QAAQ,CAAC,SAAS,KAAK,OAAO,KAAK,MAAM,CAAC;AACtD,WAAO,QAAQ,QAAQ;AAAA,EACzB;AAAA,EAEO,QAAQ,YAAiC,aAA2B;AACzE,UAAM,QAAQ,IAAI,mBAAkB,KAAK,OAAO,UAAU;AAE1D,QAAI,aAAa;AACf,YAAM,KAAK,WAAW;AAAA,IACxB;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAa,UAAU,SAAqD;AAC1E,UAAM,SAAS,oBAAI,IAAwB;AAC3C,QAAI,QAAQ,WAAW,GAAG;AAAE,aAAO;AAAA,IAAQ;AAC3C,UAAM,SAAS,IAAI,IAAI,OAAO;AAC9B,eAAW,QAAQ,KAAK,OAAO;AAC7B,UAAI,OAAO,IAAI,KAAK,MAAM,GAAG;AAAE,eAAO,IAAI,KAAK,QAAQ,IAAI;AAAA,MAAG;AAAA,IAChE;AACA,WAAO;AAAA,EACT;AAAA,EAEO,OAAO,MAAkB,YAA+E;AAC7G,QAAI,WAAW,MAAM;AACnB,iBAAW,SAAS,WAAW,MAAM;AACnC,YAAI,WAAW,KAAK,eAAe,KAAK,GAAG;AACzC,eAAK,KAAK,IAAI,WAAW,KAAK,KAAK;AAAA,QACrC;AAAA,MACF;AAAA,IACF;AAEA,QAAI,WAAW,MAAM;AACnB,iBAAW,SAAS,WAAW,MAAM;AACnC,YAAI,WAAW,KAAK,eAAe,KAAK,GAAG;AACzC,eAAK,KAAK,KAAK,WAAW,KAAK,KAAK;AAAA,QACtC;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEO,QAAQ,MAAkB,SAAkB,OAAO;AAMxD,QAAI,CAAC,QAAQ;AAAE,aAAO;AAAA,IAAO;AAG7B,SAAK,MAAM,KAAK,IAAI;AAEpB,WAAO;AAAA,EACT;AAAA,EAEO,OAAO,QAAgB;AAC5B,UAAM,YAAY,KAAK,MAAM,UAAU,CAAC,SAAS,KAAK,WAAW,MAAM;AACvE,QAAI,cAAc,IAAI;AACpB,WAAK,MAAM,OAAO,WAAW,CAAC;AAC9B,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAAA,EAEO,QAAQ;AACb,SAAK,QAAQ,CAAC;AAAA,EAChB;AAAA,EAEO,WAAW;AAAA,EAClB;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -6,6 +6,7 @@ export declare class LocalDriver implements MatchMakerDriver {
|
|
|
6
6
|
query(conditions: Partial<IRoomCache>, sortOptions?: SortOptions): IRoomCache<any>[];
|
|
7
7
|
cleanup(processId: string): Promise<void>;
|
|
8
8
|
findOne(conditions: Partial<IRoomCache>, sortOptions?: SortOptions): Promise<IRoomCache>;
|
|
9
|
+
findByIds(roomIds: string[]): Promise<Map<string, IRoomCache>>;
|
|
9
10
|
update(room: IRoomCache, operations: Partial<{
|
|
10
11
|
$set: Partial<IRoomCache>;
|
|
11
12
|
$inc: Partial<IRoomCache>;
|
|
@@ -28,6 +28,19 @@ var LocalDriver = class {
|
|
|
28
28
|
}
|
|
29
29
|
return query;
|
|
30
30
|
}
|
|
31
|
+
async findByIds(roomIds) {
|
|
32
|
+
const result = /* @__PURE__ */ new Map();
|
|
33
|
+
if (roomIds.length === 0) {
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
const wanted = new Set(roomIds);
|
|
37
|
+
for (const room of this.rooms) {
|
|
38
|
+
if (wanted.has(room.roomId)) {
|
|
39
|
+
result.set(room.roomId, room);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
31
44
|
update(room, operations) {
|
|
32
45
|
if (operations.$set) {
|
|
33
46
|
for (const field in operations.$set) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/matchmaker/LocalDriver/LocalDriver.ts"],
|
|
4
|
-
"sourcesContent": ["import { debugMatchMaking } from '../../Debug.ts';\nimport type { IRoomCache, SortOptions, MatchMakerDriver } from '../driver.ts';\nimport { Query } from './Query.ts';\n\n// re-export\nexport type { IRoomCache, SortOptions, MatchMakerDriver };\n\nexport class LocalDriver implements MatchMakerDriver {\n public rooms: IRoomCache[] = [];\n\n public has(roomId: string) {\n return this.rooms.some((room) => room.roomId === roomId);\n }\n\n public query(conditions: Partial<IRoomCache>, sortOptions?: SortOptions) {\n const query = new Query<IRoomCache>(this.rooms, conditions);\n\n if (sortOptions) {\n query.sort(sortOptions);\n }\n\n return query.filter(conditions);\n }\n\n public cleanup(processId: string) {\n const cachedRooms = this.query({ processId });\n debugMatchMaking(\"removing stale rooms by processId %s (%s rooms found)\", processId, cachedRooms.length);\n\n cachedRooms.forEach((room) => this.remove(room.roomId));\n return Promise.resolve();\n }\n\n public findOne(conditions: Partial<IRoomCache>, sortOptions?: SortOptions) {\n const query = new Query<IRoomCache>(this.rooms, conditions);\n\n if (sortOptions) {\n query.sort(sortOptions);\n }\n\n return query as unknown as Promise<IRoomCache>;\n }\n\n public update(room: IRoomCache, operations: Partial<{ $set: Partial<IRoomCache>, $inc: Partial<IRoomCache> }>) {\n if (operations.$set) {\n for (const field in operations.$set) {\n if (operations.$set.hasOwnProperty(field)) {\n room[field] = operations.$set[field];\n }\n }\n }\n\n if (operations.$inc) {\n for (const field in operations.$inc) {\n if (operations.$inc.hasOwnProperty(field)) {\n room[field] += operations.$inc[field];\n }\n }\n }\n\n return true;\n }\n\n public persist(room: IRoomCache, create: boolean = false) {\n // if (this.rooms.indexOf(room) !== -1) {\n // // already in the list\n // return true;\n // }\n\n if (!create) { return false; }\n\n // add to the list\n this.rooms.push(room);\n\n return true;\n }\n\n public remove(roomId: string) {\n const roomIndex = this.rooms.findIndex((room) => room.roomId === roomId);\n if (roomIndex !== -1) {\n this.rooms.splice(roomIndex, 1);\n return true;\n }\n return false;\n }\n\n public clear() {\n this.rooms = [];\n }\n\n public shutdown() {\n }\n}\n"],
|
|
5
|
-
"mappings": ";AAAA,SAAS,wBAAwB;AAEjC,SAAS,aAAa;AAKf,IAAM,cAAN,MAA8C;AAAA,EAA9C;AACL,SAAO,QAAsB,CAAC;AAAA;AAAA,EAEvB,IAAI,QAAgB;AACzB,WAAO,KAAK,MAAM,KAAK,CAAC,SAAS,KAAK,WAAW,MAAM;AAAA,EACzD;AAAA,EAEO,MAAM,YAAiC,aAA2B;AACvE,UAAM,QAAQ,IAAI,MAAkB,KAAK,OAAO,UAAU;AAE1D,QAAI,aAAa;AACf,YAAM,KAAK,WAAW;AAAA,IACxB;AAEA,WAAO,MAAM,OAAO,UAAU;AAAA,EAChC;AAAA,EAEO,QAAQ,WAAmB;AAChC,UAAM,cAAc,KAAK,MAAM,EAAE,UAAU,CAAC;AAC5C,qBAAiB,yDAAyD,WAAW,YAAY,MAAM;AAEvG,gBAAY,QAAQ,CAAC,SAAS,KAAK,OAAO,KAAK,MAAM,CAAC;AACtD,WAAO,QAAQ,QAAQ;AAAA,EACzB;AAAA,EAEO,QAAQ,YAAiC,aAA2B;AACzE,UAAM,QAAQ,IAAI,MAAkB,KAAK,OAAO,UAAU;AAE1D,QAAI,aAAa;AACf,YAAM,KAAK,WAAW;AAAA,IACxB;AAEA,WAAO;AAAA,EACT;AAAA,EAEO,OAAO,MAAkB,YAA+E;AAC7G,QAAI,WAAW,MAAM;AACnB,iBAAW,SAAS,WAAW,MAAM;AACnC,YAAI,WAAW,KAAK,eAAe,KAAK,GAAG;AACzC,eAAK,KAAK,IAAI,WAAW,KAAK,KAAK;AAAA,QACrC;AAAA,MACF;AAAA,IACF;AAEA,QAAI,WAAW,MAAM;AACnB,iBAAW,SAAS,WAAW,MAAM;AACnC,YAAI,WAAW,KAAK,eAAe,KAAK,GAAG;AACzC,eAAK,KAAK,KAAK,WAAW,KAAK,KAAK;AAAA,QACtC;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEO,QAAQ,MAAkB,SAAkB,OAAO;AAMxD,QAAI,CAAC,QAAQ;AAAE,aAAO;AAAA,IAAO;AAG7B,SAAK,MAAM,KAAK,IAAI;AAEpB,WAAO;AAAA,EACT;AAAA,EAEO,OAAO,QAAgB;AAC5B,UAAM,YAAY,KAAK,MAAM,UAAU,CAAC,SAAS,KAAK,WAAW,MAAM;AACvE,QAAI,cAAc,IAAI;AACpB,WAAK,MAAM,OAAO,WAAW,CAAC;AAC9B,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAAA,EAEO,QAAQ;AACb,SAAK,QAAQ,CAAC;AAAA,EAChB;AAAA,EAEO,WAAW;AAAA,EAClB;AACF;",
|
|
4
|
+
"sourcesContent": ["import { debugMatchMaking } from '../../Debug.ts';\nimport type { IRoomCache, SortOptions, MatchMakerDriver } from '../driver.ts';\nimport { Query } from './Query.ts';\n\n// re-export\nexport type { IRoomCache, SortOptions, MatchMakerDriver };\n\nexport class LocalDriver implements MatchMakerDriver {\n public rooms: IRoomCache[] = [];\n\n public has(roomId: string) {\n return this.rooms.some((room) => room.roomId === roomId);\n }\n\n public query(conditions: Partial<IRoomCache>, sortOptions?: SortOptions) {\n const query = new Query<IRoomCache>(this.rooms, conditions);\n\n if (sortOptions) {\n query.sort(sortOptions);\n }\n\n return query.filter(conditions);\n }\n\n public cleanup(processId: string) {\n const cachedRooms = this.query({ processId });\n debugMatchMaking(\"removing stale rooms by processId %s (%s rooms found)\", processId, cachedRooms.length);\n\n cachedRooms.forEach((room) => this.remove(room.roomId));\n return Promise.resolve();\n }\n\n public findOne(conditions: Partial<IRoomCache>, sortOptions?: SortOptions) {\n const query = new Query<IRoomCache>(this.rooms, conditions);\n\n if (sortOptions) {\n query.sort(sortOptions);\n }\n\n return query as unknown as Promise<IRoomCache>;\n }\n\n public async findByIds(roomIds: string[]): Promise<Map<string, IRoomCache>> {\n const result = new Map<string, IRoomCache>();\n if (roomIds.length === 0) { return result; }\n const wanted = new Set(roomIds);\n for (const room of this.rooms) {\n if (wanted.has(room.roomId)) { result.set(room.roomId, room); }\n }\n return result;\n }\n\n public update(room: IRoomCache, operations: Partial<{ $set: Partial<IRoomCache>, $inc: Partial<IRoomCache> }>) {\n if (operations.$set) {\n for (const field in operations.$set) {\n if (operations.$set.hasOwnProperty(field)) {\n room[field] = operations.$set[field];\n }\n }\n }\n\n if (operations.$inc) {\n for (const field in operations.$inc) {\n if (operations.$inc.hasOwnProperty(field)) {\n room[field] += operations.$inc[field];\n }\n }\n }\n\n return true;\n }\n\n public persist(room: IRoomCache, create: boolean = false) {\n // if (this.rooms.indexOf(room) !== -1) {\n // // already in the list\n // return true;\n // }\n\n if (!create) { return false; }\n\n // add to the list\n this.rooms.push(room);\n\n return true;\n }\n\n public remove(roomId: string) {\n const roomIndex = this.rooms.findIndex((room) => room.roomId === roomId);\n if (roomIndex !== -1) {\n this.rooms.splice(roomIndex, 1);\n return true;\n }\n return false;\n }\n\n public clear() {\n this.rooms = [];\n }\n\n public shutdown() {\n }\n}\n"],
|
|
5
|
+
"mappings": ";AAAA,SAAS,wBAAwB;AAEjC,SAAS,aAAa;AAKf,IAAM,cAAN,MAA8C;AAAA,EAA9C;AACL,SAAO,QAAsB,CAAC;AAAA;AAAA,EAEvB,IAAI,QAAgB;AACzB,WAAO,KAAK,MAAM,KAAK,CAAC,SAAS,KAAK,WAAW,MAAM;AAAA,EACzD;AAAA,EAEO,MAAM,YAAiC,aAA2B;AACvE,UAAM,QAAQ,IAAI,MAAkB,KAAK,OAAO,UAAU;AAE1D,QAAI,aAAa;AACf,YAAM,KAAK,WAAW;AAAA,IACxB;AAEA,WAAO,MAAM,OAAO,UAAU;AAAA,EAChC;AAAA,EAEO,QAAQ,WAAmB;AAChC,UAAM,cAAc,KAAK,MAAM,EAAE,UAAU,CAAC;AAC5C,qBAAiB,yDAAyD,WAAW,YAAY,MAAM;AAEvG,gBAAY,QAAQ,CAAC,SAAS,KAAK,OAAO,KAAK,MAAM,CAAC;AACtD,WAAO,QAAQ,QAAQ;AAAA,EACzB;AAAA,EAEO,QAAQ,YAAiC,aAA2B;AACzE,UAAM,QAAQ,IAAI,MAAkB,KAAK,OAAO,UAAU;AAE1D,QAAI,aAAa;AACf,YAAM,KAAK,WAAW;AAAA,IACxB;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAa,UAAU,SAAqD;AAC1E,UAAM,SAAS,oBAAI,IAAwB;AAC3C,QAAI,QAAQ,WAAW,GAAG;AAAE,aAAO;AAAA,IAAQ;AAC3C,UAAM,SAAS,IAAI,IAAI,OAAO;AAC9B,eAAW,QAAQ,KAAK,OAAO;AAC7B,UAAI,OAAO,IAAI,KAAK,MAAM,GAAG;AAAE,eAAO,IAAI,KAAK,QAAQ,IAAI;AAAA,MAAG;AAAA,IAChE;AACA,WAAO;AAAA,EACT;AAAA,EAEO,OAAO,MAAkB,YAA+E;AAC7G,QAAI,WAAW,MAAM;AACnB,iBAAW,SAAS,WAAW,MAAM;AACnC,YAAI,WAAW,KAAK,eAAe,KAAK,GAAG;AACzC,eAAK,KAAK,IAAI,WAAW,KAAK,KAAK;AAAA,QACrC;AAAA,MACF;AAAA,IACF;AAEA,QAAI,WAAW,MAAM;AACnB,iBAAW,SAAS,WAAW,MAAM;AACnC,YAAI,WAAW,KAAK,eAAe,KAAK,GAAG;AACzC,eAAK,KAAK,KAAK,WAAW,KAAK,KAAK;AAAA,QACtC;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEO,QAAQ,MAAkB,SAAkB,OAAO;AAMxD,QAAI,CAAC,QAAQ;AAAE,aAAO;AAAA,IAAO;AAG7B,SAAK,MAAM,KAAK,IAAI;AAEpB,WAAO;AAAA,EACT;AAAA,EAEO,OAAO,QAAgB;AAC5B,UAAM,YAAY,KAAK,MAAM,UAAU,CAAC,SAAS,KAAK,WAAW,MAAM;AACvE,QAAI,cAAc,IAAI;AACpB,WAAK,MAAM,OAAO,WAAW,CAAC;AAC9B,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAAA,EAEO,QAAQ;AACb,SAAK,QAAQ,CAAC;AAAA,EAChB;AAAA,EAEO,WAAW;AAAA,EAClB;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|