@axpecter/lync 1.3.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 (52) hide show
  1. package/README.md +300 -0
  2. package/package.json +38 -0
  3. package/src/Types.luau +63 -0
  4. package/src/api/Group.luau +126 -0
  5. package/src/api/Namespace.luau +226 -0
  6. package/src/api/Packet.luau +147 -0
  7. package/src/api/Query.luau +295 -0
  8. package/src/api/Signal.luau +224 -0
  9. package/src/codec/Base.luau +49 -0
  10. package/src/codec/composite/Array.luau +275 -0
  11. package/src/codec/composite/Map.luau +395 -0
  12. package/src/codec/composite/Optional.luau +47 -0
  13. package/src/codec/composite/Shared.luau +151 -0
  14. package/src/codec/composite/Struct.luau +440 -0
  15. package/src/codec/composite/Tagged.luau +222 -0
  16. package/src/codec/composite/Tuple.luau +143 -0
  17. package/src/codec/datatype/Buffer.luau +44 -0
  18. package/src/codec/datatype/CFrame.luau +51 -0
  19. package/src/codec/datatype/Color.luau +22 -0
  20. package/src/codec/datatype/Instance.luau +48 -0
  21. package/src/codec/datatype/IntVector.luau +25 -0
  22. package/src/codec/datatype/NumberRange.luau +14 -0
  23. package/src/codec/datatype/Ray.luau +27 -0
  24. package/src/codec/datatype/Rect.luau +21 -0
  25. package/src/codec/datatype/Region.luau +58 -0
  26. package/src/codec/datatype/Sequence.luau +129 -0
  27. package/src/codec/datatype/String.luau +87 -0
  28. package/src/codec/datatype/UDim.luau +27 -0
  29. package/src/codec/datatype/Vector.luau +25 -0
  30. package/src/codec/meta/Auto.luau +353 -0
  31. package/src/codec/meta/Bitfield.luau +191 -0
  32. package/src/codec/meta/Custom.luau +27 -0
  33. package/src/codec/meta/Enum.luau +80 -0
  34. package/src/codec/meta/Nothing.luau +9 -0
  35. package/src/codec/meta/Quantized.luau +170 -0
  36. package/src/codec/meta/Unknown.luau +35 -0
  37. package/src/codec/primitive/Bool.luau +30 -0
  38. package/src/codec/primitive/Float16.luau +111 -0
  39. package/src/codec/primitive/Number.luau +48 -0
  40. package/src/codec/primitive/Varint.luau +76 -0
  41. package/src/index.d.ts +279 -0
  42. package/src/init.luau +161 -0
  43. package/src/internal/Baseline.luau +41 -0
  44. package/src/internal/Channel.luau +235 -0
  45. package/src/internal/Middleware.luau +109 -0
  46. package/src/internal/Pool.luau +68 -0
  47. package/src/internal/Registry.luau +146 -0
  48. package/src/transport/Bridge.luau +66 -0
  49. package/src/transport/Client.luau +151 -0
  50. package/src/transport/Gate.luau +222 -0
  51. package/src/transport/Reader.luau +175 -0
  52. package/src/transport/Server.luau +364 -0
@@ -0,0 +1,143 @@
1
+ --!strict
2
+ --!native
3
+ -- Positional ordered codec. Like struct but indexed, not keyed.
4
+
5
+ local Channel = require (script.Parent.Parent.Parent.internal.Channel)
6
+ local alloc = Channel.alloc
7
+ local Types = require (script.Parent.Parent.Parent.Types)
8
+
9
+ type ChannelState = Types.ChannelState
10
+ type Codec<T> = Types.Codec<T>
11
+
12
+ -- Public -----------------------------------------------------------------
13
+
14
+ local Tuple = {}
15
+
16
+ function Tuple.define (...: Codec<any>): Codec<{ any }>
17
+ local codecs = { ... }
18
+ local count = #codecs
19
+ if count == 0 then
20
+ error ("[Lync] Tuple requires at least one codec")
21
+ end
22
+
23
+ table.freeze (codecs)
24
+
25
+ local canDirect = true
26
+ local totalSize: number = 0
27
+ local directWrites = table.create (count) :: { any }
28
+ local directReads = table.create (count) :: { any }
29
+ local fieldSizes = table.create (count) :: { number }
30
+
31
+ for i = 1, count do
32
+ local codec = codecs[i] :: any
33
+ local size = codec._size
34
+ local dw = codec._directWrite
35
+ local dr = codec._directRead
36
+
37
+ if size and dw and dr then
38
+ fieldSizes[i] = size
39
+ directWrites[i] = dw
40
+ directReads[i] = dr
41
+ totalSize += size
42
+ else
43
+ canDirect = false
44
+ if size then
45
+ totalSize += size
46
+ else
47
+ totalSize = nil :: any
48
+ end
49
+ break
50
+ end
51
+ end
52
+
53
+ -- Recompute totalSize for generic path if direct failed partway
54
+ if not canDirect then
55
+ totalSize = 0 :: any
56
+ for i = 1, count do
57
+ local size = (codecs[i] :: any)._size
58
+ if size then
59
+ totalSize = (totalSize :: number) + size
60
+ else
61
+ totalSize = nil :: any
62
+ break
63
+ end
64
+ end
65
+ end
66
+
67
+ if canDirect then
68
+ table.freeze (directWrites)
69
+ table.freeze (directReads)
70
+ table.freeze (fieldSizes)
71
+
72
+ local function directWriteBody (b: buffer, c: number, value: { any }): ()
73
+ for i = 1, count do
74
+ directWrites[i] (b, c, value[i])
75
+ c += fieldSizes[i]
76
+ end
77
+ end
78
+
79
+ local function directReadBody (b: buffer, pos: number): { any }
80
+ local result = table.create (count)
81
+ local c = pos
82
+ for i = 1, count do
83
+ result[i] = directReads[i] (b, c)
84
+ c += fieldSizes[i]
85
+ end
86
+ return result
87
+ end
88
+
89
+ return table.freeze ({
90
+ _size = totalSize,
91
+ _directWrite = directWriteBody,
92
+ _directRead = directReadBody,
93
+ write = function (ch: ChannelState, value: { any }): ()
94
+ local c = ch.cursor
95
+ if c + totalSize > ch.size then
96
+ alloc (ch, totalSize)
97
+ end
98
+ directWriteBody (ch.buff, c, value)
99
+ ch.cursor = c + totalSize
100
+ end,
101
+ read = function (src: buffer, pos: number, _refs: { Instance }?): ({ any }, number)
102
+ return directReadBody (src, pos), totalSize
103
+ end,
104
+ })
105
+ end
106
+
107
+ local writeFns = table.create (count) :: { any }
108
+ local readFns = table.create (count) :: { any }
109
+ for i = 1, count do
110
+ writeFns[i] = codecs[i].write
111
+ readFns[i] = codecs[i].read
112
+ end
113
+ table.freeze (writeFns)
114
+ table.freeze (readFns)
115
+
116
+ local hasFixedTotal = totalSize ~= nil
117
+
118
+ return table.freeze ({
119
+ _size = totalSize,
120
+ write = function (ch: ChannelState, value: { any }): ()
121
+ if hasFixedTotal then
122
+ alloc (ch, totalSize :: number)
123
+ end
124
+ for i = 1, count do
125
+ writeFns[i] (ch, value[i])
126
+ end
127
+ end,
128
+ read = function (src: buffer, pos: number, refs: { Instance }?): ({ any }, number)
129
+ local result = table.create (count)
130
+ local absPos = pos
131
+
132
+ for i = 1, count do
133
+ local value, n = readFns[i] (src, absPos, refs)
134
+ result[i] = value
135
+ absPos += n
136
+ end
137
+
138
+ return result, absPos - pos
139
+ end,
140
+ })
141
+ end
142
+
143
+ return table.freeze (Tuple)
@@ -0,0 +1,44 @@
1
+ --!strict
2
+ --!native
3
+ -- Raw buffer codec with varint length prefix.
4
+
5
+ local Channel = require (script.Parent.Parent.Parent.internal.Channel)
6
+ local alloc = Channel.alloc
7
+ local Types = require (script.Parent.Parent.Parent.Types)
8
+ local Varint = require (script.Parent.Parent.primitive.Varint)
9
+
10
+ type ChannelState = Types.ChannelState
11
+
12
+ -- Public -----------------------------------------------------------------
13
+
14
+ local Buffer = {}
15
+
16
+ Buffer.buff = table.freeze ({
17
+ write = function (ch: ChannelState, value: buffer): ()
18
+ local len = buffer.len (value)
19
+ Varint.write (ch, len)
20
+ local c = ch.cursor
21
+ if c + len > ch.size then
22
+ alloc (ch, len)
23
+ end
24
+ buffer.copy (ch.buff, c, value, 0, len)
25
+ ch.cursor = c + len
26
+ end,
27
+ read = function (src: buffer, pos: number, _refs: { Instance }?): (buffer, number)
28
+ local len, lenBytes = Varint.read (src, pos)
29
+ if len == 0 then
30
+ return buffer.create (0), lenBytes
31
+ end
32
+
33
+ local remaining = buffer.len (src) - pos - lenBytes
34
+ if len > remaining then
35
+ error (`[Lync] Buffer length exceeds remaining: {len} > {remaining}`)
36
+ end
37
+
38
+ local out = buffer.create (len)
39
+ buffer.copy (out, 0, src, pos + lenBytes, len)
40
+ return out, lenBytes + len
41
+ end,
42
+ })
43
+
44
+ return table.freeze (Buffer)
@@ -0,0 +1,51 @@
1
+ --!strict
2
+ --!native
3
+ -- Axis-angle CFrame codec. Zero axis at identity for clean deltas.
4
+
5
+ local Base = require (script.Parent.Parent.Base)
6
+
7
+ -- f32 machine epsilon * 2 to absorb round-trip noise.
8
+ local EPSILON = 2.4e-7
9
+ local sqrt = math.sqrt
10
+
11
+ local function writeCFrame (b: buffer, c: number, value: CFrame): ()
12
+ local position = value.Position
13
+ local axis, angle = value:ToAxisAngle ()
14
+
15
+ buffer.writef32 (b, c, position.X)
16
+ buffer.writef32 (b, c + 4, position.Y)
17
+ buffer.writef32 (b, c + 8, position.Z)
18
+
19
+ if angle < EPSILON then
20
+ buffer.writef32 (b, c + 12, 0)
21
+ buffer.writef32 (b, c + 16, 0)
22
+ buffer.writef32 (b, c + 20, 0)
23
+ else
24
+ buffer.writef32 (b, c + 12, axis.X * angle)
25
+ buffer.writef32 (b, c + 16, axis.Y * angle)
26
+ buffer.writef32 (b, c + 20, axis.Z * angle)
27
+ end
28
+ end
29
+
30
+ local function readCFrame (b: buffer, pos: number): CFrame
31
+ local px = buffer.readf32 (b, pos)
32
+ local py = buffer.readf32 (b, pos + 4)
33
+ local pz = buffer.readf32 (b, pos + 8)
34
+ local rx = buffer.readf32 (b, pos + 12)
35
+ local ry = buffer.readf32 (b, pos + 16)
36
+ local rz = buffer.readf32 (b, pos + 20)
37
+
38
+ local angle = sqrt (rx * rx + ry * ry + rz * rz)
39
+ local origin = CFrame.new (px, py, pz)
40
+
41
+ if angle < EPSILON then
42
+ return origin
43
+ end
44
+
45
+ local inv = 1 / angle
46
+ return origin * CFrame.fromAxisAngle (Vector3.new (rx * inv, ry * inv, rz * inv), angle)
47
+ end
48
+
49
+ return table.freeze ({
50
+ cframe = Base.define (24, writeCFrame, readCFrame),
51
+ })
@@ -0,0 +1,22 @@
1
+ --!strict
2
+ --!native
3
+ -- Color3 codec. 3 bytes, 0-255 per channel, clamped.
4
+
5
+ local Base = require (script.Parent.Parent.Base)
6
+
7
+ local round = math.round
8
+ local clamp = math.clamp
9
+
10
+ return table.freeze ({
11
+ color3 = Base.define (3, function (b: buffer, o: number, v: Color3): ()
12
+ buffer.writeu8 (b, o, round (clamp (v.R, 0, 1) * 255))
13
+ buffer.writeu8 (b, o + 1, round (clamp (v.G, 0, 1) * 255))
14
+ buffer.writeu8 (b, o + 2, round (clamp (v.B, 0, 1) * 255))
15
+ end, function (b: buffer, o: number): Color3
16
+ return Color3.fromRGB (
17
+ buffer.readu8 (b, o),
18
+ buffer.readu8 (b, o + 1),
19
+ buffer.readu8 (b, o + 2)
20
+ )
21
+ end),
22
+ })
@@ -0,0 +1,48 @@
1
+ --!strict
2
+ --!native
3
+ -- Instance codec via sidecar reference array.
4
+
5
+ local Channel = require (script.Parent.Parent.Parent.internal.Channel)
6
+ local alloc = Channel.alloc
7
+ local Types = require (script.Parent.Parent.Parent.Types)
8
+
9
+ type ChannelState = Types.ChannelState
10
+
11
+ -- Public -----------------------------------------------------------------
12
+
13
+ local InstanceCodec = {}
14
+
15
+ InstanceCodec.inst = table.freeze ({
16
+ _size = 2,
17
+ write = function (ch: ChannelState, value: Instance): ()
18
+ local refs = ch.refs
19
+ local idx = #refs + 1
20
+ if idx > 65535 then
21
+ error (`[Lync] Instance ref overflow: {idx} exceeds u16 max`)
22
+ end
23
+
24
+ refs[idx] = value
25
+
26
+ local c = ch.cursor
27
+ if c + 2 > ch.size then
28
+ alloc (ch, 2)
29
+ end
30
+ buffer.writeu16 (ch.buff, c, idx)
31
+ ch.cursor = c + 2
32
+ end,
33
+ read = function (src: buffer, pos: number, refs: { Instance }?): (Instance, number)
34
+ local idx = buffer.readu16 (src, pos)
35
+ if not refs then
36
+ error ("[Lync] Instance read requires refs array")
37
+ end
38
+
39
+ local inst = refs[idx]
40
+ if not inst then
41
+ error (`[Lync] Instance ref index out of bounds: {idx}`)
42
+ end
43
+
44
+ return inst, 2
45
+ end,
46
+ })
47
+
48
+ return table.freeze (InstanceCodec)
@@ -0,0 +1,25 @@
1
+ --!strict
2
+ --!native
3
+ -- Vector2int16 (4B) and Vector3int16 (6B) codecs.
4
+
5
+ local Base = require (script.Parent.Parent.Base)
6
+
7
+ return table.freeze ({
8
+ vec2int16 = Base.define (4, function (b: buffer, o: number, v: Vector2int16): ()
9
+ buffer.writei16 (b, o, v.X)
10
+ buffer.writei16 (b, o + 2, v.Y)
11
+ end, function (b: buffer, o: number): Vector2int16
12
+ return Vector2int16.new (buffer.readi16 (b, o), buffer.readi16 (b, o + 2))
13
+ end),
14
+ vec3int16 = Base.define (6, function (b: buffer, o: number, v: Vector3int16): ()
15
+ buffer.writei16 (b, o, v.X)
16
+ buffer.writei16 (b, o + 2, v.Y)
17
+ buffer.writei16 (b, o + 4, v.Z)
18
+ end, function (b: buffer, o: number): Vector3int16
19
+ return Vector3int16.new (
20
+ buffer.readi16 (b, o),
21
+ buffer.readi16 (b, o + 2),
22
+ buffer.readi16 (b, o + 4)
23
+ )
24
+ end),
25
+ })
@@ -0,0 +1,14 @@
1
+ --!strict
2
+ --!native
3
+ -- NumberRange codec. 8 bytes: min f32 + max f32.
4
+
5
+ local Base = require (script.Parent.Parent.Base)
6
+
7
+ return table.freeze ({
8
+ numberRange = Base.define (8, function (b: buffer, o: number, v: NumberRange): ()
9
+ buffer.writef32 (b, o, v.Min)
10
+ buffer.writef32 (b, o + 4, v.Max)
11
+ end, function (b: buffer, o: number): NumberRange
12
+ return NumberRange.new (buffer.readf32 (b, o), buffer.readf32 (b, o + 4))
13
+ end),
14
+ })
@@ -0,0 +1,27 @@
1
+ --!strict
2
+ --!native
3
+ -- Ray codec. 24 bytes: origin vec3 + direction vec3.
4
+
5
+ local Base = require (script.Parent.Parent.Base)
6
+
7
+ return table.freeze ({
8
+ ray = Base.define (24, function (b: buffer, o: number, v: Ray): ()
9
+ local origin = v.Origin
10
+ local direction = v.Direction
11
+ buffer.writef32 (b, o, origin.X)
12
+ buffer.writef32 (b, o + 4, origin.Y)
13
+ buffer.writef32 (b, o + 8, origin.Z)
14
+ buffer.writef32 (b, o + 12, direction.X)
15
+ buffer.writef32 (b, o + 16, direction.Y)
16
+ buffer.writef32 (b, o + 20, direction.Z)
17
+ end, function (b: buffer, o: number): Ray
18
+ return Ray.new (
19
+ Vector3.new (buffer.readf32 (b, o), buffer.readf32 (b, o + 4), buffer.readf32 (b, o + 8)),
20
+ Vector3.new (
21
+ buffer.readf32 (b, o + 12),
22
+ buffer.readf32 (b, o + 16),
23
+ buffer.readf32 (b, o + 20)
24
+ )
25
+ )
26
+ end),
27
+ })
@@ -0,0 +1,21 @@
1
+ --!strict
2
+ --!native
3
+ -- Rect codec. 16 bytes: 4x f32.
4
+
5
+ local Base = require (script.Parent.Parent.Base)
6
+
7
+ return table.freeze ({
8
+ rect = Base.define (16, function (b: buffer, o: number, v: Rect): ()
9
+ buffer.writef32 (b, o, v.Min.X)
10
+ buffer.writef32 (b, o + 4, v.Min.Y)
11
+ buffer.writef32 (b, o + 8, v.Max.X)
12
+ buffer.writef32 (b, o + 12, v.Max.Y)
13
+ end, function (b: buffer, o: number): Rect
14
+ return Rect.new (
15
+ buffer.readf32 (b, o),
16
+ buffer.readf32 (b, o + 4),
17
+ buffer.readf32 (b, o + 8),
18
+ buffer.readf32 (b, o + 12)
19
+ )
20
+ end),
21
+ })
@@ -0,0 +1,58 @@
1
+ --!strict
2
+ --!native
3
+ -- Region3 (24B) and Region3int16 (12B) codecs.
4
+
5
+ local Base = require (script.Parent.Parent.Base)
6
+
7
+ local function writeRegion3 (b: buffer, o: number, v: Region3): ()
8
+ local cf = v.CFrame
9
+ local size = v.Size
10
+ local halfSize = size * 0.5
11
+ local pos = cf.Position
12
+ local minV = pos - halfSize
13
+ local maxV = pos + halfSize
14
+ buffer.writef32 (b, o, minV.X)
15
+ buffer.writef32 (b, o + 4, minV.Y)
16
+ buffer.writef32 (b, o + 8, minV.Z)
17
+ buffer.writef32 (b, o + 12, maxV.X)
18
+ buffer.writef32 (b, o + 16, maxV.Y)
19
+ buffer.writef32 (b, o + 20, maxV.Z)
20
+ end
21
+
22
+ local function readRegion3 (b: buffer, o: number): Region3
23
+ return Region3.new (
24
+ Vector3.new (buffer.readf32 (b, o), buffer.readf32 (b, o + 4), buffer.readf32 (b, o + 8)),
25
+ Vector3.new (
26
+ buffer.readf32 (b, o + 12),
27
+ buffer.readf32 (b, o + 16),
28
+ buffer.readf32 (b, o + 20)
29
+ )
30
+ )
31
+ end
32
+
33
+ return table.freeze ({
34
+ region3 = Base.define (24, writeRegion3, readRegion3),
35
+ region3int16 = Base.define (12, function (b: buffer, o: number, v: Region3int16): ()
36
+ local minV = v.Min
37
+ local maxV = v.Max
38
+ buffer.writei16 (b, o, minV.X)
39
+ buffer.writei16 (b, o + 2, minV.Y)
40
+ buffer.writei16 (b, o + 4, minV.Z)
41
+ buffer.writei16 (b, o + 6, maxV.X)
42
+ buffer.writei16 (b, o + 8, maxV.Y)
43
+ buffer.writei16 (b, o + 10, maxV.Z)
44
+ end, function (b: buffer, o: number): Region3int16
45
+ return Region3int16.new (
46
+ Vector3int16.new (
47
+ buffer.readi16 (b, o),
48
+ buffer.readi16 (b, o + 2),
49
+ buffer.readi16 (b, o + 4)
50
+ ),
51
+ Vector3int16.new (
52
+ buffer.readi16 (b, o + 6),
53
+ buffer.readi16 (b, o + 8),
54
+ buffer.readi16 (b, o + 10)
55
+ )
56
+ )
57
+ end),
58
+ })
@@ -0,0 +1,129 @@
1
+ --!strict
2
+ --!native
3
+ -- NumberSequence and ColorSequence codecs with varint length prefix.
4
+
5
+ local Channel = require (script.Parent.Parent.Parent.internal.Channel)
6
+ local alloc = Channel.alloc
7
+ local Types = require (script.Parent.Parent.Parent.Types)
8
+ local Varint = require (script.Parent.Parent.primitive.Varint)
9
+
10
+ type ChannelState = Types.ChannelState
11
+
12
+ type ChannelState = Types.ChannelState
13
+
14
+ -- Constants --------------------------------------------------------------
15
+
16
+ local NS_STRIDE = 12
17
+ local CS_STRIDE = 7
18
+
19
+ local round = math.round
20
+ local clamp = math.clamp
21
+
22
+ -- Public -----------------------------------------------------------------
23
+
24
+ local Sequence = {}
25
+
26
+ Sequence.numberSequence = table.freeze ({
27
+ write = function (ch: ChannelState, value: NumberSequence): ()
28
+ local keypoints = value.Keypoints
29
+ local count = #keypoints
30
+ Varint.write (ch, count)
31
+
32
+ if count == 0 then
33
+ return
34
+ end
35
+
36
+ local totalBytes = count * NS_STRIDE
37
+ local c = ch.cursor
38
+ if c + totalBytes > ch.size then
39
+ alloc (ch, totalBytes)
40
+ end
41
+
42
+ local b = ch.buff
43
+ for i = 1, count do
44
+ local kp = keypoints[i]
45
+ buffer.writef32 (b, c, kp.Time)
46
+ buffer.writef32 (b, c + 4, kp.Value)
47
+ buffer.writef32 (b, c + 8, kp.Envelope)
48
+ c += NS_STRIDE
49
+ end
50
+
51
+ ch.cursor = c
52
+ end,
53
+ read = function (src: buffer, pos: number, _refs: { Instance }?): (NumberSequence, number)
54
+ local count, lenBytes = Varint.read (src, pos)
55
+ local absPos = pos + lenBytes
56
+
57
+ if count == 0 then
58
+ return NumberSequence.new (0), lenBytes
59
+ end
60
+
61
+ local keypoints = table.create (count)
62
+ for i = 1, count do
63
+ keypoints[i] = NumberSequenceKeypoint.new (
64
+ buffer.readf32 (src, absPos),
65
+ buffer.readf32 (src, absPos + 4),
66
+ buffer.readf32 (src, absPos + 8)
67
+ )
68
+ absPos += NS_STRIDE
69
+ end
70
+
71
+ return NumberSequence.new (keypoints), absPos - pos
72
+ end,
73
+ })
74
+
75
+ Sequence.colorSequence = table.freeze ({
76
+ write = function (ch: ChannelState, value: ColorSequence): ()
77
+ local keypoints = value.Keypoints
78
+ local count = #keypoints
79
+ Varint.write (ch, count)
80
+
81
+ if count == 0 then
82
+ return
83
+ end
84
+
85
+ local totalBytes = count * CS_STRIDE
86
+ local c = ch.cursor
87
+ if c + totalBytes > ch.size then
88
+ alloc (ch, totalBytes)
89
+ end
90
+
91
+ local b = ch.buff
92
+ for i = 1, count do
93
+ local kp = keypoints[i]
94
+ local color = kp.Value
95
+ buffer.writef32 (b, c, kp.Time)
96
+ buffer.writeu8 (b, c + 4, round (clamp (color.R, 0, 1) * 255))
97
+ buffer.writeu8 (b, c + 5, round (clamp (color.G, 0, 1) * 255))
98
+ buffer.writeu8 (b, c + 6, round (clamp (color.B, 0, 1) * 255))
99
+ c += CS_STRIDE
100
+ end
101
+
102
+ ch.cursor = c
103
+ end,
104
+ read = function (src: buffer, pos: number, _refs: { Instance }?): (ColorSequence, number)
105
+ local count, lenBytes = Varint.read (src, pos)
106
+ local absPos = pos + lenBytes
107
+
108
+ if count == 0 then
109
+ return ColorSequence.new (Color3.new ()), lenBytes
110
+ end
111
+
112
+ local keypoints = table.create (count)
113
+ for i = 1, count do
114
+ keypoints[i] = ColorSequenceKeypoint.new (
115
+ buffer.readf32 (src, absPos),
116
+ Color3.fromRGB (
117
+ buffer.readu8 (src, absPos + 4),
118
+ buffer.readu8 (src, absPos + 5),
119
+ buffer.readu8 (src, absPos + 6)
120
+ )
121
+ )
122
+ absPos += CS_STRIDE
123
+ end
124
+
125
+ return ColorSequence.new (keypoints), absPos - pos
126
+ end,
127
+ })
128
+
129
+ return table.freeze (Sequence)
@@ -0,0 +1,87 @@
1
+ --!strict
2
+ --!native
3
+ -- Variable-length string codec with varint length prefix.
4
+
5
+ local Channel = require (script.Parent.Parent.Parent.internal.Channel)
6
+ local alloc = Channel.alloc
7
+ local Types = require (script.Parent.Parent.Parent.Types)
8
+ local Varint = require (script.Parent.Parent.primitive.Varint)
9
+
10
+ type ChannelState = Types.ChannelState
11
+
12
+ -- Constants --------------------------------------------------------------
13
+
14
+ local floor = math.floor
15
+
16
+ -- Public -----------------------------------------------------------------
17
+
18
+ local String = {}
19
+
20
+ String.string = table.freeze ({
21
+ write = function (ch: ChannelState, value: string): ()
22
+ local len = #value
23
+
24
+ if len < 128 then
25
+ local total = 1 + len
26
+ local c = ch.cursor
27
+ if c + total > ch.size then
28
+ alloc (ch, total)
29
+ end
30
+ local b = ch.buff
31
+ buffer.writeu8 (b, c, len)
32
+ if len > 0 then
33
+ buffer.writestring (b, c + 1, value)
34
+ end
35
+ ch.cursor = c + total
36
+ else
37
+ Varint.write (ch, len)
38
+ local c = ch.cursor
39
+ if c + len > ch.size then
40
+ alloc (ch, len)
41
+ end
42
+ buffer.writestring (ch.buff, c, value)
43
+ ch.cursor = c + len
44
+ end
45
+ end,
46
+ read = function (src: buffer, pos: number, _refs: { Instance }?): (string, number)
47
+ local len, lenBytes = Varint.read (src, pos)
48
+ if len == 0 then
49
+ return "", lenBytes
50
+ end
51
+
52
+ local remaining = buffer.len (src) - pos - lenBytes
53
+ if len > remaining then
54
+ error (`[Lync] String length exceeds buffer: {len} > {remaining}`)
55
+ end
56
+
57
+ return buffer.readstring (src, pos + lenBytes, len), lenBytes + len
58
+ end,
59
+ })
60
+
61
+ function String.bounded (maxLength: number): any
62
+ if maxLength <= 0 or maxLength ~= floor (maxLength) then
63
+ error (`[Lync] boundedString maxLength must be a positive integer, got {maxLength}`)
64
+ end
65
+
66
+ return table.freeze ({
67
+ write = String.string.write,
68
+ read = function (src: buffer, pos: number, _refs: { Instance }?): (string, number)
69
+ local len, lenBytes = Varint.read (src, pos)
70
+ if len > maxLength then
71
+ error (`[Lync] String length {len} exceeds max {maxLength}`)
72
+ end
73
+ if len == 0 then
74
+ return "", lenBytes
75
+ end
76
+
77
+ local remaining = buffer.len (src) - pos - lenBytes
78
+ if len > remaining then
79
+ error (`[Lync] String length exceeds buffer: {len} > {remaining}`)
80
+ end
81
+
82
+ return buffer.readstring (src, pos + lenBytes, len), lenBytes + len
83
+ end,
84
+ })
85
+ end
86
+
87
+ return table.freeze (String)