@colyseus/core 0.18.0 → 0.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/build/MatchMaker.cjs +3 -7
  2. package/build/MatchMaker.cjs.map +2 -2
  3. package/build/MatchMaker.mjs +4 -8
  4. package/build/MatchMaker.mjs.map +2 -2
  5. package/build/Protocol.cjs +3 -3
  6. package/build/Protocol.cjs.map +1 -1
  7. package/build/Protocol.d.ts +3 -3
  8. package/build/Protocol.mjs +3 -3
  9. package/build/Protocol.mjs.map +1 -1
  10. package/build/Rewind.cjs +385 -0
  11. package/build/Rewind.cjs.map +7 -0
  12. package/build/Rewind.d.ts +291 -0
  13. package/build/Rewind.mjs +359 -0
  14. package/build/Rewind.mjs.map +7 -0
  15. package/build/Room.cjs +280 -281
  16. package/build/Room.cjs.map +3 -3
  17. package/build/Room.d.ts +185 -81
  18. package/build/Room.mjs +286 -286
  19. package/build/Room.mjs.map +2 -2
  20. package/build/RoomMessages.cjs +265 -0
  21. package/build/RoomMessages.cjs.map +7 -0
  22. package/build/RoomMessages.d.ts +49 -0
  23. package/build/RoomMessages.mjs +240 -0
  24. package/build/RoomMessages.mjs.map +7 -0
  25. package/build/Server.cjs +88 -31
  26. package/build/Server.cjs.map +2 -2
  27. package/build/Server.d.ts +36 -0
  28. package/build/Server.mjs +88 -31
  29. package/build/Server.mjs.map +2 -2
  30. package/build/Transport.cjs +26 -0
  31. package/build/Transport.cjs.map +2 -2
  32. package/build/Transport.d.ts +69 -2
  33. package/build/Transport.mjs +25 -0
  34. package/build/Transport.mjs.map +2 -2
  35. package/build/errors/RoomExceptions.cjs +7 -4
  36. package/build/errors/RoomExceptions.cjs.map +2 -2
  37. package/build/errors/RoomExceptions.d.ts +15 -3
  38. package/build/errors/RoomExceptions.mjs +5 -3
  39. package/build/errors/RoomExceptions.mjs.map +2 -2
  40. package/build/index.cjs +19 -2
  41. package/build/index.cjs.map +2 -2
  42. package/build/index.d.ts +7 -4
  43. package/build/index.mjs +17 -4
  44. package/build/index.mjs.map +2 -2
  45. package/build/input/InputBuffer.cjs +439 -16
  46. package/build/input/InputBuffer.cjs.map +2 -2
  47. package/build/input/InputBuffer.d.ts +186 -96
  48. package/build/input/InputBuffer.mjs +436 -15
  49. package/build/input/InputBuffer.mjs.map +2 -2
  50. package/build/input/RoomInput.cjs +306 -0
  51. package/build/input/RoomInput.cjs.map +7 -0
  52. package/build/input/RoomInput.d.ts +91 -0
  53. package/build/input/RoomInput.mjs +287 -0
  54. package/build/input/RoomInput.mjs.map +7 -0
  55. package/build/input/types.cjs +18 -0
  56. package/build/input/types.cjs.map +7 -0
  57. package/build/input/types.d.ts +476 -0
  58. package/build/input/types.mjs +0 -0
  59. package/build/input/types.mjs.map +7 -0
  60. package/build/rooms/QueueRoom.cjs +1 -1
  61. package/build/rooms/QueueRoom.cjs.map +2 -2
  62. package/build/rooms/QueueRoom.mjs +1 -1
  63. package/build/rooms/QueueRoom.mjs.map +2 -2
  64. package/build/router/default_routes.cjs +4 -1
  65. package/build/router/default_routes.cjs.map +2 -2
  66. package/build/router/default_routes.mjs +4 -1
  67. package/build/router/default_routes.mjs.map +2 -2
  68. package/build/router/index.cjs +6 -1
  69. package/build/router/index.cjs.map +2 -2
  70. package/build/router/index.mjs +6 -1
  71. package/build/router/index.mjs.map +2 -2
  72. package/build/router/node.cjs +37 -2
  73. package/build/router/node.cjs.map +2 -2
  74. package/build/router/node.d.ts +10 -0
  75. package/build/router/node.mjs +35 -1
  76. package/build/router/node.mjs.map +2 -2
  77. package/build/serializer/NoneSerializer.cjs +2 -2
  78. package/build/serializer/NoneSerializer.cjs.map +2 -2
  79. package/build/serializer/NoneSerializer.d.ts +3 -3
  80. package/build/serializer/NoneSerializer.mjs +2 -2
  81. package/build/serializer/NoneSerializer.mjs.map +2 -2
  82. package/build/serializer/SchemaSerializer.cjs +44 -14
  83. package/build/serializer/SchemaSerializer.cjs.map +2 -2
  84. package/build/serializer/SchemaSerializer.d.ts +3 -3
  85. package/build/serializer/SchemaSerializer.mjs +46 -16
  86. package/build/serializer/SchemaSerializer.mjs.map +2 -2
  87. package/build/serializer/Serializer.cjs.map +1 -1
  88. package/build/serializer/Serializer.d.ts +12 -2
  89. package/build/utils/DevMode.cjs +14 -14
  90. package/build/utils/DevMode.cjs.map +2 -2
  91. package/build/utils/DevMode.d.ts +0 -2
  92. package/build/utils/DevMode.mjs +14 -12
  93. package/build/utils/DevMode.mjs.map +2 -2
  94. package/build/utils/Env.cjs +8 -4
  95. package/build/utils/Env.cjs.map +3 -3
  96. package/build/utils/Env.mjs +8 -4
  97. package/build/utils/Env.mjs.map +2 -2
  98. package/build/utils/Utils.cjs +3 -0
  99. package/build/utils/Utils.cjs.map +2 -2
  100. package/build/utils/Utils.d.ts +8 -0
  101. package/build/utils/Utils.mjs +2 -0
  102. package/build/utils/Utils.mjs.map +2 -2
  103. package/package.json +11 -11
  104. package/src/MatchMaker.ts +7 -9
  105. package/src/Protocol.ts +3 -3
  106. package/src/Rewind.ts +572 -0
  107. package/src/Room.ts +409 -448
  108. package/src/RoomMessages.ts +342 -0
  109. package/src/Server.ts +112 -38
  110. package/src/Transport.ts +97 -2
  111. package/src/errors/RoomExceptions.ts +18 -4
  112. package/src/index.ts +13 -2
  113. package/src/input/InputBuffer.ts +464 -107
  114. package/src/input/RoomInput.ts +337 -0
  115. package/src/input/types.ts +503 -0
  116. package/src/rooms/QueueRoom.ts +1 -1
  117. package/src/router/default_routes.ts +6 -1
  118. package/src/router/index.ts +6 -1
  119. package/src/router/node.ts +44 -0
  120. package/src/serializer/NoneSerializer.ts +3 -3
  121. package/src/serializer/SchemaSerializer.ts +110 -19
  122. package/src/serializer/Serializer.ts +13 -2
  123. package/src/utils/DevMode.ts +18 -13
  124. package/src/utils/Env.ts +12 -4
  125. package/src/utils/Utils.ts +9 -0
@@ -1,15 +1,214 @@
1
1
  // packages/core/src/input/InputBuffer.ts
2
+ import { $METADATA } from "../utils/Utils.mjs";
3
+ function validateSubSteps(subSteps, source) {
4
+ if (subSteps === void 0) {
5
+ return void 0;
6
+ }
7
+ if (!Number.isInteger(subSteps) || subSteps < 1) {
8
+ throw new Error(`[${source}] subSteps must be an integer >= 1 (got ${subSteps}).`);
9
+ }
10
+ return subSteps;
11
+ }
12
+ function compileSanitizer(spec) {
13
+ if (typeof spec === "function") {
14
+ return spec;
15
+ }
16
+ const names = Object.keys(spec);
17
+ const mins = new Float64Array(names.length);
18
+ const maxs = new Float64Array(names.length);
19
+ for (let i = 0; i < names.length; i++) {
20
+ const range = spec[names[i]];
21
+ mins[i] = range[0];
22
+ maxs[i] = range[1];
23
+ }
24
+ return (input) => {
25
+ const inst = input;
26
+ for (let i = 0; i < names.length; i++) {
27
+ const v = inst[names[i]];
28
+ inst[names[i]] = v >= mins[i] ? v <= maxs[i] ? v : maxs[i] : mins[i];
29
+ }
30
+ };
31
+ }
32
+ function fieldNamesOf(ctor) {
33
+ const md = ctor[$METADATA];
34
+ const names = [];
35
+ if (md) {
36
+ for (let i = 0; ; i++) {
37
+ const f = md[i];
38
+ if (!f || typeof f.name !== "string") break;
39
+ names.push(f.name);
40
+ }
41
+ }
42
+ return names;
43
+ }
44
+ var COMPACT_THRESHOLD = 32;
45
+ var DONE_RESULT = Object.freeze({ value: void 0, done: true });
46
+ var DONE_ITERATOR = Object.freeze({
47
+ [Symbol.iterator]() {
48
+ return this;
49
+ },
50
+ next() {
51
+ return DONE_RESULT;
52
+ }
53
+ });
2
54
  var InputBufferImpl = class {
3
- constructor(maxSize, seqField) {
55
+ constructor(maxSize, seqField, ctor, client, idle) {
56
+ /** Pending snapshots, indexed `[_head, _items.length)`. Consumed inputs are
57
+ * NOT spliced off per-read — `_head` advances and the prefix is reclaimed in
58
+ * bulk by {@link compact} (a `shift()` per read is O(n), so a burst would be
59
+ * O(n²)). `_renderTimes`/`_reckonTimes` stay parallel to `_items`. */
4
60
  this._items = [];
61
+ /** Read cursor: index of the oldest UNCONSUMED input (== `_items.length` when empty). */
62
+ this._head = 0;
5
63
  this._lastSeq = -Infinity;
64
+ /**
65
+ * Cumulative count of inputs CONSUMED from this buffer (via any consume
66
+ * primitive — {@link next}/{@link take}/{@link drain}/{@link consume} — plus
67
+ * {@link clear} and overflow). Consumed = "removed from the pending set",
68
+ * whether applied to state or discarded. The server echoes this in the TIMED
69
+ * prefix as the reconciliation ack: how many of the client's inputs are
70
+ * reflected in (or finished influencing) the authoritative state, which lags
71
+ * the receive counter by inputs still buffered. Distinct from
72
+ * `_receivedInputCount` (receive-time, for RTT), which leads the state.
73
+ */
74
+ this.consumedCount = 0;
75
+ /** Render times parallel to `_items` (server-clock ms; `0` when unset). */
76
+ this._renderTimes = [];
77
+ /** Render time of the most recently drained input (see {@link renderTime}). */
78
+ this._lastRenderTime = 0;
79
+ /** Reckon-display stamps parallel to `_items` — the client's serverNow
80
+ * estimate at input-sample time, i.e. the instant its forward-reckoned
81
+ * entities were displayed at (server-clock ms; `0` when unset). */
82
+ this._reckonTimes = [];
83
+ /** Reckon stamp of the most recently consumed input (see {@link reckonTime}). */
84
+ this._lastReckonTime = 0;
85
+ /** Seq VALUE of the last consumed input — the reconciliation ack echoed to the
86
+ * client (see {@link ackSeq}). Only meaningful when {@link _seqs} is tracked
87
+ * (unreliable); reliable's ack falls back to {@link consumedCount}. */
88
+ this._lastConsumedSeq = 0;
89
+ /** Whether the most recently produced frame was a synthesized idle (see {@link wasIdle}). */
90
+ this._lastWasIdle = false;
91
+ this._iterRemaining = 0;
92
+ this._iterActive = false;
93
+ this._iterRes = { value: void 0, done: false };
94
+ /** Reused ctx for the idle callback form (see {@link IdleContext}). */
95
+ this._idleCtx = { latest: void 0, sessionId: "" };
6
96
  this._maxSize = maxSize;
7
97
  this._seqField = seqField;
98
+ this._ctor = ctor;
99
+ this._client = client;
100
+ this._roomIdle = idle;
101
+ }
102
+ /** The effective idle policy for one consume call: per-call `false` suppresses,
103
+ * per-call value overrides, else the room-level default (or none). */
104
+ effectiveIdle(opts) {
105
+ return opts?.idle === false ? void 0 : opts?.idle ?? this._roomIdle;
106
+ }
107
+ /** Resolve an {@link IdleInput} to overrides — invokes the callback form
108
+ * LAZILY, here at synthesis time (the buffer is known to be empty). */
109
+ resolveIdle(idle) {
110
+ if (typeof idle !== "function") return idle;
111
+ this._idleCtx.latest = this._client?._input;
112
+ this._idleCtx.sessionId = this._client?.sessionId ?? "";
113
+ return idle(this._idleCtx) ?? true;
114
+ }
115
+ /**
116
+ * The synthesized "no input this tick" frame: schema defaults overlaid with
117
+ * `overrides` (`true` = none). Copies BY FIELD NAME from the schema metadata —
118
+ * schema fields are prototype accessors (no own props), so `Object.assign`
119
+ * can't source from a schema instance; the name walk reads getters, letting
120
+ * `overrides` be a plain partial OR a schema instance (e.g. `latest`).
121
+ * ONE reused instance — refilled per call, never advances the ack.
122
+ */
123
+ idleFrame(overrides) {
124
+ if (this._idle === void 0) {
125
+ this._idle = new this._ctor();
126
+ this._fieldNames = fieldNamesOf(this._ctor);
127
+ const fresh = this._idle;
128
+ this._defaults = this._fieldNames.map((n) => fresh[n]);
129
+ }
130
+ const idle = this._idle;
131
+ const names = this._fieldNames;
132
+ const defaults = this._defaults;
133
+ const ov = overrides === true ? void 0 : overrides;
134
+ for (let i = 0; i < names.length; i++) {
135
+ const v = ov?.[names[i]];
136
+ idle[names[i]] = v !== void 0 ? v : defaults[i];
137
+ }
138
+ return this._idle;
8
139
  }
9
- push(snapshot) {
140
+ /** The idle frame to synthesize on an empty tick (room policy or per-call
141
+ * `opts.idle`), or `undefined` when no policy is in effect. Reuses the single
142
+ * idle instance; never advances the ack. Callers set {@link wasIdle}. */
143
+ idleFrameOnEmpty(opts) {
144
+ const idle = this._ctor !== void 0 ? this.effectiveIdle(opts) : void 0;
145
+ return idle === void 0 ? void 0 : this.idleFrame(this.resolveIdle(idle));
146
+ }
147
+ /** Consume the input at the cursor: advance cursor + ack, stamp THIS input's
148
+ * render/reckon times, clear {@link wasIdle}. The shared per-input step behind
149
+ * {@link next}, {@link consume}'s iterator, and the re-entrant fallback. */
150
+ stepHead() {
151
+ const i = this._head;
152
+ this._lastWasIdle = false;
153
+ this._lastRenderTime = this._renderTimes[i];
154
+ this._lastReckonTime = this._reckonTimes[i];
155
+ if (this._seqs !== void 0) {
156
+ this._lastConsumedSeq = this._seqs[i];
157
+ }
158
+ this._head = i + 1;
159
+ this.consumedCount++;
160
+ return this._items[i];
161
+ }
162
+ /** Drop ALL slots, reusing the backing arrays' capacity — the zero-GC reset for
163
+ * the consume/next path ({@link compact}/{@link clear}). `drain()` deliberately
164
+ * does NOT use this (it returns an array, so fresh `[]` is faster there). */
165
+ truncate() {
166
+ this._items.length = 0;
167
+ this._renderTimes.length = 0;
168
+ this._reckonTimes.length = 0;
169
+ if (this._seqs !== void 0) {
170
+ this._seqs.length = 0;
171
+ }
172
+ this._head = 0;
173
+ }
174
+ /**
175
+ * Append a decoded input snapshot. `seq` is the framework wire seq (unreliable);
176
+ * omit it for reliable inputs, which get an implicit monotonic receive count so
177
+ * {@link ackSeq} stays well-defined in both modes.
178
+ */
179
+ push(snapshot, renderTime = 0, reckonTime = 0, seq) {
10
180
  this._items.push(snapshot);
11
- if (this._items.length > this._maxSize) {
12
- this._items.shift();
181
+ this._renderTimes.push(renderTime);
182
+ this._reckonTimes.push(reckonTime);
183
+ if (seq !== void 0) {
184
+ (this._seqs ??= []).push(seq);
185
+ }
186
+ if (this.size > this._maxSize) {
187
+ if (this._seqs !== void 0) {
188
+ this._lastConsumedSeq = this._seqs[this._head];
189
+ }
190
+ this._head++;
191
+ this.consumedCount++;
192
+ this.compact();
193
+ }
194
+ }
195
+ /** Reclaim the consumed prefix `[0, _head)`. Free when fully drained (reuse the
196
+ * arrays); otherwise splice only once the cursor passes {@link COMPACT_THRESHOLD},
197
+ * so the amortized per-input cost stays O(1). */
198
+ compact() {
199
+ if (this._head === 0) {
200
+ return;
201
+ }
202
+ if (this._head >= this._items.length) {
203
+ this.truncate();
204
+ } else if (this._head >= COMPACT_THRESHOLD) {
205
+ this._items.splice(0, this._head);
206
+ this._renderTimes.splice(0, this._head);
207
+ this._reckonTimes.splice(0, this._head);
208
+ if (this._seqs !== void 0) {
209
+ this._seqs.splice(0, this._head);
210
+ }
211
+ this._head = 0;
13
212
  }
14
213
  }
15
214
  /** Returns true if `value` hasn't been seen, and updates the last-seen marker. */
@@ -20,19 +219,163 @@ var InputBufferImpl = class {
20
219
  this._lastSeq = value;
21
220
  return true;
22
221
  }
23
- drain() {
24
- const out = this._items;
222
+ drain(opts) {
223
+ if (this.size === 0) {
224
+ const idle = this.idleFrameOnEmpty(opts);
225
+ this._lastWasIdle = idle !== void 0;
226
+ return idle !== void 0 ? [idle] : [];
227
+ }
228
+ this._lastWasIdle = false;
229
+ const last = this._items.length - 1;
230
+ this._lastRenderTime = this._renderTimes[last];
231
+ this._lastReckonTime = this._reckonTimes[last];
232
+ if (this._seqs !== void 0) {
233
+ this._lastConsumedSeq = this._seqs[last];
234
+ }
235
+ const out = this._head === 0 ? this._items : this._items.slice(this._head);
236
+ this.consumedCount += out.length;
25
237
  this._items = [];
238
+ this._renderTimes = [];
239
+ this._reckonTimes = [];
240
+ if (this._seqs !== void 0) {
241
+ this._seqs = [];
242
+ }
243
+ this._head = 0;
244
+ return out;
245
+ }
246
+ /** Consume the single oldest input (ack advances by one); `undefined` if
247
+ * empty — or the synthesized idle frame when an idle policy is in effect. */
248
+ next(opts) {
249
+ if (this.size === 0) {
250
+ const idle = this.idleFrameOnEmpty(opts);
251
+ this._lastWasIdle = idle !== void 0;
252
+ return idle;
253
+ }
254
+ const item = this.stepHead();
255
+ this.compact();
256
+ return item;
257
+ }
258
+ /** Consume up to `n` oldest inputs (ack advances by the count actually taken). */
259
+ take(n) {
260
+ this._lastWasIdle = false;
261
+ const avail = this.size;
262
+ if (n <= 0 || avail === 0) {
263
+ return [];
264
+ }
265
+ const count = Math.min(n, avail);
266
+ const start = this._head;
267
+ const out = this._items.slice(start, start + count);
268
+ this._lastRenderTime = this._renderTimes[start + count - 1];
269
+ this._lastReckonTime = this._reckonTimes[start + count - 1];
270
+ if (this._seqs !== void 0) {
271
+ this._lastConsumedSeq = this._seqs[start + count - 1];
272
+ }
273
+ this._head += count;
274
+ this.consumedCount += count;
275
+ this.compact();
26
276
  return out;
27
277
  }
278
+ /**
279
+ * Iterate pending inputs one at a time (per-yield ack + stamp updates), or
280
+ * exactly one synthesized idle frame on an empty buffer with a policy in
281
+ * effect. See {@link InputAccessor.consume}.
282
+ *
283
+ * Returns a POOLED iterator (reused across calls — no generator frame, no
284
+ * per-call allocation) that consumes at most the pending count snapshotted at
285
+ * the call — never past the live tail — plus one optional trailing idle frame.
286
+ * The cursor advances per `next()`, so `break` (which calls `return()`)
287
+ * leaves the rest buffered and compacts. A nested
288
+ * `consume()` of the SAME buffer while one is live falls back to a fresh
289
+ * generator so the pooled `_iterRes` isn't clobbered (nesting is meaningless —
290
+ * both share the cursor — but it must not corrupt).
291
+ */
292
+ consume(opts) {
293
+ const empty = this.size === 0;
294
+ const idle = empty ? this.idleFrameOnEmpty(opts) : void 0;
295
+ if (empty && idle === void 0) {
296
+ this._lastWasIdle = false;
297
+ return DONE_ITERATOR;
298
+ }
299
+ if (this._iterActive) {
300
+ return this.consumeGen(idle);
301
+ }
302
+ this._iterActive = true;
303
+ this._iterIdle = idle;
304
+ this._iterRemaining = this.size;
305
+ return this.ensureIter();
306
+ }
307
+ [Symbol.iterator]() {
308
+ return this.consume();
309
+ }
310
+ /** Lazily mint the reused iterator. Closures over `this` so it reads the
311
+ * private cursor directly; one `_iterRes` is reused — read each result before
312
+ * the next `next()`, as `for..of` / spread / `Array.from` all do. */
313
+ ensureIter() {
314
+ if (this._iter !== void 0) {
315
+ return this._iter;
316
+ }
317
+ const self = this;
318
+ const res = this._iterRes;
319
+ const finish = () => {
320
+ self.compact();
321
+ self._iterActive = false;
322
+ self._iterRemaining = 0;
323
+ self._iterIdle = void 0;
324
+ res.value = void 0;
325
+ res.done = true;
326
+ return res;
327
+ };
328
+ this._iter = {
329
+ [Symbol.iterator]() {
330
+ return this;
331
+ },
332
+ next() {
333
+ if (self._iterRemaining > 0 && self.size > 0) {
334
+ self._iterRemaining--;
335
+ res.value = self.stepHead();
336
+ res.done = false;
337
+ return res;
338
+ }
339
+ if (self._iterIdle !== void 0) {
340
+ self._lastWasIdle = true;
341
+ res.value = self._iterIdle;
342
+ self._iterIdle = void 0;
343
+ res.done = false;
344
+ return res;
345
+ }
346
+ return finish();
347
+ },
348
+ return() {
349
+ return finish();
350
+ }
351
+ // `break` → compact + release
352
+ };
353
+ return this._iter;
354
+ }
355
+ /** Re-entrant fallback: a fresh generator for a nested `consume()` of this same
356
+ * buffer (the pooled iterator is single-active). Same accounting + compaction. */
357
+ *consumeGen(idleFrame) {
358
+ let remaining = this.size;
359
+ try {
360
+ while (remaining-- > 0 && this.size > 0) {
361
+ yield this.stepHead();
362
+ }
363
+ if (idleFrame !== void 0) {
364
+ this._lastWasIdle = true;
365
+ yield idleFrame;
366
+ }
367
+ } finally {
368
+ this.compact();
369
+ }
370
+ }
28
371
  peek() {
29
- return this._items.slice();
372
+ return this._items.slice(this._head);
30
373
  }
31
374
  at(value) {
32
375
  if (this._seqField === void 0) {
33
376
  return void 0;
34
377
  }
35
- for (let i = 0; i < this._items.length; i++) {
378
+ for (let i = this._head; i < this._items.length; i++) {
36
379
  if (this._items[i][this._seqField] === value) {
37
380
  return this._items[i];
38
381
  }
@@ -40,16 +383,51 @@ var InputBufferImpl = class {
40
383
  return void 0;
41
384
  }
42
385
  get size() {
43
- return this._items.length;
386
+ return this._items.length - this._head;
387
+ }
388
+ /** Render time (server-clock ms) of the most recently drained input; `0`
389
+ * until the first render-time-stamped input is drained. */
390
+ get renderTime() {
391
+ return this._lastRenderTime;
392
+ }
393
+ /** Reckon-display stamp of the most recently consumed input — the client's
394
+ * serverNow estimate when it sampled that input, i.e. the EXACT instant its
395
+ * forward-reckoned entities were displayed at. Same consume semantics as
396
+ * {@link renderTime}. RAW: stays `0` until stamped — the resolved,
397
+ * always-usable value is the accessor's `reckonTime`
398
+ * (`room.inputs.get(sid)`); `rewind.lastSeenBy()`'s midpoint-reconstruction
399
+ * fallback depends on this raw `0`. */
400
+ get reckonTime() {
401
+ return this._lastReckonTime;
402
+ }
403
+ /** Seq VALUE of the last consumed input — the reconciliation ack sent to the
404
+ * client. Reliable (no wire seq tracked): falls back to {@link consumedCount}
405
+ * for free. Unreliable: the framework wire seq, so a fully-dropped input doesn't
406
+ * make the ack lag the client's sent seq by the lost count. `0` before the first
407
+ * consume. */
408
+ get ackSeq() {
409
+ return this._seqs !== void 0 ? this._lastConsumedSeq : this.consumedCount;
410
+ }
411
+ /** Whether the most recently produced frame was a synthesized idle (see {@link wasIdle}). */
412
+ get wasIdle() {
413
+ return this._lastWasIdle;
44
414
  }
45
415
  clear() {
46
- this._items.length = 0;
416
+ if (this._seqs !== void 0 && this._items.length > 0) {
417
+ this._lastConsumedSeq = this._seqs[this._items.length - 1];
418
+ }
419
+ this.consumedCount += this.size;
420
+ this.truncate();
47
421
  this._lastSeq = -Infinity;
422
+ this._iterActive = false;
423
+ this._iterRemaining = 0;
424
+ this._iterIdle = void 0;
48
425
  }
49
426
  };
50
427
  var InputAccessorImpl = class {
51
- constructor(client) {
428
+ constructor(client, nowOf) {
52
429
  this._client = client;
430
+ this._nowOf = nowOf;
53
431
  }
54
432
  get latest() {
55
433
  return this._client._input;
@@ -57,8 +435,20 @@ var InputAccessorImpl = class {
57
435
  at(value) {
58
436
  return this._client._inputBuffer?.at(value);
59
437
  }
60
- drain() {
61
- return this._client._inputBuffer?.drain() ?? [];
438
+ consume(opts) {
439
+ return this._client._inputBuffer?.consume(opts) ?? DONE_ITERATOR;
440
+ }
441
+ [Symbol.iterator]() {
442
+ return this.consume();
443
+ }
444
+ drain(opts) {
445
+ return this._client._inputBuffer?.drain(opts) ?? [];
446
+ }
447
+ next(opts) {
448
+ return this._client._inputBuffer?.next(opts);
449
+ }
450
+ take(n) {
451
+ return this._client._inputBuffer?.take(n) ?? [];
62
452
  }
63
453
  peek() {
64
454
  return this._client._inputBuffer?.peek() ?? [];
@@ -66,21 +456,52 @@ var InputAccessorImpl = class {
66
456
  get size() {
67
457
  return this._client._inputBuffer?.size ?? 0;
68
458
  }
459
+ get consumedCount() {
460
+ return this._client._inputBuffer?.consumedCount ?? 0;
461
+ }
462
+ get wasIdle() {
463
+ return this._client._inputBuffer?.wasIdle ?? false;
464
+ }
69
465
  clear() {
70
466
  this._client._inputBuffer?.clear();
71
467
  }
468
+ get renderTime() {
469
+ return this._client._inputBuffer?.renderTime ?? 0;
470
+ }
471
+ /** @internal RAW reckon stamp (0 until stamped). The rewind binding reads
472
+ * THIS, not the resolved getter below: `Rewind._aim`'s midpoint fallback
473
+ * must still see 0 for unstamped clients (a resolved value would silently
474
+ * take the direct-stamp clamp path). */
475
+ get rawReckonTime() {
476
+ return this._client._inputBuffer?.reckonTime ?? 0;
477
+ }
478
+ get reckonTime() {
479
+ const raw = this._client._inputBuffer?.reckonTime ?? 0;
480
+ return raw > 0 ? raw : this._nowOf !== void 0 ? this._nowOf() : 0;
481
+ }
72
482
  };
73
483
  var NO_OP_INPUT_ACCESSOR = Object.freeze({
74
484
  latest: void 0,
75
485
  at: () => void 0,
486
+ consume: () => DONE_ITERATOR,
487
+ [Symbol.iterator]: () => DONE_ITERATOR,
76
488
  drain: () => [],
489
+ next: () => void 0,
490
+ take: () => [],
77
491
  peek: () => [],
78
492
  size: 0,
493
+ consumedCount: 0,
494
+ wasIdle: false,
79
495
  clear: () => {
80
- }
496
+ },
497
+ renderTime: 0,
498
+ reckonTime: 0
499
+ // stays 0 — an unknown session / input-less room has no clock to resolve against
81
500
  });
82
501
  export {
83
502
  InputAccessorImpl,
84
503
  InputBufferImpl,
85
- NO_OP_INPUT_ACCESSOR
504
+ NO_OP_INPUT_ACCESSOR,
505
+ compileSanitizer,
506
+ validateSubSteps
86
507
  };
@@ -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';\n\n/**\n * Names of fields on `I` whose values are `number` \u2014 used by\n * `Room.defineInput()` to constrain `seqField` to actually-numeric fields\n * on the input schema. Filters out booleans, strings, methods, etc.\n */\nexport type NumericFieldsOf<I> = {\n [K in keyof I]: I[K] extends number ? (K extends string ? K : never) : never;\n}[keyof I];\n\n/**\n * Internal: input configuration captured by `Room.defineInput()`. The schema\n * constructor is stored here so the runtime doesn't need to know it through\n * the public `room.input` (which is now a callable accessor).\n *\n * @internal\n */\nexport interface InputOptions {\n /**\n * Schema constructor used to allocate per-client input instances on join.\n * Captured by `defineInput()` from its `type` argument.\n *\n * Typed loosely (`new () => any`) to sidestep type-identity issues across\n * duplicate `@colyseus/schema` installs; the runtime calls\n * `instance.clone()` and friends, which match by shape.\n */\n ctor: new () => any;\n\n /**\n * Name of a monotonically-increasing numeric field on the input schema used\n * to order and dedupe incoming frames. When set, the framework:\n * - Drops redundant frames (`input[seqField]` \u2264 the last-seen value are\n * discarded before they enter the buffer). Matches the unreliable-mode\n * ring-redundancy pattern out of the box.\n * - Powers `room.input(sessionId).at(value)` lookups.\n *\n * Despite the name, \"seq\" here is broader than an integer counter \u2014 any\n * monotonic numeric field works:\n * - **Sequence counter** (`\"seq\"`, `\"tick\"`, `\"frame\"`) \u2014 typical for\n * lockstep / rollback netcode (Photon Quantum, GGPO).\n * - **Timestamp** (`\"timestamp\"`, milliseconds or seconds) \u2014 useful for\n * variable-rate clients, lag compensation, hit registration (Unreal CMC\n * uses float-seconds timestamps via `FSavedMove_Character.TimeStamp`).\n *\n * Whichever you use, the field must increase monotonically across frames\n * for dedupe to work.\n */\n seqField?: string;\n\n /**\n * > 0 enables per-client buffering of cloned snapshots \u2014 required for\n * `room.input(sessionId).drain() / .peek() / .at()` to return populated\n * data. Oldest drops on overflow. Set to `0` to disable (`.latest` still\n * works).\n */\n bufferMaxSize: number;\n}\n\n/**\n * Per-client input accessor returned by `room.input(sessionId)`. Combines the\n * latest decoded instance with the (optional) snapshot ring buffer.\n *\n * - {@link latest} \u2014 the bound Schema instance, mutated in place by the\n * decoder. Cheapest read; use when only the most recent state matters.\n * - {@link drain} / {@link peek} / {@link at} \u2014 populated when\n * `defineInput()` was called with `bufferMaxSize > 0` (default 32).\n * Use for rollback netcode / lockstep where every frame matters.\n *\n * Returned for unknown sessionIds and rooms without `defineInput()` is a\n * frozen no-op accessor (latest=undefined, drain/peek=[], at=undefined,\n * size=0, clear=no-op).\n */\nexport interface InputAccessor<I = any> {\n /** Latest decoded input. `undefined` when unknown sessionId or no input declared. */\n readonly latest: I | undefined;\n\n /**\n * Find the buffered snapshot whose `[seqField]` equals `value`. The field\n * name is the Room's `defineInput()` `seqField`. Linear scan \u2014 cheap for\n * typical buffer sizes; not intended for very large rings. Returns\n * `undefined` when no match is buffered (or `seqField` isn't configured).\n *\n * Useful for tick-aligned retrieval (lockstep, rollback).\n */\n at(value: number): I | undefined;\n\n /** Take everything buffered (oldest \u2192 newest) and clear. Snapshots are safe to retain. */\n drain(): I[];\n\n /** Read everything buffered without consuming. */\n peek(): I[];\n\n /** Number of snapshots currently buffered. */\n readonly size: number;\n\n /** Drop all buffered snapshots (also resets the dedupe tracker). */\n clear(): void;\n}\n\n/**\n * Callable returned by `Room.defineInput()`. Assign it to `this.input` and\n * call `room.input(sessionId)` per tick to read each client's latest input\n * and/or buffered snapshots.\n */\nexport type InputAPI<I = any> = (sessionId: string) => InputAccessor<I>;\n\n/** @internal */\nexport class InputBufferImpl<I = any> {\n private _items: I[] = [];\n private _lastSeq: number = -Infinity;\n private readonly _maxSize: number;\n private readonly _seqField: string | undefined;\n\n constructor(maxSize: number, seqField: string | undefined) {\n this._maxSize = maxSize;\n this._seqField = seqField;\n }\n\n push(snapshot: I): void {\n this._items.push(snapshot);\n if (this._items.length > this._maxSize) { this._items.shift(); }\n }\n\n /** Returns true if `value` hasn't been seen, and updates the last-seen marker. */\n accept(value: number): boolean {\n if (value <= this._lastSeq) { return false; }\n this._lastSeq = value;\n return true;\n }\n\n drain(): I[] {\n const out = this._items;\n this._items = [];\n return out;\n }\n\n peek(): I[] {\n return this._items.slice();\n }\n\n at(value: number): I | undefined {\n if (this._seqField === undefined) { return undefined; }\n for (let i = 0; i < this._items.length; i++) {\n if ((this._items[i] as any)[this._seqField] === value) { return this._items[i]; }\n }\n return undefined;\n }\n\n get size(): number {\n return this._items.length;\n }\n\n clear(): void {\n this._items.length = 0;\n this._lastSeq = -Infinity;\n }\n}\n\n/**\n * Default per-client accessor. Reads `_input` and `_inputBuffer` off the\n * client at access time \u2014 both are nullable until the room declares input\n * via `defineInput()`. Cached as `client._inputAccessor` at join, so\n * `room.input(sessionId)` is a Map lookup + property read.\n *\n * @internal\n */\nexport class InputAccessorImpl<I = any> implements InputAccessor<I> {\n private _client: ClientPrivate;\n constructor(client: ClientPrivate) { this._client = client; }\n get latest(): I | undefined { return this._client._input as I | undefined; }\n at(value: number): I | undefined { return this._client._inputBuffer?.at(value) as I | undefined; }\n drain(): I[] { return (this._client._inputBuffer?.drain() ?? []) as I[]; }\n peek(): I[] { return (this._client._inputBuffer?.peek() ?? []) as I[]; }\n get size(): number { return this._client._inputBuffer?.size ?? 0; }\n clear(): void { this._client._inputBuffer?.clear(); }\n}\n\n/**\n * Returned by `room.input(sessionId)` for unknown sessions and for rooms\n * that didn't call `defineInput()`.\n *\n * @internal\n */\nexport const NO_OP_INPUT_ACCESSOR: InputAccessor<any> = Object.freeze({\n latest: undefined,\n at: () => undefined,\n drain: () => [],\n peek: () => [],\n size: 0,\n clear: () => {},\n});\n"],
5
- "mappings": ";AA4GO,IAAM,kBAAN,MAA+B;AAAA,EAMpC,YAAY,SAAiB,UAA8B;AAL3D,SAAQ,SAAc,CAAC;AACvB,SAAQ,WAAmB;AAKzB,SAAK,WAAW;AAChB,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,KAAK,UAAmB;AACtB,SAAK,OAAO,KAAK,QAAQ;AACzB,QAAI,KAAK,OAAO,SAAS,KAAK,UAAU;AAAE,WAAK,OAAO,MAAM;AAAA,IAAG;AAAA,EACjE;AAAA;AAAA,EAGA,OAAO,OAAwB;AAC7B,QAAI,SAAS,KAAK,UAAU;AAAE,aAAO;AAAA,IAAO;AAC5C,SAAK,WAAW;AAChB,WAAO;AAAA,EACT;AAAA,EAEA,QAAa;AACX,UAAM,MAAM,KAAK;AACjB,SAAK,SAAS,CAAC;AACf,WAAO;AAAA,EACT;AAAA,EAEA,OAAY;AACV,WAAO,KAAK,OAAO,MAAM;AAAA,EAC3B;AAAA,EAEA,GAAG,OAA8B;AAC/B,QAAI,KAAK,cAAc,QAAW;AAAE,aAAO;AAAA,IAAW;AACtD,aAAS,IAAI,GAAG,IAAI,KAAK,OAAO,QAAQ,KAAK;AAC3C,UAAK,KAAK,OAAO,CAAC,EAAU,KAAK,SAAS,MAAM,OAAO;AAAE,eAAO,KAAK,OAAO,CAAC;AAAA,MAAG;AAAA,IAClF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,IAAI,OAAe;AACjB,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA,EAEA,QAAc;AACZ,SAAK,OAAO,SAAS;AACrB,SAAK,WAAW;AAAA,EAClB;AACF;AAUO,IAAM,oBAAN,MAA6D;AAAA,EAElE,YAAY,QAAuB;AAAE,SAAK,UAAU;AAAA,EAAQ;AAAA,EAC5D,IAAI,SAAwB;AAAE,WAAO,KAAK,QAAQ;AAAA,EAAyB;AAAA,EAC3E,GAAG,OAA8B;AAAE,WAAO,KAAK,QAAQ,cAAc,GAAG,KAAK;AAAA,EAAoB;AAAA,EACjG,QAAa;AAAE,WAAQ,KAAK,QAAQ,cAAc,MAAM,KAAK,CAAC;AAAA,EAAW;AAAA,EACzE,OAAY;AAAE,WAAQ,KAAK,QAAQ,cAAc,KAAK,KAAK,CAAC;AAAA,EAAW;AAAA,EACvE,IAAI,OAAe;AAAE,WAAO,KAAK,QAAQ,cAAc,QAAQ;AAAA,EAAG;AAAA,EAClE,QAAc;AAAE,SAAK,QAAQ,cAAc,MAAM;AAAA,EAAG;AACtD;AAQO,IAAM,uBAA2C,OAAO,OAAO;AAAA,EACpE,QAAQ;AAAA,EACR,IAAI,MAAM;AAAA,EACV,OAAO,MAAM,CAAC;AAAA,EACd,MAAM,MAAM,CAAC;AAAA,EACb,MAAM;AAAA,EACN,OAAO,MAAM;AAAA,EAAC;AAChB,CAAC;",
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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",
6
6
  "names": []
7
7
  }