@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.
- package/LICENSE +21 -0
- package/README.md +302 -299
- package/Types.luau +215 -0
- package/api/Define.luau +154 -0
- package/api/Lifecycle.luau +119 -0
- package/api/Packet.luau +108 -0
- package/api/Query.luau +149 -0
- package/api/Set.luau +189 -0
- package/channel/Blocks.luau +124 -0
- package/channel/Group.luau +118 -0
- package/channel/Link.luau +135 -0
- package/channel/Outbox.luau +261 -0
- package/channel/Recipients.luau +129 -0
- package/channel/Transport.luau +138 -0
- package/codec/Base.luau +236 -0
- package/codec/Composites.luau +128 -0
- package/codec/Datatypes.luau +162 -0
- package/codec/Legality.luau +61 -0
- package/codec/Scalars.luau +72 -0
- package/codec/Text.luau +89 -0
- package/codec/init.luau +39 -0
- package/index.d.ts +285 -0
- package/init.luau +91 -0
- package/kernel/Bits.luau +16 -0
- package/kernel/Catalog.luau +121 -0
- package/kernel/Heap.luau +80 -0
- package/kernel/Listeners.luau +95 -0
- package/kernel/Log.luau +197 -0
- package/kernel/Tally.luau +128 -0
- package/kernel/Threads.luau +44 -0
- package/kernel/Wide.luau +267 -0
- package/package.json +15 -32
- package/runtime/Boundary.luau +180 -0
- package/runtime/Dispatch.luau +91 -0
- package/runtime/Handles.luau +114 -0
- package/runtime/Handshake.luau +90 -0
- package/runtime/Pending.luau +196 -0
- package/runtime/Router.luau +379 -0
- package/runtime/Session.luau +731 -0
- package/shape/Compile.luau +340 -0
- package/shape/Folds.luau +332 -0
- package/shape/Frames.luau +170 -0
- package/shape/Grid.luau +169 -0
- package/shape/Ir.luau +540 -0
- package/shape/Joins.luau +125 -0
- package/shape/Legality.luau +273 -0
- package/shape/Lower.luau +419 -0
- package/shape/Order.luau +64 -0
- package/shape/Regions.luau +113 -0
- package/shape/Rotation.luau +472 -0
- package/shape/Serial.luau +258 -0
- package/shape/Words.luau +89 -0
- package/shape/emit/Block.luau +453 -0
- package/shape/emit/Column.luau +338 -0
- package/shape/emit/Leaf.luau +462 -0
- package/shape/emit/Word.luau +234 -0
- package/shape/emit/init.luau +222 -0
- package/shape/init.luau +78 -0
- package/state/Apply.luau +330 -0
- package/state/Audience.luau +126 -0
- package/state/Catchup.luau +218 -0
- package/state/Coalesce.luau +43 -0
- package/state/Fieldset.luau +177 -0
- package/state/Newest.luau +339 -0
- package/state/Plan.luau +441 -0
- package/state/Sections.luau +468 -0
- package/state/Store.luau +188 -0
- package/state/Sync.luau +762 -0
- package/wire/Bytes.luau +118 -0
- package/wire/Clock.luau +37 -0
- package/wire/Protocol.luau +110 -0
- package/wire/Sequence.luau +40 -0
- package/wire/Varint.luau +124 -0
- package/src/Types.luau +0 -180
- package/src/api/Group.luau +0 -156
- package/src/api/Packet.luau +0 -317
- package/src/api/Query.luau +0 -338
- package/src/api/Scope.luau +0 -90
- package/src/api/Signal.luau +0 -193
- package/src/codec/Base.luau +0 -136
- package/src/codec/composite/Array.luau +0 -473
- package/src/codec/composite/Map.luau +0 -485
- package/src/codec/composite/Optional.luau +0 -54
- package/src/codec/composite/Shared.luau +0 -311
- package/src/codec/composite/Struct.luau +0 -494
- package/src/codec/composite/Tagged.luau +0 -103
- package/src/codec/composite/Tuple.luau +0 -143
- package/src/codec/datatype/Buffer.luau +0 -66
- package/src/codec/datatype/CFrame.luau +0 -109
- package/src/codec/datatype/Color.luau +0 -32
- package/src/codec/datatype/Instance.luau +0 -52
- package/src/codec/datatype/IntVector.luau +0 -39
- package/src/codec/datatype/NumberRange.luau +0 -39
- package/src/codec/datatype/Ray.luau +0 -36
- package/src/codec/datatype/Rect.luau +0 -31
- package/src/codec/datatype/Region.luau +0 -62
- package/src/codec/datatype/Sequence.luau +0 -184
- package/src/codec/datatype/String.luau +0 -94
- package/src/codec/datatype/UDim.luau +0 -46
- package/src/codec/datatype/Vector.luau +0 -130
- package/src/codec/meta/Auto.luau +0 -318
- package/src/codec/meta/Bitfield.luau +0 -160
- package/src/codec/meta/Custom.luau +0 -33
- package/src/codec/meta/DeltaScalar.luau +0 -390
- package/src/codec/meta/Enum.luau +0 -50
- package/src/codec/meta/Float.luau +0 -38
- package/src/codec/meta/Nothing.luau +0 -13
- package/src/codec/meta/Unknown.luau +0 -41
- package/src/codec/primitive/Bool.luau +0 -26
- package/src/codec/primitive/Float16.luau +0 -99
- package/src/codec/primitive/Int.luau +0 -59
- package/src/codec/primitive/Number.luau +0 -24
- package/src/codec/primitive/Signed.luau +0 -32
- package/src/codec/primitive/Varint.luau +0 -165
- package/src/codec/primitive/Zint.luau +0 -99
- package/src/index.d.ts +0 -423
- package/src/init.luau +0 -362
- package/src/internal/Baseline.luau +0 -57
- package/src/internal/Channel.luau +0 -484
- package/src/internal/Middleware.luau +0 -100
- package/src/internal/Pool.luau +0 -66
- package/src/internal/Registry.luau +0 -119
- package/src/internal/Transport.luau +0 -28
- package/src/transport/Bridge.luau +0 -110
- package/src/transport/Client.luau +0 -97
- package/src/transport/Gate.luau +0 -488
- package/src/transport/Reader.luau +0 -331
- package/src/transport/Server.luau +0 -233
- package/src/util/Array.luau +0 -18
- package/src/util/Buffer.luau +0 -90
- package/src/util/Constants.luau +0 -30
- package/src/util/Log.luau +0 -68
- package/src/util/Player.luau +0 -14
- package/src/util/Quantize.luau +0 -84
- 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)
|
package/src/internal/Pool.luau
DELETED
|
@@ -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)
|