@axpecter/lync 2.0.0 → 2.1.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 (55) hide show
  1. package/README.md +342 -428
  2. package/package.json +1 -1
  3. package/src/Types.luau +63 -31
  4. package/src/api/Group.luau +101 -106
  5. package/src/api/Packet.luau +187 -186
  6. package/src/api/Query.luau +223 -284
  7. package/src/api/Scope.luau +46 -57
  8. package/src/api/Signal.luau +104 -137
  9. package/src/codec/Base.luau +69 -20
  10. package/src/codec/composite/Array.luau +179 -270
  11. package/src/codec/composite/Map.luau +97 -347
  12. package/src/codec/composite/Optional.luau +27 -24
  13. package/src/codec/composite/Shared.luau +96 -65
  14. package/src/codec/composite/Struct.luau +200 -360
  15. package/src/codec/composite/Tagged.luau +62 -187
  16. package/src/codec/composite/Tuple.luau +79 -103
  17. package/src/codec/datatype/Buffer.luau +49 -21
  18. package/src/codec/datatype/CFrame.luau +207 -30
  19. package/src/codec/datatype/Color.luau +21 -11
  20. package/src/codec/datatype/Instance.luau +28 -21
  21. package/src/codec/datatype/IntVector.luau +33 -21
  22. package/src/codec/datatype/NumberRange.luau +19 -10
  23. package/src/codec/datatype/Ray.luau +26 -22
  24. package/src/codec/datatype/Rect.luau +20 -16
  25. package/src/codec/datatype/Region.luau +51 -45
  26. package/src/codec/datatype/Sequence.luau +64 -56
  27. package/src/codec/datatype/String.luau +89 -56
  28. package/src/codec/datatype/UDim.luau +40 -22
  29. package/src/codec/datatype/Vector.luau +181 -17
  30. package/src/codec/meta/Auto.luau +295 -253
  31. package/src/codec/meta/Bitfield.luau +104 -148
  32. package/src/codec/meta/Custom.luau +8 -4
  33. package/src/codec/meta/Enum.luau +29 -57
  34. package/src/codec/meta/Float.luau +64 -0
  35. package/src/codec/meta/Nothing.luau +9 -5
  36. package/src/codec/meta/Unknown.luau +44 -36
  37. package/src/codec/primitive/Bool.luau +16 -26
  38. package/src/codec/primitive/Float16.luau +58 -64
  39. package/src/codec/primitive/Int.luau +68 -0
  40. package/src/codec/primitive/Number.luau +21 -43
  41. package/src/codec/primitive/Varint.luau +67 -46
  42. package/src/index.d.ts +251 -335
  43. package/src/init.luau +319 -207
  44. package/src/internal/Baseline.luau +29 -24
  45. package/src/internal/Channel.luau +333 -278
  46. package/src/internal/Middleware.luau +60 -85
  47. package/src/internal/Pool.luau +19 -30
  48. package/src/internal/Registry.luau +56 -113
  49. package/src/transport/Bridge.luau +64 -43
  50. package/src/transport/Client.luau +59 -170
  51. package/src/transport/Gate.luau +282 -142
  52. package/src/transport/Reader.luau +148 -140
  53. package/src/transport/Server.luau +138 -488
  54. package/src/api/Namespace.luau +0 -256
  55. 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 Baseline = require(script.Parent.Parent.Parent.internal.Baseline)
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
- type ChannelState = Types.ChannelState
12
- type Codec<T> = Types.Codec<T>
13
- type InternalCodec<T> = Types.InternalCodec<T>
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 FLAG_DELTA = Shared.FLAG_DELTA
21
- local FLAG_FULL = Shared.FLAG_FULL
22
- local FLAG_UNCHANGED = Shared.FLAG_UNCHANGED
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.deltaArray(element: Codec<any>, maxCount: number?): Codec<{ any }>
29
- local deltaId = Shared.allocDeltaId()
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
- local result = table.create(len)
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
- _hasUnknown = nil,
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 = ceil(len / 8)
227
- local c = ch.cursor
228
- if c + byteCount > ch.size then
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
- local byteIdx = 0
234
- local acc = 0
235
- for i = 1, len do
236
- local bit = (i - 1) % 8
237
- if value[i] then
238
- acc = bor(acc, lshift(1, bit))
239
- end
240
- if bit == 7 then
241
- buffer.writeu8(b, c + byteIdx, acc)
242
- byteIdx += 1
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
- -- Flush remaining bits
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 result, lenBytes
73
+ return {}, lenBytes
262
74
  end
263
75
 
264
- local byteCount = ceil(len / 8)
265
- local c = pos + lenBytes
266
- for i = 1, len do
267
- local byteIdx = rshift(i - 1, 3)
268
- local bit = (i - 1) % 8
269
- local byte = buffer.readu8(src, c + byteIdx)
270
- result[i] = band(byte, lshift(1, bit)) ~= 0
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
- if elementSize and directWrite and directRead then
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
- _hasUnknown = (element :: any)._hasUnknown or nil,
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 totalBytes = len * elementSize
289
- local c = ch.cursor
290
- if c + totalBytes > ch.size then
291
- alloc(ch, totalBytes)
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
- directWrite(b, c, value[i])
297
- c += elementSize
117
+ elemDW(b, cursor + (i - 1) * elemSize, value[i])
298
118
  end
299
- ch.cursor = c
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 result = table.create(len)
309
- local c = pos + lenBytes
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] = directRead(src, c)
313
- c += elementSize
143
+ result[i] = elemDR(src, dataStart + (i - 1) * elemSize)
314
144
  end
315
145
 
316
- return result, c - pos
146
+ return result, lenBytes + payloadBytes
317
147
  end,
318
- })
148
+ } :: any)
319
149
  end
320
150
 
321
- local writeElement = element.write
322
- local readElement = element.read
323
-
151
+ -- Generic array
324
152
  return table.freeze({
325
- _hasUnknown = (element :: any)._hasUnknown or nil,
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
- writeElement(ch, value[i])
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 absPos = pos + lenBytes
345
-
171
+ local total = lenBytes
346
172
  for i = 1, len do
347
- local value, n = readElement(src, absPos, refs)
348
- result[i] = value
349
- absPos += n
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
- return result, absPos - pos
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)