@colyseus/core 0.18.1 → 0.18.3

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.
Files changed (39) hide show
  1. package/build/Room.cjs.map +1 -1
  2. package/build/Room.mjs.map +1 -1
  3. package/build/Transport.cjs.map +2 -2
  4. package/build/Transport.d.ts +7 -6
  5. package/build/Transport.mjs.map +2 -2
  6. package/build/errors/RoomExceptions.cjs.map +1 -1
  7. package/build/errors/RoomExceptions.d.ts +14 -14
  8. package/build/errors/RoomExceptions.mjs.map +1 -1
  9. package/build/input/InputBuffer.cjs +26 -0
  10. package/build/input/InputBuffer.cjs.map +2 -2
  11. package/build/input/InputBuffer.d.ts +10 -0
  12. package/build/input/InputBuffer.mjs +25 -0
  13. package/build/input/InputBuffer.mjs.map +2 -2
  14. package/build/input/RoomInput.cjs +1 -0
  15. package/build/input/RoomInput.cjs.map +2 -2
  16. package/build/input/RoomInput.mjs +2 -0
  17. package/build/input/RoomInput.mjs.map +2 -2
  18. package/build/presence/LocalPresence.cjs.map +2 -2
  19. package/build/presence/LocalPresence.d.ts +1 -1
  20. package/build/presence/LocalPresence.mjs.map +2 -2
  21. package/build/router/index.cjs +13 -6
  22. package/build/router/index.cjs.map +2 -2
  23. package/build/router/index.d.ts +1 -1
  24. package/build/router/index.mjs +13 -6
  25. package/build/router/index.mjs.map +2 -2
  26. package/build/serializer/SchemaSerializer.cjs.map +1 -1
  27. package/build/serializer/SchemaSerializer.mjs.map +1 -1
  28. package/build/serializer/Serializer.cjs.map +1 -1
  29. package/build/serializer/Serializer.d.ts +4 -4
  30. package/package.json +7 -4
  31. package/src/Room.ts +1 -1
  32. package/src/Transport.ts +7 -6
  33. package/src/errors/RoomExceptions.ts +18 -18
  34. package/src/input/InputBuffer.ts +32 -0
  35. package/src/input/RoomInput.ts +6 -1
  36. package/src/presence/LocalPresence.ts +1 -1
  37. package/src/router/index.ts +35 -14
  38. package/src/serializer/SchemaSerializer.ts +4 -4
  39. package/src/serializer/Serializer.ts +4 -4
@@ -25,6 +25,9 @@ function compileSanitizer(spec) {
25
25
  const inst = input;
26
26
  for (let i = 0; i < names.length; i++) {
27
27
  const v = inst[names[i]];
28
+ if (v === void 0) {
29
+ continue;
30
+ }
28
31
  inst[names[i]] = v >= mins[i] ? v <= maxs[i] ? v : maxs[i] : mins[i];
29
32
  }
30
33
  };
@@ -41,6 +44,26 @@ function fieldNamesOf(ctor) {
41
44
  }
42
45
  return names;
43
46
  }
47
+ function seedInputZeroValues(instance, ctor) {
48
+ const md = ctor[$METADATA];
49
+ if (!md) {
50
+ return;
51
+ }
52
+ for (let i = 0; ; i++) {
53
+ const f = md[i];
54
+ if (!f || typeof f.name !== "string") break;
55
+ if (instance[f.name] !== void 0) {
56
+ continue;
57
+ }
58
+ const type = f.type;
59
+ if (typeof type === "string") {
60
+ instance[f.name] = type === "string" ? "" : type === "boolean" ? false : 0;
61
+ } else if (type !== null && typeof type === "object" && "quantized" in type) {
62
+ const q = type.quantized;
63
+ instance[f.name] = q.min > 0 ? q.min : q.max < 0 ? q.max : 0;
64
+ }
65
+ }
66
+ }
44
67
  var COMPACT_THRESHOLD = 32;
45
68
  var DONE_RESULT = Object.freeze({ value: void 0, done: true });
46
69
  var DONE_ITERATOR = Object.freeze({
@@ -123,6 +146,7 @@ var InputBufferImpl = class {
123
146
  idleFrame(overrides) {
124
147
  if (this._idle === void 0) {
125
148
  this._idle = new this._ctor();
149
+ seedInputZeroValues(this._idle, this._ctor);
126
150
  this._fieldNames = fieldNamesOf(this._ctor);
127
151
  const fresh = this._idle;
128
152
  this._defaults = this._fieldNames.map((n) => fresh[n]);
@@ -503,5 +527,6 @@ export {
503
527
  InputBufferImpl,
504
528
  NO_OP_INPUT_ACCESSOR,
505
529
  compileSanitizer,
530
+ seedInputZeroValues,
506
531
  validateSubSteps
507
532
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/input/InputBuffer.ts"],
4
- "sourcesContent": ["import type { ClientPrivate } from '../Transport.ts';\nimport { $METADATA } from '../utils/Utils.ts';\n\nimport type {\n ConsumeOptions, IdleContext, IdleInput, InputAccessor, SanitizeInput,\n} from './types.ts';\n\n/**\n * @internal Validate a `subSteps` count. Throws on a fractional/non-positive\n * value: both sides loop `subSteps` times at `dt/subSteps`, so anything but a\n * whole number is a determinism bug, not a tunable. Shared by `defineInput`\n * and `setFixedTimestep`.\n */\nexport function validateSubSteps(subSteps: number | undefined, source: string): number | undefined {\n if (subSteps === undefined) { return undefined; }\n if (!Number.isInteger(subSteps) || subSteps < 1) {\n throw new Error(`[${source}] subSteps must be an integer >= 1 (got ${subSteps}).`);\n }\n return subSteps;\n}\n\n/**\n * @internal Compile a {@link SanitizeInput} spec into the per-frame function the\n * decode path applies. The map form precompiles to dense min/max arrays walked\n * with the NaN-safe branch clamp; the callback form passes through.\n */\nexport function compileSanitizer<I>(spec: SanitizeInput<I>): (input: I) => void {\n if (typeof spec === 'function') { return spec; }\n const names = Object.keys(spec);\n const mins = new Float64Array(names.length);\n const maxs = new Float64Array(names.length);\n for (let i = 0; i < names.length; i++) {\n const range = (spec as Record<string, readonly [number, number]>)[names[i]]!;\n mins[i] = range[0];\n maxs[i] = range[1];\n }\n return (input: I) => {\n const inst = input as Record<string, number>;\n for (let i = 0; i < names.length; i++) {\n const v = inst[names[i]];\n // NaN-safe: NaN fails both comparisons \u2192 clamp floor.\n inst[names[i]] = v >= mins[i] ? (v <= maxs[i] ? v : maxs[i]) : mins[i];\n }\n };\n}\n\n/** Field names of an input schema ctor, in declaration order (indices are dense\n * from 0 in the metadata). Resolved ONCE per buffer (cold path). */\nfunction fieldNamesOf(ctor: new () => any): string[] {\n const md = (ctor as any)[$METADATA] as Record<number, { name?: string }> | undefined;\n const names: string[] = [];\n if (md) {\n for (let i = 0; ; i++) {\n const f = md[i];\n if (!f || typeof f.name !== \"string\") break;\n names.push(f.name);\n }\n }\n return names;\n}\n\n/** Reclaim the consumed prefix once the read cursor passes this many items \u2014\n * bounds the parallel arrays' slack to O(THRESHOLD) between compactions while\n * keeping per-consume work O(1) (no `shift()` re-index on every input). */\nconst COMPACT_THRESHOLD = 32;\n\n/** Shared zero-state \"already done\" iterator \u2014 returned by `consume()` on an\n * empty buffer with no idle policy, and by the no-op accessor. Frozen + a frozen\n * result, so it's allocation-free and safe to share across all callers. */\nconst DONE_RESULT: IteratorResult<any> = Object.freeze({ value: undefined, done: true });\nconst DONE_ITERATOR: IterableIterator<any> = Object.freeze({\n [Symbol.iterator]() { return this; },\n next() { return DONE_RESULT; },\n});\n\n/** @internal */\nexport class InputBufferImpl<I = any> {\n /** Pending snapshots, indexed `[_head, _items.length)`. Consumed inputs are\n * NOT spliced off per-read \u2014 `_head` advances and the prefix is reclaimed in\n * bulk by {@link compact} (a `shift()` per read is O(n), so a burst would be\n * O(n\u00B2)). `_renderTimes`/`_reckonTimes` stay parallel to `_items`. */\n private _items: I[] = [];\n /** Read cursor: index of the oldest UNCONSUMED input (== `_items.length` when empty). */\n private _head = 0;\n private _lastSeq: number = -Infinity;\n private readonly _maxSize: number;\n private readonly _seqField: string | undefined;\n\n /**\n * Cumulative count of inputs CONSUMED from this buffer (via any consume\n * primitive \u2014 {@link next}/{@link take}/{@link drain}/{@link consume} \u2014 plus\n * {@link clear} and overflow). Consumed = \"removed from the pending set\",\n * whether applied to state or discarded. The server echoes this in the TIMED\n * prefix as the reconciliation ack: how many of the client's inputs are\n * reflected in (or finished influencing) the authoritative state, which lags\n * the receive counter by inputs still buffered. Distinct from\n * `_receivedInputCount` (receive-time, for RTT), which leads the state.\n */\n consumedCount = 0;\n\n /** Render times parallel to `_items` (server-clock ms; `0` when unset). */\n private _renderTimes: number[] = [];\n /** Render time of the most recently drained input (see {@link renderTime}). */\n private _lastRenderTime = 0;\n /** Reckon-display stamps parallel to `_items` \u2014 the client's serverNow\n * estimate at input-sample time, i.e. the instant its forward-reckoned\n * entities were displayed at (server-clock ms; `0` when unset). */\n private _reckonTimes: number[] = [];\n /** Reckon stamp of the most recently consumed input (see {@link reckonTime}). */\n private _lastReckonTime = 0;\n /** Framework wire seq per buffered input (parallel to `_items`) \u2014 UNRELIABLE\n * ONLY, lazily created on the first seq-carrying push. Reliable inputs are\n * contiguous, so their consumed-seq-value is always exactly {@link consumedCount}\n * and needs no parallel array; `undefined` here marks that (the common, default\n * path pays nothing). A buffer is single-mode, so this is either never created\n * (reliable) or created once and kept parallel (unreliable). */\n private _seqs?: number[];\n /** Seq VALUE of the last consumed input \u2014 the reconciliation ack echoed to the\n * client (see {@link ackSeq}). Only meaningful when {@link _seqs} is tracked\n * (unreliable); reliable's ack falls back to {@link consumedCount}. */\n private _lastConsumedSeq = 0;\n /** Whether the most recently produced frame was a synthesized idle (see {@link wasIdle}). */\n private _lastWasIdle = false;\n\n /** Reused {@link consume} iterator (lazily minted) + its per-call state, so a\n * per-tick loop allocates no generator frame. `_iterRemaining` snapshots the\n * COUNT to walk \u2014 a count, not an index: mid-pass mutations (`next()`/`take()`/\n * `clear()`/compaction) shift indices but can only shorten a count-based pass,\n * never point it past the tail. `_iterIdle` is an optional trailing synthesized\n * frame; `_iterActive` guards against a nested consume() clobbering the shared\n * `_iterRes`. */\n private _iter?: IterableIterator<I>;\n private _iterRemaining = 0;\n private _iterActive = false;\n private _iterIdle?: I;\n private readonly _iterRes: { value: I | undefined; done: boolean } = { value: undefined, done: false };\n\n /** Input schema ctor \u2014 mints the reused idle frame; idle is off without it. */\n private readonly _ctor?: new () => I;\n /** Client slice this buffer belongs to (the live object is `Client & ClientPrivate`)\n * \u2014 feeds the idle callback's ctx (latest + sessionId). */\n private readonly _client?: Pick<ClientPrivate, '_input'> & { sessionId: string };\n /** Room-level idle policy (`defineInput({ idle })`) \u2014 the bare-call default. */\n private readonly _roomIdle?: IdleInput<I>;\n /** Reused synthesized idle frame + the schema's field names/defaults (lazy). */\n private _idle?: I;\n private _fieldNames?: string[];\n private _defaults?: unknown[];\n /** Reused ctx for the idle callback form (see {@link IdleContext}). */\n private readonly _idleCtx: IdleContext<I> = { latest: undefined, sessionId: \"\" };\n\n constructor(maxSize: number, seqField: string | undefined, ctor?: new () => I, client?: Pick<ClientPrivate, '_input'> & { sessionId: string }, idle?: IdleInput<I>) {\n this._maxSize = maxSize;\n this._seqField = seqField;\n this._ctor = ctor;\n this._client = client;\n this._roomIdle = idle;\n }\n\n /** The effective idle policy for one consume call: per-call `false` suppresses,\n * per-call value overrides, else the room-level default (or none). */\n private effectiveIdle(opts?: ConsumeOptions<I>): IdleInput<I> | undefined {\n return opts?.idle === false ? undefined : (opts?.idle ?? this._roomIdle);\n }\n\n /** Resolve an {@link IdleInput} to overrides \u2014 invokes the callback form\n * LAZILY, here at synthesis time (the buffer is known to be empty). */\n private resolveIdle(idle: IdleInput<I>): true | Partial<I> {\n if (typeof idle !== \"function\") return idle;\n this._idleCtx.latest = this._client?._input as I | undefined;\n this._idleCtx.sessionId = this._client?.sessionId ?? \"\";\n return idle(this._idleCtx) ?? true;\n }\n\n /**\n * The synthesized \"no input this tick\" frame: schema defaults overlaid with\n * `overrides` (`true` = none). Copies BY FIELD NAME from the schema metadata \u2014\n * schema fields are prototype accessors (no own props), so `Object.assign`\n * can't source from a schema instance; the name walk reads getters, letting\n * `overrides` be a plain partial OR a schema instance (e.g. `latest`).\n * ONE reused instance \u2014 refilled per call, never advances the ack.\n */\n private idleFrame(overrides: true | Partial<I>): I {\n if (this._idle === undefined) {\n // Lazy mint (cold): the fresh instance doubles as the defaults source.\n this._idle = new this._ctor!();\n this._fieldNames = fieldNamesOf(this._ctor!);\n const fresh = this._idle as Record<string, unknown>;\n this._defaults = this._fieldNames.map((n) => fresh[n]);\n }\n const idle = this._idle as Record<string, unknown>;\n const names = this._fieldNames!;\n const defaults = this._defaults!;\n const ov = overrides === true ? undefined : (overrides as Record<string, unknown>);\n for (let i = 0; i < names.length; i++) {\n const v = ov?.[names[i]];\n idle[names[i]] = v !== undefined ? v : defaults[i];\n }\n return this._idle;\n }\n\n /** The idle frame to synthesize on an empty tick (room policy or per-call\n * `opts.idle`), or `undefined` when no policy is in effect. Reuses the single\n * idle instance; never advances the ack. Callers set {@link wasIdle}. */\n private idleFrameOnEmpty(opts?: ConsumeOptions<I>): I | undefined {\n const idle = this._ctor !== undefined ? this.effectiveIdle(opts) : undefined;\n return idle === undefined ? undefined : this.idleFrame(this.resolveIdle(idle));\n }\n\n /** Consume the input at the cursor: advance cursor + ack, stamp THIS input's\n * render/reckon times, clear {@link wasIdle}. The shared per-input step behind\n * {@link next}, {@link consume}'s iterator, and the re-entrant fallback. */\n private stepHead(): I {\n const i = this._head;\n this._lastWasIdle = false;\n this._lastRenderTime = this._renderTimes[i];\n this._lastReckonTime = this._reckonTimes[i];\n if (this._seqs !== undefined) { this._lastConsumedSeq = this._seqs[i]; }\n this._head = i + 1;\n this.consumedCount++;\n return this._items[i];\n }\n\n /** Drop ALL slots, reusing the backing arrays' capacity \u2014 the zero-GC reset for\n * the consume/next path ({@link compact}/{@link clear}). `drain()` deliberately\n * does NOT use this (it returns an array, so fresh `[]` is faster there). */\n private truncate(): void {\n this._items.length = 0;\n this._renderTimes.length = 0;\n this._reckonTimes.length = 0;\n if (this._seqs !== undefined) { this._seqs.length = 0; }\n this._head = 0;\n }\n\n /**\n * Append a decoded input snapshot. `seq` is the framework wire seq (unreliable);\n * omit it for reliable inputs, which get an implicit monotonic receive count so\n * {@link ackSeq} stays well-defined in both modes.\n */\n push(snapshot: I, renderTime: number = 0, reckonTime: number = 0, seq?: number): void {\n this._items.push(snapshot);\n this._renderTimes.push(renderTime);\n this._reckonTimes.push(reckonTime);\n // Track the wire seq only for unreliable (seq provided); reliable derives its\n // ack from consumedCount, so it never allocates/maintains this array.\n if (seq !== undefined) { (this._seqs ??= []).push(seq); }\n // Overflow drops the oldest unconsumed input; count it consumed so the ack\n // still advances past it. Advance the cursor (don't shift) \u2014 keeps it O(1).\n if (this.size > this._maxSize) {\n if (this._seqs !== undefined) { this._lastConsumedSeq = this._seqs[this._head]; } // dropped oldest counts as acked\n this._head++;\n this.consumedCount++;\n this.compact();\n }\n }\n\n /** Reclaim the consumed prefix `[0, _head)`. Free when fully drained (reuse the\n * arrays); otherwise splice only once the cursor passes {@link COMPACT_THRESHOLD},\n * so the amortized per-input cost stays O(1). */\n private compact(): void {\n if (this._head === 0) { return; }\n if (this._head >= this._items.length) {\n this.truncate();\n } else if (this._head >= COMPACT_THRESHOLD) {\n this._items.splice(0, this._head);\n this._renderTimes.splice(0, this._head);\n this._reckonTimes.splice(0, this._head);\n if (this._seqs !== undefined) { this._seqs.splice(0, this._head); }\n this._head = 0;\n }\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(opts?: ConsumeOptions<I>): I[] {\n // Empty: synthesize one idle frame (NOT consumed \u2014 no ack bump, stamps\n // untouched) when a policy is in effect, else [].\n if (this.size === 0) {\n const idle = this.idleFrameOnEmpty(opts);\n this._lastWasIdle = idle !== undefined;\n return idle !== undefined ? [idle] : [];\n }\n this._lastWasIdle = false;\n const last = this._items.length - 1;\n this._lastRenderTime = this._renderTimes[last]; // drain reports the NEWEST stamps\n this._lastReckonTime = this._reckonTimes[last];\n if (this._seqs !== undefined) { this._lastConsumedSeq = this._seqs[last]; }\n // Hand off the backing array untouched when nothing was partially consumed\n // (O(1) \u2014 the caller may retain it); else copy out the unconsumed tail. Fresh\n // arrays here, NOT truncate(): drain already allocates the returned array, and\n // V8 makes `= []` + refill cheaper than `length = 0` reuse (~1.8\u00D7 at small N),\n // so the speed wins and the 3 tiny empties are negligible next to the return.\n const out = this._head === 0 ? this._items : this._items.slice(this._head);\n this.consumedCount += out.length;\n this._items = [];\n this._renderTimes = [];\n this._reckonTimes = [];\n if (this._seqs !== undefined) { this._seqs = []; }\n this._head = 0;\n return out;\n }\n\n /** Consume the single oldest input (ack advances by one); `undefined` if\n * empty \u2014 or the synthesized idle frame when an idle policy is in effect. */\n next(opts?: ConsumeOptions<I>): I | undefined {\n if (this.size === 0) {\n const idle = this.idleFrameOnEmpty(opts);\n this._lastWasIdle = idle !== undefined;\n return idle;\n }\n const item = this.stepHead();\n this.compact();\n return item;\n }\n\n /** Consume up to `n` oldest inputs (ack advances by the count actually taken). */\n take(n: number): I[] {\n this._lastWasIdle = false; // take never synthesizes idle\n const avail = this.size;\n if (n <= 0 || avail === 0) { return []; }\n const count = Math.min(n, avail);\n const start = this._head;\n const out = this._items.slice(start, start + count);\n this._lastRenderTime = this._renderTimes[start + count - 1]; // newest taken input's stamps\n this._lastReckonTime = this._reckonTimes[start + count - 1];\n if (this._seqs !== undefined) { this._lastConsumedSeq = this._seqs[start + count - 1]; }\n this._head += count;\n this.consumedCount += count;\n this.compact();\n return out;\n }\n\n /**\n * Iterate pending inputs one at a time (per-yield ack + stamp updates), or\n * exactly one synthesized idle frame on an empty buffer with a policy in\n * effect. See {@link InputAccessor.consume}.\n *\n * Returns a POOLED iterator (reused across calls \u2014 no generator frame, no\n * per-call allocation) that consumes at most the pending count snapshotted at\n * the call \u2014 never past the live tail \u2014 plus one optional trailing idle frame.\n * The cursor advances per `next()`, so `break` (which calls `return()`)\n * leaves the rest buffered and compacts. A nested\n * `consume()` of the SAME buffer while one is live falls back to a fresh\n * generator so the pooled `_iterRes` isn't clobbered (nesting is meaningless \u2014\n * both share the cursor \u2014 but it must not corrupt).\n */\n consume(opts?: ConsumeOptions<I>): IterableIterator<I> {\n const empty = this.size === 0;\n const idle = empty ? this.idleFrameOnEmpty(opts) : undefined;\n if (empty && idle === undefined) { this._lastWasIdle = false; return DONE_ITERATOR as IterableIterator<I>; }\n // A nested consume() of this same buffer would clobber the pooled iterator's\n // shared state \u2192 hand the rare case a fresh generator instead.\n if (this._iterActive) { return this.consumeGen(idle); }\n this._iterActive = true;\n this._iterIdle = idle; // one trailing idle frame, or undefined\n this._iterRemaining = this.size; // count snapshot \u2014 a (hypothetical) mid-loop push isn't consumed this pass\n return this.ensureIter();\n }\n\n [Symbol.iterator](): IterableIterator<I> {\n return this.consume();\n }\n\n /** Lazily mint the reused iterator. Closures over `this` so it reads the\n * private cursor directly; one `_iterRes` is reused \u2014 read each result before\n * the next `next()`, as `for..of` / spread / `Array.from` all do. */\n private ensureIter(): IterableIterator<I> {\n if (this._iter !== undefined) { return this._iter; }\n const self = this;\n const res = this._iterRes;\n const finish = (): IteratorResult<I> => {\n self.compact();\n self._iterActive = false;\n self._iterRemaining = 0;\n self._iterIdle = undefined;\n res.value = undefined;\n res.done = true;\n return res as IteratorResult<I>;\n };\n this._iter = {\n [Symbol.iterator]() { return this; },\n next(): IteratorResult<I> {\n // `size > 0` re-checks the live tail: a mid-loop next()/take()/clear()\n // can only shorten the pass, never make it read past the end.\n if (self._iterRemaining > 0 && self.size > 0) { // a buffered input\n self._iterRemaining--;\n res.value = self.stepHead();\n res.done = false;\n return res as IteratorResult<I>;\n }\n if (self._iterIdle !== undefined) { // one trailing idle frame\n self._lastWasIdle = true;\n res.value = self._iterIdle;\n self._iterIdle = undefined;\n res.done = false;\n return res as IteratorResult<I>;\n }\n return finish();\n },\n return(): IteratorResult<I> { return finish(); }, // `break` \u2192 compact + release\n };\n return this._iter;\n }\n\n /** Re-entrant fallback: a fresh generator for a nested `consume()` of this same\n * buffer (the pooled iterator is single-active). Same accounting + compaction. */\n private *consumeGen(idleFrame: I | undefined): IterableIterator<I> {\n let remaining = this.size; // count-based walk, same reason as the pooled pass\n try {\n while (remaining-- > 0 && this.size > 0) { yield this.stepHead(); }\n if (idleFrame !== undefined) { this._lastWasIdle = true; yield idleFrame; }\n } finally {\n this.compact();\n }\n }\n\n peek(): I[] {\n return this._items.slice(this._head);\n }\n\n at(value: number): I | undefined {\n if (this._seqField === undefined) { return undefined; }\n for (let i = this._head; 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 - this._head;\n }\n\n /** Render time (server-clock ms) of the most recently drained input; `0`\n * until the first render-time-stamped input is drained. */\n get renderTime(): number {\n return this._lastRenderTime;\n }\n\n /** Reckon-display stamp of the most recently consumed input \u2014 the client's\n * serverNow estimate when it sampled that input, i.e. the EXACT instant its\n * forward-reckoned entities were displayed at. Same consume semantics as\n * {@link renderTime}. RAW: stays `0` until stamped \u2014 the resolved,\n * always-usable value is the accessor's `reckonTime`\n * (`room.inputs.get(sid)`); `rewind.lastSeenBy()`'s midpoint-reconstruction\n * fallback depends on this raw `0`. */\n get reckonTime(): number {\n return this._lastReckonTime;\n }\n\n /** Seq VALUE of the last consumed input \u2014 the reconciliation ack sent to the\n * client. Reliable (no wire seq tracked): falls back to {@link consumedCount}\n * for free. Unreliable: the framework wire seq, so a fully-dropped input doesn't\n * make the ack lag the client's sent seq by the lost count. `0` before the first\n * consume. */\n get ackSeq(): number {\n return this._seqs !== undefined ? this._lastConsumedSeq : this.consumedCount;\n }\n\n /** Whether the most recently produced frame was a synthesized idle (see {@link wasIdle}). */\n get wasIdle(): boolean {\n return this._lastWasIdle;\n }\n\n clear(): void {\n // Cleared inputs count as consumed: advance both the count and the seq-value ack\n // past them so the client's pending set drains (capture the newest seq before truncate).\n if (this._seqs !== undefined && this._items.length > 0) { this._lastConsumedSeq = this._seqs[this._items.length - 1]; }\n this.consumedCount += this.size;\n this.truncate();\n this._lastSeq = -Infinity;\n this._iterActive = false; // recovery hatch if a consume() iterator was abandoned unclosed\n this._iterRemaining = 0; // an abandoned iterator must not consume post-clear pushes\n this._iterIdle = undefined;\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.inputs.get(sessionId)` is a Map lookup + property read.\n *\n * @internal\n */\nexport class InputAccessorImpl<I = any> implements InputAccessor<I> {\n private _client: ClientPrivate;\n /** Current room time (`clock.elapsedTime`) \u2014 resolves the {@link reckonTime}\n * fallback. Threaded by `RoomInput.allocate()`; optional so bare\n * constructions (tests) keep the raw 0. */\n private _nowOf?: () => number;\n constructor(client: ClientPrivate, nowOf?: () => number) {\n this._client = client;\n this._nowOf = nowOf;\n }\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 consume(opts?: ConsumeOptions<I>): IterableIterator<I> {\n return (this._client._inputBuffer?.consume(opts) ?? DONE_ITERATOR) as IterableIterator<I>;\n }\n [Symbol.iterator](): IterableIterator<I> { return this.consume(); }\n drain(opts?: ConsumeOptions<I>): I[] { return (this._client._inputBuffer?.drain(opts) ?? []) as I[]; }\n next(): I | undefined;\n next(opts: { idle: IdleInput<I> }): I;\n next(opts?: ConsumeOptions<I>): I | undefined { return this._client._inputBuffer?.next(opts) as I | undefined; }\n take(n: number): I[] { return (this._client._inputBuffer?.take(n) ?? []) as I[]; }\n peek(): I[] { return (this._client._inputBuffer?.peek() ?? []) as I[]; }\n get size(): number { return this._client._inputBuffer?.size ?? 0; }\n get consumedCount(): number { return this._client._inputBuffer?.consumedCount ?? 0; }\n get wasIdle(): boolean { return this._client._inputBuffer?.wasIdle ?? false; }\n clear(): void { this._client._inputBuffer?.clear(); }\n get renderTime(): number { return this._client._inputBuffer?.renderTime ?? 0; }\n /** @internal RAW reckon stamp (0 until stamped). The rewind binding reads\n * THIS, not the resolved getter below: `Rewind._aim`'s midpoint fallback\n * must still see 0 for unstamped clients (a resolved value would silently\n * take the direct-stamp clamp path). */\n get rawReckonTime(): number { return this._client._inputBuffer?.reckonTime ?? 0; }\n get reckonTime(): number {\n const raw = this._client._inputBuffer?.reckonTime ?? 0;\n return raw > 0 ? raw : this._nowOf !== undefined ? this._nowOf() : 0;\n }\n}\n\n/**\n * Returned by `room.inputs.get(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 consume: () => DONE_ITERATOR,\n [Symbol.iterator]: () => DONE_ITERATOR,\n drain: () => [],\n next: () => undefined,\n take: () => [],\n peek: () => [],\n size: 0,\n consumedCount: 0,\n wasIdle: false,\n clear: () => {},\n renderTime: 0,\n reckonTime: 0, // stays 0 \u2014 an unknown session / input-less room has no clock to resolve against\n});\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { ClientPrivate } from '../Transport.ts';\nimport { $METADATA } from '../utils/Utils.ts';\n\nimport type {\n ConsumeOptions, IdleContext, IdleInput, InputAccessor, SanitizeInput,\n} from './types.ts';\n\n/**\n * @internal Validate a `subSteps` count. Throws on a fractional/non-positive\n * value: both sides loop `subSteps` times at `dt/subSteps`, so anything but a\n * whole number is a determinism bug, not a tunable. Shared by `defineInput`\n * and `setFixedTimestep`.\n */\nexport function validateSubSteps(subSteps: number | undefined, source: string): number | undefined {\n if (subSteps === undefined) { return undefined; }\n if (!Number.isInteger(subSteps) || subSteps < 1) {\n throw new Error(`[${source}] subSteps must be an integer >= 1 (got ${subSteps}).`);\n }\n return subSteps;\n}\n\n/**\n * @internal Compile a {@link SanitizeInput} spec into the per-frame function the\n * decode path applies. The map form precompiles to dense min/max arrays walked\n * with the NaN-safe branch clamp; the callback form passes through.\n */\nexport function compileSanitizer<I>(spec: SanitizeInput<I>): (input: I) => void {\n if (typeof spec === 'function') { return spec; }\n const names = Object.keys(spec);\n const mins = new Float64Array(names.length);\n const maxs = new Float64Array(names.length);\n for (let i = 0; i < names.length; i++) {\n const range = (spec as Record<string, readonly [number, number]>)[names[i]]!;\n mins[i] = range[0];\n maxs[i] = range[1];\n }\n return (input: I) => {\n const inst = input as Record<string, number>;\n for (let i = 0; i < names.length; i++) {\n const v = inst[names[i]];\n // Never-received field: no input to sanitize \u2014 clamping would INVENT\n // one (delta-encoding clients legitimately never transmit fields they\n // never assigned; the range floor turned \"no input\" into \"held stick\").\n if (v === undefined) { continue; }\n // NaN-safe: NaN fails both comparisons \u2192 clamp floor.\n inst[names[i]] = v >= mins[i] ? (v <= maxs[i] ? v : maxs[i]) : mins[i];\n }\n };\n}\n\n/** Field names of an input schema ctor, in declaration order (indices are dense\n * from 0 in the metadata). Resolved ONCE per buffer (cold path). */\nfunction fieldNamesOf(ctor: new () => any): string[] {\n const md = (ctor as any)[$METADATA] as Record<number, { name?: string }> | undefined;\n const names: string[] = [];\n if (md) {\n for (let i = 0; ; i++) {\n const f = md[i];\n if (!f || typeof f.name !== \"string\") break;\n names.push(f.name);\n }\n }\n return names;\n}\n\n/**\n * @internal Seed every declared field that has no construction default with\n * its wire-neutral zero value (0 / false / \"\"; quantized: 0 clamped into the\n * descriptor's range). The input contract is \"the server holds the last\n * received value\" \u2014 before anything is received, that value is the ZERO of\n * the field's type, never `undefined`: a sim would NaN-propagate it and a\n * range sanitizer would clamp it into a phantom input. Clients only transmit\n * fields they assign, so an untouched field stays absent on the wire forever.\n */\nexport function seedInputZeroValues(instance: any, ctor: new () => any): void {\n const md = (ctor as any)[$METADATA] as\n Record<number, { name?: string, type?: unknown }> | undefined;\n if (!md) { return; }\n for (let i = 0; ; i++) {\n const f = md[i];\n if (!f || typeof f.name !== \"string\") break;\n if (instance[f.name] !== undefined) { continue; } // .default() wins\n const type = f.type;\n if (typeof type === \"string\") {\n instance[f.name] = type === \"string\" ? \"\" : type === \"boolean\" ? false : 0;\n } else if (type !== null && typeof type === \"object\" && \"quantized\" in (type as object)) {\n const q = (type as { quantized: { min: number, max: number } }).quantized;\n instance[f.name] = q.min > 0 ? q.min : q.max < 0 ? q.max : 0;\n }\n }\n}\n\n/** Reclaim the consumed prefix once the read cursor passes this many items \u2014\n * bounds the parallel arrays' slack to O(THRESHOLD) between compactions while\n * keeping per-consume work O(1) (no `shift()` re-index on every input). */\nconst COMPACT_THRESHOLD = 32;\n\n/** Shared zero-state \"already done\" iterator \u2014 returned by `consume()` on an\n * empty buffer with no idle policy, and by the no-op accessor. Frozen + a frozen\n * result, so it's allocation-free and safe to share across all callers. */\nconst DONE_RESULT: IteratorResult<any> = Object.freeze({ value: undefined, done: true });\nconst DONE_ITERATOR: IterableIterator<any> = Object.freeze({\n [Symbol.iterator]() { return this; },\n next() { return DONE_RESULT; },\n});\n\n/** @internal */\nexport class InputBufferImpl<I = any> {\n /** Pending snapshots, indexed `[_head, _items.length)`. Consumed inputs are\n * NOT spliced off per-read \u2014 `_head` advances and the prefix is reclaimed in\n * bulk by {@link compact} (a `shift()` per read is O(n), so a burst would be\n * O(n\u00B2)). `_renderTimes`/`_reckonTimes` stay parallel to `_items`. */\n private _items: I[] = [];\n /** Read cursor: index of the oldest UNCONSUMED input (== `_items.length` when empty). */\n private _head = 0;\n private _lastSeq: number = -Infinity;\n private readonly _maxSize: number;\n private readonly _seqField: string | undefined;\n\n /**\n * Cumulative count of inputs CONSUMED from this buffer (via any consume\n * primitive \u2014 {@link next}/{@link take}/{@link drain}/{@link consume} \u2014 plus\n * {@link clear} and overflow). Consumed = \"removed from the pending set\",\n * whether applied to state or discarded. The server echoes this in the TIMED\n * prefix as the reconciliation ack: how many of the client's inputs are\n * reflected in (or finished influencing) the authoritative state, which lags\n * the receive counter by inputs still buffered. Distinct from\n * `_receivedInputCount` (receive-time, for RTT), which leads the state.\n */\n consumedCount = 0;\n\n /** Render times parallel to `_items` (server-clock ms; `0` when unset). */\n private _renderTimes: number[] = [];\n /** Render time of the most recently drained input (see {@link renderTime}). */\n private _lastRenderTime = 0;\n /** Reckon-display stamps parallel to `_items` \u2014 the client's serverNow\n * estimate at input-sample time, i.e. the instant its forward-reckoned\n * entities were displayed at (server-clock ms; `0` when unset). */\n private _reckonTimes: number[] = [];\n /** Reckon stamp of the most recently consumed input (see {@link reckonTime}). */\n private _lastReckonTime = 0;\n /** Framework wire seq per buffered input (parallel to `_items`) \u2014 UNRELIABLE\n * ONLY, lazily created on the first seq-carrying push. Reliable inputs are\n * contiguous, so their consumed-seq-value is always exactly {@link consumedCount}\n * and needs no parallel array; `undefined` here marks that (the common, default\n * path pays nothing). A buffer is single-mode, so this is either never created\n * (reliable) or created once and kept parallel (unreliable). */\n private _seqs?: number[];\n /** Seq VALUE of the last consumed input \u2014 the reconciliation ack echoed to the\n * client (see {@link ackSeq}). Only meaningful when {@link _seqs} is tracked\n * (unreliable); reliable's ack falls back to {@link consumedCount}. */\n private _lastConsumedSeq = 0;\n /** Whether the most recently produced frame was a synthesized idle (see {@link wasIdle}). */\n private _lastWasIdle = false;\n\n /** Reused {@link consume} iterator (lazily minted) + its per-call state, so a\n * per-tick loop allocates no generator frame. `_iterRemaining` snapshots the\n * COUNT to walk \u2014 a count, not an index: mid-pass mutations (`next()`/`take()`/\n * `clear()`/compaction) shift indices but can only shorten a count-based pass,\n * never point it past the tail. `_iterIdle` is an optional trailing synthesized\n * frame; `_iterActive` guards against a nested consume() clobbering the shared\n * `_iterRes`. */\n private _iter?: IterableIterator<I>;\n private _iterRemaining = 0;\n private _iterActive = false;\n private _iterIdle?: I;\n private readonly _iterRes: { value: I | undefined; done: boolean } = { value: undefined, done: false };\n\n /** Input schema ctor \u2014 mints the reused idle frame; idle is off without it. */\n private readonly _ctor?: new () => I;\n /** Client slice this buffer belongs to (the live object is `Client & ClientPrivate`)\n * \u2014 feeds the idle callback's ctx (latest + sessionId). */\n private readonly _client?: Pick<ClientPrivate, '_input'> & { sessionId: string };\n /** Room-level idle policy (`defineInput({ idle })`) \u2014 the bare-call default. */\n private readonly _roomIdle?: IdleInput<I>;\n /** Reused synthesized idle frame + the schema's field names/defaults (lazy). */\n private _idle?: I;\n private _fieldNames?: string[];\n private _defaults?: unknown[];\n /** Reused ctx for the idle callback form (see {@link IdleContext}). */\n private readonly _idleCtx: IdleContext<I> = { latest: undefined, sessionId: \"\" };\n\n constructor(maxSize: number, seqField: string | undefined, ctor?: new () => I, client?: Pick<ClientPrivate, '_input'> & { sessionId: string }, idle?: IdleInput<I>) {\n this._maxSize = maxSize;\n this._seqField = seqField;\n this._ctor = ctor;\n this._client = client;\n this._roomIdle = idle;\n }\n\n /** The effective idle policy for one consume call: per-call `false` suppresses,\n * per-call value overrides, else the room-level default (or none). */\n private effectiveIdle(opts?: ConsumeOptions<I>): IdleInput<I> | undefined {\n return opts?.idle === false ? undefined : (opts?.idle ?? this._roomIdle);\n }\n\n /** Resolve an {@link IdleInput} to overrides \u2014 invokes the callback form\n * LAZILY, here at synthesis time (the buffer is known to be empty). */\n private resolveIdle(idle: IdleInput<I>): true | Partial<I> {\n if (typeof idle !== \"function\") return idle;\n this._idleCtx.latest = this._client?._input as I | undefined;\n this._idleCtx.sessionId = this._client?.sessionId ?? \"\";\n return idle(this._idleCtx) ?? true;\n }\n\n /**\n * The synthesized \"no input this tick\" frame: schema defaults overlaid with\n * `overrides` (`true` = none). Copies BY FIELD NAME from the schema metadata \u2014\n * schema fields are prototype accessors (no own props), so `Object.assign`\n * can't source from a schema instance; the name walk reads getters, letting\n * `overrides` be a plain partial OR a schema instance (e.g. `latest`).\n * ONE reused instance \u2014 refilled per call, never advances the ack.\n */\n private idleFrame(overrides: true | Partial<I>): I {\n if (this._idle === undefined) {\n // Lazy mint (cold): the fresh instance doubles as the defaults source.\n this._idle = new this._ctor!();\n seedInputZeroValues(this._idle, this._ctor!);\n this._fieldNames = fieldNamesOf(this._ctor!);\n const fresh = this._idle as Record<string, unknown>;\n this._defaults = this._fieldNames.map((n) => fresh[n]);\n }\n const idle = this._idle as Record<string, unknown>;\n const names = this._fieldNames!;\n const defaults = this._defaults!;\n const ov = overrides === true ? undefined : (overrides as Record<string, unknown>);\n for (let i = 0; i < names.length; i++) {\n const v = ov?.[names[i]];\n idle[names[i]] = v !== undefined ? v : defaults[i];\n }\n return this._idle;\n }\n\n /** The idle frame to synthesize on an empty tick (room policy or per-call\n * `opts.idle`), or `undefined` when no policy is in effect. Reuses the single\n * idle instance; never advances the ack. Callers set {@link wasIdle}. */\n private idleFrameOnEmpty(opts?: ConsumeOptions<I>): I | undefined {\n const idle = this._ctor !== undefined ? this.effectiveIdle(opts) : undefined;\n return idle === undefined ? undefined : this.idleFrame(this.resolveIdle(idle));\n }\n\n /** Consume the input at the cursor: advance cursor + ack, stamp THIS input's\n * render/reckon times, clear {@link wasIdle}. The shared per-input step behind\n * {@link next}, {@link consume}'s iterator, and the re-entrant fallback. */\n private stepHead(): I {\n const i = this._head;\n this._lastWasIdle = false;\n this._lastRenderTime = this._renderTimes[i];\n this._lastReckonTime = this._reckonTimes[i];\n if (this._seqs !== undefined) { this._lastConsumedSeq = this._seqs[i]; }\n this._head = i + 1;\n this.consumedCount++;\n return this._items[i];\n }\n\n /** Drop ALL slots, reusing the backing arrays' capacity \u2014 the zero-GC reset for\n * the consume/next path ({@link compact}/{@link clear}). `drain()` deliberately\n * does NOT use this (it returns an array, so fresh `[]` is faster there). */\n private truncate(): void {\n this._items.length = 0;\n this._renderTimes.length = 0;\n this._reckonTimes.length = 0;\n if (this._seqs !== undefined) { this._seqs.length = 0; }\n this._head = 0;\n }\n\n /**\n * Append a decoded input snapshot. `seq` is the framework wire seq (unreliable);\n * omit it for reliable inputs, which get an implicit monotonic receive count so\n * {@link ackSeq} stays well-defined in both modes.\n */\n push(snapshot: I, renderTime: number = 0, reckonTime: number = 0, seq?: number): void {\n this._items.push(snapshot);\n this._renderTimes.push(renderTime);\n this._reckonTimes.push(reckonTime);\n // Track the wire seq only for unreliable (seq provided); reliable derives its\n // ack from consumedCount, so it never allocates/maintains this array.\n if (seq !== undefined) { (this._seqs ??= []).push(seq); }\n // Overflow drops the oldest unconsumed input; count it consumed so the ack\n // still advances past it. Advance the cursor (don't shift) \u2014 keeps it O(1).\n if (this.size > this._maxSize) {\n if (this._seqs !== undefined) { this._lastConsumedSeq = this._seqs[this._head]; } // dropped oldest counts as acked\n this._head++;\n this.consumedCount++;\n this.compact();\n }\n }\n\n /** Reclaim the consumed prefix `[0, _head)`. Free when fully drained (reuse the\n * arrays); otherwise splice only once the cursor passes {@link COMPACT_THRESHOLD},\n * so the amortized per-input cost stays O(1). */\n private compact(): void {\n if (this._head === 0) { return; }\n if (this._head >= this._items.length) {\n this.truncate();\n } else if (this._head >= COMPACT_THRESHOLD) {\n this._items.splice(0, this._head);\n this._renderTimes.splice(0, this._head);\n this._reckonTimes.splice(0, this._head);\n if (this._seqs !== undefined) { this._seqs.splice(0, this._head); }\n this._head = 0;\n }\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(opts?: ConsumeOptions<I>): I[] {\n // Empty: synthesize one idle frame (NOT consumed \u2014 no ack bump, stamps\n // untouched) when a policy is in effect, else [].\n if (this.size === 0) {\n const idle = this.idleFrameOnEmpty(opts);\n this._lastWasIdle = idle !== undefined;\n return idle !== undefined ? [idle] : [];\n }\n this._lastWasIdle = false;\n const last = this._items.length - 1;\n this._lastRenderTime = this._renderTimes[last]; // drain reports the NEWEST stamps\n this._lastReckonTime = this._reckonTimes[last];\n if (this._seqs !== undefined) { this._lastConsumedSeq = this._seqs[last]; }\n // Hand off the backing array untouched when nothing was partially consumed\n // (O(1) \u2014 the caller may retain it); else copy out the unconsumed tail. Fresh\n // arrays here, NOT truncate(): drain already allocates the returned array, and\n // V8 makes `= []` + refill cheaper than `length = 0` reuse (~1.8\u00D7 at small N),\n // so the speed wins and the 3 tiny empties are negligible next to the return.\n const out = this._head === 0 ? this._items : this._items.slice(this._head);\n this.consumedCount += out.length;\n this._items = [];\n this._renderTimes = [];\n this._reckonTimes = [];\n if (this._seqs !== undefined) { this._seqs = []; }\n this._head = 0;\n return out;\n }\n\n /** Consume the single oldest input (ack advances by one); `undefined` if\n * empty \u2014 or the synthesized idle frame when an idle policy is in effect. */\n next(opts?: ConsumeOptions<I>): I | undefined {\n if (this.size === 0) {\n const idle = this.idleFrameOnEmpty(opts);\n this._lastWasIdle = idle !== undefined;\n return idle;\n }\n const item = this.stepHead();\n this.compact();\n return item;\n }\n\n /** Consume up to `n` oldest inputs (ack advances by the count actually taken). */\n take(n: number): I[] {\n this._lastWasIdle = false; // take never synthesizes idle\n const avail = this.size;\n if (n <= 0 || avail === 0) { return []; }\n const count = Math.min(n, avail);\n const start = this._head;\n const out = this._items.slice(start, start + count);\n this._lastRenderTime = this._renderTimes[start + count - 1]; // newest taken input's stamps\n this._lastReckonTime = this._reckonTimes[start + count - 1];\n if (this._seqs !== undefined) { this._lastConsumedSeq = this._seqs[start + count - 1]; }\n this._head += count;\n this.consumedCount += count;\n this.compact();\n return out;\n }\n\n /**\n * Iterate pending inputs one at a time (per-yield ack + stamp updates), or\n * exactly one synthesized idle frame on an empty buffer with a policy in\n * effect. See {@link InputAccessor.consume}.\n *\n * Returns a POOLED iterator (reused across calls \u2014 no generator frame, no\n * per-call allocation) that consumes at most the pending count snapshotted at\n * the call \u2014 never past the live tail \u2014 plus one optional trailing idle frame.\n * The cursor advances per `next()`, so `break` (which calls `return()`)\n * leaves the rest buffered and compacts. A nested\n * `consume()` of the SAME buffer while one is live falls back to a fresh\n * generator so the pooled `_iterRes` isn't clobbered (nesting is meaningless \u2014\n * both share the cursor \u2014 but it must not corrupt).\n */\n consume(opts?: ConsumeOptions<I>): IterableIterator<I> {\n const empty = this.size === 0;\n const idle = empty ? this.idleFrameOnEmpty(opts) : undefined;\n if (empty && idle === undefined) { this._lastWasIdle = false; return DONE_ITERATOR as IterableIterator<I>; }\n // A nested consume() of this same buffer would clobber the pooled iterator's\n // shared state \u2192 hand the rare case a fresh generator instead.\n if (this._iterActive) { return this.consumeGen(idle); }\n this._iterActive = true;\n this._iterIdle = idle; // one trailing idle frame, or undefined\n this._iterRemaining = this.size; // count snapshot \u2014 a (hypothetical) mid-loop push isn't consumed this pass\n return this.ensureIter();\n }\n\n [Symbol.iterator](): IterableIterator<I> {\n return this.consume();\n }\n\n /** Lazily mint the reused iterator. Closures over `this` so it reads the\n * private cursor directly; one `_iterRes` is reused \u2014 read each result before\n * the next `next()`, as `for..of` / spread / `Array.from` all do. */\n private ensureIter(): IterableIterator<I> {\n if (this._iter !== undefined) { return this._iter; }\n const self = this;\n const res = this._iterRes;\n const finish = (): IteratorResult<I> => {\n self.compact();\n self._iterActive = false;\n self._iterRemaining = 0;\n self._iterIdle = undefined;\n res.value = undefined;\n res.done = true;\n return res as IteratorResult<I>;\n };\n this._iter = {\n [Symbol.iterator]() { return this; },\n next(): IteratorResult<I> {\n // `size > 0` re-checks the live tail: a mid-loop next()/take()/clear()\n // can only shorten the pass, never make it read past the end.\n if (self._iterRemaining > 0 && self.size > 0) { // a buffered input\n self._iterRemaining--;\n res.value = self.stepHead();\n res.done = false;\n return res as IteratorResult<I>;\n }\n if (self._iterIdle !== undefined) { // one trailing idle frame\n self._lastWasIdle = true;\n res.value = self._iterIdle;\n self._iterIdle = undefined;\n res.done = false;\n return res as IteratorResult<I>;\n }\n return finish();\n },\n return(): IteratorResult<I> { return finish(); }, // `break` \u2192 compact + release\n };\n return this._iter;\n }\n\n /** Re-entrant fallback: a fresh generator for a nested `consume()` of this same\n * buffer (the pooled iterator is single-active). Same accounting + compaction. */\n private *consumeGen(idleFrame: I | undefined): IterableIterator<I> {\n let remaining = this.size; // count-based walk, same reason as the pooled pass\n try {\n while (remaining-- > 0 && this.size > 0) { yield this.stepHead(); }\n if (idleFrame !== undefined) { this._lastWasIdle = true; yield idleFrame; }\n } finally {\n this.compact();\n }\n }\n\n peek(): I[] {\n return this._items.slice(this._head);\n }\n\n at(value: number): I | undefined {\n if (this._seqField === undefined) { return undefined; }\n for (let i = this._head; 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 - this._head;\n }\n\n /** Render time (server-clock ms) of the most recently drained input; `0`\n * until the first render-time-stamped input is drained. */\n get renderTime(): number {\n return this._lastRenderTime;\n }\n\n /** Reckon-display stamp of the most recently consumed input \u2014 the client's\n * serverNow estimate when it sampled that input, i.e. the EXACT instant its\n * forward-reckoned entities were displayed at. Same consume semantics as\n * {@link renderTime}. RAW: stays `0` until stamped \u2014 the resolved,\n * always-usable value is the accessor's `reckonTime`\n * (`room.inputs.get(sid)`); `rewind.lastSeenBy()`'s midpoint-reconstruction\n * fallback depends on this raw `0`. */\n get reckonTime(): number {\n return this._lastReckonTime;\n }\n\n /** Seq VALUE of the last consumed input \u2014 the reconciliation ack sent to the\n * client. Reliable (no wire seq tracked): falls back to {@link consumedCount}\n * for free. Unreliable: the framework wire seq, so a fully-dropped input doesn't\n * make the ack lag the client's sent seq by the lost count. `0` before the first\n * consume. */\n get ackSeq(): number {\n return this._seqs !== undefined ? this._lastConsumedSeq : this.consumedCount;\n }\n\n /** Whether the most recently produced frame was a synthesized idle (see {@link wasIdle}). */\n get wasIdle(): boolean {\n return this._lastWasIdle;\n }\n\n clear(): void {\n // Cleared inputs count as consumed: advance both the count and the seq-value ack\n // past them so the client's pending set drains (capture the newest seq before truncate).\n if (this._seqs !== undefined && this._items.length > 0) { this._lastConsumedSeq = this._seqs[this._items.length - 1]; }\n this.consumedCount += this.size;\n this.truncate();\n this._lastSeq = -Infinity;\n this._iterActive = false; // recovery hatch if a consume() iterator was abandoned unclosed\n this._iterRemaining = 0; // an abandoned iterator must not consume post-clear pushes\n this._iterIdle = undefined;\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.inputs.get(sessionId)` is a Map lookup + property read.\n *\n * @internal\n */\nexport class InputAccessorImpl<I = any> implements InputAccessor<I> {\n private _client: ClientPrivate;\n /** Current room time (`clock.elapsedTime`) \u2014 resolves the {@link reckonTime}\n * fallback. Threaded by `RoomInput.allocate()`; optional so bare\n * constructions (tests) keep the raw 0. */\n private _nowOf?: () => number;\n constructor(client: ClientPrivate, nowOf?: () => number) {\n this._client = client;\n this._nowOf = nowOf;\n }\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 consume(opts?: ConsumeOptions<I>): IterableIterator<I> {\n return (this._client._inputBuffer?.consume(opts) ?? DONE_ITERATOR) as IterableIterator<I>;\n }\n [Symbol.iterator](): IterableIterator<I> { return this.consume(); }\n drain(opts?: ConsumeOptions<I>): I[] { return (this._client._inputBuffer?.drain(opts) ?? []) as I[]; }\n next(): I | undefined;\n next(opts: { idle: IdleInput<I> }): I;\n next(opts?: ConsumeOptions<I>): I | undefined { return this._client._inputBuffer?.next(opts) as I | undefined; }\n take(n: number): I[] { return (this._client._inputBuffer?.take(n) ?? []) as I[]; }\n peek(): I[] { return (this._client._inputBuffer?.peek() ?? []) as I[]; }\n get size(): number { return this._client._inputBuffer?.size ?? 0; }\n get consumedCount(): number { return this._client._inputBuffer?.consumedCount ?? 0; }\n get wasIdle(): boolean { return this._client._inputBuffer?.wasIdle ?? false; }\n clear(): void { this._client._inputBuffer?.clear(); }\n get renderTime(): number { return this._client._inputBuffer?.renderTime ?? 0; }\n /** @internal RAW reckon stamp (0 until stamped). The rewind binding reads\n * THIS, not the resolved getter below: `Rewind._aim`'s midpoint fallback\n * must still see 0 for unstamped clients (a resolved value would silently\n * take the direct-stamp clamp path). */\n get rawReckonTime(): number { return this._client._inputBuffer?.reckonTime ?? 0; }\n get reckonTime(): number {\n const raw = this._client._inputBuffer?.reckonTime ?? 0;\n return raw > 0 ? raw : this._nowOf !== undefined ? this._nowOf() : 0;\n }\n}\n\n/**\n * Returned by `room.inputs.get(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 consume: () => DONE_ITERATOR,\n [Symbol.iterator]: () => DONE_ITERATOR,\n drain: () => [],\n next: () => undefined,\n take: () => [],\n peek: () => [],\n size: 0,\n consumedCount: 0,\n wasIdle: false,\n clear: () => {},\n renderTime: 0,\n reckonTime: 0, // stays 0 \u2014 an unknown session / input-less room has no clock to resolve against\n});\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -120,6 +120,7 @@ var RoomInput = class {
120
120
  * via `bufferMaxSize > 0`, for rollback/lockstep), and the accessor. */
121
121
  allocate(client) {
122
122
  client._input = new this.options.ctor();
123
+ (0, import_InputBuffer.seedInputZeroValues)(client._input, this.options.ctor);
123
124
  client._inputDecoder = new import_input.InputDecoder(client._input);
124
125
  client._reckonBaseline = 0;
125
126
  const maxSize = this.options.bufferMaxSize;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/input/RoomInput.ts"],
4
- "sourcesContent": ["import { decode, encode, Encoder, Reflection, type Iterator } from '@colyseus/schema';\nimport { InputDecoder } from '@colyseus/schema/input';\nimport { HandshakeSection, InputFlags, ProtocolModifier } from '@colyseus/shared-types';\n\nimport {\n InputAccessorImpl, InputBufferImpl, NO_OP_INPUT_ACCESSOR,\n compileSanitizer, validateSubSteps,\n} from './InputBuffer.ts';\nimport type {\n InputAccessor, InputAPI, NormalizedInputOptions,\n DefineInputOptions, IdleDeclared,\n} from './types.ts';\nimport type { Client, ClientPrivate } from '../Transport.ts';\nimport type { Room } from '../Room.ts';\nimport { debugAndPrintError } from '../Debug.ts';\n\n/**\n * Module-level cache of `Reflection.encode` output keyed by input\n * constructor \u2014 pays the encoding cost once per Room class regardless of\n * room instance count. WeakMap so unused classes can be GC'd.\n */\nconst _inputReflectionCache = new WeakMap<Function, Uint8Array>();\n\n/**\n * Runtime behind {@link InputAPI}. A class, not a per-`define()` object literal:\n * literal (and `defineProperty`) accessors carry their closure identity in the\n * hidden class, so every room instance would get a UNIQUE map \u2014 sending shared\n * `this.inputs.*` call sites megamorphic. Prototype getters are created once,\n * every instance shares one hidden class (reads stay monomorphic), and\n * `define()` allocates a single 1-field object instead of 7 closures + a map\n * lineage per room.\n *\n * @internal\n */\nclass InputAPIImpl {\n private input: RoomInput;\n constructor(input: RoomInput) { this.input = input; }\n /** Registry, not room.clients: an accessor outlives its client through the\n * reconnection window, so a dropped seat still synthesizes the idle policy. */\n get(sessionId: string): InputAccessor<any> {\n return this.input.accessors.get(sessionId) ?? NO_OP_INPUT_ACCESSOR;\n }\n // Live reads off `options` (mutated in place by a later setTimestep/\n // setFixedTimestep back-fill, so a derived tickRate is reflected); 1/hz is\n // correctly-rounded IEEE-754 \u2192 bit-identical to the client's stepSeconds.\n get tickRate(): number | undefined { return this.input.options.tickRate; }\n get stepSeconds(): number | undefined { const hz = this.input.options.tickRate; return hz ? 1 / hz : undefined; }\n get stepMs(): number | undefined { const hz = this.input.options.tickRate; return hz ? 1000 / hz : undefined; }\n // Sub-step trio: same derivation as the client handle ((1/hz)/n) \u2014 bit-identical dt.\n get subSteps(): number { return this.input.options.subSteps ?? 1; }\n get subStepSeconds(): number | undefined { const hz = this.input.options.tickRate; return hz ? (1 / hz) / (this.input.options.subSteps ?? 1) : undefined; }\n get subStepMs(): number | undefined { const hz = this.input.options.tickRate; return hz ? (1000 / hz) / (this.input.options.subSteps ?? 1) : undefined; }\n}\n\n/**\n * Per-room input subsystem, owned by {@link Room} and created lazily on the\n * first {@link Room.defineInput} call \u2014 rooms without inputs allocate none of\n * it. Owns the input options, the per-session accessor registry (decoupled\n * from `this.clients` so an entry outlives a dropped client across its\n * reconnection window), the wire stamp mode, and the encode/decode/handshake\n * machinery. Reads back into its Room for context it doesn't own (the patch\n * rate and the rewind timeline mode).\n *\n * @internal\n */\nexport class RoomInput {\n /** Owning room \u2014 for `patchRate` and the rewind timeline mode. */\n private room: Room<any>;\n\n /** Input configuration (ctor, seqField, bufferMaxSize, idle, sanitize,\n * tickRate, subSteps). `tickRate`/`subSteps` may be back-filled by\n * `setTimestep`/`setFixedTimestep`. Set in {@link define}. */\n options!: NormalizedInputOptions;\n\n /** The `InputAPI` returned to userland from `defineInput`, also the\n * framework-owned handle the rewind binding resolves accessors through. */\n api!: InputAPI<any>;\n\n /** sessionId \u2192 accessor. Decoupled from `this.clients`: an entry outlives its\n * client across the reconnection window, so {@link InputAPI.get} keeps\n * synthesizing the idle policy for an absent seat (its buffer was cleared on\n * leave \u2192 idle). Set on join, deleted on full leave. Read by\n * {@link InputAPIImpl.get} (hence not `private`). */\n readonly accessors: Map<string, InputAccessor<any>> = new Map();\n\n // Wire stamp mode derived from the rewind timeline, resolved once on first\n // handshake/decode then frozen so advertise + decode never disagree mid-session:\n // the renderTime/reckonTime prefix per input.\n #stampRender = false;\n #stampReckon = false;\n #stampResolved = false;\n\n /** One shared now-resolver handed to every accessor (see {@link InputAccessorImpl}). */\n private nowOf: () => number;\n\n constructor(room: Room<any>) {\n this.room = room;\n this.nowOf = () => this.room.clock.elapsedTime;\n }\n\n /**\n * Build the input configuration + `InputAPI` (the body of\n * {@link Room.defineInput}). Returns the api; the Room hands it back to\n * userland and keeps the framework-owned reference.\n */\n define<\n C extends new () => any,\n O extends DefineInputOptions<InstanceType<C>> = DefineInputOptions<InstanceType<C>>,\n >(\n type: C,\n opts?: O,\n ): InputAPI<InstanceType<C>, IdleDeclared<O, InstanceType<C>>> {\n // Normalize the step declaration to the canonical wire form (Hz). stepSeconds\n // / stepMs are the unit-safe spellings; both sides derive dt = 1/tickRate, so\n // one declared number drives the server's physics step AND the client's.\n const tickRate =\n opts?.stepSeconds !== undefined ? 1 / opts.stepSeconds :\n opts?.stepMs !== undefined ? 1000 / opts.stepMs :\n opts?.tickRate;\n if (\n opts?.tickRate !== undefined && opts.stepMs === undefined && opts.stepSeconds === undefined &&\n (!Number.isInteger(opts.tickRate) || opts.tickRate > 240)\n ) {\n console.warn(\n `[defineInput] tickRate is a rate in Hz (got ${opts.tickRate}); a value ` +\n `like 1000/rate is a step interval in ms \u2014 pass { stepMs } instead.`,\n );\n }\n this.options = {\n ctor: type,\n // Opt-in: a default would silently drop frames for any app with a numeric `seq` field.\n seqField: opts?.seqField,\n bufferMaxSize: opts?.bufferMaxSize ?? 32,\n idle: opts?.idle,\n // Compiled once (map \u2192 dense min/max walk); applied per decoded frame.\n sanitize: opts?.sanitize !== undefined ? compileSanitizer(opts.sanitize) : undefined,\n tickRate,\n subSteps: validateSubSteps(opts?.subSteps, 'defineInput'),\n };\n if (!_inputReflectionCache.has(type)) {\n // SDK-deserializable ctor bytes (Reflection.decode rebuilds the ctor client-side).\n _inputReflectionCache.set(type, Reflection.encode(new Encoder(new type())));\n }\n const api = new InputAPIImpl(this) as InputAPI<InstanceType<C>, IdleDeclared<O, InstanceType<C>>>;\n this.api = api;\n return api;\n }\n\n // --- per-client lifecycle (called from Room._onJoin/_onLeave/etc.) ---\n\n /** Allocate the per-client input instance + decoder, the ring buffer (opt-in\n * via `bufferMaxSize > 0`, for rollback/lockstep), and the accessor. */\n allocate(client: Client & ClientPrivate): void {\n client._input = new this.options.ctor();\n client._inputDecoder = new InputDecoder(client._input);\n client._reckonBaseline = 0; // mirrors the SDK's delta-coded stamp baseline (reset together on (re)connect)\n const maxSize = this.options.bufferMaxSize;\n if (maxSize > 0) {\n // ctor \u2192 idle synthesis; client ref \u2192 idle ctx; idle policy \u2192 total drain()/next().\n client._inputBuffer = new InputBufferImpl(maxSize, this.options.seqField, this.options.ctor, client, this.options.idle);\n }\n client._inputAccessor = new InputAccessorImpl(client, this.nowOf);\n }\n\n /** @internal Raw reckon stamp for {@link Room.allowRewindState}'s\n * `bindReckonTime` \u2014 bypasses the accessor's resolved getter so the rewind\n * midpoint fallback still engages for unstamped clients. instanceof-narrowed:\n * the registry only ever holds `InputAccessorImpl` (see {@link allocate}),\n * so the 0 fallback covers only a missing session. */\n rawReckonTime(sessionId: string): number {\n const acc = this.accessors.get(sessionId);\n return acc instanceof InputAccessorImpl ? acc.rawReckonTime : 0;\n }\n\n /** Register a (re)joined client's accessor. On reconnect this overwrites the\n * dropped session's stale entry. Called after the client is in `this.clients`\n * (so a pre-push failure can't leak it) and before onJoin (so `get()` resolves\n * there). */\n register(sessionId: string, client: ClientPrivate): void {\n this.accessors.set(sessionId, client._inputAccessor!);\n }\n\n /** Freeze a leaving seat: drop pending inputs so a held (reconnecting) session\n * idles from the first tick rather than replaying last-known moves. `_input`\n * (the idle ctx's `latest`) is left intact. */\n freeze(client: ClientPrivate): void {\n client._inputBuffer?.clear();\n }\n\n /** Drop a fully-gone session's accessor (delete of a missing key is a no-op). */\n release(sessionId: string): void {\n this.accessors.delete(sessionId);\n }\n\n /** Release every held accessor (room dispose). */\n dispose(): void {\n this.accessors.clear();\n }\n\n // --- encode / decode (called from Room._onMessage) ---\n\n /**\n * Sanitize the freshly-decoded `client._input`, then (when buffering is on)\n * push a clone into the per-client buffer. Honors the framework seq\n * (unreliable) / a user `inputOptions.seqField` (reliable) to dedupe the\n * redundancy ring.\n */\n capture(client: ClientPrivate, renderTime: number = 0, reckonTime: number = 0, seq?: number): void {\n // Sanitize before anything reads it (latest, the clone below, the idle ctx).\n this.options.sanitize?.(client._input);\n const buf = client._inputBuffer;\n if (!buf) { return; } // no consumer registered \u2014 skip the clone allocation\n const inst = client._input!;\n if (seq !== undefined) {\n // Unreliable: dedup the ring by the framework wire seq (user seqField is for .at() only).\n if (!buf.accept(seq)) { return; }\n } else {\n // Reliable: no framework seq (implicit count). Honor a user `seqField` if set.\n const seqField = this.options.seqField;\n if (seqField !== undefined) {\n const value = (inst as any)[seqField] as number;\n if (typeof value === 'number' && !buf.accept(value)) { return; }\n }\n }\n buf.push(inst.clone() as any, renderTime, reckonTime, seq);\n }\n\n /** Decode a `ROOM_INPUT_RELIABLE` frame: an optional TIMED stamp prefix (shape\n * set by this room's derived stamp mode) then the input body. `it` starts at\n * offset 1, so the body begins at `it.offset`. */\n decodeReliable(client: ClientPrivate, buffer: Buffer, it: Iterator, modifiers: number): void {\n if (!client._inputDecoder) { return; }\n // Optional stamp prefix (TIMED bit), DELTA-CODED, length set by this room's\n // stamp mode (the timeline value is reconstructed against the per-client\n // baseline; the wire carries only the signed change from the previous frame):\n // both: [varint \u0394reckon][uint16 renderDelta] \u2192 renderTime = reckon \u2212 renderDelta\n // reckon-only: [varint \u0394reckon]\n // render-only: [varint \u0394render]\n let renderTime = 0;\n let reckonTime = 0;\n if (modifiers & ProtocolModifier.TIMED) {\n this.#resolveWireModes();\n // Reconstruct the absolute timeline value: baseline += signed delta. Both\n // sides start at 0 (so the first delta is absolute) and re-zero together on\n // (re)connect; reliable+in-order keeps the mirror locked.\n const stamp = client._reckonBaseline! += decode.number(buffer, it);\n if (this.#stampReckon && this.#stampRender) {\n reckonTime = stamp;\n const renderDelta = decode.uint16(buffer, it);\n renderTime = stamp > renderDelta ? stamp - renderDelta : 0;\n } else if (this.#stampReckon) {\n reckonTime = stamp;\n } else {\n renderTime = stamp;\n }\n }\n try {\n client._inputDecoder.decode(buffer.subarray(it.offset, buffer.byteLength));\n } catch (e: any) {\n debugAndPrintError(e);\n return;\n }\n client._lastInputReceivedAt = performance.now();\n // Mirrors the SDK's sent count; echoed back as lastInputSeq for RTT.\n client._receivedInputCount = (client._receivedInputCount ?? 0) + 1;\n this.capture(client, renderTime, reckonTime);\n }\n\n /** Decode a `ROOM_INPUT_UNRELIABLE` redundancy ring \u2014 each slot carries its\n * framework seq (base seq + position) for ring dedupe, no user seqField. */\n decodeUnreliable(client: ClientPrivate, buffer: Buffer, _modifiers: number): void {\n if (!client._inputDecoder) { return; }\n try {\n client._inputDecoder.decodeAll(buffer.subarray(1), (_inst, seq) => this.capture(client, 0, 0, seq));\n } catch (e: any) {\n debugAndPrintError(e);\n return;\n }\n client._lastInputReceivedAt = performance.now();\n }\n\n // --- handshake + stamp mode ---\n\n /** Resolve the wire stamp mode from the rewind timeline (once, then frozen). */\n #resolveWireModes(): void {\n if (this.#stampResolved) return;\n this.#stampResolved = true;\n const tl = this.room._timelineMode();\n this.#stampRender = tl?.snapshot ?? false;\n this.#stampReckon = tl?.reckon ?? false;\n }\n\n /**\n * The join-handshake input sections: INPUT_REFLECTION (the SDK-deserializable\n * input ctor bytes) and, when any runtime config differs from defaults,\n * INPUT_OPTIONS (`[flags uint8][tickRate varint?][patchRate varint?][subSteps\n * varint?]`). The client mirrors RENDER_TIME/RECKON_TIME (auto-stamp timeline),\n * tickRate (predict at dt=1/tickRate), patchRate (reconcile cadence), and\n * subSteps (physics sub-steps per input, omitted when 1). Returns `undefined`\n * when there's nothing to add.\n */\n handshakeSections(): Array<{ tag: number; bytes: Uint8Array }> | undefined {\n let sections: Array<{ tag: number; bytes: Uint8Array }> | undefined;\n const inputBytes = _inputReflectionCache.get(this.options.ctor);\n if (inputBytes !== undefined) {\n sections = [{ tag: HandshakeSection.INPUT_REFLECTION, bytes: inputBytes }];\n }\n this.#resolveWireModes();\n const stampRender = this.#stampRender;\n const stampReckon = this.#stampReckon;\n const tickRate = this.options.tickRate;\n const patchRate = (typeof this.room.patchRate === \"number\" && this.room.patchRate > 0)\n ? Math.round(this.room.patchRate) : undefined;\n const subSteps = (this.options.subSteps !== undefined && this.options.subSteps > 1)\n ? this.options.subSteps : undefined;\n if (stampRender || stampReckon || tickRate || patchRate || subSteps) {\n let flags = 0;\n if (stampRender) { flags |= InputFlags.RENDER_TIME; }\n if (stampReckon) { flags |= InputFlags.RECKON_TIME; }\n if (tickRate) { flags |= InputFlags.FIXED_TIMESTEP; }\n if (patchRate) { flags |= InputFlags.PATCH_RATE; }\n if (subSteps) { flags |= InputFlags.SUB_STEPS; }\n // 24B: worst case = flags + float64 tickRate (9) + uint32 patchRate (5) + subSteps (2).\n const buf = new Uint8Array(24);\n const sit = { offset: 0 };\n buf[sit.offset++] = flags;\n if (tickRate) { encode.number(buf, tickRate, sit); }\n if (patchRate) { encode.number(buf, patchRate, sit); }\n if (subSteps) { encode.number(buf, subSteps, sit); }\n (sections ??= []).push({\n tag: HandshakeSection.INPUT_OPTIONS,\n bytes: buf.subarray(0, sit.offset),\n });\n }\n return sections;\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { decode, encode, Encoder, Reflection, type Iterator } from '@colyseus/schema';\nimport { InputDecoder } from '@colyseus/schema/input';\nimport { HandshakeSection, InputFlags, ProtocolModifier } from '@colyseus/shared-types';\n\nimport {\n InputAccessorImpl, InputBufferImpl, NO_OP_INPUT_ACCESSOR,\n compileSanitizer, seedInputZeroValues, validateSubSteps,\n} from './InputBuffer.ts';\nimport type {\n InputAccessor, InputAPI, NormalizedInputOptions,\n DefineInputOptions, IdleDeclared,\n} from './types.ts';\nimport type { Client, ClientPrivate } from '../Transport.ts';\nimport type { Room } from '../Room.ts';\nimport { debugAndPrintError } from '../Debug.ts';\n\n/**\n * Module-level cache of `Reflection.encode` output keyed by input\n * constructor \u2014 pays the encoding cost once per Room class regardless of\n * room instance count. WeakMap so unused classes can be GC'd.\n */\nconst _inputReflectionCache = new WeakMap<Function, Uint8Array>();\n\n/**\n * Runtime behind {@link InputAPI}. A class, not a per-`define()` object literal:\n * literal (and `defineProperty`) accessors carry their closure identity in the\n * hidden class, so every room instance would get a UNIQUE map \u2014 sending shared\n * `this.inputs.*` call sites megamorphic. Prototype getters are created once,\n * every instance shares one hidden class (reads stay monomorphic), and\n * `define()` allocates a single 1-field object instead of 7 closures + a map\n * lineage per room.\n *\n * @internal\n */\nclass InputAPIImpl {\n private input: RoomInput;\n constructor(input: RoomInput) { this.input = input; }\n /** Registry, not room.clients: an accessor outlives its client through the\n * reconnection window, so a dropped seat still synthesizes the idle policy. */\n get(sessionId: string): InputAccessor<any> {\n return this.input.accessors.get(sessionId) ?? NO_OP_INPUT_ACCESSOR;\n }\n // Live reads off `options` (mutated in place by a later setTimestep/\n // setFixedTimestep back-fill, so a derived tickRate is reflected); 1/hz is\n // correctly-rounded IEEE-754 \u2192 bit-identical to the client's stepSeconds.\n get tickRate(): number | undefined { return this.input.options.tickRate; }\n get stepSeconds(): number | undefined { const hz = this.input.options.tickRate; return hz ? 1 / hz : undefined; }\n get stepMs(): number | undefined { const hz = this.input.options.tickRate; return hz ? 1000 / hz : undefined; }\n // Sub-step trio: same derivation as the client handle ((1/hz)/n) \u2014 bit-identical dt.\n get subSteps(): number { return this.input.options.subSteps ?? 1; }\n get subStepSeconds(): number | undefined { const hz = this.input.options.tickRate; return hz ? (1 / hz) / (this.input.options.subSteps ?? 1) : undefined; }\n get subStepMs(): number | undefined { const hz = this.input.options.tickRate; return hz ? (1000 / hz) / (this.input.options.subSteps ?? 1) : undefined; }\n}\n\n/**\n * Per-room input subsystem, owned by {@link Room} and created lazily on the\n * first {@link Room.defineInput} call \u2014 rooms without inputs allocate none of\n * it. Owns the input options, the per-session accessor registry (decoupled\n * from `this.clients` so an entry outlives a dropped client across its\n * reconnection window), the wire stamp mode, and the encode/decode/handshake\n * machinery. Reads back into its Room for context it doesn't own (the patch\n * rate and the rewind timeline mode).\n *\n * @internal\n */\nexport class RoomInput {\n /** Owning room \u2014 for `patchRate` and the rewind timeline mode. */\n private room: Room<any>;\n\n /** Input configuration (ctor, seqField, bufferMaxSize, idle, sanitize,\n * tickRate, subSteps). `tickRate`/`subSteps` may be back-filled by\n * `setTimestep`/`setFixedTimestep`. Set in {@link define}. */\n options!: NormalizedInputOptions;\n\n /** The `InputAPI` returned to userland from `defineInput`, also the\n * framework-owned handle the rewind binding resolves accessors through. */\n api!: InputAPI<any>;\n\n /** sessionId \u2192 accessor. Decoupled from `this.clients`: an entry outlives its\n * client across the reconnection window, so {@link InputAPI.get} keeps\n * synthesizing the idle policy for an absent seat (its buffer was cleared on\n * leave \u2192 idle). Set on join, deleted on full leave. Read by\n * {@link InputAPIImpl.get} (hence not `private`). */\n readonly accessors: Map<string, InputAccessor<any>> = new Map();\n\n // Wire stamp mode derived from the rewind timeline, resolved once on first\n // handshake/decode then frozen so advertise + decode never disagree mid-session:\n // the renderTime/reckonTime prefix per input.\n #stampRender = false;\n #stampReckon = false;\n #stampResolved = false;\n\n /** One shared now-resolver handed to every accessor (see {@link InputAccessorImpl}). */\n private nowOf: () => number;\n\n constructor(room: Room<any>) {\n this.room = room;\n this.nowOf = () => this.room.clock.elapsedTime;\n }\n\n /**\n * Build the input configuration + `InputAPI` (the body of\n * {@link Room.defineInput}). Returns the api; the Room hands it back to\n * userland and keeps the framework-owned reference.\n */\n define<\n C extends new () => any,\n O extends DefineInputOptions<InstanceType<C>> = DefineInputOptions<InstanceType<C>>,\n >(\n type: C,\n opts?: O,\n ): InputAPI<InstanceType<C>, IdleDeclared<O, InstanceType<C>>> {\n // Normalize the step declaration to the canonical wire form (Hz). stepSeconds\n // / stepMs are the unit-safe spellings; both sides derive dt = 1/tickRate, so\n // one declared number drives the server's physics step AND the client's.\n const tickRate =\n opts?.stepSeconds !== undefined ? 1 / opts.stepSeconds :\n opts?.stepMs !== undefined ? 1000 / opts.stepMs :\n opts?.tickRate;\n if (\n opts?.tickRate !== undefined && opts.stepMs === undefined && opts.stepSeconds === undefined &&\n (!Number.isInteger(opts.tickRate) || opts.tickRate > 240)\n ) {\n console.warn(\n `[defineInput] tickRate is a rate in Hz (got ${opts.tickRate}); a value ` +\n `like 1000/rate is a step interval in ms \u2014 pass { stepMs } instead.`,\n );\n }\n this.options = {\n ctor: type,\n // Opt-in: a default would silently drop frames for any app with a numeric `seq` field.\n seqField: opts?.seqField,\n bufferMaxSize: opts?.bufferMaxSize ?? 32,\n idle: opts?.idle,\n // Compiled once (map \u2192 dense min/max walk); applied per decoded frame.\n sanitize: opts?.sanitize !== undefined ? compileSanitizer(opts.sanitize) : undefined,\n tickRate,\n subSteps: validateSubSteps(opts?.subSteps, 'defineInput'),\n };\n if (!_inputReflectionCache.has(type)) {\n // SDK-deserializable ctor bytes (Reflection.decode rebuilds the ctor client-side).\n _inputReflectionCache.set(type, Reflection.encode(new Encoder(new type())));\n }\n const api = new InputAPIImpl(this) as InputAPI<InstanceType<C>, IdleDeclared<O, InstanceType<C>>>;\n this.api = api;\n return api;\n }\n\n // --- per-client lifecycle (called from Room._onJoin/_onLeave/etc.) ---\n\n /** Allocate the per-client input instance + decoder, the ring buffer (opt-in\n * via `bufferMaxSize > 0`, for rollback/lockstep), and the accessor. */\n allocate(client: Client & ClientPrivate): void {\n client._input = new this.options.ctor();\n // Wire-neutral zero values for fields with no construction default: a\n // client only transmits fields it assigns, and `undefined` here would\n // NaN-propagate into the sim (or get floor-clamped by a range sanitizer\n // into a PHANTOM held input).\n seedInputZeroValues(client._input, this.options.ctor);\n client._inputDecoder = new InputDecoder(client._input);\n client._reckonBaseline = 0; // mirrors the SDK's delta-coded stamp baseline (reset together on (re)connect)\n const maxSize = this.options.bufferMaxSize;\n if (maxSize > 0) {\n // ctor \u2192 idle synthesis; client ref \u2192 idle ctx; idle policy \u2192 total drain()/next().\n client._inputBuffer = new InputBufferImpl(maxSize, this.options.seqField, this.options.ctor, client, this.options.idle);\n }\n client._inputAccessor = new InputAccessorImpl(client, this.nowOf);\n }\n\n /** @internal Raw reckon stamp for {@link Room.allowRewindState}'s\n * `bindReckonTime` \u2014 bypasses the accessor's resolved getter so the rewind\n * midpoint fallback still engages for unstamped clients. instanceof-narrowed:\n * the registry only ever holds `InputAccessorImpl` (see {@link allocate}),\n * so the 0 fallback covers only a missing session. */\n rawReckonTime(sessionId: string): number {\n const acc = this.accessors.get(sessionId);\n return acc instanceof InputAccessorImpl ? acc.rawReckonTime : 0;\n }\n\n /** Register a (re)joined client's accessor. On reconnect this overwrites the\n * dropped session's stale entry. Called after the client is in `this.clients`\n * (so a pre-push failure can't leak it) and before onJoin (so `get()` resolves\n * there). */\n register(sessionId: string, client: ClientPrivate): void {\n this.accessors.set(sessionId, client._inputAccessor!);\n }\n\n /** Freeze a leaving seat: drop pending inputs so a held (reconnecting) session\n * idles from the first tick rather than replaying last-known moves. `_input`\n * (the idle ctx's `latest`) is left intact. */\n freeze(client: ClientPrivate): void {\n client._inputBuffer?.clear();\n }\n\n /** Drop a fully-gone session's accessor (delete of a missing key is a no-op). */\n release(sessionId: string): void {\n this.accessors.delete(sessionId);\n }\n\n /** Release every held accessor (room dispose). */\n dispose(): void {\n this.accessors.clear();\n }\n\n // --- encode / decode (called from Room._onMessage) ---\n\n /**\n * Sanitize the freshly-decoded `client._input`, then (when buffering is on)\n * push a clone into the per-client buffer. Honors the framework seq\n * (unreliable) / a user `inputOptions.seqField` (reliable) to dedupe the\n * redundancy ring.\n */\n capture(client: ClientPrivate, renderTime: number = 0, reckonTime: number = 0, seq?: number): void {\n // Sanitize before anything reads it (latest, the clone below, the idle ctx).\n this.options.sanitize?.(client._input);\n const buf = client._inputBuffer;\n if (!buf) { return; } // no consumer registered \u2014 skip the clone allocation\n const inst = client._input!;\n if (seq !== undefined) {\n // Unreliable: dedup the ring by the framework wire seq (user seqField is for .at() only).\n if (!buf.accept(seq)) { return; }\n } else {\n // Reliable: no framework seq (implicit count). Honor a user `seqField` if set.\n const seqField = this.options.seqField;\n if (seqField !== undefined) {\n const value = (inst as any)[seqField] as number;\n if (typeof value === 'number' && !buf.accept(value)) { return; }\n }\n }\n buf.push(inst.clone() as any, renderTime, reckonTime, seq);\n }\n\n /** Decode a `ROOM_INPUT_RELIABLE` frame: an optional TIMED stamp prefix (shape\n * set by this room's derived stamp mode) then the input body. `it` starts at\n * offset 1, so the body begins at `it.offset`. */\n decodeReliable(client: ClientPrivate, buffer: Buffer, it: Iterator, modifiers: number): void {\n if (!client._inputDecoder) { return; }\n // Optional stamp prefix (TIMED bit), DELTA-CODED, length set by this room's\n // stamp mode (the timeline value is reconstructed against the per-client\n // baseline; the wire carries only the signed change from the previous frame):\n // both: [varint \u0394reckon][uint16 renderDelta] \u2192 renderTime = reckon \u2212 renderDelta\n // reckon-only: [varint \u0394reckon]\n // render-only: [varint \u0394render]\n let renderTime = 0;\n let reckonTime = 0;\n if (modifiers & ProtocolModifier.TIMED) {\n this.#resolveWireModes();\n // Reconstruct the absolute timeline value: baseline += signed delta. Both\n // sides start at 0 (so the first delta is absolute) and re-zero together on\n // (re)connect; reliable+in-order keeps the mirror locked.\n const stamp = client._reckonBaseline! += decode.number(buffer, it);\n if (this.#stampReckon && this.#stampRender) {\n reckonTime = stamp;\n const renderDelta = decode.uint16(buffer, it);\n renderTime = stamp > renderDelta ? stamp - renderDelta : 0;\n } else if (this.#stampReckon) {\n reckonTime = stamp;\n } else {\n renderTime = stamp;\n }\n }\n try {\n client._inputDecoder.decode(buffer.subarray(it.offset, buffer.byteLength));\n } catch (e: any) {\n debugAndPrintError(e);\n return;\n }\n client._lastInputReceivedAt = performance.now();\n // Mirrors the SDK's sent count; echoed back as lastInputSeq for RTT.\n client._receivedInputCount = (client._receivedInputCount ?? 0) + 1;\n this.capture(client, renderTime, reckonTime);\n }\n\n /** Decode a `ROOM_INPUT_UNRELIABLE` redundancy ring \u2014 each slot carries its\n * framework seq (base seq + position) for ring dedupe, no user seqField. */\n decodeUnreliable(client: ClientPrivate, buffer: Buffer, _modifiers: number): void {\n if (!client._inputDecoder) { return; }\n try {\n client._inputDecoder.decodeAll(buffer.subarray(1), (_inst, seq) => this.capture(client, 0, 0, seq));\n } catch (e: any) {\n debugAndPrintError(e);\n return;\n }\n client._lastInputReceivedAt = performance.now();\n }\n\n // --- handshake + stamp mode ---\n\n /** Resolve the wire stamp mode from the rewind timeline (once, then frozen). */\n #resolveWireModes(): void {\n if (this.#stampResolved) return;\n this.#stampResolved = true;\n const tl = this.room._timelineMode();\n this.#stampRender = tl?.snapshot ?? false;\n this.#stampReckon = tl?.reckon ?? false;\n }\n\n /**\n * The join-handshake input sections: INPUT_REFLECTION (the SDK-deserializable\n * input ctor bytes) and, when any runtime config differs from defaults,\n * INPUT_OPTIONS (`[flags uint8][tickRate varint?][patchRate varint?][subSteps\n * varint?]`). The client mirrors RENDER_TIME/RECKON_TIME (auto-stamp timeline),\n * tickRate (predict at dt=1/tickRate), patchRate (reconcile cadence), and\n * subSteps (physics sub-steps per input, omitted when 1). Returns `undefined`\n * when there's nothing to add.\n */\n handshakeSections(): Array<{ tag: number; bytes: Uint8Array }> | undefined {\n let sections: Array<{ tag: number; bytes: Uint8Array }> | undefined;\n const inputBytes = _inputReflectionCache.get(this.options.ctor);\n if (inputBytes !== undefined) {\n sections = [{ tag: HandshakeSection.INPUT_REFLECTION, bytes: inputBytes }];\n }\n this.#resolveWireModes();\n const stampRender = this.#stampRender;\n const stampReckon = this.#stampReckon;\n const tickRate = this.options.tickRate;\n const patchRate = (typeof this.room.patchRate === \"number\" && this.room.patchRate > 0)\n ? Math.round(this.room.patchRate) : undefined;\n const subSteps = (this.options.subSteps !== undefined && this.options.subSteps > 1)\n ? this.options.subSteps : undefined;\n if (stampRender || stampReckon || tickRate || patchRate || subSteps) {\n let flags = 0;\n if (stampRender) { flags |= InputFlags.RENDER_TIME; }\n if (stampReckon) { flags |= InputFlags.RECKON_TIME; }\n if (tickRate) { flags |= InputFlags.FIXED_TIMESTEP; }\n if (patchRate) { flags |= InputFlags.PATCH_RATE; }\n if (subSteps) { flags |= InputFlags.SUB_STEPS; }\n // 24B: worst case = flags + float64 tickRate (9) + uint32 patchRate (5) + subSteps (2).\n const buf = new Uint8Array(24);\n const sit = { offset: 0 };\n buf[sit.offset++] = flags;\n if (tickRate) { encode.number(buf, tickRate, sit); }\n if (patchRate) { encode.number(buf, patchRate, sit); }\n if (subSteps) { encode.number(buf, subSteps, sit); }\n (sections ??= []).push({\n tag: HandshakeSection.INPUT_OPTIONS,\n bytes: buf.subarray(0, sit.offset),\n });\n }\n return sections;\n }\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAAmE;AACnE,mBAA6B;AAC7B,0BAA+D;AAE/D,yBAGO;AAOP,mBAAmC;AAOnC,IAAM,wBAAwB,oBAAI,QAA8B;AAahE,IAAM,eAAN,MAAmB;AAAA,EAEjB,YAAY,OAAkB;AAAE,SAAK,QAAQ;AAAA,EAAO;AAAA;AAAA;AAAA,EAGpD,IAAI,WAAuC;AACzC,WAAO,KAAK,MAAM,UAAU,IAAI,SAAS,KAAK;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA,EAIA,IAAI,WAA+B;AAAE,WAAO,KAAK,MAAM,QAAQ;AAAA,EAAU;AAAA,EACzE,IAAI,cAAkC;AAAE,UAAM,KAAK,KAAK,MAAM,QAAQ;AAAU,WAAO,KAAK,IAAI,KAAK;AAAA,EAAW;AAAA,EAChH,IAAI,SAA6B;AAAE,UAAM,KAAK,KAAK,MAAM,QAAQ;AAAU,WAAO,KAAK,MAAO,KAAK;AAAA,EAAW;AAAA;AAAA,EAE9G,IAAI,WAAmB;AAAE,WAAO,KAAK,MAAM,QAAQ,YAAY;AAAA,EAAG;AAAA,EAClE,IAAI,iBAAqC;AAAE,UAAM,KAAK,KAAK,MAAM,QAAQ;AAAU,WAAO,KAAM,IAAI,MAAO,KAAK,MAAM,QAAQ,YAAY,KAAK;AAAA,EAAW;AAAA,EAC1J,IAAI,YAAgC;AAAE,UAAM,KAAK,KAAK,MAAM,QAAQ;AAAU,WAAO,KAAM,MAAO,MAAO,KAAK,MAAM,QAAQ,YAAY,KAAK;AAAA,EAAW;AAC1J;AAaO,IAAM,YAAN,MAAgB;AAAA,EA8BrB,YAAY,MAAiB;AAZ7B;AAAA;AAAA;AAAA;AAAA;AAAA,SAAS,YAA6C,oBAAI,IAAI;AAK9D;AAAA;AAAA;AAAA,wBAAe;AACf,wBAAe;AACf,0BAAiB;AAMf,SAAK,OAAO;AACZ,SAAK,QAAQ,MAAM,KAAK,KAAK,MAAM;AAAA,EACrC;AAAA,EAVA;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,OAIE,MACA,MAC6D;AAI7D,UAAM,WACJ,MAAM,gBAAgB,SAAY,IAAI,KAAK,cAC3C,MAAM,WAAW,SAAY,MAAO,KAAK,SACzC,MAAM;AACR,QACE,MAAM,aAAa,UAAa,KAAK,WAAW,UAAa,KAAK,gBAAgB,WACjF,CAAC,OAAO,UAAU,KAAK,QAAQ,KAAK,KAAK,WAAW,MACrD;AACA,cAAQ;AAAA,QACN,+CAA+C,KAAK,QAAQ;AAAA,MAE9D;AAAA,IACF;AACA,SAAK,UAAU;AAAA,MACb,MAAM;AAAA;AAAA,MAEN,UAAU,MAAM;AAAA,MAChB,eAAe,MAAM,iBAAiB;AAAA,MACtC,MAAM,MAAM;AAAA;AAAA,MAEZ,UAAU,MAAM,aAAa,aAAY,qCAAiB,KAAK,QAAQ,IAAI;AAAA,MAC3E;AAAA,MACA,cAAU,qCAAiB,MAAM,UAAU,aAAa;AAAA,IAC1D;AACA,QAAI,CAAC,sBAAsB,IAAI,IAAI,GAAG;AAEpC,4BAAsB,IAAI,MAAM,yBAAW,OAAO,IAAI,sBAAQ,IAAI,KAAK,CAAC,CAAC,CAAC;AAAA,IAC5E;AACA,UAAM,MAAM,IAAI,aAAa,IAAI;AACjC,SAAK,MAAM;AACX,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAMA,SAAS,QAAsC;AAC7C,WAAO,SAAS,IAAI,KAAK,QAAQ,KAAK;AAKtC,gDAAoB,OAAO,QAAQ,KAAK,QAAQ,IAAI;AACpD,WAAO,gBAAgB,IAAI,0BAAa,OAAO,MAAM;AACrD,WAAO,kBAAkB;AACzB,UAAM,UAAU,KAAK,QAAQ;AAC7B,QAAI,UAAU,GAAG;AAEf,aAAO,eAAe,IAAI,mCAAgB,SAAS,KAAK,QAAQ,UAAU,KAAK,QAAQ,MAAM,QAAQ,KAAK,QAAQ,IAAI;AAAA,IACxH;AACA,WAAO,iBAAiB,IAAI,qCAAkB,QAAQ,KAAK,KAAK;AAAA,EAClE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,cAAc,WAA2B;AACvC,UAAM,MAAM,KAAK,UAAU,IAAI,SAAS;AACxC,WAAO,eAAe,uCAAoB,IAAI,gBAAgB;AAAA,EAChE;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,SAAS,WAAmB,QAA6B;AACvD,SAAK,UAAU,IAAI,WAAW,OAAO,cAAe;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,QAA6B;AAClC,WAAO,cAAc,MAAM;AAAA,EAC7B;AAAA;AAAA,EAGA,QAAQ,WAAyB;AAC/B,SAAK,UAAU,OAAO,SAAS;AAAA,EACjC;AAAA;AAAA,EAGA,UAAgB;AACd,SAAK,UAAU,MAAM;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,QAAQ,QAAuB,aAAqB,GAAG,aAAqB,GAAG,KAAoB;AAEjG,SAAK,QAAQ,WAAW,OAAO,MAAM;AACrC,UAAM,MAAM,OAAO;AACnB,QAAI,CAAC,KAAK;AAAE;AAAA,IAAQ;AACpB,UAAM,OAAO,OAAO;AACpB,QAAI,QAAQ,QAAW;AAErB,UAAI,CAAC,IAAI,OAAO,GAAG,GAAG;AAAE;AAAA,MAAQ;AAAA,IAClC,OAAO;AAEL,YAAM,WAAW,KAAK,QAAQ;AAC9B,UAAI,aAAa,QAAW;AAC1B,cAAM,QAAS,KAAa,QAAQ;AACpC,YAAI,OAAO,UAAU,YAAY,CAAC,IAAI,OAAO,KAAK,GAAG;AAAE;AAAA,QAAQ;AAAA,MACjE;AAAA,IACF;AACA,QAAI,KAAK,KAAK,MAAM,GAAU,YAAY,YAAY,GAAG;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA,EAKA,eAAe,QAAuB,QAAgB,IAAc,WAAyB;AAC3F,QAAI,CAAC,OAAO,eAAe;AAAE;AAAA,IAAQ;AAOrC,QAAI,aAAa;AACjB,QAAI,aAAa;AACjB,QAAI,YAAY,qCAAiB,OAAO;AACtC,WAAK,kBAAkB;AAIvB,YAAM,QAAQ,OAAO,mBAAoB,qBAAO,OAAO,QAAQ,EAAE;AACjE,UAAI,KAAK,gBAAgB,KAAK,cAAc;AAC1C,qBAAa;AACb,cAAM,cAAc,qBAAO,OAAO,QAAQ,EAAE;AAC5C,qBAAa,QAAQ,cAAc,QAAQ,cAAc;AAAA,MAC3D,WAAW,KAAK,cAAc;AAC5B,qBAAa;AAAA,MACf,OAAO;AACL,qBAAa;AAAA,MACf;AAAA,IACF;AACA,QAAI;AACF,aAAO,cAAc,OAAO,OAAO,SAAS,GAAG,QAAQ,OAAO,UAAU,CAAC;AAAA,IAC3E,SAAS,GAAQ;AACf,2CAAmB,CAAC;AACpB;AAAA,IACF;AACA,WAAO,uBAAuB,YAAY,IAAI;AAE9C,WAAO,uBAAuB,OAAO,uBAAuB,KAAK;AACjE,SAAK,QAAQ,QAAQ,YAAY,UAAU;AAAA,EAC7C;AAAA;AAAA;AAAA,EAIA,iBAAiB,QAAuB,QAAgB,YAA0B;AAChF,QAAI,CAAC,OAAO,eAAe;AAAE;AAAA,IAAQ;AACrC,QAAI;AACF,aAAO,cAAc,UAAU,OAAO,SAAS,CAAC,GAAG,CAAC,OAAO,QAAQ,KAAK,QAAQ,QAAQ,GAAG,GAAG,GAAG,CAAC;AAAA,IACpG,SAAS,GAAQ;AACf,2CAAmB,CAAC;AACpB;AAAA,IACF;AACA,WAAO,uBAAuB,YAAY,IAAI;AAAA,EAChD;AAAA;AAAA;AAAA,EAKA,oBAA0B;AACxB,QAAI,KAAK,eAAgB;AACzB,SAAK,iBAAiB;AACtB,UAAM,KAAK,KAAK,KAAK,cAAc;AACnC,SAAK,eAAe,IAAI,YAAY;AACpC,SAAK,eAAe,IAAI,UAAU;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,oBAA2E;AACzE,QAAI;AACJ,UAAM,aAAa,sBAAsB,IAAI,KAAK,QAAQ,IAAI;AAC9D,QAAI,eAAe,QAAW;AAC5B,iBAAW,CAAC,EAAE,KAAK,qCAAiB,kBAAkB,OAAO,WAAW,CAAC;AAAA,IAC3E;AACA,SAAK,kBAAkB;AACvB,UAAM,cAAc,KAAK;AACzB,UAAM,cAAc,KAAK;AACzB,UAAM,WAAW,KAAK,QAAQ;AAC9B,UAAM,YAAa,OAAO,KAAK,KAAK,cAAc,YAAY,KAAK,KAAK,YAAY,IAChF,KAAK,MAAM,KAAK,KAAK,SAAS,IAAI;AACtC,UAAM,WAAY,KAAK,QAAQ,aAAa,UAAa,KAAK,QAAQ,WAAW,IAC7E,KAAK,QAAQ,WAAW;AAC5B,QAAI,eAAe,eAAe,YAAY,aAAa,UAAU;AACnE,UAAI,QAAQ;AACZ,UAAI,aAAa;AAAE,iBAAS,+BAAW;AAAA,MAAa;AACpD,UAAI,aAAa;AAAE,iBAAS,+BAAW;AAAA,MAAa;AACpD,UAAI,UAAU;AAAE,iBAAS,+BAAW;AAAA,MAAgB;AACpD,UAAI,WAAW;AAAE,iBAAS,+BAAW;AAAA,MAAY;AACjD,UAAI,UAAU;AAAE,iBAAS,+BAAW;AAAA,MAAW;AAE/C,YAAM,MAAM,IAAI,WAAW,EAAE;AAC7B,YAAM,MAAM,EAAE,QAAQ,EAAE;AACxB,UAAI,IAAI,QAAQ,IAAI;AACpB,UAAI,UAAU;AAAE,6BAAO,OAAO,KAAK,UAAU,GAAG;AAAA,MAAG;AACnD,UAAI,WAAW;AAAE,6BAAO,OAAO,KAAK,WAAW,GAAG;AAAA,MAAG;AACrD,UAAI,UAAU;AAAE,6BAAO,OAAO,KAAK,UAAU,GAAG;AAAA,MAAG;AACnD,OAAC,aAAa,CAAC,GAAG,KAAK;AAAA,QACrB,KAAK,qCAAiB;AAAA,QACtB,OAAO,IAAI,SAAS,GAAG,IAAI,MAAM;AAAA,MACnC,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT;AACF;",
6
6
  "names": []
7
7
  }
@@ -7,6 +7,7 @@ import {
7
7
  InputBufferImpl,
8
8
  NO_OP_INPUT_ACCESSOR,
9
9
  compileSanitizer,
10
+ seedInputZeroValues,
10
11
  validateSubSteps
11
12
  } from "./InputBuffer.mjs";
12
13
  import { debugAndPrintError } from "../Debug.mjs";
@@ -102,6 +103,7 @@ var RoomInput = class {
102
103
  * via `bufferMaxSize > 0`, for rollback/lockstep), and the accessor. */
103
104
  allocate(client) {
104
105
  client._input = new this.options.ctor();
106
+ seedInputZeroValues(client._input, this.options.ctor);
105
107
  client._inputDecoder = new InputDecoder(client._input);
106
108
  client._reckonBaseline = 0;
107
109
  const maxSize = this.options.bufferMaxSize;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/input/RoomInput.ts"],
4
- "sourcesContent": ["import { decode, encode, Encoder, Reflection, type Iterator } from '@colyseus/schema';\nimport { InputDecoder } from '@colyseus/schema/input';\nimport { HandshakeSection, InputFlags, ProtocolModifier } from '@colyseus/shared-types';\n\nimport {\n InputAccessorImpl, InputBufferImpl, NO_OP_INPUT_ACCESSOR,\n compileSanitizer, validateSubSteps,\n} from './InputBuffer.ts';\nimport type {\n InputAccessor, InputAPI, NormalizedInputOptions,\n DefineInputOptions, IdleDeclared,\n} from './types.ts';\nimport type { Client, ClientPrivate } from '../Transport.ts';\nimport type { Room } from '../Room.ts';\nimport { debugAndPrintError } from '../Debug.ts';\n\n/**\n * Module-level cache of `Reflection.encode` output keyed by input\n * constructor \u2014 pays the encoding cost once per Room class regardless of\n * room instance count. WeakMap so unused classes can be GC'd.\n */\nconst _inputReflectionCache = new WeakMap<Function, Uint8Array>();\n\n/**\n * Runtime behind {@link InputAPI}. A class, not a per-`define()` object literal:\n * literal (and `defineProperty`) accessors carry their closure identity in the\n * hidden class, so every room instance would get a UNIQUE map \u2014 sending shared\n * `this.inputs.*` call sites megamorphic. Prototype getters are created once,\n * every instance shares one hidden class (reads stay monomorphic), and\n * `define()` allocates a single 1-field object instead of 7 closures + a map\n * lineage per room.\n *\n * @internal\n */\nclass InputAPIImpl {\n private input: RoomInput;\n constructor(input: RoomInput) { this.input = input; }\n /** Registry, not room.clients: an accessor outlives its client through the\n * reconnection window, so a dropped seat still synthesizes the idle policy. */\n get(sessionId: string): InputAccessor<any> {\n return this.input.accessors.get(sessionId) ?? NO_OP_INPUT_ACCESSOR;\n }\n // Live reads off `options` (mutated in place by a later setTimestep/\n // setFixedTimestep back-fill, so a derived tickRate is reflected); 1/hz is\n // correctly-rounded IEEE-754 \u2192 bit-identical to the client's stepSeconds.\n get tickRate(): number | undefined { return this.input.options.tickRate; }\n get stepSeconds(): number | undefined { const hz = this.input.options.tickRate; return hz ? 1 / hz : undefined; }\n get stepMs(): number | undefined { const hz = this.input.options.tickRate; return hz ? 1000 / hz : undefined; }\n // Sub-step trio: same derivation as the client handle ((1/hz)/n) \u2014 bit-identical dt.\n get subSteps(): number { return this.input.options.subSteps ?? 1; }\n get subStepSeconds(): number | undefined { const hz = this.input.options.tickRate; return hz ? (1 / hz) / (this.input.options.subSteps ?? 1) : undefined; }\n get subStepMs(): number | undefined { const hz = this.input.options.tickRate; return hz ? (1000 / hz) / (this.input.options.subSteps ?? 1) : undefined; }\n}\n\n/**\n * Per-room input subsystem, owned by {@link Room} and created lazily on the\n * first {@link Room.defineInput} call \u2014 rooms without inputs allocate none of\n * it. Owns the input options, the per-session accessor registry (decoupled\n * from `this.clients` so an entry outlives a dropped client across its\n * reconnection window), the wire stamp mode, and the encode/decode/handshake\n * machinery. Reads back into its Room for context it doesn't own (the patch\n * rate and the rewind timeline mode).\n *\n * @internal\n */\nexport class RoomInput {\n /** Owning room \u2014 for `patchRate` and the rewind timeline mode. */\n private room: Room<any>;\n\n /** Input configuration (ctor, seqField, bufferMaxSize, idle, sanitize,\n * tickRate, subSteps). `tickRate`/`subSteps` may be back-filled by\n * `setTimestep`/`setFixedTimestep`. Set in {@link define}. */\n options!: NormalizedInputOptions;\n\n /** The `InputAPI` returned to userland from `defineInput`, also the\n * framework-owned handle the rewind binding resolves accessors through. */\n api!: InputAPI<any>;\n\n /** sessionId \u2192 accessor. Decoupled from `this.clients`: an entry outlives its\n * client across the reconnection window, so {@link InputAPI.get} keeps\n * synthesizing the idle policy for an absent seat (its buffer was cleared on\n * leave \u2192 idle). Set on join, deleted on full leave. Read by\n * {@link InputAPIImpl.get} (hence not `private`). */\n readonly accessors: Map<string, InputAccessor<any>> = new Map();\n\n // Wire stamp mode derived from the rewind timeline, resolved once on first\n // handshake/decode then frozen so advertise + decode never disagree mid-session:\n // the renderTime/reckonTime prefix per input.\n #stampRender = false;\n #stampReckon = false;\n #stampResolved = false;\n\n /** One shared now-resolver handed to every accessor (see {@link InputAccessorImpl}). */\n private nowOf: () => number;\n\n constructor(room: Room<any>) {\n this.room = room;\n this.nowOf = () => this.room.clock.elapsedTime;\n }\n\n /**\n * Build the input configuration + `InputAPI` (the body of\n * {@link Room.defineInput}). Returns the api; the Room hands it back to\n * userland and keeps the framework-owned reference.\n */\n define<\n C extends new () => any,\n O extends DefineInputOptions<InstanceType<C>> = DefineInputOptions<InstanceType<C>>,\n >(\n type: C,\n opts?: O,\n ): InputAPI<InstanceType<C>, IdleDeclared<O, InstanceType<C>>> {\n // Normalize the step declaration to the canonical wire form (Hz). stepSeconds\n // / stepMs are the unit-safe spellings; both sides derive dt = 1/tickRate, so\n // one declared number drives the server's physics step AND the client's.\n const tickRate =\n opts?.stepSeconds !== undefined ? 1 / opts.stepSeconds :\n opts?.stepMs !== undefined ? 1000 / opts.stepMs :\n opts?.tickRate;\n if (\n opts?.tickRate !== undefined && opts.stepMs === undefined && opts.stepSeconds === undefined &&\n (!Number.isInteger(opts.tickRate) || opts.tickRate > 240)\n ) {\n console.warn(\n `[defineInput] tickRate is a rate in Hz (got ${opts.tickRate}); a value ` +\n `like 1000/rate is a step interval in ms \u2014 pass { stepMs } instead.`,\n );\n }\n this.options = {\n ctor: type,\n // Opt-in: a default would silently drop frames for any app with a numeric `seq` field.\n seqField: opts?.seqField,\n bufferMaxSize: opts?.bufferMaxSize ?? 32,\n idle: opts?.idle,\n // Compiled once (map \u2192 dense min/max walk); applied per decoded frame.\n sanitize: opts?.sanitize !== undefined ? compileSanitizer(opts.sanitize) : undefined,\n tickRate,\n subSteps: validateSubSteps(opts?.subSteps, 'defineInput'),\n };\n if (!_inputReflectionCache.has(type)) {\n // SDK-deserializable ctor bytes (Reflection.decode rebuilds the ctor client-side).\n _inputReflectionCache.set(type, Reflection.encode(new Encoder(new type())));\n }\n const api = new InputAPIImpl(this) as InputAPI<InstanceType<C>, IdleDeclared<O, InstanceType<C>>>;\n this.api = api;\n return api;\n }\n\n // --- per-client lifecycle (called from Room._onJoin/_onLeave/etc.) ---\n\n /** Allocate the per-client input instance + decoder, the ring buffer (opt-in\n * via `bufferMaxSize > 0`, for rollback/lockstep), and the accessor. */\n allocate(client: Client & ClientPrivate): void {\n client._input = new this.options.ctor();\n client._inputDecoder = new InputDecoder(client._input);\n client._reckonBaseline = 0; // mirrors the SDK's delta-coded stamp baseline (reset together on (re)connect)\n const maxSize = this.options.bufferMaxSize;\n if (maxSize > 0) {\n // ctor \u2192 idle synthesis; client ref \u2192 idle ctx; idle policy \u2192 total drain()/next().\n client._inputBuffer = new InputBufferImpl(maxSize, this.options.seqField, this.options.ctor, client, this.options.idle);\n }\n client._inputAccessor = new InputAccessorImpl(client, this.nowOf);\n }\n\n /** @internal Raw reckon stamp for {@link Room.allowRewindState}'s\n * `bindReckonTime` \u2014 bypasses the accessor's resolved getter so the rewind\n * midpoint fallback still engages for unstamped clients. instanceof-narrowed:\n * the registry only ever holds `InputAccessorImpl` (see {@link allocate}),\n * so the 0 fallback covers only a missing session. */\n rawReckonTime(sessionId: string): number {\n const acc = this.accessors.get(sessionId);\n return acc instanceof InputAccessorImpl ? acc.rawReckonTime : 0;\n }\n\n /** Register a (re)joined client's accessor. On reconnect this overwrites the\n * dropped session's stale entry. Called after the client is in `this.clients`\n * (so a pre-push failure can't leak it) and before onJoin (so `get()` resolves\n * there). */\n register(sessionId: string, client: ClientPrivate): void {\n this.accessors.set(sessionId, client._inputAccessor!);\n }\n\n /** Freeze a leaving seat: drop pending inputs so a held (reconnecting) session\n * idles from the first tick rather than replaying last-known moves. `_input`\n * (the idle ctx's `latest`) is left intact. */\n freeze(client: ClientPrivate): void {\n client._inputBuffer?.clear();\n }\n\n /** Drop a fully-gone session's accessor (delete of a missing key is a no-op). */\n release(sessionId: string): void {\n this.accessors.delete(sessionId);\n }\n\n /** Release every held accessor (room dispose). */\n dispose(): void {\n this.accessors.clear();\n }\n\n // --- encode / decode (called from Room._onMessage) ---\n\n /**\n * Sanitize the freshly-decoded `client._input`, then (when buffering is on)\n * push a clone into the per-client buffer. Honors the framework seq\n * (unreliable) / a user `inputOptions.seqField` (reliable) to dedupe the\n * redundancy ring.\n */\n capture(client: ClientPrivate, renderTime: number = 0, reckonTime: number = 0, seq?: number): void {\n // Sanitize before anything reads it (latest, the clone below, the idle ctx).\n this.options.sanitize?.(client._input);\n const buf = client._inputBuffer;\n if (!buf) { return; } // no consumer registered \u2014 skip the clone allocation\n const inst = client._input!;\n if (seq !== undefined) {\n // Unreliable: dedup the ring by the framework wire seq (user seqField is for .at() only).\n if (!buf.accept(seq)) { return; }\n } else {\n // Reliable: no framework seq (implicit count). Honor a user `seqField` if set.\n const seqField = this.options.seqField;\n if (seqField !== undefined) {\n const value = (inst as any)[seqField] as number;\n if (typeof value === 'number' && !buf.accept(value)) { return; }\n }\n }\n buf.push(inst.clone() as any, renderTime, reckonTime, seq);\n }\n\n /** Decode a `ROOM_INPUT_RELIABLE` frame: an optional TIMED stamp prefix (shape\n * set by this room's derived stamp mode) then the input body. `it` starts at\n * offset 1, so the body begins at `it.offset`. */\n decodeReliable(client: ClientPrivate, buffer: Buffer, it: Iterator, modifiers: number): void {\n if (!client._inputDecoder) { return; }\n // Optional stamp prefix (TIMED bit), DELTA-CODED, length set by this room's\n // stamp mode (the timeline value is reconstructed against the per-client\n // baseline; the wire carries only the signed change from the previous frame):\n // both: [varint \u0394reckon][uint16 renderDelta] \u2192 renderTime = reckon \u2212 renderDelta\n // reckon-only: [varint \u0394reckon]\n // render-only: [varint \u0394render]\n let renderTime = 0;\n let reckonTime = 0;\n if (modifiers & ProtocolModifier.TIMED) {\n this.#resolveWireModes();\n // Reconstruct the absolute timeline value: baseline += signed delta. Both\n // sides start at 0 (so the first delta is absolute) and re-zero together on\n // (re)connect; reliable+in-order keeps the mirror locked.\n const stamp = client._reckonBaseline! += decode.number(buffer, it);\n if (this.#stampReckon && this.#stampRender) {\n reckonTime = stamp;\n const renderDelta = decode.uint16(buffer, it);\n renderTime = stamp > renderDelta ? stamp - renderDelta : 0;\n } else if (this.#stampReckon) {\n reckonTime = stamp;\n } else {\n renderTime = stamp;\n }\n }\n try {\n client._inputDecoder.decode(buffer.subarray(it.offset, buffer.byteLength));\n } catch (e: any) {\n debugAndPrintError(e);\n return;\n }\n client._lastInputReceivedAt = performance.now();\n // Mirrors the SDK's sent count; echoed back as lastInputSeq for RTT.\n client._receivedInputCount = (client._receivedInputCount ?? 0) + 1;\n this.capture(client, renderTime, reckonTime);\n }\n\n /** Decode a `ROOM_INPUT_UNRELIABLE` redundancy ring \u2014 each slot carries its\n * framework seq (base seq + position) for ring dedupe, no user seqField. */\n decodeUnreliable(client: ClientPrivate, buffer: Buffer, _modifiers: number): void {\n if (!client._inputDecoder) { return; }\n try {\n client._inputDecoder.decodeAll(buffer.subarray(1), (_inst, seq) => this.capture(client, 0, 0, seq));\n } catch (e: any) {\n debugAndPrintError(e);\n return;\n }\n client._lastInputReceivedAt = performance.now();\n }\n\n // --- handshake + stamp mode ---\n\n /** Resolve the wire stamp mode from the rewind timeline (once, then frozen). */\n #resolveWireModes(): void {\n if (this.#stampResolved) return;\n this.#stampResolved = true;\n const tl = this.room._timelineMode();\n this.#stampRender = tl?.snapshot ?? false;\n this.#stampReckon = tl?.reckon ?? false;\n }\n\n /**\n * The join-handshake input sections: INPUT_REFLECTION (the SDK-deserializable\n * input ctor bytes) and, when any runtime config differs from defaults,\n * INPUT_OPTIONS (`[flags uint8][tickRate varint?][patchRate varint?][subSteps\n * varint?]`). The client mirrors RENDER_TIME/RECKON_TIME (auto-stamp timeline),\n * tickRate (predict at dt=1/tickRate), patchRate (reconcile cadence), and\n * subSteps (physics sub-steps per input, omitted when 1). Returns `undefined`\n * when there's nothing to add.\n */\n handshakeSections(): Array<{ tag: number; bytes: Uint8Array }> | undefined {\n let sections: Array<{ tag: number; bytes: Uint8Array }> | undefined;\n const inputBytes = _inputReflectionCache.get(this.options.ctor);\n if (inputBytes !== undefined) {\n sections = [{ tag: HandshakeSection.INPUT_REFLECTION, bytes: inputBytes }];\n }\n this.#resolveWireModes();\n const stampRender = this.#stampRender;\n const stampReckon = this.#stampReckon;\n const tickRate = this.options.tickRate;\n const patchRate = (typeof this.room.patchRate === \"number\" && this.room.patchRate > 0)\n ? Math.round(this.room.patchRate) : undefined;\n const subSteps = (this.options.subSteps !== undefined && this.options.subSteps > 1)\n ? this.options.subSteps : undefined;\n if (stampRender || stampReckon || tickRate || patchRate || subSteps) {\n let flags = 0;\n if (stampRender) { flags |= InputFlags.RENDER_TIME; }\n if (stampReckon) { flags |= InputFlags.RECKON_TIME; }\n if (tickRate) { flags |= InputFlags.FIXED_TIMESTEP; }\n if (patchRate) { flags |= InputFlags.PATCH_RATE; }\n if (subSteps) { flags |= InputFlags.SUB_STEPS; }\n // 24B: worst case = flags + float64 tickRate (9) + uint32 patchRate (5) + subSteps (2).\n const buf = new Uint8Array(24);\n const sit = { offset: 0 };\n buf[sit.offset++] = flags;\n if (tickRate) { encode.number(buf, tickRate, sit); }\n if (patchRate) { encode.number(buf, patchRate, sit); }\n if (subSteps) { encode.number(buf, subSteps, sit); }\n (sections ??= []).push({\n tag: HandshakeSection.INPUT_OPTIONS,\n bytes: buf.subarray(0, sit.offset),\n });\n }\n return sections;\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { decode, encode, Encoder, Reflection, type Iterator } from '@colyseus/schema';\nimport { InputDecoder } from '@colyseus/schema/input';\nimport { HandshakeSection, InputFlags, ProtocolModifier } from '@colyseus/shared-types';\n\nimport {\n InputAccessorImpl, InputBufferImpl, NO_OP_INPUT_ACCESSOR,\n compileSanitizer, seedInputZeroValues, validateSubSteps,\n} from './InputBuffer.ts';\nimport type {\n InputAccessor, InputAPI, NormalizedInputOptions,\n DefineInputOptions, IdleDeclared,\n} from './types.ts';\nimport type { Client, ClientPrivate } from '../Transport.ts';\nimport type { Room } from '../Room.ts';\nimport { debugAndPrintError } from '../Debug.ts';\n\n/**\n * Module-level cache of `Reflection.encode` output keyed by input\n * constructor \u2014 pays the encoding cost once per Room class regardless of\n * room instance count. WeakMap so unused classes can be GC'd.\n */\nconst _inputReflectionCache = new WeakMap<Function, Uint8Array>();\n\n/**\n * Runtime behind {@link InputAPI}. A class, not a per-`define()` object literal:\n * literal (and `defineProperty`) accessors carry their closure identity in the\n * hidden class, so every room instance would get a UNIQUE map \u2014 sending shared\n * `this.inputs.*` call sites megamorphic. Prototype getters are created once,\n * every instance shares one hidden class (reads stay monomorphic), and\n * `define()` allocates a single 1-field object instead of 7 closures + a map\n * lineage per room.\n *\n * @internal\n */\nclass InputAPIImpl {\n private input: RoomInput;\n constructor(input: RoomInput) { this.input = input; }\n /** Registry, not room.clients: an accessor outlives its client through the\n * reconnection window, so a dropped seat still synthesizes the idle policy. */\n get(sessionId: string): InputAccessor<any> {\n return this.input.accessors.get(sessionId) ?? NO_OP_INPUT_ACCESSOR;\n }\n // Live reads off `options` (mutated in place by a later setTimestep/\n // setFixedTimestep back-fill, so a derived tickRate is reflected); 1/hz is\n // correctly-rounded IEEE-754 \u2192 bit-identical to the client's stepSeconds.\n get tickRate(): number | undefined { return this.input.options.tickRate; }\n get stepSeconds(): number | undefined { const hz = this.input.options.tickRate; return hz ? 1 / hz : undefined; }\n get stepMs(): number | undefined { const hz = this.input.options.tickRate; return hz ? 1000 / hz : undefined; }\n // Sub-step trio: same derivation as the client handle ((1/hz)/n) \u2014 bit-identical dt.\n get subSteps(): number { return this.input.options.subSteps ?? 1; }\n get subStepSeconds(): number | undefined { const hz = this.input.options.tickRate; return hz ? (1 / hz) / (this.input.options.subSteps ?? 1) : undefined; }\n get subStepMs(): number | undefined { const hz = this.input.options.tickRate; return hz ? (1000 / hz) / (this.input.options.subSteps ?? 1) : undefined; }\n}\n\n/**\n * Per-room input subsystem, owned by {@link Room} and created lazily on the\n * first {@link Room.defineInput} call \u2014 rooms without inputs allocate none of\n * it. Owns the input options, the per-session accessor registry (decoupled\n * from `this.clients` so an entry outlives a dropped client across its\n * reconnection window), the wire stamp mode, and the encode/decode/handshake\n * machinery. Reads back into its Room for context it doesn't own (the patch\n * rate and the rewind timeline mode).\n *\n * @internal\n */\nexport class RoomInput {\n /** Owning room \u2014 for `patchRate` and the rewind timeline mode. */\n private room: Room<any>;\n\n /** Input configuration (ctor, seqField, bufferMaxSize, idle, sanitize,\n * tickRate, subSteps). `tickRate`/`subSteps` may be back-filled by\n * `setTimestep`/`setFixedTimestep`. Set in {@link define}. */\n options!: NormalizedInputOptions;\n\n /** The `InputAPI` returned to userland from `defineInput`, also the\n * framework-owned handle the rewind binding resolves accessors through. */\n api!: InputAPI<any>;\n\n /** sessionId \u2192 accessor. Decoupled from `this.clients`: an entry outlives its\n * client across the reconnection window, so {@link InputAPI.get} keeps\n * synthesizing the idle policy for an absent seat (its buffer was cleared on\n * leave \u2192 idle). Set on join, deleted on full leave. Read by\n * {@link InputAPIImpl.get} (hence not `private`). */\n readonly accessors: Map<string, InputAccessor<any>> = new Map();\n\n // Wire stamp mode derived from the rewind timeline, resolved once on first\n // handshake/decode then frozen so advertise + decode never disagree mid-session:\n // the renderTime/reckonTime prefix per input.\n #stampRender = false;\n #stampReckon = false;\n #stampResolved = false;\n\n /** One shared now-resolver handed to every accessor (see {@link InputAccessorImpl}). */\n private nowOf: () => number;\n\n constructor(room: Room<any>) {\n this.room = room;\n this.nowOf = () => this.room.clock.elapsedTime;\n }\n\n /**\n * Build the input configuration + `InputAPI` (the body of\n * {@link Room.defineInput}). Returns the api; the Room hands it back to\n * userland and keeps the framework-owned reference.\n */\n define<\n C extends new () => any,\n O extends DefineInputOptions<InstanceType<C>> = DefineInputOptions<InstanceType<C>>,\n >(\n type: C,\n opts?: O,\n ): InputAPI<InstanceType<C>, IdleDeclared<O, InstanceType<C>>> {\n // Normalize the step declaration to the canonical wire form (Hz). stepSeconds\n // / stepMs are the unit-safe spellings; both sides derive dt = 1/tickRate, so\n // one declared number drives the server's physics step AND the client's.\n const tickRate =\n opts?.stepSeconds !== undefined ? 1 / opts.stepSeconds :\n opts?.stepMs !== undefined ? 1000 / opts.stepMs :\n opts?.tickRate;\n if (\n opts?.tickRate !== undefined && opts.stepMs === undefined && opts.stepSeconds === undefined &&\n (!Number.isInteger(opts.tickRate) || opts.tickRate > 240)\n ) {\n console.warn(\n `[defineInput] tickRate is a rate in Hz (got ${opts.tickRate}); a value ` +\n `like 1000/rate is a step interval in ms \u2014 pass { stepMs } instead.`,\n );\n }\n this.options = {\n ctor: type,\n // Opt-in: a default would silently drop frames for any app with a numeric `seq` field.\n seqField: opts?.seqField,\n bufferMaxSize: opts?.bufferMaxSize ?? 32,\n idle: opts?.idle,\n // Compiled once (map \u2192 dense min/max walk); applied per decoded frame.\n sanitize: opts?.sanitize !== undefined ? compileSanitizer(opts.sanitize) : undefined,\n tickRate,\n subSteps: validateSubSteps(opts?.subSteps, 'defineInput'),\n };\n if (!_inputReflectionCache.has(type)) {\n // SDK-deserializable ctor bytes (Reflection.decode rebuilds the ctor client-side).\n _inputReflectionCache.set(type, Reflection.encode(new Encoder(new type())));\n }\n const api = new InputAPIImpl(this) as InputAPI<InstanceType<C>, IdleDeclared<O, InstanceType<C>>>;\n this.api = api;\n return api;\n }\n\n // --- per-client lifecycle (called from Room._onJoin/_onLeave/etc.) ---\n\n /** Allocate the per-client input instance + decoder, the ring buffer (opt-in\n * via `bufferMaxSize > 0`, for rollback/lockstep), and the accessor. */\n allocate(client: Client & ClientPrivate): void {\n client._input = new this.options.ctor();\n // Wire-neutral zero values for fields with no construction default: a\n // client only transmits fields it assigns, and `undefined` here would\n // NaN-propagate into the sim (or get floor-clamped by a range sanitizer\n // into a PHANTOM held input).\n seedInputZeroValues(client._input, this.options.ctor);\n client._inputDecoder = new InputDecoder(client._input);\n client._reckonBaseline = 0; // mirrors the SDK's delta-coded stamp baseline (reset together on (re)connect)\n const maxSize = this.options.bufferMaxSize;\n if (maxSize > 0) {\n // ctor \u2192 idle synthesis; client ref \u2192 idle ctx; idle policy \u2192 total drain()/next().\n client._inputBuffer = new InputBufferImpl(maxSize, this.options.seqField, this.options.ctor, client, this.options.idle);\n }\n client._inputAccessor = new InputAccessorImpl(client, this.nowOf);\n }\n\n /** @internal Raw reckon stamp for {@link Room.allowRewindState}'s\n * `bindReckonTime` \u2014 bypasses the accessor's resolved getter so the rewind\n * midpoint fallback still engages for unstamped clients. instanceof-narrowed:\n * the registry only ever holds `InputAccessorImpl` (see {@link allocate}),\n * so the 0 fallback covers only a missing session. */\n rawReckonTime(sessionId: string): number {\n const acc = this.accessors.get(sessionId);\n return acc instanceof InputAccessorImpl ? acc.rawReckonTime : 0;\n }\n\n /** Register a (re)joined client's accessor. On reconnect this overwrites the\n * dropped session's stale entry. Called after the client is in `this.clients`\n * (so a pre-push failure can't leak it) and before onJoin (so `get()` resolves\n * there). */\n register(sessionId: string, client: ClientPrivate): void {\n this.accessors.set(sessionId, client._inputAccessor!);\n }\n\n /** Freeze a leaving seat: drop pending inputs so a held (reconnecting) session\n * idles from the first tick rather than replaying last-known moves. `_input`\n * (the idle ctx's `latest`) is left intact. */\n freeze(client: ClientPrivate): void {\n client._inputBuffer?.clear();\n }\n\n /** Drop a fully-gone session's accessor (delete of a missing key is a no-op). */\n release(sessionId: string): void {\n this.accessors.delete(sessionId);\n }\n\n /** Release every held accessor (room dispose). */\n dispose(): void {\n this.accessors.clear();\n }\n\n // --- encode / decode (called from Room._onMessage) ---\n\n /**\n * Sanitize the freshly-decoded `client._input`, then (when buffering is on)\n * push a clone into the per-client buffer. Honors the framework seq\n * (unreliable) / a user `inputOptions.seqField` (reliable) to dedupe the\n * redundancy ring.\n */\n capture(client: ClientPrivate, renderTime: number = 0, reckonTime: number = 0, seq?: number): void {\n // Sanitize before anything reads it (latest, the clone below, the idle ctx).\n this.options.sanitize?.(client._input);\n const buf = client._inputBuffer;\n if (!buf) { return; } // no consumer registered \u2014 skip the clone allocation\n const inst = client._input!;\n if (seq !== undefined) {\n // Unreliable: dedup the ring by the framework wire seq (user seqField is for .at() only).\n if (!buf.accept(seq)) { return; }\n } else {\n // Reliable: no framework seq (implicit count). Honor a user `seqField` if set.\n const seqField = this.options.seqField;\n if (seqField !== undefined) {\n const value = (inst as any)[seqField] as number;\n if (typeof value === 'number' && !buf.accept(value)) { return; }\n }\n }\n buf.push(inst.clone() as any, renderTime, reckonTime, seq);\n }\n\n /** Decode a `ROOM_INPUT_RELIABLE` frame: an optional TIMED stamp prefix (shape\n * set by this room's derived stamp mode) then the input body. `it` starts at\n * offset 1, so the body begins at `it.offset`. */\n decodeReliable(client: ClientPrivate, buffer: Buffer, it: Iterator, modifiers: number): void {\n if (!client._inputDecoder) { return; }\n // Optional stamp prefix (TIMED bit), DELTA-CODED, length set by this room's\n // stamp mode (the timeline value is reconstructed against the per-client\n // baseline; the wire carries only the signed change from the previous frame):\n // both: [varint \u0394reckon][uint16 renderDelta] \u2192 renderTime = reckon \u2212 renderDelta\n // reckon-only: [varint \u0394reckon]\n // render-only: [varint \u0394render]\n let renderTime = 0;\n let reckonTime = 0;\n if (modifiers & ProtocolModifier.TIMED) {\n this.#resolveWireModes();\n // Reconstruct the absolute timeline value: baseline += signed delta. Both\n // sides start at 0 (so the first delta is absolute) and re-zero together on\n // (re)connect; reliable+in-order keeps the mirror locked.\n const stamp = client._reckonBaseline! += decode.number(buffer, it);\n if (this.#stampReckon && this.#stampRender) {\n reckonTime = stamp;\n const renderDelta = decode.uint16(buffer, it);\n renderTime = stamp > renderDelta ? stamp - renderDelta : 0;\n } else if (this.#stampReckon) {\n reckonTime = stamp;\n } else {\n renderTime = stamp;\n }\n }\n try {\n client._inputDecoder.decode(buffer.subarray(it.offset, buffer.byteLength));\n } catch (e: any) {\n debugAndPrintError(e);\n return;\n }\n client._lastInputReceivedAt = performance.now();\n // Mirrors the SDK's sent count; echoed back as lastInputSeq for RTT.\n client._receivedInputCount = (client._receivedInputCount ?? 0) + 1;\n this.capture(client, renderTime, reckonTime);\n }\n\n /** Decode a `ROOM_INPUT_UNRELIABLE` redundancy ring \u2014 each slot carries its\n * framework seq (base seq + position) for ring dedupe, no user seqField. */\n decodeUnreliable(client: ClientPrivate, buffer: Buffer, _modifiers: number): void {\n if (!client._inputDecoder) { return; }\n try {\n client._inputDecoder.decodeAll(buffer.subarray(1), (_inst, seq) => this.capture(client, 0, 0, seq));\n } catch (e: any) {\n debugAndPrintError(e);\n return;\n }\n client._lastInputReceivedAt = performance.now();\n }\n\n // --- handshake + stamp mode ---\n\n /** Resolve the wire stamp mode from the rewind timeline (once, then frozen). */\n #resolveWireModes(): void {\n if (this.#stampResolved) return;\n this.#stampResolved = true;\n const tl = this.room._timelineMode();\n this.#stampRender = tl?.snapshot ?? false;\n this.#stampReckon = tl?.reckon ?? false;\n }\n\n /**\n * The join-handshake input sections: INPUT_REFLECTION (the SDK-deserializable\n * input ctor bytes) and, when any runtime config differs from defaults,\n * INPUT_OPTIONS (`[flags uint8][tickRate varint?][patchRate varint?][subSteps\n * varint?]`). The client mirrors RENDER_TIME/RECKON_TIME (auto-stamp timeline),\n * tickRate (predict at dt=1/tickRate), patchRate (reconcile cadence), and\n * subSteps (physics sub-steps per input, omitted when 1). Returns `undefined`\n * when there's nothing to add.\n */\n handshakeSections(): Array<{ tag: number; bytes: Uint8Array }> | undefined {\n let sections: Array<{ tag: number; bytes: Uint8Array }> | undefined;\n const inputBytes = _inputReflectionCache.get(this.options.ctor);\n if (inputBytes !== undefined) {\n sections = [{ tag: HandshakeSection.INPUT_REFLECTION, bytes: inputBytes }];\n }\n this.#resolveWireModes();\n const stampRender = this.#stampRender;\n const stampReckon = this.#stampReckon;\n const tickRate = this.options.tickRate;\n const patchRate = (typeof this.room.patchRate === \"number\" && this.room.patchRate > 0)\n ? Math.round(this.room.patchRate) : undefined;\n const subSteps = (this.options.subSteps !== undefined && this.options.subSteps > 1)\n ? this.options.subSteps : undefined;\n if (stampRender || stampReckon || tickRate || patchRate || subSteps) {\n let flags = 0;\n if (stampRender) { flags |= InputFlags.RENDER_TIME; }\n if (stampReckon) { flags |= InputFlags.RECKON_TIME; }\n if (tickRate) { flags |= InputFlags.FIXED_TIMESTEP; }\n if (patchRate) { flags |= InputFlags.PATCH_RATE; }\n if (subSteps) { flags |= InputFlags.SUB_STEPS; }\n // 24B: worst case = flags + float64 tickRate (9) + uint32 patchRate (5) + subSteps (2).\n const buf = new Uint8Array(24);\n const sit = { offset: 0 };\n buf[sit.offset++] = flags;\n if (tickRate) { encode.number(buf, tickRate, sit); }\n if (patchRate) { encode.number(buf, patchRate, sit); }\n if (subSteps) { encode.number(buf, subSteps, sit); }\n (sections ??= []).push({\n tag: HandshakeSection.INPUT_OPTIONS,\n bytes: buf.subarray(0, sit.offset),\n });\n }\n return sections;\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/presence/LocalPresence.ts"],
4
- "sourcesContent": ["\nimport { EventEmitter } from 'events';\nimport { spliceOne } from '../utils/Utils.ts';\nimport type { Presence } from './Presence.ts';\n\nimport { hasDevModeCache, isDevMode, getDevModeCache, writeDevModeCache } from '../utils/DevMode.ts';\n\ntype Callback = (...args: any[]) => void;\n\nexport class LocalPresence implements Presence {\n public subscriptions = new EventEmitter();\n\n public data: {[roomName: string]: string[]} = {};\n public hash: {[roomName: string]: {[key: string]: string}} = {};\n\n public keys: {[name: string]: string | number} = {};\n\n private timeouts: {[name: string]: NodeJS.Timeout} = {};\n\n constructor() {\n //\n // reload from local cache on devMode\n //\n if (\n isDevMode &&\n hasDevModeCache()\n ) {\n const cache = getDevModeCache();\n if (cache.data) { this.data = cache.data; }\n if (cache.hash) { this.hash = cache.hash; }\n if (cache.keys) { this.keys = cache.keys; }\n }\n }\n\n public subscribe(topic: string, callback: (...args: any[]) => void) {\n this.subscriptions.on(topic, callback);\n return Promise.resolve(this);\n }\n\n public unsubscribe(topic: string, callback?: Callback) {\n if (callback) {\n this.subscriptions.removeListener(topic, callback);\n\n } else {\n this.subscriptions.removeAllListeners(topic);\n }\n\n return this;\n }\n\n public publish(topic: string, data: any) {\n this.subscriptions.emit(topic, data);\n return this;\n }\n\n public async channels (pattern?: string) {\n let eventNames = this.subscriptions.eventNames() as string[];\n if (pattern) {\n //\n // This is a limited glob pattern to regexp implementation.\n // If needed, we can use a full implementation like picomatch: https://github.com/micromatch/picomatch/\n //\n const regexp = new RegExp(\n pattern.\n replaceAll(\".\", \"\\\\.\").\n replaceAll(\"$\", \"\\\\$\").\n replaceAll(\"*\", \".*\").\n replaceAll(\"?\", \".\"),\n \"i\"\n );\n eventNames = eventNames.filter((eventName) => regexp.test(eventName));\n }\n return eventNames;\n }\n\n public async exists(key: string): Promise<boolean> {\n return (\n this.keys[key] !== undefined ||\n this.data[key] !== undefined ||\n this.hash[key] !== undefined\n );\n }\n\n public set(key: string, value: string) {\n this.keys[key] = value;\n }\n\n public setex(key: string, value: string, seconds: number) {\n this.keys[key] = value;\n this.expire(key, seconds);\n }\n\n public expire(key: string, seconds: number) {\n // ensure previous timeout is clear before setting another one.\n if (this.timeouts[key]) {\n clearTimeout(this.timeouts[key]);\n }\n this.timeouts[key] = setTimeout(() => {\n delete this.keys[key];\n delete this.timeouts[key];\n }, seconds * 1000);\n }\n\n public get(key: string) {\n return this.keys[key];\n }\n\n public del(key: string) {\n delete this.keys[key];\n delete this.data[key];\n delete this.hash[key];\n }\n\n public sadd(key: string, value: any) {\n if (!this.data[key]) {\n this.data[key] = [];\n }\n\n if (this.data[key].indexOf(value) === -1) {\n this.data[key].push(value);\n }\n }\n\n public async smembers(key: string): Promise<string[]> {\n return this.data[key] || [];\n }\n\n public async sismember(key: string, field: string) {\n return this.data[key] && this.data[key].includes(field) ? 1 : 0;\n }\n\n public srem(key: string, value: any) {\n if (this.data[key]) {\n spliceOne(this.data[key], this.data[key].indexOf(value));\n }\n }\n\n public scard(key: string) {\n return (this.data[key] || []).length;\n }\n\n public async sinter(...keys: string[]) {\n const intersection: {[value: string]: number} = {};\n\n for (let i = 0, l = keys.length; i < l; i++) {\n (await this.smembers(keys[i])).forEach((member) => {\n if (!intersection[member]) {\n intersection[member] = 0;\n }\n\n intersection[member]++;\n });\n }\n\n return Object.keys(intersection).reduce((prev, curr) => {\n if (intersection[curr] > 1) {\n prev.push(curr);\n }\n return prev;\n }, []);\n }\n\n public hset(key: string, field: string, value: string) {\n if (!this.hash[key]) { this.hash[key] = {}; }\n this.hash[key][field] = value;\n return Promise.resolve(true);\n }\n\n public hincrby(key: string, field: string, incrBy: number) {\n if (!this.hash[key]) { this.hash[key] = {}; }\n let value = Number(this.hash[key][field] || '0');\n value += incrBy;\n this.hash[key][field] = value.toString();\n return Promise.resolve(value);\n }\n\n public hincrbyex(key: string, field: string, incrBy: number, expireInSeconds: number) {\n if (!this.hash[key]) { this.hash[key] = {}; }\n let value = Number(this.hash[key][field] || '0');\n value += incrBy;\n this.hash[key][field] = value.toString();\n\n //\n // FIXME: delete only hash[key][field]\n // (we can't use \"HEXPIRE\" in Redis because it's only available since Redis version 7.4.0+)\n //\n if (this.timeouts[key]) {\n clearTimeout(this.timeouts[key]);\n }\n this.timeouts[key] = setTimeout(() => {\n delete this.hash[key];\n delete this.timeouts[key];\n }, expireInSeconds * 1000);\n\n return Promise.resolve(value);\n }\n\n public async hget(key: string, field: string) {\n return (typeof(this.hash[key]) === 'object')\n ? this.hash[key][field] ?? null\n : null;\n }\n\n public async hgetall(key: string) {\n return this.hash[key] || {};\n }\n\n public hdel(key: string, field: any) {\n const success = this.hash?.[key]?.[field] !== undefined;\n if (success) {\n delete this.hash[key][field];\n }\n return Promise.resolve(success);\n }\n\n public async hlen(key: string) {\n return this.hash[key] && Object.keys(this.hash[key]).length || 0;\n }\n\n public async incr(key: string) {\n if (!this.keys[key]) {\n this.keys[key] = 0;\n }\n (this.keys[key] as number)++;\n return Promise.resolve(this.keys[key] as number);\n }\n\n public async decr(key: string) {\n if (!this.keys[key]) {\n this.keys[key] = 0;\n }\n (this.keys[key] as number)--;\n return Promise.resolve(this.keys[key] as number);\n }\n\n public llen(key: string) {\n return Promise.resolve((this.data[key] && this.data[key].length) || 0);\n }\n\n public rpush(key: string, ...values: string[]): Promise<number> {\n if (!this.data[key]) { this.data[key] = []; }\n\n let lastLength: number = 0;\n\n values.forEach(value => {\n lastLength = this.data[key].push(value);\n });\n\n return Promise.resolve(lastLength);\n }\n\n public lpush(key: string, ...values: string[]): Promise<number> {\n if (!this.data[key]) { this.data[key] = []; }\n\n let lastLength: number = 0;\n\n values.forEach(value => {\n lastLength = this.data[key].unshift(value);\n });\n\n return Promise.resolve(lastLength);\n }\n\n public lpop(key: string): Promise<string> {\n return Promise.resolve(Array.isArray(this.data[key])\n ? this.data[key].shift()\n : null);\n }\n\n public rpop(key: string): Promise<string | null> {\n return Promise.resolve(this.data[key].pop());\n }\n\n public brpop(...args: [...keys: string[], timeoutInSeconds: number]): Promise<[string, string] | null> {\n const keys = args.slice(0, -1) as string[];\n const timeoutInSeconds = args[args.length - 1] as number;\n\n const getFirstPopulated = (): [string, string] | null => {\n const keyWithValue = keys.find(key => this.data[key] && this.data[key].length > 0);\n if (keyWithValue) {\n return [keyWithValue, this.data[keyWithValue].pop()];\n } else {\n return null;\n }\n }\n\n const firstPopulated = getFirstPopulated();\n\n if (firstPopulated) {\n // return first populated key + item\n return Promise.resolve(firstPopulated);\n\n } else {\n // 8 retries per second\n const maxRetries = timeoutInSeconds * 8;\n\n let tries = 0;\n return new Promise((resolve) => {\n const interval = setInterval(() => {\n tries++;\n\n const firstPopulated = getFirstPopulated();\n if (firstPopulated) {\n clearInterval(interval);\n return resolve(firstPopulated);\n\n } else if (tries >= maxRetries) {\n clearInterval(interval);\n return resolve(null);\n }\n\n }, (timeoutInSeconds * 1000) / maxRetries);\n });\n }\n }\n\n public setMaxListeners(number: number) {\n this.subscriptions.setMaxListeners(number);\n }\n\n public shutdown() {\n if (isDevMode) {\n writeDevModeCache({\n data: this.data,\n hash: this.hash,\n keys: this.keys\n });\n }\n }\n\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\nimport { EventEmitter } from 'events';\nimport { spliceOne } from '../utils/Utils.ts';\nimport type { Presence } from './Presence.ts';\n\nimport { hasDevModeCache, isDevMode, getDevModeCache, writeDevModeCache } from '../utils/DevMode.ts';\n\ntype Callback = (...args: any[]) => void;\n\nexport class LocalPresence implements Presence {\n public subscriptions: EventEmitter = new EventEmitter();\n\n public data: {[roomName: string]: string[]} = {};\n public hash: {[roomName: string]: {[key: string]: string}} = {};\n\n public keys: {[name: string]: string | number} = {};\n\n private timeouts: {[name: string]: NodeJS.Timeout} = {};\n\n constructor() {\n //\n // reload from local cache on devMode\n //\n if (\n isDevMode &&\n hasDevModeCache()\n ) {\n const cache = getDevModeCache();\n if (cache.data) { this.data = cache.data; }\n if (cache.hash) { this.hash = cache.hash; }\n if (cache.keys) { this.keys = cache.keys; }\n }\n }\n\n public subscribe(topic: string, callback: (...args: any[]) => void) {\n this.subscriptions.on(topic, callback);\n return Promise.resolve(this);\n }\n\n public unsubscribe(topic: string, callback?: Callback) {\n if (callback) {\n this.subscriptions.removeListener(topic, callback);\n\n } else {\n this.subscriptions.removeAllListeners(topic);\n }\n\n return this;\n }\n\n public publish(topic: string, data: any) {\n this.subscriptions.emit(topic, data);\n return this;\n }\n\n public async channels (pattern?: string) {\n let eventNames = this.subscriptions.eventNames() as string[];\n if (pattern) {\n //\n // This is a limited glob pattern to regexp implementation.\n // If needed, we can use a full implementation like picomatch: https://github.com/micromatch/picomatch/\n //\n const regexp = new RegExp(\n pattern.\n replaceAll(\".\", \"\\\\.\").\n replaceAll(\"$\", \"\\\\$\").\n replaceAll(\"*\", \".*\").\n replaceAll(\"?\", \".\"),\n \"i\"\n );\n eventNames = eventNames.filter((eventName) => regexp.test(eventName));\n }\n return eventNames;\n }\n\n public async exists(key: string): Promise<boolean> {\n return (\n this.keys[key] !== undefined ||\n this.data[key] !== undefined ||\n this.hash[key] !== undefined\n );\n }\n\n public set(key: string, value: string) {\n this.keys[key] = value;\n }\n\n public setex(key: string, value: string, seconds: number) {\n this.keys[key] = value;\n this.expire(key, seconds);\n }\n\n public expire(key: string, seconds: number) {\n // ensure previous timeout is clear before setting another one.\n if (this.timeouts[key]) {\n clearTimeout(this.timeouts[key]);\n }\n this.timeouts[key] = setTimeout(() => {\n delete this.keys[key];\n delete this.timeouts[key];\n }, seconds * 1000);\n }\n\n public get(key: string) {\n return this.keys[key];\n }\n\n public del(key: string) {\n delete this.keys[key];\n delete this.data[key];\n delete this.hash[key];\n }\n\n public sadd(key: string, value: any) {\n if (!this.data[key]) {\n this.data[key] = [];\n }\n\n if (this.data[key].indexOf(value) === -1) {\n this.data[key].push(value);\n }\n }\n\n public async smembers(key: string): Promise<string[]> {\n return this.data[key] || [];\n }\n\n public async sismember(key: string, field: string) {\n return this.data[key] && this.data[key].includes(field) ? 1 : 0;\n }\n\n public srem(key: string, value: any) {\n if (this.data[key]) {\n spliceOne(this.data[key], this.data[key].indexOf(value));\n }\n }\n\n public scard(key: string) {\n return (this.data[key] || []).length;\n }\n\n public async sinter(...keys: string[]) {\n const intersection: {[value: string]: number} = {};\n\n for (let i = 0, l = keys.length; i < l; i++) {\n (await this.smembers(keys[i])).forEach((member) => {\n if (!intersection[member]) {\n intersection[member] = 0;\n }\n\n intersection[member]++;\n });\n }\n\n return Object.keys(intersection).reduce((prev, curr) => {\n if (intersection[curr] > 1) {\n prev.push(curr);\n }\n return prev;\n }, []);\n }\n\n public hset(key: string, field: string, value: string) {\n if (!this.hash[key]) { this.hash[key] = {}; }\n this.hash[key][field] = value;\n return Promise.resolve(true);\n }\n\n public hincrby(key: string, field: string, incrBy: number) {\n if (!this.hash[key]) { this.hash[key] = {}; }\n let value = Number(this.hash[key][field] || '0');\n value += incrBy;\n this.hash[key][field] = value.toString();\n return Promise.resolve(value);\n }\n\n public hincrbyex(key: string, field: string, incrBy: number, expireInSeconds: number) {\n if (!this.hash[key]) { this.hash[key] = {}; }\n let value = Number(this.hash[key][field] || '0');\n value += incrBy;\n this.hash[key][field] = value.toString();\n\n //\n // FIXME: delete only hash[key][field]\n // (we can't use \"HEXPIRE\" in Redis because it's only available since Redis version 7.4.0+)\n //\n if (this.timeouts[key]) {\n clearTimeout(this.timeouts[key]);\n }\n this.timeouts[key] = setTimeout(() => {\n delete this.hash[key];\n delete this.timeouts[key];\n }, expireInSeconds * 1000);\n\n return Promise.resolve(value);\n }\n\n public async hget(key: string, field: string) {\n return (typeof(this.hash[key]) === 'object')\n ? this.hash[key][field] ?? null\n : null;\n }\n\n public async hgetall(key: string) {\n return this.hash[key] || {};\n }\n\n public hdel(key: string, field: any) {\n const success = this.hash?.[key]?.[field] !== undefined;\n if (success) {\n delete this.hash[key][field];\n }\n return Promise.resolve(success);\n }\n\n public async hlen(key: string) {\n return this.hash[key] && Object.keys(this.hash[key]).length || 0;\n }\n\n public async incr(key: string) {\n if (!this.keys[key]) {\n this.keys[key] = 0;\n }\n (this.keys[key] as number)++;\n return Promise.resolve(this.keys[key] as number);\n }\n\n public async decr(key: string) {\n if (!this.keys[key]) {\n this.keys[key] = 0;\n }\n (this.keys[key] as number)--;\n return Promise.resolve(this.keys[key] as number);\n }\n\n public llen(key: string) {\n return Promise.resolve((this.data[key] && this.data[key].length) || 0);\n }\n\n public rpush(key: string, ...values: string[]): Promise<number> {\n if (!this.data[key]) { this.data[key] = []; }\n\n let lastLength: number = 0;\n\n values.forEach(value => {\n lastLength = this.data[key].push(value);\n });\n\n return Promise.resolve(lastLength);\n }\n\n public lpush(key: string, ...values: string[]): Promise<number> {\n if (!this.data[key]) { this.data[key] = []; }\n\n let lastLength: number = 0;\n\n values.forEach(value => {\n lastLength = this.data[key].unshift(value);\n });\n\n return Promise.resolve(lastLength);\n }\n\n public lpop(key: string): Promise<string> {\n return Promise.resolve(Array.isArray(this.data[key])\n ? this.data[key].shift()\n : null);\n }\n\n public rpop(key: string): Promise<string | null> {\n return Promise.resolve(this.data[key].pop());\n }\n\n public brpop(...args: [...keys: string[], timeoutInSeconds: number]): Promise<[string, string] | null> {\n const keys = args.slice(0, -1) as string[];\n const timeoutInSeconds = args[args.length - 1] as number;\n\n const getFirstPopulated = (): [string, string] | null => {\n const keyWithValue = keys.find(key => this.data[key] && this.data[key].length > 0);\n if (keyWithValue) {\n return [keyWithValue, this.data[keyWithValue].pop()];\n } else {\n return null;\n }\n }\n\n const firstPopulated = getFirstPopulated();\n\n if (firstPopulated) {\n // return first populated key + item\n return Promise.resolve(firstPopulated);\n\n } else {\n // 8 retries per second\n const maxRetries = timeoutInSeconds * 8;\n\n let tries = 0;\n return new Promise((resolve) => {\n const interval = setInterval(() => {\n tries++;\n\n const firstPopulated = getFirstPopulated();\n if (firstPopulated) {\n clearInterval(interval);\n return resolve(firstPopulated);\n\n } else if (tries >= maxRetries) {\n clearInterval(interval);\n return resolve(null);\n }\n\n }, (timeoutInSeconds * 1000) / maxRetries);\n });\n }\n }\n\n public setMaxListeners(number: number) {\n this.subscriptions.setMaxListeners(number);\n }\n\n public shutdown() {\n if (isDevMode) {\n writeDevModeCache({\n data: this.data,\n hash: this.hash,\n keys: this.keys\n });\n }\n }\n\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["firstPopulated"]
7
7
  }
@@ -2,7 +2,7 @@ import { EventEmitter } from 'events';
2
2
  import type { Presence } from './Presence.ts';
3
3
  type Callback = (...args: any[]) => void;
4
4
  export declare class LocalPresence implements Presence {
5
- subscriptions: EventEmitter<[never]>;
5
+ subscriptions: EventEmitter;
6
6
  data: {
7
7
  [roomName: string]: string[];
8
8
  };