@axpecter/lync 2.3.3 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +302 -299
  3. package/Types.luau +215 -0
  4. package/api/Define.luau +154 -0
  5. package/api/Lifecycle.luau +119 -0
  6. package/api/Packet.luau +108 -0
  7. package/api/Query.luau +149 -0
  8. package/api/Set.luau +189 -0
  9. package/channel/Blocks.luau +124 -0
  10. package/channel/Group.luau +118 -0
  11. package/channel/Link.luau +135 -0
  12. package/channel/Outbox.luau +261 -0
  13. package/channel/Recipients.luau +129 -0
  14. package/channel/Transport.luau +138 -0
  15. package/codec/Base.luau +236 -0
  16. package/codec/Composites.luau +128 -0
  17. package/codec/Datatypes.luau +162 -0
  18. package/codec/Legality.luau +61 -0
  19. package/codec/Scalars.luau +72 -0
  20. package/codec/Text.luau +89 -0
  21. package/codec/init.luau +39 -0
  22. package/index.d.ts +285 -0
  23. package/init.luau +91 -0
  24. package/kernel/Bits.luau +16 -0
  25. package/kernel/Catalog.luau +121 -0
  26. package/kernel/Heap.luau +80 -0
  27. package/kernel/Listeners.luau +95 -0
  28. package/kernel/Log.luau +197 -0
  29. package/kernel/Tally.luau +128 -0
  30. package/kernel/Threads.luau +44 -0
  31. package/kernel/Wide.luau +267 -0
  32. package/package.json +15 -32
  33. package/runtime/Boundary.luau +180 -0
  34. package/runtime/Dispatch.luau +91 -0
  35. package/runtime/Handles.luau +114 -0
  36. package/runtime/Handshake.luau +90 -0
  37. package/runtime/Pending.luau +196 -0
  38. package/runtime/Router.luau +379 -0
  39. package/runtime/Session.luau +731 -0
  40. package/shape/Compile.luau +340 -0
  41. package/shape/Folds.luau +332 -0
  42. package/shape/Frames.luau +170 -0
  43. package/shape/Grid.luau +169 -0
  44. package/shape/Ir.luau +540 -0
  45. package/shape/Joins.luau +125 -0
  46. package/shape/Legality.luau +273 -0
  47. package/shape/Lower.luau +419 -0
  48. package/shape/Order.luau +64 -0
  49. package/shape/Regions.luau +113 -0
  50. package/shape/Rotation.luau +472 -0
  51. package/shape/Serial.luau +258 -0
  52. package/shape/Words.luau +89 -0
  53. package/shape/emit/Block.luau +453 -0
  54. package/shape/emit/Column.luau +338 -0
  55. package/shape/emit/Leaf.luau +462 -0
  56. package/shape/emit/Word.luau +234 -0
  57. package/shape/emit/init.luau +222 -0
  58. package/shape/init.luau +78 -0
  59. package/state/Apply.luau +330 -0
  60. package/state/Audience.luau +126 -0
  61. package/state/Catchup.luau +218 -0
  62. package/state/Coalesce.luau +43 -0
  63. package/state/Fieldset.luau +177 -0
  64. package/state/Newest.luau +339 -0
  65. package/state/Plan.luau +441 -0
  66. package/state/Sections.luau +468 -0
  67. package/state/Store.luau +188 -0
  68. package/state/Sync.luau +762 -0
  69. package/wire/Bytes.luau +118 -0
  70. package/wire/Clock.luau +37 -0
  71. package/wire/Protocol.luau +110 -0
  72. package/wire/Sequence.luau +40 -0
  73. package/wire/Varint.luau +124 -0
  74. package/src/Types.luau +0 -180
  75. package/src/api/Group.luau +0 -156
  76. package/src/api/Packet.luau +0 -317
  77. package/src/api/Query.luau +0 -338
  78. package/src/api/Scope.luau +0 -90
  79. package/src/api/Signal.luau +0 -193
  80. package/src/codec/Base.luau +0 -136
  81. package/src/codec/composite/Array.luau +0 -473
  82. package/src/codec/composite/Map.luau +0 -485
  83. package/src/codec/composite/Optional.luau +0 -54
  84. package/src/codec/composite/Shared.luau +0 -311
  85. package/src/codec/composite/Struct.luau +0 -494
  86. package/src/codec/composite/Tagged.luau +0 -103
  87. package/src/codec/composite/Tuple.luau +0 -143
  88. package/src/codec/datatype/Buffer.luau +0 -66
  89. package/src/codec/datatype/CFrame.luau +0 -109
  90. package/src/codec/datatype/Color.luau +0 -32
  91. package/src/codec/datatype/Instance.luau +0 -52
  92. package/src/codec/datatype/IntVector.luau +0 -39
  93. package/src/codec/datatype/NumberRange.luau +0 -39
  94. package/src/codec/datatype/Ray.luau +0 -36
  95. package/src/codec/datatype/Rect.luau +0 -31
  96. package/src/codec/datatype/Region.luau +0 -62
  97. package/src/codec/datatype/Sequence.luau +0 -184
  98. package/src/codec/datatype/String.luau +0 -94
  99. package/src/codec/datatype/UDim.luau +0 -46
  100. package/src/codec/datatype/Vector.luau +0 -130
  101. package/src/codec/meta/Auto.luau +0 -318
  102. package/src/codec/meta/Bitfield.luau +0 -160
  103. package/src/codec/meta/Custom.luau +0 -33
  104. package/src/codec/meta/DeltaScalar.luau +0 -390
  105. package/src/codec/meta/Enum.luau +0 -50
  106. package/src/codec/meta/Float.luau +0 -38
  107. package/src/codec/meta/Nothing.luau +0 -13
  108. package/src/codec/meta/Unknown.luau +0 -41
  109. package/src/codec/primitive/Bool.luau +0 -26
  110. package/src/codec/primitive/Float16.luau +0 -99
  111. package/src/codec/primitive/Int.luau +0 -59
  112. package/src/codec/primitive/Number.luau +0 -24
  113. package/src/codec/primitive/Signed.luau +0 -32
  114. package/src/codec/primitive/Varint.luau +0 -165
  115. package/src/codec/primitive/Zint.luau +0 -99
  116. package/src/index.d.ts +0 -423
  117. package/src/init.luau +0 -362
  118. package/src/internal/Baseline.luau +0 -57
  119. package/src/internal/Channel.luau +0 -484
  120. package/src/internal/Middleware.luau +0 -100
  121. package/src/internal/Pool.luau +0 -66
  122. package/src/internal/Registry.luau +0 -119
  123. package/src/internal/Transport.luau +0 -28
  124. package/src/transport/Bridge.luau +0 -110
  125. package/src/transport/Client.luau +0 -97
  126. package/src/transport/Gate.luau +0 -488
  127. package/src/transport/Reader.luau +0 -331
  128. package/src/transport/Server.luau +0 -233
  129. package/src/util/Array.luau +0 -18
  130. package/src/util/Buffer.luau +0 -90
  131. package/src/util/Constants.luau +0 -30
  132. package/src/util/Log.luau +0 -68
  133. package/src/util/Player.luau +0 -14
  134. package/src/util/Quantize.luau +0 -84
  135. package/src/util/Quat.luau +0 -124
@@ -1,484 +0,0 @@
1
- --!strict
2
- --!native
3
- -- Per-channel buffer state with MSB batch framing, XOR delta, timestamps.
4
-
5
- local Base = require(script.Parent.Parent.codec.Base)
6
- local Buf = require(script.Parent.Parent.util.Buffer)
7
- local Constants = require(script.Parent.Parent.util.Constants)
8
- local Log = require(script.Parent.Parent.util.Log)
9
- local Types = require(script.Parent.Parent.Types)
10
- local Varint = require(script.Parent.Parent.codec.primitive.Varint)
11
-
12
- -- Constants --------------------------------------------------------------
13
-
14
- local TS_NONE = 0
15
- local TS_FRAME = 1
16
- local TS_OFFSET = 2
17
- local TS_FULL = 3
18
-
19
- local INITIAL_BUF = 1024
20
- local HEADER_BYTE = 1
21
- local U8_MAX = Constants.U8_MAX
22
- local U16_MAX = Constants.U16_MAX
23
- local MAX_BATCH_ITEMS = Constants.MAX_BATCH_ITEMS
24
-
25
- -- 65.535s window so round((clock % win) * 1000) cannot overflow u16.
26
- local OFFSET_WIN = 65.535
27
-
28
- local TS_BYTES = table.freeze({
29
- [TS_NONE] = 0,
30
- [TS_FRAME] = 1,
31
- [TS_OFFSET] = 2,
32
- [TS_FULL] = 8,
33
- })
34
-
35
- -- State ------------------------------------------------------------------
36
-
37
- local _timestampsEnabled = false
38
- local _statsEnabled = false
39
-
40
- -- Refreshed by updateTimestamp once per flush.
41
- local _frameCounter = 0
42
- local _offsetMs = 0
43
- local _clock = 0.0
44
-
45
- local EMPTY_REFS = table.freeze({}) :: { Instance }
46
-
47
- -- Private ----------------------------------------------------------------
48
-
49
- local alloc = Base.alloc
50
- local ensure = Base.ensure
51
- local band = bit32.band
52
- local bufCopy = buffer.copy
53
- local writeu8 = buffer.writeu8
54
- local writeu16 = buffer.writeu16
55
- local writef64 = buffer.writef64
56
- local round = math.round
57
- local osClock = os.clock
58
-
59
- -- id is in [1, 127] (Registry.MAX_ID), so id + 128 == bor(FRAME_MSB, id).
60
-
61
- --[[
62
- Single -> multi upgrade. Patches the previously-emitted single-item
63
- header, splices a 2-byte count cell after the timestamp, and shifts the
64
- payload right. Hot only when a packet fires twice in a row to the same
65
- channel without an intervening flush.
66
- ]]
67
- local function upgradeSingleToMulti(ch: Types.ChannelState, regId: number, headerSize: number): ()
68
- local headerPos = ch.singlePos
69
- writeu8(ch.buff, headerPos, regId)
70
-
71
- local afterTs = headerPos + headerSize
72
- local cursor = ch.cursor
73
- local payloadLen = cursor - afterTs
74
- ensure(ch, 2)
75
- -- Re-bind: ensure() may have realloced the buffer.
76
- local b = ch.buff
77
- bufCopy(b, afterTs + 2, b, afterTs, payloadLen)
78
-
79
- ch.countPos = afterTs
80
- writeu16(b, afterTs, 0)
81
- ch.cursor = cursor + 2
82
- ch.singleMode = false
83
- end
84
-
85
- -- itemCount bump is left to the caller, AFTER the body write succeeds.
86
- local function openOrUpgrade(ch: Types.ChannelState, reg: Types.Registration): ()
87
- if reg.id ~= ch.lastId or ch.itemCount >= MAX_BATCH_ITEMS then
88
- reg._openFn(ch, reg.id)
89
- elseif ch.singleMode then
90
- upgradeSingleToMulti(ch, reg.id, reg.headerSize)
91
- end
92
- end
93
-
94
- local function spliceEncoded(ch: Types.ChannelState, payload: buffer, payloadLen: number): ()
95
- local cursor = ch.cursor
96
- if cursor + payloadLen > ch.size then
97
- alloc(ch, payloadLen)
98
- end
99
- bufCopy(ch.buff, cursor, payload, 0, payloadLen)
100
- ch.cursor = cursor + payloadLen
101
- end
102
-
103
- --[[
104
- Hot-swapped by enableStats(). A throwing codec must rewind cursor + refs:
105
- the next flush sends `[0, ch.cursor)`, so a partial write past `cursor`
106
- desyncs the receiver from the truncation point onward.
107
- ]]
108
- local writeBatchPlain: (Types.ChannelState, Types.Registration, any) -> ()
109
- local writeBatchStats: (Types.ChannelState, Types.Registration, any) -> ()
110
-
111
- local function rewindOnThrow(ch: Types.ChannelState, cursor: number, refCount: number): ()
112
- ch.cursor = cursor
113
- if ch.refCount > refCount then
114
- local refs = ch.refs
115
- for i = refCount + 1, ch.refCount do
116
- refs[i] = nil
117
- end
118
- ch.refCount = refCount
119
- end
120
- end
121
-
122
- writeBatchPlain = function(ch: Types.ChannelState, reg: Types.Registration, data: any): ()
123
- openOrUpgrade(ch, reg)
124
- local rewindCursor = ch.cursor
125
- local rewindRefCount = ch.refCount
126
- local ok, err = pcall(reg.codec.write, ch, data)
127
- if not ok then
128
- rewindOnThrow(ch, rewindCursor, rewindRefCount)
129
- error(err, 0)
130
- end
131
- ch.itemCount += 1
132
- end
133
-
134
- writeBatchStats = function(ch: Types.ChannelState, reg: Types.Registration, data: any): ()
135
- openOrUpgrade(ch, reg)
136
- local rewindCursor = ch.cursor
137
- local rewindRefCount = ch.refCount
138
- local ok, err = pcall(reg.codec.write, ch, data)
139
- if not ok then
140
- rewindOnThrow(ch, rewindCursor, rewindRefCount)
141
- error(err, 0)
142
- end
143
- ch.itemCount += 1
144
- reg.bytesSent += ch.cursor - rewindCursor
145
- end
146
-
147
- --[[
148
- Splice a pre-encoded payload into the channel. Used by Packet.broadcast
149
- to encode the codec output once per fanout instead of N times.
150
- ]]
151
- local writeBatchEncodedPlain: (Types.ChannelState, Types.Registration, buffer, number) -> ()
152
- local writeBatchEncodedStats: (Types.ChannelState, Types.Registration, buffer, number) -> ()
153
-
154
- writeBatchEncodedPlain = function(
155
- ch: Types.ChannelState,
156
- reg: Types.Registration,
157
- payload: buffer,
158
- payloadLen: number
159
- ): ()
160
- openOrUpgrade(ch, reg)
161
- local rewindCursor = ch.cursor
162
- local ok, err = pcall(spliceEncoded, ch, payload, payloadLen)
163
- if not ok then
164
- ch.cursor = rewindCursor
165
- error(err, 0)
166
- end
167
- ch.itemCount += 1
168
- end
169
-
170
- writeBatchEncodedStats = function(
171
- ch: Types.ChannelState,
172
- reg: Types.Registration,
173
- payload: buffer,
174
- payloadLen: number
175
- ): ()
176
- openOrUpgrade(ch, reg)
177
- local rewindCursor = ch.cursor
178
- local ok, err = pcall(spliceEncoded, ch, payload, payloadLen)
179
- if not ok then
180
- ch.cursor = rewindCursor
181
- error(err, 0)
182
- end
183
- ch.itemCount += 1
184
- reg.bytesSent += payloadLen
185
- end
186
-
187
- -- Public -----------------------------------------------------------------
188
-
189
- -- Module table is intentionally unfrozen: enableStats() hot-swaps writeBatch.
190
- local Channel = {}
191
-
192
- Channel.TS_NONE = TS_NONE
193
- Channel.TS_FRAME = TS_FRAME
194
- Channel.TS_OFFSET = TS_OFFSET
195
- Channel.TS_FULL = TS_FULL
196
-
197
- function Channel.create(): Types.ChannelState
198
- return {
199
- buff = buffer.create(INITIAL_BUF),
200
- cursor = 0,
201
- size = INITIAL_BUF,
202
- refs = {},
203
- refCount = 0,
204
- lastId = -1,
205
- countPos = -1,
206
- itemCount = 0,
207
- singleMode = false,
208
- singlePos = 0,
209
- deltas = {},
210
- prevDump = nil,
211
- prevDumpLen = 0,
212
- currentPacket = nil,
213
- }
214
- end
215
-
216
- function Channel.writeByte(ch: Types.ChannelState, byte: number): ()
217
- local cursor = ch.cursor
218
- if cursor + HEADER_BYTE > ch.size then
219
- alloc(ch, HEADER_BYTE)
220
- end
221
- writeu8(ch.buff, cursor, byte)
222
- ch.cursor = cursor + HEADER_BYTE
223
- end
224
-
225
- -- Append `value` to the sidecar refs, write its 1-based u16 index.
226
- function Channel.pushRef(ch: Types.ChannelState, value: any): ()
227
- local idx = ch.refCount + 1
228
- Log.assert(idx <= U16_MAX, "ref overflow, max 65535")
229
- ch.refs[idx] = value
230
- ch.refCount = idx
231
-
232
- local cursor = ch.cursor
233
- if cursor + 2 > ch.size then
234
- alloc(ch, 2)
235
- end
236
- writeu16(ch.buff, cursor, idx)
237
- ch.cursor = cursor + 2
238
- end
239
-
240
- --[[
241
- Per-flush reset. Preserves prevDump/prevDumpLen (XOR baseline) and
242
- ch.deltas (delta caches): both must outlive flushes for the same owner.
243
- ]]
244
- function Channel.reset(ch: Types.ChannelState): ()
245
- ch.cursor = 0
246
- ch.lastId = -1
247
- ch.countPos = -1
248
- ch.itemCount = 0
249
- ch.singleMode = false
250
- ch.singlePos = 0
251
- ch.currentPacket = nil
252
- if ch.refCount > 0 then
253
- table.clear(ch.refs)
254
- ch.refCount = 0
255
- end
256
- end
257
-
258
- --[[
259
- Full reset including XOR baseline and delta caches. Pool.acquire calls
260
- this so a recycled state cannot leak the previous owner's baseline.
261
- ]]
262
- function Channel.fullReset(ch: Types.ChannelState): ()
263
- Channel.reset(ch)
264
- ch.prevDump = nil
265
- ch.prevDumpLen = 0
266
- if next(ch.deltas) ~= nil then
267
- table.clear(ch.deltas)
268
- end
269
- end
270
-
271
- --[[
272
- Per-mode openFn variants. Each seals the prior batch, writes the
273
- single-mode header [0x80 | id] + optional timestamp, and records the
274
- cursor so writeBatch can upgrade single -> multi later.
275
- ]]
276
- local function makeOpenNoTs(name: string): (Types.ChannelState, number) -> ()
277
- return function(ch: Types.ChannelState, id: number): ()
278
- if ch.lastId >= 0 and not ch.singleMode then
279
- writeu16(ch.buff, ch.countPos, ch.itemCount)
280
- end
281
- ch.currentPacket = name
282
- ch.lastId = id
283
- ch.itemCount = 0
284
- local cursor = ch.cursor
285
- if cursor + 1 > ch.size then
286
- alloc(ch, 1)
287
- end
288
- writeu8(ch.buff, cursor, id + 128)
289
- ch.singleMode = true
290
- ch.singlePos = cursor
291
- ch.cursor = cursor + 1
292
- end
293
- end
294
-
295
- local function makeOpenFrameTs(name: string): (Types.ChannelState, number) -> ()
296
- return function(ch: Types.ChannelState, id: number): ()
297
- if ch.lastId >= 0 and not ch.singleMode then
298
- writeu16(ch.buff, ch.countPos, ch.itemCount)
299
- end
300
- ch.currentPacket = name
301
- ch.lastId = id
302
- ch.itemCount = 0
303
- local cursor = ch.cursor
304
- if cursor + 2 > ch.size then
305
- alloc(ch, 2)
306
- end
307
- local b = ch.buff
308
- writeu8(b, cursor, id + 128)
309
- writeu8(b, cursor + 1, _frameCounter)
310
- ch.singleMode = true
311
- ch.singlePos = cursor
312
- ch.cursor = cursor + 2
313
- end
314
- end
315
-
316
- local function makeOpenOffsetTs(name: string): (Types.ChannelState, number) -> ()
317
- return function(ch: Types.ChannelState, id: number): ()
318
- if ch.lastId >= 0 and not ch.singleMode then
319
- writeu16(ch.buff, ch.countPos, ch.itemCount)
320
- end
321
- ch.currentPacket = name
322
- ch.lastId = id
323
- ch.itemCount = 0
324
- local cursor = ch.cursor
325
- if cursor + 3 > ch.size then
326
- alloc(ch, 3)
327
- end
328
- local b = ch.buff
329
- writeu8(b, cursor, id + 128)
330
- writeu16(b, cursor + 1, _offsetMs)
331
- ch.singleMode = true
332
- ch.singlePos = cursor
333
- ch.cursor = cursor + 3
334
- end
335
- end
336
-
337
- local function makeOpenFullTs(name: string): (Types.ChannelState, number) -> ()
338
- return function(ch: Types.ChannelState, id: number): ()
339
- if ch.lastId >= 0 and not ch.singleMode then
340
- writeu16(ch.buff, ch.countPos, ch.itemCount)
341
- end
342
- ch.currentPacket = name
343
- ch.lastId = id
344
- ch.itemCount = 0
345
- local cursor = ch.cursor
346
- if cursor + 9 > ch.size then
347
- alloc(ch, 9)
348
- end
349
- local b = ch.buff
350
- writeu8(b, cursor, id + 128)
351
- writef64(b, cursor + 1, _clock)
352
- ch.singleMode = true
353
- ch.singlePos = cursor
354
- ch.cursor = cursor + 9
355
- end
356
- end
357
-
358
- function Channel.resolveOpenFn(
359
- timestampMode: number,
360
- name: string
361
- ): (ch: Types.ChannelState, id: number) -> ()
362
- if timestampMode == TS_NONE then
363
- return makeOpenNoTs(name)
364
- end
365
- if timestampMode == TS_FRAME then
366
- return makeOpenFrameTs(name)
367
- end
368
- if timestampMode == TS_OFFSET then
369
- return makeOpenOffsetTs(name)
370
- end
371
- return makeOpenFullTs(name)
372
- end
373
-
374
- Channel.writeBatch = writeBatchPlain
375
- Channel.writeBatchEncoded = writeBatchEncodedPlain
376
-
377
- --[[
378
- Every caller (Server.flushReliable / flushUnreliable, Client.flush)
379
- pre-checks `ch.cursor > 0`, so cursor here is always positive.
380
- ]]
381
- function Channel.sealAndDump(ch: Types.ChannelState): (buffer, { Instance }, number, number)
382
- if ch.lastId >= 0 and not ch.singleMode then
383
- writeu16(ch.buff, ch.countPos, ch.itemCount)
384
- end
385
-
386
- local cursor = ch.cursor
387
- local snapshot = buffer.create(cursor)
388
- bufCopy(snapshot, 0, ch.buff, 0, cursor)
389
-
390
- local refCount = ch.refCount
391
- local refs = if refCount > 0 then table.clone(ch.refs) else EMPTY_REFS
392
- return snapshot, refs, cursor, refCount
393
- end
394
-
395
- Channel.xorApply = Buf.xorApply
396
-
397
- --[[
398
- Query frame. MSB on the id byte = nil response; cleared = payload follows.
399
- Correlation IDs are varint-encoded.
400
- ]]
401
- function Channel.writeQuery(
402
- ch: Types.ChannelState,
403
- id: number,
404
- correlationId: number,
405
- codec: Types.InternalCodec<any>?,
406
- data: any,
407
- name: string?
408
- ): ()
409
- if ch.lastId >= 0 and not ch.singleMode then
410
- writeu16(ch.buff, ch.countPos, ch.itemCount)
411
- end
412
- --[[
413
- lastId = -1 forces the next writeBatch to call _openFn (which resets
414
- singleMode). Every other reader of singleMode (writeQuery,
415
- sealAndDump) is gated by `lastId >= 0` first, so stamping singleMode
416
- here would never be observed before the next reset overwrites it.
417
- ]]
418
- ch.lastId = -1
419
- -- Without this, an alloc overflow inside the payload codec would log
420
- -- the previous packet's name (whichever _openFn last touched currentPacket).
421
- if name then
422
- ch.currentPacket = name
423
- end
424
-
425
- local hasPayload = codec ~= nil and data ~= nil
426
- local headerByte = if hasPayload then id else id + 128
427
-
428
- local cursor = ch.cursor
429
- if cursor + 1 > ch.size then
430
- alloc(ch, 1)
431
- end
432
- writeu8(ch.buff, cursor, headerByte)
433
- ch.cursor = cursor + 1
434
-
435
- Varint.write(ch, correlationId)
436
- if hasPayload then
437
- (codec :: Types.InternalCodec<any>).write(ch, data)
438
- end
439
- end
440
-
441
- -- Configuration ----------------------------------------------------------
442
-
443
- function Channel.setMaxSize(bytes: number): ()
444
- Base.setMaxSize(bytes)
445
- end
446
-
447
- function Channel.updateTimestamp(): ()
448
- _frameCounter = band(_frameCounter + 1, U8_MAX)
449
- local now = osClock()
450
- _offsetMs = round((now % OFFSET_WIN) * 1000)
451
- _clock = now
452
- end
453
-
454
- function Channel.enableTimestamps(): ()
455
- _timestampsEnabled = true
456
- end
457
-
458
- function Channel.hasTimestamps(): boolean
459
- return _timestampsEnabled
460
- end
461
-
462
- function Channel.enableStats(): ()
463
- _statsEnabled = true
464
- Channel.writeBatch = writeBatchStats
465
- Channel.writeBatchEncoded = writeBatchEncodedStats
466
- end
467
-
468
- function Channel.statsEnabled(): boolean
469
- return _statsEnabled
470
- end
471
-
472
- -- Module-level config + timestamp counters. Distinct from Channel.reset(ch).
473
- function Channel.resetGlobals(): ()
474
- Base.reset()
475
- _timestampsEnabled = false
476
- _statsEnabled = false
477
- _frameCounter = 0
478
- _offsetMs = 0
479
- _clock = 0.0
480
- Channel.writeBatch = writeBatchPlain
481
- Channel.writeBatchEncoded = writeBatchEncodedPlain
482
- end
483
-
484
- return Channel
@@ -1,100 +0,0 @@
1
- --!strict
2
- --!optimize 2
3
- -- Ordered intercept chains for send/receive/drop hooks.
4
-
5
- local Signal = require(script.Parent.Parent.api.Signal)
6
- local Types = require(script.Parent.Parent.Types)
7
-
8
- -- State ------------------------------------------------------------------
9
-
10
- local _sendSignal = Signal.create()
11
- local _receiveSignal = Signal.create()
12
- local _dropSignal = Signal.create()
13
-
14
- -- Private ----------------------------------------------------------------
15
-
16
- -- Mirrors Signal's STATE_DISCONNECTED. Entries in this state are mid-fire disconnects.
17
- local STATE_DISCONNECTED = 0
18
-
19
- --[[
20
- Snapshot before iterating: a handler can disconnect itself or others,
21
- and a fresh chain pull would skip or double-fire entries depending on
22
- swap direction. Snapshotting locks the per-frame view.
23
- ]]
24
- local function runChain(
25
- signal: { _count: number, _entries: { any } },
26
- value: any,
27
- name: string,
28
- player: Player?
29
- ): any
30
- local count = signal._count
31
- if count == 0 then
32
- return value
33
- end
34
-
35
- local entries = signal._entries
36
- local snapshot = table.create(count)
37
- table.move(entries, 1, count, 1, snapshot)
38
-
39
- local result = value
40
- for i = 1, count do
41
- local entry = snapshot[i]
42
- if entry._state ~= STATE_DISCONNECTED then
43
- local returned = entry.fn(result, name, player)
44
- -- Hooks return non-nil to replace the value; nil keeps the prior result.
45
- if returned ~= nil then
46
- result = returned
47
- end
48
- end
49
- end
50
- return result
51
- end
52
-
53
- -- Public -----------------------------------------------------------------
54
-
55
- local Middleware = {}
56
-
57
- function Middleware.onSend(fn: (data: any, name: string, player: Player?) -> any): Types.Connection
58
- return _sendSignal:connect(fn)
59
- end
60
-
61
- function Middleware.onReceive(
62
- fn: (data: any, name: string, player: Player?) -> any
63
- ): Types.Connection
64
- return _receiveSignal:connect(fn)
65
- end
66
-
67
- function Middleware.onDrop(
68
- fn: (player: Player, reason: string, name: string, data: any?) -> ()
69
- ): Types.Connection
70
- return _dropSignal:connect(fn)
71
- end
72
-
73
- function Middleware.runSend(data: any, name: string, player: Player?): any
74
- return runChain(_sendSignal, data, name, player)
75
- end
76
-
77
- function Middleware.runReceive(data: any, name: string, player: Player?): any
78
- return runChain(_receiveSignal, data, name, player)
79
- end
80
-
81
- function Middleware.fireDrop(player: Player, reason: string, name: string, data: any?): ()
82
- _dropSignal:fireSync(player, reason, name, data)
83
- end
84
-
85
- -- Hot-path callers gate on these to skip runChain's snapshot allocation.
86
- function Middleware.hasSendHooks(): boolean
87
- return _sendSignal:hasListeners()
88
- end
89
-
90
- function Middleware.hasReceiveHooks(): boolean
91
- return _receiveSignal:hasListeners()
92
- end
93
-
94
- function Middleware.reset(): ()
95
- _sendSignal = Signal.create()
96
- _receiveSignal = Signal.create()
97
- _dropSignal = Signal.create()
98
- end
99
-
100
- return table.freeze(Middleware)
@@ -1,66 +0,0 @@
1
- --!strict
2
- --!optimize 2
3
- -- ChannelState pool. Acquire fullResets to wipe prior owner state.
4
-
5
- local Channel = require(script.Parent.Channel)
6
- local Log = require(script.Parent.Parent.util.Log)
7
- local Types = require(script.Parent.Parent.Types)
8
-
9
- -- Constants --------------------------------------------------------------
10
-
11
- local DEFAULT_SIZE = 16
12
-
13
- -- State ------------------------------------------------------------------
14
-
15
- local _stack: { Types.ChannelState } = {}
16
- local _depth = 0
17
- local _maxSize = DEFAULT_SIZE
18
-
19
- -- Public -----------------------------------------------------------------
20
-
21
- local Pool = {}
22
-
23
- function Pool.acquire(): Types.ChannelState
24
- if _depth > 0 then
25
- local ch = _stack[_depth]
26
- _stack[_depth] = nil
27
- _depth -= 1
28
- return ch
29
- end
30
- return Channel.create()
31
- end
32
-
33
- -- Scrub on release so the pool never holds the prior owner's Instance refs
34
- -- or XOR baseline buffer; otherwise a player who disconnects mid-frame leaves
35
- -- those rooted in the pool until the slot is re-acquired (potentially never).
36
- function Pool.release(ch: Types.ChannelState): ()
37
- if _depth >= _maxSize then
38
- return
39
- end
40
- Channel.fullReset(ch)
41
- _depth += 1
42
- _stack[_depth] = ch
43
- end
44
-
45
- function Pool.setMaxSize(size: number): ()
46
- if size < 0 then
47
- Log.error(`size must be non-negative, got {size}`)
48
- end
49
- _maxSize = size
50
- while _depth > _maxSize do
51
- _stack[_depth] = nil
52
- _depth -= 1
53
- end
54
- end
55
-
56
- function Pool.count(): number
57
- return _depth
58
- end
59
-
60
- function Pool.reset(): ()
61
- table.clear(_stack)
62
- _depth = 0
63
- _maxSize = DEFAULT_SIZE
64
- end
65
-
66
- return table.freeze(Pool)