@axpecter/lync 1.5.2 → 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.
Files changed (55) hide show
  1. package/README.md +499 -357
  2. package/package.json +2 -2
  3. package/src/Types.luau +69 -27
  4. package/src/api/Group.luau +101 -106
  5. package/src/api/Packet.luau +191 -157
  6. package/src/api/Query.luau +223 -282
  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 +189 -201
  11. package/src/codec/composite/Map.luau +97 -341
  12. package/src/codec/composite/Optional.luau +27 -23
  13. package/src/codec/composite/Shared.luau +96 -65
  14. package/src/codec/composite/Struct.luau +201 -339
  15. package/src/codec/composite/Tagged.luau +64 -178
  16. package/src/codec/composite/Tuple.luau +81 -95
  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 +29 -19
  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 -35
  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 +249 -306
  43. package/src/init.luau +332 -173
  44. package/src/internal/Baseline.luau +29 -24
  45. package/src/internal/Channel.luau +346 -161
  46. package/src/internal/Middleware.luau +60 -85
  47. package/src/internal/Pool.luau +19 -30
  48. package/src/internal/Registry.luau +61 -101
  49. package/src/transport/Bridge.luau +64 -43
  50. package/src/transport/Client.luau +60 -117
  51. package/src/transport/Gate.luau +282 -133
  52. package/src/transport/Reader.luau +159 -100
  53. package/src/transport/Server.luau +147 -292
  54. package/src/api/Namespace.luau +0 -251
  55. package/src/codec/meta/Quantized.luau +0 -174
@@ -2,408 +2,164 @@
2
2
  --!native
3
3
  -- map and deltaMap 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
14
15
 
15
- local alloc = Channel.alloc
16
- local rangeEqual = Shared.rangeEqual
17
16
  local acquireScratch = Shared.acquireScratch
18
17
  local releaseScratch = Shared.releaseScratch
19
-
20
- local FLAG_DELTA = Shared.FLAG_DELTA
21
- local FLAG_FULL = Shared.FLAG_FULL
22
- local FLAG_UNCHANGED = Shared.FLAG_UNCHANGED
18
+ local rangeEqual = Shared.rangeEqual
19
+ local allocDeltaId = Shared.allocDeltaId
20
+ local DELTA_UNCHANGED = Shared.DELTA_UNCHANGED
23
21
 
24
22
  -- Public --------------------------------------------------------------
25
23
 
26
24
  local Map = {}
27
25
 
28
26
  function Map.map(
29
- keyCodec: Codec<any>,
30
- valueCodec: Codec<any>,
27
+ keyCodec: Types.InternalCodec<any>,
28
+ valueCodec: Types.InternalCodec<any>,
31
29
  maxCount: number?
32
- ): Codec<{ [any]: any }>
33
- local keyInternal = keyCodec :: InternalCodec<any>
34
- local valInternal = valueCodec :: InternalCodec<any>
35
-
36
- local keySize = keyInternal._size
37
- local valSize = valInternal._size
38
- local keyDWrite = keyInternal._directWrite
39
- local valDWrite = valInternal._directWrite
40
- local keyDRead = keyInternal._directRead
41
- local valDRead = valInternal._directRead
42
-
43
- if keySize and valSize and keyDWrite and valDWrite and keyDRead and valDRead then
44
- local entrySize = keySize + valSize
45
-
46
- return table.freeze({
47
- write = function(ch: ChannelState, value: { [any]: any }): ()
48
- local count = 0
49
- for _ in value do
50
- count += 1
51
- end
52
-
53
- Varint.write(ch, count)
54
-
55
- if count > 0 then
56
- local totalBytes = count * entrySize
57
- local c = ch.cursor
58
- if c + totalBytes > ch.size then
59
- alloc(ch, totalBytes)
60
- end
61
-
62
- local b = ch.buff
63
- for k, v in value do
64
- keyDWrite(b, c, k)
65
- valDWrite(b, c + keySize, v)
66
- c += entrySize
67
- end
68
- ch.cursor = c
69
- end
70
- end,
71
- read = function(
72
- src: buffer,
73
- pos: number,
74
- _refs: { Instance }?
75
- ): ({ [any]: any }, number)
76
- local count, lenBytes = Varint.read(src, pos)
77
-
78
- if maxCount and count > maxCount then
79
- error(`[Lync] Map count {count} exceeds max {maxCount}`)
80
- end
81
-
82
- local result = {}
83
- local c = pos + lenBytes
84
-
85
- for _ = 1, count do
86
- local k = keyDRead(src, c)
87
- local v = valDRead(src, c + keySize)
88
- result[k] = v
89
- c += entrySize
90
- end
91
-
92
- return result, c - pos
93
- end,
94
- })
95
- end
96
-
97
- local writeKey = keyCodec.write
98
- local writeVal = valueCodec.write
99
- local readKey = keyCodec.read
100
- local readVal = valueCodec.read
101
-
30
+ ): Types.InternalCodec<any>
102
31
  return table.freeze({
103
- write = function(ch: ChannelState, value: { [any]: any }): ()
32
+ write = function(ch: Types.ChannelState, value: any): ()
33
+ -- Count entries
104
34
  local count = 0
105
35
  for _ in value do
106
36
  count += 1
107
37
  end
108
38
 
109
39
  Varint.write(ch, count)
40
+ if count == 0 then
41
+ return
42
+ end
43
+
44
+ -- Pre-alloc if both fixed size
45
+ local kAny = keyCodec :: any
46
+ local vAny = valueCodec :: any
47
+ if kAny._size and vAny._size then
48
+ local entrySize = kAny._size + vAny._size
49
+ local cursor = ch.cursor
50
+ if cursor + count * entrySize > ch.size then
51
+ alloc(ch, count * entrySize)
52
+ end
53
+ end
110
54
 
111
55
  for k, v in value do
112
- writeKey(ch, k)
113
- writeVal(ch, v)
56
+ keyCodec.write(ch, k)
57
+ valueCodec.write(ch, v)
114
58
  end
115
59
  end,
116
- read = function(src: buffer, pos: number, refs: { Instance }?): ({ [any]: any }, number)
117
- local count, lenBytes = Varint.read(src, pos)
118
60
 
61
+ read = function(src: buffer, pos: number, refs: { Instance }?): (any, number)
62
+ local count, lenBytes = Varint.read(src, pos)
119
63
  if maxCount and count > maxCount then
120
- error(`[Lync] Map count {count} exceeds max {maxCount}`)
64
+ error(`[Lync] Map: count {count} exceeds max {maxCount}`)
121
65
  end
122
66
 
123
- local result = {}
124
- local absPos = pos + lenBytes
67
+ local result: { [any]: any } = {}
68
+ local total = lenBytes
125
69
 
126
70
  for _ = 1, count do
127
- local k, kn = readKey(src, absPos, refs)
128
- absPos += kn
129
- local v, vn = readVal(src, absPos, refs)
130
- absPos += vn
71
+ local k, kc = keyCodec.read(src, pos + total, refs)
72
+ total += kc
73
+ local v, vc = valueCodec.read(src, pos + total, refs)
74
+ total += vc
131
75
  result[k] = v
132
76
  end
133
77
 
134
- return result, absPos - pos
78
+ return result, total
135
79
  end,
136
- })
80
+ } :: any)
137
81
  end
138
82
 
139
- --[[
140
- Delta map: tracks per-key changes. First frame is full, then upserts + removes only.
141
-
142
- Wire format:
143
- FLAG_FULL: u8(1) + varint(count) + [key+value ...]
144
- FLAG_UNCHANGED: u8(2)
145
- FLAG_DELTA: u8(0) + varint(upsertN) + [key+value ...] + varint(removeN) + [key ...]
146
- ]]
147
83
  function Map.deltaMap(
148
- keyCodec: Codec<any>,
149
- valueCodec: Codec<any>,
84
+ keyCodec: Types.InternalCodec<any>,
85
+ valueCodec: Types.InternalCodec<any>,
150
86
  maxCount: number?
151
- ): Codec<{ [any]: any }>
152
- local deltaId = Shared.allocDeltaId()
153
-
154
- local writeKey = keyCodec.write
155
- local writeVal = valueCodec.write
156
- local readKey = keyCodec.read
157
- local readVal = valueCodec.read
158
-
159
- local keyInternal = keyCodec :: InternalCodec<any>
160
- local valInternal = valueCodec :: InternalCodec<any>
161
-
162
- local keySize = keyInternal._size
163
- local valSize = valInternal._size
164
- local keyDWrite = keyInternal._directWrite
165
- local valDWrite = valInternal._directWrite
166
- local keyDRead = keyInternal._directRead
167
- local valDRead = valInternal._directRead
168
-
169
- local scratchDirect = keySize and valSize and keyDWrite and valDWrite
170
- local entrySize = if keySize and valSize then keySize + valSize else nil
171
-
172
- type EntryBounds = {
173
- keyOff: number,
174
- keyLen: number,
175
- valOff: number,
176
- valLen: number,
177
- }
178
-
179
- type DeltaMapCache = {
180
- raw: buffer,
181
- entries: { EntryBounds },
182
- keyLookup: { [string]: number },
183
- count: number,
184
- }
87
+ ): Types.InternalCodec<any>
88
+ local deltaId = allocDeltaId()
89
+ local innerMap = Map.map(keyCodec, valueCodec, maxCount)
185
90
 
186
91
  return table.freeze({
187
92
  _isDelta = true,
188
93
 
189
- write = function(ch: ChannelState, value: { [any]: any }): ()
190
- local scratch = acquireScratch()
191
-
192
- local entries = {} :: { EntryBounds }
193
- local keyLookup = {} :: { [string]: number }
194
- local entryCount = 0
195
-
196
- if scratchDirect then
197
- for k, v in value do
198
- entryCount += 1
199
- local c = scratch.cursor
200
- alloc(scratch, entrySize :: number)
201
- local b = scratch.buff
202
- keyDWrite(b, c, k)
203
- valDWrite(b, c + (keySize :: number), v)
204
- scratch.cursor = c + (entrySize :: number)
205
-
206
- entries[entryCount] = {
207
- keyOff = c,
208
- keyLen = keySize :: number,
209
- valOff = c + (keySize :: number),
210
- valLen = valSize :: number,
211
- }
212
-
213
- local keyId = buffer.readstring(b, c, keySize :: number)
214
- keyLookup[keyId] = entryCount
215
- end
216
- else
217
- for k, v in value do
218
- entryCount += 1
219
- local keyStart = scratch.cursor
220
- writeKey(scratch, k)
221
- local keyEnd = scratch.cursor
222
- writeVal(scratch, v)
223
- local valEnd = scratch.cursor
224
-
225
- entries[entryCount] = {
226
- keyOff = keyStart,
227
- keyLen = keyEnd - keyStart,
228
- valOff = keyEnd,
229
- valLen = valEnd - keyEnd,
230
- }
231
-
232
- local keyId = buffer.readstring(scratch.buff, keyStart, keyEnd - keyStart)
233
- keyLookup[keyId] = entryCount
234
- end
235
- end
236
-
237
- local scratchTotal = scratch.cursor
238
- local cache = ch.deltas[deltaId] :: DeltaMapCache?
94
+ write = function(ch: Types.ChannelState, value: any): ()
95
+ local cache = ch.deltas[deltaId]
239
96
 
240
97
  if not cache then
241
- alloc(ch, 1)
242
- buffer.writeu8(ch.buff, ch.cursor, FLAG_FULL)
243
- ch.cursor += 1
244
- Varint.write(ch, entryCount)
245
-
246
- if scratchTotal > 0 then
247
- alloc(ch, scratchTotal)
248
- buffer.copy(ch.buff, ch.cursor, scratch.buff, 0, scratchTotal)
249
- ch.cursor += scratchTotal
250
- end
251
- else
252
- local cacheRaw = cache.raw
253
- local cacheEntries = cache.entries
254
- local cacheLookup = cache.keyLookup
255
-
256
- local upsertKeys = {} :: { number }
257
- local upsertN = 0
258
-
259
- for keyId, idx in keyLookup do
260
- local oldIdx = cacheLookup[keyId]
261
- if not oldIdx then
262
- upsertN += 1
263
- upsertKeys[upsertN] = idx
264
- else
265
- local newE = entries[idx]
266
- local oldE = cacheEntries[oldIdx]
267
- if
268
- newE.valLen ~= oldE.valLen
269
- or not rangeEqual(
270
- scratch.buff,
271
- newE.valOff,
272
- cacheRaw,
273
- oldE.valOff,
274
- newE.valLen
275
- )
276
- then
277
- upsertN += 1
278
- upsertKeys[upsertN] = idx
279
- end
280
- end
281
- end
282
-
283
- local removeKeyIds = {} :: { string }
284
- local removeN = 0
285
-
286
- for keyId in cacheLookup do
287
- if not keyLookup[keyId] then
288
- removeN += 1
289
- removeKeyIds[removeN] = keyId
290
- end
291
- end
292
-
293
- if upsertN == 0 and removeN == 0 then
98
+ local cursor = ch.cursor
99
+ if cursor + 1 > ch.size then
294
100
  alloc(ch, 1)
295
- buffer.writeu8(ch.buff, ch.cursor, FLAG_UNCHANGED)
296
- ch.cursor += 1
297
- releaseScratch()
298
- return
299
- end
300
-
301
- alloc(ch, 1)
302
- buffer.writeu8(ch.buff, ch.cursor, FLAG_DELTA)
303
- ch.cursor += 1
304
-
305
- Varint.write(ch, upsertN)
306
- for i = 1, upsertN do
307
- local e = entries[upsertKeys[i]]
308
- local totalLen = e.keyLen + e.valLen
309
- alloc(ch, totalLen)
310
- buffer.copy(ch.buff, ch.cursor, scratch.buff, e.keyOff, totalLen)
311
- ch.cursor += totalLen
312
101
  end
102
+ writeu8(ch.buff, cursor, 3) -- FLAG_FULL
103
+ ch.cursor = cursor + 1
104
+
105
+ local scratch = acquireScratch()
106
+ innerMap.write(scratch, value)
107
+ local snapLen = scratch.cursor
108
+ local snapBuf = buffer.create(snapLen)
109
+ buffer.copy(snapBuf, 0, scratch.buff, 0, snapLen)
110
+
111
+ ch.deltas[deltaId] = { raw = snapBuf, len = snapLen }
112
+ innerMap.write(ch, value)
113
+ releaseScratch()
114
+ return
115
+ end
313
116
 
314
- Varint.write(ch, removeN)
315
- for i = 1, removeN do
316
- local keyId = removeKeyIds[i]
317
- local oldIdx = cacheLookup[keyId]
318
- local oldE = cacheEntries[oldIdx]
319
- alloc(ch, oldE.keyLen)
320
- buffer.copy(ch.buff, ch.cursor, cacheRaw, oldE.keyOff, oldE.keyLen)
321
- ch.cursor += oldE.keyLen
117
+ local scratch = acquireScratch()
118
+ innerMap.write(scratch, value)
119
+ local scratchLen = scratch.cursor
120
+ local cachedLen = cache.len
121
+
122
+ if
123
+ scratchLen == cachedLen and rangeEqual(scratch.buff, 0, cache.raw, 0, scratchLen)
124
+ then
125
+ local cursor = ch.cursor
126
+ if cursor + 1 > ch.size then
127
+ alloc(ch, 1)
322
128
  end
129
+ writeu8(ch.buff, cursor, DELTA_UNCHANGED)
130
+ ch.cursor = cursor + 1
131
+ releaseScratch()
132
+ return
323
133
  end
324
134
 
325
- local rawBuf = buffer.create(scratchTotal)
326
- if scratchTotal > 0 then
327
- buffer.copy(rawBuf, 0, scratch.buff, 0, scratchTotal)
135
+ -- Changed: full re-send
136
+ local cursor = ch.cursor
137
+ if cursor + 1 > ch.size then
138
+ alloc(ch, 1)
328
139
  end
140
+ writeu8(ch.buff, cursor, 3) -- FLAG_FULL
141
+ ch.cursor = cursor + 1
142
+ innerMap.write(ch, value)
329
143
 
330
- ch.deltas[deltaId] = {
331
- raw = rawBuf,
332
- entries = entries,
333
- keyLookup = keyLookup,
334
- count = entryCount,
335
- }
144
+ local newBuf = buffer.create(scratchLen)
145
+ buffer.copy(newBuf, 0, scratch.buff, 0, scratchLen)
146
+ cache.raw = newBuf
147
+ cache.len = scratchLen
336
148
 
337
149
  releaseScratch()
338
150
  end,
339
151
 
340
- read = function(src: buffer, pos: number, refs: { Instance }?): ({ [any]: any }, number)
341
- local flag = buffer.readu8(src, pos)
342
- local absPos = pos + 1
343
-
344
- if flag == FLAG_UNCHANGED then
345
- local cache = Baseline.getCache(deltaId)
346
- return if cache then table.clone(cache) else {}, 1
347
- end
348
-
349
- if flag == FLAG_FULL then
350
- local count, lenBytes = Varint.read(src, absPos)
351
- absPos += lenBytes
352
-
353
- if maxCount and count > maxCount then
354
- error(`[Lync] Map count {count} exceeds max {maxCount}`)
355
- end
356
-
357
- local result = {}
358
-
359
- if keyDRead and valDRead and keySize and valSize then
360
- for _ = 1, count do
361
- local k = keyDRead(src, absPos)
362
- local v = valDRead(src, absPos + keySize)
363
- result[k] = v
364
- absPos += entrySize :: number
365
- end
366
- else
367
- for _ = 1, count do
368
- local k, kn = readKey(src, absPos, refs)
369
- absPos += kn
370
- local v, vn = readVal(src, absPos, refs)
371
- absPos += vn
372
- result[k] = v
373
- end
374
- end
375
-
376
- Baseline.setCache(deltaId, result)
377
- return result, absPos - pos
378
- end
379
-
380
- local cache = Baseline.getCache(deltaId) :: { [any]: any }?
381
- local result = if cache then table.clone(cache) else {}
382
-
383
- local upsertN, unBytes = Varint.read(src, absPos)
384
- absPos += unBytes
385
-
386
- for _ = 1, upsertN do
387
- local k, kn = readKey(src, absPos, refs)
388
- absPos += kn
389
- local v, vn = readVal(src, absPos, refs)
390
- absPos += vn
391
- result[k] = v
392
- end
393
-
394
- local removeN, rmBytes = Varint.read(src, absPos)
395
- absPos += rmBytes
152
+ read = function(src: buffer, pos: number, refs: { Instance }?): (any, number)
153
+ local flag = readu8(src, pos)
396
154
 
397
- for _ = 1, removeN do
398
- local k, kn = readKey(src, absPos, refs)
399
- absPos += kn
400
- result[k] = nil
155
+ if flag == DELTA_UNCHANGED then
156
+ return {} :: any, 1
401
157
  end
402
158
 
403
- Baseline.setCache(deltaId, result)
404
- return result, absPos - pos
159
+ local value, consumed = innerMap.read(src, pos + 1, refs)
160
+ return value, 1 + consumed
405
161
  end,
406
- })
162
+ } :: any)
407
163
  end
408
164
 
409
165
  return table.freeze(Map)
@@ -1,48 +1,52 @@
1
1
  --!strict
2
- --!native
2
+ --!optimize 2
3
3
  -- Optional codec. 1 byte flag, value only if present.
4
4
 
5
- local Channel = require(script.Parent.Parent.Parent.internal.Channel)
5
+ local Base = require(script.Parent.Parent.Base)
6
6
  local Types = require(script.Parent.Parent.Parent.Types)
7
7
 
8
- type ChannelState = Types.ChannelState
9
- type Codec<T> = Types.Codec<T>
8
+ -- Private -------------------------------------------------------------
10
9
 
11
- local alloc = Channel.alloc
10
+ local alloc = Base.alloc
11
+ local writeu8 = buffer.writeu8
12
+ local readu8 = buffer.readu8
12
13
 
13
14
  -- Public --------------------------------------------------------------
14
15
 
15
16
  local Optional = {}
16
17
 
17
- function Optional.optional(inner: Codec<any>): Codec<any>
18
- local innerWrite = inner.write
19
- local innerRead = inner.read
20
-
21
- return table.freeze({
22
- write = function(ch: ChannelState, value: any): ()
23
- local c = ch.cursor
24
- if c + 1 > ch.size then
18
+ function Optional.optional(inner: Types.InternalCodec<any>): Types.InternalCodec<any>
19
+ local codec: any = {
20
+ write = function(ch: Types.ChannelState, value: any): ()
21
+ local cursor = ch.cursor
22
+ if cursor + 1 > ch.size then
25
23
  alloc(ch, 1)
26
24
  end
27
25
  if value == nil then
28
- buffer.writeu8(ch.buff, c, 0)
29
- ch.cursor = c + 1
26
+ writeu8(ch.buff, cursor, 0)
27
+ ch.cursor = cursor + 1
30
28
  else
31
- buffer.writeu8(ch.buff, c, 1)
32
- ch.cursor = c + 1
33
- innerWrite(ch, value)
29
+ writeu8(ch.buff, cursor, 1)
30
+ ch.cursor = cursor + 1
31
+ inner.write(ch, value)
34
32
  end
35
33
  end,
34
+
36
35
  read = function(src: buffer, pos: number, refs: { Instance }?): (any, number)
37
- local flag = buffer.readu8(src, pos)
36
+ local flag = readu8(src, pos)
38
37
  if flag == 0 then
39
38
  return nil, 1
40
39
  end
41
-
42
- local value, n = innerRead(src, pos + 1, refs)
43
- return value, 1 + n
40
+ local value, consumed = inner.read(src, pos + 1, refs)
41
+ return value, 1 + consumed
44
42
  end,
45
- })
43
+ }
44
+
45
+ if (inner :: any)._hasUnknown then
46
+ codec._hasUnknown = true
47
+ end
48
+
49
+ return table.freeze(codec)
46
50
  end
47
51
 
48
52
  return table.freeze(Optional)