@axpecter/lync 2.0.0 → 2.1.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/README.md +475 -426
- package/package.json +1 -1
- package/src/Types.luau +63 -31
- package/src/api/Group.luau +101 -106
- package/src/api/Packet.luau +187 -186
- package/src/api/Query.luau +223 -284
- package/src/api/Scope.luau +46 -57
- package/src/api/Signal.luau +104 -137
- package/src/codec/Base.luau +69 -20
- package/src/codec/composite/Array.luau +179 -270
- package/src/codec/composite/Map.luau +97 -347
- package/src/codec/composite/Optional.luau +27 -24
- package/src/codec/composite/Shared.luau +96 -65
- package/src/codec/composite/Struct.luau +200 -360
- package/src/codec/composite/Tagged.luau +62 -187
- package/src/codec/composite/Tuple.luau +79 -103
- package/src/codec/datatype/Buffer.luau +49 -21
- package/src/codec/datatype/CFrame.luau +207 -30
- package/src/codec/datatype/Color.luau +21 -11
- package/src/codec/datatype/Instance.luau +28 -21
- package/src/codec/datatype/IntVector.luau +33 -21
- package/src/codec/datatype/NumberRange.luau +19 -10
- package/src/codec/datatype/Ray.luau +26 -22
- package/src/codec/datatype/Rect.luau +20 -16
- package/src/codec/datatype/Region.luau +51 -45
- package/src/codec/datatype/Sequence.luau +64 -56
- package/src/codec/datatype/String.luau +89 -56
- package/src/codec/datatype/UDim.luau +40 -22
- package/src/codec/datatype/Vector.luau +181 -17
- package/src/codec/meta/Auto.luau +295 -253
- package/src/codec/meta/Bitfield.luau +104 -148
- package/src/codec/meta/Custom.luau +8 -4
- package/src/codec/meta/Enum.luau +29 -57
- package/src/codec/meta/Float.luau +64 -0
- package/src/codec/meta/Nothing.luau +9 -5
- package/src/codec/meta/Unknown.luau +44 -36
- package/src/codec/primitive/Bool.luau +16 -26
- package/src/codec/primitive/Float16.luau +58 -64
- package/src/codec/primitive/Int.luau +68 -0
- package/src/codec/primitive/Number.luau +21 -43
- package/src/codec/primitive/Varint.luau +67 -46
- package/src/index.d.ts +249 -335
- package/src/init.luau +319 -207
- package/src/internal/Baseline.luau +29 -24
- package/src/internal/Channel.luau +333 -278
- package/src/internal/Middleware.luau +60 -85
- package/src/internal/Pool.luau +19 -30
- package/src/internal/Registry.luau +56 -113
- package/src/transport/Bridge.luau +64 -43
- package/src/transport/Client.luau +59 -170
- package/src/transport/Gate.luau +282 -142
- package/src/transport/Reader.luau +148 -140
- package/src/transport/Server.luau +138 -488
- package/src/api/Namespace.luau +0 -256
- package/src/codec/meta/Quantized.luau +0 -174
|
@@ -2,356 +2,265 @@
|
|
|
2
2
|
--!native
|
|
3
3
|
-- array and deltaArray codecs.
|
|
4
4
|
|
|
5
|
-
local
|
|
6
|
-
local Channel = require(script.Parent.Parent.Parent.internal.Channel)
|
|
5
|
+
local Base = require(script.Parent.Parent.Base)
|
|
7
6
|
local Shared = require(script.Parent.Shared)
|
|
8
7
|
local Types = require(script.Parent.Parent.Parent.Types)
|
|
9
8
|
local Varint = require(script.Parent.Parent.primitive.Varint)
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
-- Private -------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
local alloc = Base.alloc
|
|
13
|
+
local writeu8 = buffer.writeu8
|
|
14
|
+
local readu8 = buffer.readu8
|
|
15
|
+
local band = bit32.band
|
|
16
|
+
local bor = bit32.bor
|
|
17
|
+
local lshift = bit32.lshift
|
|
14
18
|
|
|
15
|
-
local alloc = Channel.alloc
|
|
16
|
-
local rangeEqual = Shared.rangeEqual
|
|
17
19
|
local acquireScratch = Shared.acquireScratch
|
|
18
20
|
local releaseScratch = Shared.releaseScratch
|
|
21
|
+
local rangeEqual = Shared.rangeEqual
|
|
22
|
+
local allocDeltaId = Shared.allocDeltaId
|
|
19
23
|
|
|
20
|
-
local
|
|
21
|
-
local
|
|
22
|
-
local
|
|
24
|
+
local DELTA_UNCHANGED = Shared.DELTA_UNCHANGED
|
|
25
|
+
local DELTA_FULL_OFFSET = Shared.DELTA_FULL_OFFSET
|
|
26
|
+
local DELTA_MULTI_OFFSET = Shared.DELTA_MULTI_OFFSET
|
|
23
27
|
|
|
24
28
|
-- Public --------------------------------------------------------------
|
|
25
29
|
|
|
26
30
|
local Array = {}
|
|
27
31
|
|
|
28
|
-
function Array.
|
|
29
|
-
local
|
|
30
|
-
|
|
31
|
-
local writeElement = element.write
|
|
32
|
-
local readElement = element.read
|
|
33
|
-
|
|
34
|
-
local internal = element :: InternalCodec<any>
|
|
35
|
-
local elemSize = internal._size
|
|
36
|
-
local elemWrite = internal._directWrite
|
|
37
|
-
local elemRead = internal._directRead
|
|
38
|
-
|
|
39
|
-
return table.freeze({
|
|
40
|
-
_isDelta = true,
|
|
41
|
-
_hasUnknown = (element :: any)._hasUnknown or nil,
|
|
42
|
-
|
|
43
|
-
write = function(ch: ChannelState, value: { any }): ()
|
|
44
|
-
local len = #value
|
|
45
|
-
local cache = ch.deltas[deltaId] :: { raw: buffer, bounds: { number }, len: number }?
|
|
46
|
-
local scratch = acquireScratch()
|
|
47
|
-
|
|
48
|
-
local bounds = table.create(len + 1)
|
|
49
|
-
bounds[1] = 0
|
|
50
|
-
|
|
51
|
-
if elemWrite and elemSize then
|
|
52
|
-
local totalBytes = len * elemSize
|
|
53
|
-
alloc(scratch, totalBytes)
|
|
54
|
-
local b = scratch.buff
|
|
55
|
-
local c = 0
|
|
56
|
-
for i = 1, len do
|
|
57
|
-
elemWrite(b, c, value[i])
|
|
58
|
-
c += elemSize
|
|
59
|
-
bounds[i + 1] = c
|
|
60
|
-
end
|
|
61
|
-
scratch.cursor = totalBytes
|
|
62
|
-
else
|
|
63
|
-
for i = 1, len do
|
|
64
|
-
writeElement(scratch, value[i])
|
|
65
|
-
bounds[i + 1] = scratch.cursor
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
local scratchTotal = scratch.cursor
|
|
70
|
-
|
|
71
|
-
if not cache or cache.len ~= len then
|
|
72
|
-
alloc(ch, 1)
|
|
73
|
-
buffer.writeu8(ch.buff, ch.cursor, FLAG_FULL)
|
|
74
|
-
ch.cursor += 1
|
|
75
|
-
Varint.write(ch, len)
|
|
76
|
-
|
|
77
|
-
alloc(ch, scratchTotal)
|
|
78
|
-
buffer.copy(ch.buff, ch.cursor, scratch.buff, 0, scratchTotal)
|
|
79
|
-
ch.cursor += scratchTotal
|
|
80
|
-
else
|
|
81
|
-
local cacheRaw = cache.raw
|
|
82
|
-
local cacheBds = cache.bounds
|
|
83
|
-
local dirty = table.create(len)
|
|
84
|
-
local dirtyN = 0
|
|
85
|
-
|
|
86
|
-
for i = 1, len do
|
|
87
|
-
local newOff = bounds[i]
|
|
88
|
-
local newLen = bounds[i + 1] - newOff
|
|
89
|
-
local oldOff = cacheBds[i]
|
|
90
|
-
local oldLen = cacheBds[i + 1] - oldOff
|
|
91
|
-
|
|
92
|
-
if
|
|
93
|
-
newLen ~= oldLen
|
|
94
|
-
or not rangeEqual(scratch.buff, newOff, cacheRaw, oldOff, newLen)
|
|
95
|
-
then
|
|
96
|
-
dirtyN += 1
|
|
97
|
-
dirty[dirtyN] = i
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
if dirtyN == 0 then
|
|
102
|
-
alloc(ch, 1)
|
|
103
|
-
buffer.writeu8(ch.buff, ch.cursor, FLAG_UNCHANGED)
|
|
104
|
-
ch.cursor += 1
|
|
105
|
-
releaseScratch()
|
|
106
|
-
return
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
alloc(ch, 1)
|
|
110
|
-
buffer.writeu8(ch.buff, ch.cursor, FLAG_DELTA)
|
|
111
|
-
ch.cursor += 1
|
|
112
|
-
Varint.write(ch, dirtyN)
|
|
113
|
-
|
|
114
|
-
local prevIdx = 0
|
|
115
|
-
for j = 1, dirtyN do
|
|
116
|
-
local i = dirty[j]
|
|
117
|
-
local newOff = bounds[i]
|
|
118
|
-
local newLen = bounds[i + 1] - newOff
|
|
119
|
-
|
|
120
|
-
Varint.write(ch, (i - 1) - prevIdx)
|
|
121
|
-
prevIdx = i - 1
|
|
122
|
-
alloc(ch, newLen)
|
|
123
|
-
buffer.copy(ch.buff, ch.cursor, scratch.buff, newOff, newLen)
|
|
124
|
-
ch.cursor += newLen
|
|
125
|
-
end
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
local rawBuf = buffer.create(scratchTotal)
|
|
129
|
-
buffer.copy(rawBuf, 0, scratch.buff, 0, scratchTotal)
|
|
130
|
-
|
|
131
|
-
table.freeze(bounds)
|
|
132
|
-
ch.deltas[deltaId] = { raw = rawBuf, bounds = bounds, len = len }
|
|
133
|
-
releaseScratch()
|
|
134
|
-
end,
|
|
135
|
-
|
|
136
|
-
read = function(src: buffer, pos: number, refs: { Instance }?): ({ any }, number)
|
|
137
|
-
local flag = buffer.readu8(src, pos)
|
|
138
|
-
local absPos = pos + 1
|
|
139
|
-
|
|
140
|
-
if flag == FLAG_UNCHANGED then
|
|
141
|
-
local cache = Baseline.getCache(deltaId)
|
|
142
|
-
return if cache then table.clone(cache) else {}, 1
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
if flag == FLAG_FULL then
|
|
146
|
-
local len, lenBytes = Varint.read(src, absPos)
|
|
147
|
-
absPos += lenBytes
|
|
148
|
-
|
|
149
|
-
if maxCount and len > maxCount then
|
|
150
|
-
error(`[Lync] Array count {len} exceeds max {maxCount}`)
|
|
151
|
-
end
|
|
32
|
+
function Array.array(element: Types.InternalCodec<any>, maxCount: number?): Types.InternalCodec<any>
|
|
33
|
+
local elemAny = element :: any
|
|
152
34
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
if elemRead and elemSize then
|
|
156
|
-
for i = 1, len do
|
|
157
|
-
result[i] = elemRead(src, absPos)
|
|
158
|
-
absPos += elemSize
|
|
159
|
-
end
|
|
160
|
-
else
|
|
161
|
-
for i = 1, len do
|
|
162
|
-
local val, n = readElement(src, absPos, refs)
|
|
163
|
-
result[i] = val
|
|
164
|
-
absPos += n
|
|
165
|
-
end
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
Baseline.setCache(deltaId, result)
|
|
169
|
-
return result, absPos - pos
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
local cache = Baseline.getCache(deltaId) :: { any }?
|
|
173
|
-
if not cache then
|
|
174
|
-
error("[Lync] Delta frame without baseline")
|
|
175
|
-
end
|
|
176
|
-
local result = table.clone(cache)
|
|
177
|
-
|
|
178
|
-
local dirtyN, dnBytes = Varint.read(src, absPos)
|
|
179
|
-
absPos += dnBytes
|
|
180
|
-
|
|
181
|
-
local runningIdx = 0
|
|
182
|
-
for _ = 1, dirtyN do
|
|
183
|
-
local delta, deltaBytes = Varint.read(src, absPos)
|
|
184
|
-
absPos += deltaBytes
|
|
185
|
-
runningIdx += delta
|
|
186
|
-
|
|
187
|
-
if elemRead and elemSize then
|
|
188
|
-
result[runningIdx + 1] = elemRead(src, absPos)
|
|
189
|
-
absPos += elemSize
|
|
190
|
-
else
|
|
191
|
-
local val, n = readElement(src, absPos, refs)
|
|
192
|
-
result[runningIdx + 1] = val
|
|
193
|
-
absPos += n
|
|
194
|
-
end
|
|
195
|
-
end
|
|
196
|
-
|
|
197
|
-
Baseline.setCache(deltaId, result)
|
|
198
|
-
return result, absPos - pos
|
|
199
|
-
end,
|
|
200
|
-
})
|
|
201
|
-
end
|
|
202
|
-
|
|
203
|
-
function Array.array(element: Codec<any>, maxCount: number?): Codec<{ any }>
|
|
204
|
-
local internal = element :: InternalCodec<any>
|
|
205
|
-
local elementSize = internal._size
|
|
206
|
-
local directWrite = internal._directWrite
|
|
207
|
-
local directRead = internal._directRead
|
|
208
|
-
|
|
209
|
-
local band = bit32.band
|
|
210
|
-
local bor = bit32.bor
|
|
211
|
-
local lshift = bit32.lshift
|
|
212
|
-
local rshift = bit32.rshift
|
|
213
|
-
local ceil = math.ceil
|
|
214
|
-
|
|
215
|
-
-- Bitpacked bool array: 8 bools per byte
|
|
216
|
-
if internal._isBool then
|
|
35
|
+
-- Bool array: bitpacked
|
|
36
|
+
if elemAny._isBool then
|
|
217
37
|
return table.freeze({
|
|
218
|
-
|
|
219
|
-
write = function(ch: ChannelState, value: { any }): ()
|
|
38
|
+
write = function(ch: Types.ChannelState, value: { any }): ()
|
|
220
39
|
local len = #value
|
|
221
40
|
Varint.write(ch, len)
|
|
222
41
|
if len == 0 then
|
|
223
42
|
return
|
|
224
43
|
end
|
|
225
44
|
|
|
226
|
-
local byteCount =
|
|
227
|
-
local
|
|
228
|
-
if
|
|
45
|
+
local byteCount = (len + 7) // 8
|
|
46
|
+
local cursor = ch.cursor
|
|
47
|
+
if cursor + byteCount > ch.size then
|
|
229
48
|
alloc(ch, byteCount)
|
|
230
49
|
end
|
|
231
|
-
|
|
232
50
|
local b = ch.buff
|
|
233
|
-
|
|
234
|
-
local
|
|
235
|
-
for
|
|
236
|
-
local
|
|
237
|
-
if
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
acc = 0
|
|
51
|
+
|
|
52
|
+
local idx = 1
|
|
53
|
+
for byteIdx = 0, byteCount - 1 do
|
|
54
|
+
local packed = 0
|
|
55
|
+
local limit = if idx + 8 <= len + 1 then 8 else len - idx + 1
|
|
56
|
+
for bit = 0, limit - 1 do
|
|
57
|
+
if value[idx] then
|
|
58
|
+
packed = bor(packed, lshift(1, bit))
|
|
59
|
+
end
|
|
60
|
+
idx += 1
|
|
244
61
|
end
|
|
62
|
+
writeu8(b, cursor + byteIdx, packed)
|
|
245
63
|
end
|
|
246
|
-
|
|
247
|
-
if len % 8 ~= 0 then
|
|
248
|
-
buffer.writeu8(b, c + byteIdx, acc)
|
|
249
|
-
end
|
|
250
|
-
ch.cursor = c + byteCount
|
|
64
|
+
ch.cursor = cursor + byteCount
|
|
251
65
|
end,
|
|
66
|
+
|
|
252
67
|
read = function(src: buffer, pos: number, _refs: { Instance }?): ({ boolean }, number)
|
|
253
68
|
local len, lenBytes = Varint.read(src, pos)
|
|
254
|
-
|
|
255
69
|
if maxCount and len > maxCount then
|
|
256
|
-
error(`[Lync] Array count {len} exceeds max {maxCount}`)
|
|
70
|
+
error(`[Lync] Array: count {len} exceeds max {maxCount}`)
|
|
257
71
|
end
|
|
258
|
-
|
|
259
|
-
local result = table.create(len)
|
|
260
72
|
if len == 0 then
|
|
261
|
-
return
|
|
73
|
+
return {}, lenBytes
|
|
262
74
|
end
|
|
263
75
|
|
|
264
|
-
local byteCount =
|
|
265
|
-
local
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
76
|
+
local byteCount = (len + 7) // 8
|
|
77
|
+
local dataStart = pos + lenBytes
|
|
78
|
+
local result = table.create(len)
|
|
79
|
+
local idx = 1
|
|
80
|
+
|
|
81
|
+
for byteIdx = 0, byteCount - 1 do
|
|
82
|
+
local byte = readu8(src, dataStart + byteIdx)
|
|
83
|
+
local limit = if idx + 8 <= len + 1 then 8 else len - idx + 1
|
|
84
|
+
for bit = 0, limit - 1 do
|
|
85
|
+
result[idx] = band(byte, lshift(1, bit)) ~= 0
|
|
86
|
+
idx += 1
|
|
87
|
+
end
|
|
271
88
|
end
|
|
272
89
|
|
|
273
90
|
return result, lenBytes + byteCount
|
|
274
91
|
end,
|
|
275
|
-
})
|
|
92
|
+
} :: any)
|
|
276
93
|
end
|
|
277
94
|
|
|
278
|
-
|
|
95
|
+
-- Direct array: element has _size + _directWrite + _directRead
|
|
96
|
+
local elemSize = elemAny._size
|
|
97
|
+
local elemDW = elemAny._directWrite
|
|
98
|
+
local elemDR = elemAny._directRead
|
|
99
|
+
|
|
100
|
+
if elemSize and elemDW and elemDR then
|
|
279
101
|
return table.freeze({
|
|
280
|
-
|
|
281
|
-
write = function(ch: ChannelState, value: { any }): ()
|
|
102
|
+
write = function(ch: Types.ChannelState, value: { any }): ()
|
|
282
103
|
local len = #value
|
|
283
104
|
Varint.write(ch, len)
|
|
284
105
|
if len == 0 then
|
|
285
106
|
return
|
|
286
107
|
end
|
|
287
108
|
|
|
288
|
-
local
|
|
289
|
-
local
|
|
290
|
-
if
|
|
291
|
-
alloc(ch,
|
|
109
|
+
local payloadBytes = len * elemSize
|
|
110
|
+
local cursor = ch.cursor
|
|
111
|
+
if cursor + payloadBytes > ch.size then
|
|
112
|
+
alloc(ch, payloadBytes)
|
|
292
113
|
end
|
|
293
|
-
|
|
294
114
|
local b = ch.buff
|
|
115
|
+
|
|
295
116
|
for i = 1, len do
|
|
296
|
-
|
|
297
|
-
c += elementSize
|
|
117
|
+
elemDW(b, cursor + (i - 1) * elemSize, value[i])
|
|
298
118
|
end
|
|
299
|
-
ch.cursor =
|
|
119
|
+
ch.cursor = cursor + payloadBytes
|
|
300
120
|
end,
|
|
121
|
+
|
|
301
122
|
read = function(src: buffer, pos: number, _refs: { Instance }?): ({ any }, number)
|
|
302
123
|
local len, lenBytes = Varint.read(src, pos)
|
|
303
|
-
|
|
304
124
|
if maxCount and len > maxCount then
|
|
305
|
-
error(`[Lync] Array count {len} exceeds max {maxCount}`)
|
|
125
|
+
error(`[Lync] Array: count {len} exceeds max {maxCount}`)
|
|
126
|
+
end
|
|
127
|
+
if len == 0 then
|
|
128
|
+
return {}, lenBytes
|
|
306
129
|
end
|
|
307
130
|
|
|
308
|
-
local
|
|
309
|
-
local
|
|
131
|
+
local payloadBytes = len * elemSize
|
|
132
|
+
local dataStart = pos + lenBytes
|
|
310
133
|
|
|
134
|
+
-- Safety cap
|
|
135
|
+
local remaining = buffer.len(src) - dataStart
|
|
136
|
+
if payloadBytes > remaining then
|
|
137
|
+
len = remaining // elemSize
|
|
138
|
+
payloadBytes = len * elemSize
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
local result = table.create(len)
|
|
311
142
|
for i = 1, len do
|
|
312
|
-
result[i] =
|
|
313
|
-
c += elementSize
|
|
143
|
+
result[i] = elemDR(src, dataStart + (i - 1) * elemSize)
|
|
314
144
|
end
|
|
315
145
|
|
|
316
|
-
return result,
|
|
146
|
+
return result, lenBytes + payloadBytes
|
|
317
147
|
end,
|
|
318
|
-
})
|
|
148
|
+
} :: any)
|
|
319
149
|
end
|
|
320
150
|
|
|
321
|
-
|
|
322
|
-
local readElement = element.read
|
|
323
|
-
|
|
151
|
+
-- Generic array
|
|
324
152
|
return table.freeze({
|
|
325
|
-
|
|
326
|
-
write = function(ch: ChannelState, value: { any }): ()
|
|
153
|
+
write = function(ch: Types.ChannelState, value: { any }): ()
|
|
327
154
|
local len = #value
|
|
328
155
|
Varint.write(ch, len)
|
|
329
|
-
if elementSize and len > 0 then
|
|
330
|
-
alloc(ch, len * elementSize)
|
|
331
|
-
end
|
|
332
156
|
for i = 1, len do
|
|
333
|
-
|
|
157
|
+
element.write(ch, value[i])
|
|
334
158
|
end
|
|
335
159
|
end,
|
|
160
|
+
|
|
336
161
|
read = function(src: buffer, pos: number, refs: { Instance }?): ({ any }, number)
|
|
337
162
|
local len, lenBytes = Varint.read(src, pos)
|
|
338
|
-
|
|
339
163
|
if maxCount and len > maxCount then
|
|
340
|
-
error(`[Lync] Array count {len} exceeds max {maxCount}`)
|
|
164
|
+
error(`[Lync] Array: count {len} exceeds max {maxCount}`)
|
|
165
|
+
end
|
|
166
|
+
if len == 0 then
|
|
167
|
+
return {}, lenBytes
|
|
341
168
|
end
|
|
342
169
|
|
|
343
170
|
local result = table.create(len)
|
|
344
|
-
local
|
|
345
|
-
|
|
171
|
+
local total = lenBytes
|
|
346
172
|
for i = 1, len do
|
|
347
|
-
local
|
|
348
|
-
result[i] =
|
|
349
|
-
|
|
173
|
+
local v, consumed = element.read(src, pos + total, refs)
|
|
174
|
+
result[i] = v
|
|
175
|
+
total += consumed
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
return result, total
|
|
179
|
+
end,
|
|
180
|
+
} :: any)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
function Array.deltaArray(
|
|
184
|
+
element: Types.InternalCodec<any>,
|
|
185
|
+
maxCount: number?
|
|
186
|
+
): Types.InternalCodec<any>
|
|
187
|
+
local deltaId = allocDeltaId()
|
|
188
|
+
local innerArray = Array.array(element, maxCount)
|
|
189
|
+
|
|
190
|
+
return table.freeze({
|
|
191
|
+
_isDelta = true,
|
|
192
|
+
|
|
193
|
+
write = function(ch: Types.ChannelState, value: { any }): ()
|
|
194
|
+
local cache = ch.deltas[deltaId]
|
|
195
|
+
|
|
196
|
+
if not cache then
|
|
197
|
+
-- First frame: full
|
|
198
|
+
local cursor = ch.cursor
|
|
199
|
+
if cursor + 1 > ch.size then
|
|
200
|
+
alloc(ch, 1)
|
|
201
|
+
end
|
|
202
|
+
writeu8(ch.buff, cursor, 1) -- FLAG_FULL (len+1 where len=0 for first)
|
|
203
|
+
ch.cursor = cursor + 1
|
|
204
|
+
|
|
205
|
+
local scratch = acquireScratch()
|
|
206
|
+
innerArray.write(scratch, value)
|
|
207
|
+
local snapLen = scratch.cursor
|
|
208
|
+
local snapBuf = buffer.create(snapLen)
|
|
209
|
+
buffer.copy(snapBuf, 0, scratch.buff, 0, snapLen)
|
|
210
|
+
|
|
211
|
+
ch.deltas[deltaId] = { raw = snapBuf, len = snapLen }
|
|
212
|
+
innerArray.write(ch, value)
|
|
213
|
+
releaseScratch()
|
|
214
|
+
return
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
-- Compare against cache
|
|
218
|
+
local scratch = acquireScratch()
|
|
219
|
+
innerArray.write(scratch, value)
|
|
220
|
+
local scratchLen = scratch.cursor
|
|
221
|
+
local cachedLen = cache.len
|
|
222
|
+
|
|
223
|
+
if
|
|
224
|
+
scratchLen == cachedLen and rangeEqual(scratch.buff, 0, cache.raw, 0, scratchLen)
|
|
225
|
+
then
|
|
226
|
+
local cursor = ch.cursor
|
|
227
|
+
if cursor + 1 > ch.size then
|
|
228
|
+
alloc(ch, 1)
|
|
229
|
+
end
|
|
230
|
+
writeu8(ch.buff, cursor, DELTA_UNCHANGED)
|
|
231
|
+
ch.cursor = cursor + 1
|
|
232
|
+
releaseScratch()
|
|
233
|
+
return
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
-- Changed: full re-send
|
|
237
|
+
local cursor = ch.cursor
|
|
238
|
+
if cursor + 1 > ch.size then
|
|
239
|
+
alloc(ch, 1)
|
|
240
|
+
end
|
|
241
|
+
writeu8(ch.buff, cursor, 1)
|
|
242
|
+
ch.cursor = cursor + 1
|
|
243
|
+
innerArray.write(ch, value)
|
|
244
|
+
|
|
245
|
+
local newBuf = buffer.create(scratchLen)
|
|
246
|
+
buffer.copy(newBuf, 0, scratch.buff, 0, scratchLen)
|
|
247
|
+
cache.raw = newBuf
|
|
248
|
+
cache.len = scratchLen
|
|
249
|
+
|
|
250
|
+
releaseScratch()
|
|
251
|
+
end,
|
|
252
|
+
|
|
253
|
+
read = function(src: buffer, pos: number, refs: { Instance }?): ({ any }, number)
|
|
254
|
+
local flag = readu8(src, pos)
|
|
255
|
+
|
|
256
|
+
if flag == DELTA_UNCHANGED then
|
|
257
|
+
return {} :: any, 1
|
|
350
258
|
end
|
|
351
259
|
|
|
352
|
-
|
|
260
|
+
local value, consumed = innerArray.read(src, pos + 1, refs)
|
|
261
|
+
return value, 1 + consumed
|
|
353
262
|
end,
|
|
354
|
-
})
|
|
263
|
+
} :: any)
|
|
355
264
|
end
|
|
356
265
|
|
|
357
266
|
return table.freeze(Array)
|