@axpecter/lync 2.1.2 → 2.2.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 (56) hide show
  1. package/README.md +241 -349
  2. package/package.json +1 -1
  3. package/src/Types.luau +53 -13
  4. package/src/api/Group.luau +41 -51
  5. package/src/api/Packet.luau +163 -152
  6. package/src/api/Query.luau +224 -211
  7. package/src/api/Scope.luau +36 -38
  8. package/src/api/Signal.luau +68 -94
  9. package/src/codec/Base.luau +43 -23
  10. package/src/codec/composite/Array.luau +161 -152
  11. package/src/codec/composite/Map.luau +37 -53
  12. package/src/codec/composite/Optional.luau +15 -21
  13. package/src/codec/composite/Shared.luau +78 -41
  14. package/src/codec/composite/Struct.luau +64 -125
  15. package/src/codec/composite/Tagged.luau +15 -25
  16. package/src/codec/composite/Tuple.luau +15 -20
  17. package/src/codec/datatype/Buffer.luau +44 -51
  18. package/src/codec/datatype/CFrame.luau +72 -104
  19. package/src/codec/datatype/Color.luau +14 -10
  20. package/src/codec/datatype/Instance.luau +32 -42
  21. package/src/codec/datatype/IntVector.luau +24 -22
  22. package/src/codec/datatype/NumberRange.luau +21 -12
  23. package/src/codec/datatype/Ray.luau +13 -7
  24. package/src/codec/datatype/Rect.luau +13 -7
  25. package/src/codec/datatype/Region.luau +25 -22
  26. package/src/codec/datatype/Sequence.luau +144 -118
  27. package/src/codec/datatype/String.luau +29 -59
  28. package/src/codec/datatype/UDim.luau +29 -30
  29. package/src/codec/datatype/Vector.luau +89 -105
  30. package/src/codec/meta/Auto.luau +194 -246
  31. package/src/codec/meta/Bitfield.luau +37 -36
  32. package/src/codec/meta/Custom.luau +10 -10
  33. package/src/codec/meta/Enum.luau +8 -11
  34. package/src/codec/meta/Float.luau +16 -20
  35. package/src/codec/meta/Nothing.luau +2 -2
  36. package/src/codec/meta/Unknown.luau +22 -32
  37. package/src/codec/primitive/Bool.luau +9 -5
  38. package/src/codec/primitive/Float16.luau +13 -23
  39. package/src/codec/primitive/Int.luau +20 -28
  40. package/src/codec/primitive/Number.luau +6 -10
  41. package/src/codec/primitive/Signed.luau +32 -0
  42. package/src/codec/primitive/Varint.luau +59 -28
  43. package/src/index.d.ts +8 -2
  44. package/src/init.luau +133 -143
  45. package/src/internal/Baseline.luau +17 -18
  46. package/src/internal/Channel.luau +143 -212
  47. package/src/internal/Middleware.luau +37 -47
  48. package/src/internal/Pool.luau +10 -10
  49. package/src/internal/Registry.luau +38 -34
  50. package/src/internal/Transport.luau +28 -0
  51. package/src/internal/Util.luau +26 -0
  52. package/src/transport/Bridge.luau +32 -24
  53. package/src/transport/Client.luau +51 -53
  54. package/src/transport/Gate.luau +183 -113
  55. package/src/transport/Reader.luau +95 -66
  56. package/src/transport/Server.luau +130 -125
@@ -1,15 +1,15 @@
1
1
  --!strict
2
2
  --!optimize 2
3
- -- Positional ordered codec. Like struct but indexed, not keyed.
3
+ -- Positional ordered codec. Like struct but indexed.
4
4
 
5
5
  local Base = require(script.Parent.Parent.Base)
6
6
  local Types = require(script.Parent.Parent.Parent.Types)
7
7
 
8
- -- Private -------------------------------------------------------------
8
+ -- Private ----------------------------------------------------------------
9
9
 
10
10
  local alloc = Base.alloc
11
11
 
12
- -- Public --------------------------------------------------------------
12
+ -- Public -----------------------------------------------------------------
13
13
 
14
14
  local Tuple = {}
15
15
 
@@ -20,46 +20,41 @@ function Tuple.define(...: Types.InternalCodec<any>): Types.InternalCodec<any>
20
20
  error("[Lync] Lync.tuple: requires at least one codec")
21
21
  end
22
22
 
23
- -- Check if all codecs are direct
24
- local allDirect = true
25
- local totalSize = 0
26
- local hasUnknown = false
27
-
23
+ local allDirect, totalSize, hasUnknown = true, 0, false
28
24
  for i = 1, count do
29
- local c = codecs[i] :: any
25
+ local c = codecs[i]
30
26
  if c._hasUnknown then
31
27
  hasUnknown = true
32
28
  end
33
29
  if not c._size or not c._directWrite or not c._directRead then
34
30
  allDirect = false
35
- else
31
+ elseif allDirect then
36
32
  totalSize += c._size
37
33
  end
38
34
  end
39
-
40
35
  table.freeze(codecs)
41
36
 
42
37
  if allDirect then
43
- -- Build precomputed offsets
44
38
  local offsets = table.create(count)
45
39
  local directWrites = table.create(count)
46
40
  local directReads = table.create(count)
47
41
  local cursor = 0
48
42
 
49
43
  for i = 1, count do
50
- local c = codecs[i] :: any
44
+ local c = codecs[i]
51
45
  offsets[i] = cursor
52
46
  directWrites[i] = c._directWrite
53
47
  directReads[i] = c._directRead
54
- cursor += c._size
48
+ cursor += c._size :: number
55
49
  end
56
-
57
50
  table.freeze(offsets)
58
51
  table.freeze(directWrites)
59
52
  table.freeze(directReads)
60
53
 
61
- local codec: any = {
54
+ local codec: Types.InternalCodec<any> = {
62
55
  _size = totalSize,
56
+ _isTuple = true,
57
+ _elements = codecs,
63
58
 
64
59
  _directWrite = function(b: buffer, off: number, value: any): ()
65
60
  for i = 1, count do
@@ -95,15 +90,16 @@ function Tuple.define(...: Types.InternalCodec<any>): Types.InternalCodec<any>
95
90
  return result, totalSize
96
91
  end,
97
92
  }
98
-
99
93
  if hasUnknown then
100
94
  codec._hasUnknown = true
101
95
  end
102
96
  return table.freeze(codec)
103
97
  end
104
98
 
105
- -- Generic path
106
- local codec: any = {
99
+ local codec: Types.InternalCodec<any> = {
100
+ _isTuple = true,
101
+ _elements = codecs,
102
+
107
103
  write = function(ch: Types.ChannelState, value: any): ()
108
104
  for i = 1, count do
109
105
  codecs[i].write(ch, value[i])
@@ -121,7 +117,6 @@ function Tuple.define(...: Types.InternalCodec<any>): Types.InternalCodec<any>
121
117
  return result, total
122
118
  end,
123
119
  }
124
-
125
120
  if hasUnknown then
126
121
  codec._hasUnknown = true
127
122
  end
@@ -6,68 +6,61 @@ local Base = require(script.Parent.Parent.Base)
6
6
  local Types = require(script.Parent.Parent.Parent.Types)
7
7
  local Varint = require(script.Parent.Parent.primitive.Varint)
8
8
 
9
- -- Private -------------------------------------------------------------
9
+ -- Private ----------------------------------------------------------------
10
10
 
11
11
  local alloc = Base.alloc
12
12
  local INLINE = Varint.INLINE_MAX
13
13
 
14
- -- Public --------------------------------------------------------------
14
+ local function writeBuffer(ch: Types.ChannelState, value: buffer): ()
15
+ local len = buffer.len(value)
16
+ local cursor = ch.cursor
15
17
 
16
- local BufferCodec = {}
18
+ if len <= INLINE then
19
+ if cursor + 1 + len > ch.size then
20
+ alloc(ch, 1 + len)
21
+ end
22
+ local b = ch.buff
23
+ buffer.writeu8(b, cursor, len)
24
+ if len > 0 then
25
+ buffer.copy(b, cursor + 1, value, 0, len)
26
+ end
27
+ ch.cursor = cursor + 1 + len
28
+ return
29
+ end
17
30
 
18
- BufferCodec.buff = table.freeze({
19
- _typeCheck = "buffer",
31
+ Varint.write(ch, len)
32
+ cursor = ch.cursor
33
+ if cursor + len > ch.size then
34
+ alloc(ch, len)
35
+ end
36
+ buffer.copy(ch.buff, cursor, value, 0, len)
37
+ ch.cursor = cursor + len
38
+ end
20
39
 
21
- write = function(ch: Types.ChannelState, value: buffer): ()
22
- local len = buffer.len(value)
23
- local cursor = ch.cursor
40
+ local function readBuffer(src: buffer, pos: number, _refs: { Instance }?): (buffer, number)
41
+ local len, lenBytes = Varint.readLengthPrefix(src, pos)
42
+ if len == 0 then
43
+ return buffer.create(0), lenBytes
44
+ end
24
45
 
25
- if len <= INLINE then
26
- if cursor + 1 + len > ch.size then
27
- alloc(ch, 1 + len)
28
- end
29
- local b = ch.buff
30
- buffer.writeu8(b, cursor, len)
31
- if len > 0 then
32
- buffer.copy(b, cursor + 1, value, 0, len)
33
- end
34
- ch.cursor = cursor + 1 + len
35
- else
36
- Varint.write(ch, len)
37
- cursor = ch.cursor
38
- if cursor + len > ch.size then
39
- alloc(ch, len)
40
- end
41
- buffer.copy(ch.buff, cursor, value, 0, len)
42
- ch.cursor = cursor + len
43
- end
44
- end,
46
+ local dataStart = pos + lenBytes
47
+ if dataStart + len > buffer.len(src) then
48
+ error("[Lync] Buffer.read: length exceeds remaining buffer")
49
+ end
45
50
 
46
- read = function(src: buffer, pos: number, _refs: { Instance }?): (buffer, number)
47
- local b0 = buffer.readu8(src, pos)
48
- local len: number
49
- local lenBytes: number
51
+ local out = buffer.create(len)
52
+ buffer.copy(out, 0, src, dataStart, len)
53
+ return out, lenBytes + len
54
+ end
50
55
 
51
- if b0 <= INLINE then
52
- len = b0
53
- lenBytes = 1
54
- else
55
- len, lenBytes = Varint.read(src, pos)
56
- end
56
+ -- Public -----------------------------------------------------------------
57
57
 
58
- if len == 0 then
59
- return buffer.create(0), lenBytes
60
- end
61
-
62
- local dataStart = pos + lenBytes
63
- if dataStart + len > buffer.len(src) then
64
- error("[Lync] Buffer: length exceeds remaining buffer")
65
- end
58
+ local BufferCodec = {}
66
59
 
67
- local out = buffer.create(len)
68
- buffer.copy(out, 0, src, dataStart, len)
69
- return out, lenBytes + len
70
- end,
71
- } :: any)
60
+ BufferCodec.buff = table.freeze({
61
+ _typeCheck = "buffer",
62
+ write = writeBuffer,
63
+ read = readBuffer,
64
+ })
72
65
 
73
66
  return table.freeze(BufferCodec)
@@ -1,32 +1,35 @@
1
1
  --!strict
2
2
  --!native
3
- -- CFrame codec with __call for smallest-three compressed variant.
3
+ -- CFrame codec with __call factory for smallest-three quaternion compression.
4
4
 
5
5
  local Base = require(script.Parent.Parent.Base)
6
+ local Types = require(script.Parent.Parent.Parent.Types)
6
7
 
7
- -- Constants -----------------------------------------------------------
8
+ -- Constants --------------------------------------------------------------
8
9
 
9
10
  local EPSILON = 2.4e-7
10
11
  local EPSILON_SQ = EPSILON * EPSILON
11
- local SQRT = math.sqrt
12
- local COS = math.cos
13
- local SIN = math.sin
14
- local ABS = math.abs
15
- local ROUND = math.round
16
- local ACOS = math.acos
17
-
18
- -- Smallest-three quantization constants
19
- local INV_SQRT2 = 1 / SQRT(2)
12
+
13
+ local INV_SQRT2 = 1 / math.sqrt(2)
20
14
  local Q_SCALE = 1023 / (2 * INV_SQRT2)
21
15
  local Q_INV = (2 * INV_SQRT2) / 1023
22
16
 
17
+ -- Private ----------------------------------------------------------------
18
+
19
+ local alloc = Base.alloc
20
+ local sqrt = math.sqrt
21
+ local cos = math.cos
22
+ local sin = math.sin
23
+ local abs = math.abs
24
+ local round = math.round
25
+ local acos = math.acos
26
+ local clamp = math.clamp
27
+
23
28
  local band = bit32.band
24
29
  local bor = bit32.bor
25
30
  local lshift = bit32.lshift
26
31
  local rshift = bit32.rshift
27
32
 
28
- -- Private: Lossless axis-angle (24 bytes) -----------------------------
29
-
30
33
  local function writeLossless(b: buffer, off: number, value: CFrame): ()
31
34
  local pos = value.Position
32
35
  buffer.writef32(b, off, pos.X)
@@ -38,11 +41,11 @@ local function writeLossless(b: buffer, off: number, value: CFrame): ()
38
41
  buffer.writef32(b, off + 12, 0)
39
42
  buffer.writef32(b, off + 16, 0)
40
43
  buffer.writef32(b, off + 20, 0)
41
- else
42
- buffer.writef32(b, off + 12, axis.X * angle)
43
- buffer.writef32(b, off + 16, axis.Y * angle)
44
- buffer.writef32(b, off + 20, axis.Z * angle)
44
+ return
45
45
  end
46
+ buffer.writef32(b, off + 12, axis.X * angle)
47
+ buffer.writef32(b, off + 16, axis.Y * angle)
48
+ buffer.writef32(b, off + 20, axis.Z * angle)
46
49
  end
47
50
 
48
51
  local function readLossless(b: buffer, off: number): CFrame
@@ -58,53 +61,43 @@ local function readLossless(b: buffer, off: number): CFrame
58
61
  return CFrame.new(px, py, pz)
59
62
  end
60
63
 
61
- local angle = SQRT(sqLen)
64
+ local angle = sqrt(sqLen)
62
65
  local inv = 1 / angle
63
66
  return CFrame.new(px, py, pz)
64
67
  * CFrame.fromAxisAngle(Vector3.new(rx * inv, ry * inv, rz * inv), angle)
65
68
  end
66
69
 
67
- -- Private: Smallest-three compressed (16 bytes) -----------------------
68
-
70
+ --[[
71
+ Compressed wire layout (16 bytes total):
72
+ [0..11] position 3x f32
73
+ [12..15] packed [largest:2][a:10][b:10][c:10] — smallest-three quat
74
+ ]]
69
75
  local function writeCompressed(b: buffer, off: number, value: CFrame): ()
70
- -- Position: 3x f32
71
76
  local pos = value.Position
72
77
  buffer.writef32(b, off, pos.X)
73
78
  buffer.writef32(b, off + 4, pos.Y)
74
79
  buffer.writef32(b, off + 8, pos.Z)
75
80
 
76
- --[[
77
- Convert CFrame rotation to quaternion via axis-angle.
78
- Then apply smallest-three: drop the largest component,
79
- quantize the other three into 10 bits each, pack into u32.
80
- ]]
81
81
  local axis, angle = value:ToAxisAngle()
82
- local halfAngle = angle * 0.5
83
- local s = SIN(halfAngle)
82
+ local h = angle * 0.5
83
+ local s = sin(h)
84
84
  local qx = axis.X * s
85
85
  local qy = axis.Y * s
86
86
  local qz = axis.Z * s
87
- local qw = COS(halfAngle)
88
-
89
- -- Find largest component
90
- local ax, ay, az, aw = ABS(qx), ABS(qy), ABS(qz), ABS(qw)
91
- local largest = 3
92
- local maxVal = aw
87
+ local qw = cos(h)
93
88
 
89
+ local ax, ay, az, aw = abs(qx), abs(qy), abs(qz), abs(qw)
90
+ local largest, maxVal = 3, aw
94
91
  if ax > maxVal then
95
- largest = 0
96
- maxVal = ax
92
+ largest, maxVal = 0, ax
97
93
  end
98
94
  if ay > maxVal then
99
- largest = 1
100
- maxVal = ay
95
+ largest, maxVal = 1, ay
101
96
  end
102
97
  if az > maxVal then
103
98
  largest = 2
104
- maxVal = az
105
99
  end
106
100
 
107
- -- Negate quaternion if largest component is negative
108
101
  local sign: number
109
102
  if largest == 0 then
110
103
  sign = if qx < 0 then -1 else 1
@@ -115,36 +108,24 @@ local function writeCompressed(b: buffer, off: number, value: CFrame): ()
115
108
  else
116
109
  sign = if qw < 0 then -1 else 1
117
110
  end
118
- qx *= sign
119
- qy *= sign
120
- qz *= sign
121
- qw *= sign
111
+ qx, qy, qz, qw = qx * sign, qy * sign, qz * sign, qw * sign
122
112
 
123
- -- Select the three smallest components
124
- local a, b2, c: number
113
+ local a, bComp, c: number
125
114
  if largest == 0 then
126
- a, b2, c = qy, qz, qw
115
+ a, bComp, c = qy, qz, qw
127
116
  elseif largest == 1 then
128
- a, b2, c = qx, qz, qw
117
+ a, bComp, c = qx, qz, qw
129
118
  elseif largest == 2 then
130
- a, b2, c = qx, qy, qw
119
+ a, bComp, c = qx, qy, qw
131
120
  else
132
- a, b2, c = qx, qy, qz
121
+ a, bComp, c = qx, qy, qz
133
122
  end
134
123
 
135
- -- Quantize from [-1/sqrt(2), 1/sqrt(2)] to [0, 1023]
136
- local qa = ROUND((a + INV_SQRT2) * Q_SCALE)
137
- local qb = ROUND((b2 + INV_SQRT2) * Q_SCALE)
138
- local qc = ROUND((c + INV_SQRT2) * Q_SCALE)
139
-
140
- -- Clamp to [0, 1023]
141
- qa = if qa < 0 then 0 elseif qa > 1023 then 1023 else qa
142
- qb = if qb < 0 then 0 elseif qb > 1023 then 1023 else qb
143
- qc = if qc < 0 then 0 elseif qc > 1023 then 1023 else qc
124
+ local qa = clamp(round((a + INV_SQRT2) * Q_SCALE), 0, 1023)
125
+ local qb = clamp(round((bComp + INV_SQRT2) * Q_SCALE), 0, 1023)
126
+ local qc = clamp(round((c + INV_SQRT2) * Q_SCALE), 0, 1023)
144
127
 
145
- -- Pack: [index:2][a:10][b:10][c:10] = 32 bits
146
- local packed = bor(lshift(largest, 30), lshift(qa, 20), lshift(qb, 10), qc)
147
- buffer.writeu32(b, off + 12, packed)
128
+ buffer.writeu32(b, off + 12, bor(lshift(largest, 30), lshift(qa, 20), lshift(qb, 10), qc))
148
129
  end
149
130
 
150
131
  local function readCompressed(b: buffer, off: number): CFrame
@@ -158,72 +139,59 @@ local function readCompressed(b: buffer, off: number): CFrame
158
139
  local qb = band(rshift(packed, 10), 0x3FF)
159
140
  local qc = band(packed, 0x3FF)
160
141
 
161
- -- Dequantize from [0, 1023] to [-1/sqrt(2), 1/sqrt(2)]
162
142
  local a = qa * Q_INV - INV_SQRT2
163
- local b2 = qb * Q_INV - INV_SQRT2
143
+ local bComp = qb * Q_INV - INV_SQRT2
164
144
  local c = qc * Q_INV - INV_SQRT2
165
145
 
166
- -- Reconstruct largest component
167
- local sqSum = a * a + b2 * b2 + c * c
168
- local d = SQRT(if sqSum < 1 then 1 - sqSum else 0)
146
+ local sqSum = a * a + bComp * bComp + c * c
147
+ local d = sqrt(if sqSum < 1 then 1 - sqSum else 0)
169
148
 
170
- -- Place components
171
149
  local qx, qy, qz, qw: number
172
150
  if largest == 0 then
173
- qx, qy, qz, qw = d, a, b2, c
151
+ qx, qy, qz, qw = d, a, bComp, c
174
152
  elseif largest == 1 then
175
- qx, qy, qz, qw = a, d, b2, c
153
+ qx, qy, qz, qw = a, d, bComp, c
176
154
  elseif largest == 2 then
177
- qx, qy, qz, qw = a, b2, d, c
155
+ qx, qy, qz, qw = a, bComp, d, c
178
156
  else
179
- qx, qy, qz, qw = a, b2, c, d
157
+ qx, qy, qz, qw = a, bComp, c, d
180
158
  end
181
159
 
182
- -- Build CFrame from position + quaternion
183
160
  local origin = CFrame.new(px, py, pz)
184
161
  if qx * qx + qy * qy + qz * qz < EPSILON_SQ then
185
162
  return origin
186
163
  end
187
164
 
188
- -- Convert quaternion to axis-angle for CFrame construction
189
- local len = SQRT(qx * qx + qy * qy + qz * qz)
190
- local angle = 2 * ACOS(if qw > 1 then 1 elseif qw < -1 then -1 else qw)
165
+ local len = sqrt(qx * qx + qy * qy + qz * qz)
166
+ local angle = 2 * acos(clamp(qw, -1, 1))
191
167
  local inv = 1 / len
192
168
  return origin * CFrame.fromAxisAngle(Vector3.new(qx * inv, qy * inv, qz * inv), angle)
193
169
  end
194
170
 
195
- -- __call factory for compressed variant
196
- local function compressedFactory(_self: any): any
197
- return Base.define(16, writeCompressed, readCompressed, {
198
- _typeCheck = "CFrame",
199
- })
171
+ local function compressedFactory(_self): Types.InternalCodec<CFrame>
172
+ return Base.define(16, writeCompressed, readCompressed, { _typeCheck = "CFrame" })
200
173
  end
201
174
 
202
- -- Public --------------------------------------------------------------
175
+ -- Public -----------------------------------------------------------------
203
176
 
204
177
  local CFrameCodec = {}
205
178
 
206
- CFrameCodec.cframe = setmetatable(
207
- {
208
- _size = 24,
209
- _directWrite = writeLossless,
210
- _directRead = readLossless,
211
- _typeCheck = "CFrame",
212
-
213
- write = function(ch: Types.ChannelState, value: CFrame): ()
214
- local cursor = ch.cursor
215
- if cursor + 24 > ch.size then
216
- alloc(ch, 24)
217
- end
218
- writeLossless(ch.buff, cursor, value)
219
- ch.cursor = cursor + 24
220
- end,
221
-
222
- read = function(src: buffer, pos: number, _refs: { Instance }?): (CFrame, number)
223
- return readLossless(src, pos), 24
224
- end,
225
- } :: any,
226
- { __call = compressedFactory }
227
- )
179
+ CFrameCodec.cframe = table.freeze(setmetatable({
180
+ _size = 24,
181
+ _directWrite = writeLossless,
182
+ _directRead = readLossless,
183
+ _typeCheck = "CFrame",
184
+ write = function(ch: Types.ChannelState, value: CFrame): ()
185
+ local cursor = ch.cursor
186
+ if cursor + 24 > ch.size then
187
+ alloc(ch, 24)
188
+ end
189
+ writeLossless(ch.buff, cursor, value)
190
+ ch.cursor = cursor + 24
191
+ end,
192
+ read = function(src: buffer, pos: number, _refs: { Instance }?): (CFrame, number)
193
+ return readLossless(src, pos), 24
194
+ end,
195
+ }, table.freeze({ __call = compressedFactory })))
228
196
 
229
197
  return table.freeze(CFrameCodec)
@@ -1,28 +1,32 @@
1
1
  --!strict
2
2
  --!optimize 2
3
- -- Color3 codec. 3 bytes, RGB u8 per channel, clamped.
3
+ -- Color3 codec. 3 bytes, R/G/B u8 per channel, clamped to [0, 1].
4
4
 
5
5
  local Base = require(script.Parent.Parent.Base)
6
6
 
7
- -- Private -------------------------------------------------------------
7
+ -- Private ----------------------------------------------------------------
8
8
 
9
9
  local round = math.round
10
10
  local clamp = math.clamp
11
11
 
12
- -- Public --------------------------------------------------------------
13
-
14
- local ColorCodec = {}
15
-
16
- ColorCodec.color3 = Base.define(3, function(b: buffer, off: number, value: Color3): ()
12
+ local function writeColor3(b: buffer, off: number, value: Color3): ()
17
13
  buffer.writeu8(b, off, round(clamp(value.R, 0, 1) * 255))
18
14
  buffer.writeu8(b, off + 1, round(clamp(value.G, 0, 1) * 255))
19
15
  buffer.writeu8(b, off + 2, round(clamp(value.B, 0, 1) * 255))
20
- end, function(b: buffer, off: number): Color3
16
+ end
17
+
18
+ local function readColor3(b: buffer, off: number): Color3
21
19
  return Color3.fromRGB(
22
20
  buffer.readu8(b, off),
23
21
  buffer.readu8(b, off + 1),
24
22
  buffer.readu8(b, off + 2)
25
23
  )
26
- end, { _typeCheck = "Color3" })
24
+ end
25
+
26
+ -- Public -----------------------------------------------------------------
27
+
28
+ local Color = {}
29
+
30
+ Color.color3 = Base.define(3, writeColor3, readColor3, { _typeCheck = "Color3" })
27
31
 
28
- return table.freeze(ColorCodec)
32
+ return table.freeze(Color)
@@ -2,58 +2,48 @@
2
2
  --!optimize 2
3
3
  -- Instance codec via sidecar reference array.
4
4
 
5
- local Base = require(script.Parent.Parent.Base)
5
+ local Channel = require(script.Parent.Parent.Parent.internal.Channel)
6
6
  local Types = require(script.Parent.Parent.Parent.Types)
7
7
 
8
- -- Private -------------------------------------------------------------
8
+ -- Private ----------------------------------------------------------------
9
9
 
10
- local alloc = Base.alloc
11
- local writeu16 = buffer.writeu16
10
+ local pushRef = Channel.pushRef
12
11
  local readu16 = buffer.readu16
13
12
 
14
- -- Public --------------------------------------------------------------
13
+ local function writeInstance(ch: Types.ChannelState, value: Instance): ()
14
+ pushRef(ch, value)
15
+ end
16
+
17
+ local function readInstance(src: buffer, pos: number, refs: { Instance }?): (Instance, number)
18
+ if not refs then
19
+ error("[Lync] Instance.read: refs array is required")
20
+ end
21
+
22
+ local idx = readu16(src, pos)
23
+ if idx < 1 or idx > #refs then
24
+ error(`[Lync] Instance.read: ref index {idx} out of bounds`)
25
+ end
26
+
27
+ local inst = refs[idx]
28
+ if typeof(inst) ~= "Instance" then
29
+ error("[Lync] Instance.read: ref is not an Instance")
30
+ end
31
+ return inst, 2
32
+ end
33
+
34
+ -- Public -----------------------------------------------------------------
15
35
 
16
36
  local InstanceCodec = {}
17
37
 
18
- -- No _directWrite/_directRead: write needs ch.refs, so structs can't use the all-direct path
38
+ --[[
39
+ No _directWrite/_directRead: write needs ch.refs, so structs/arrays
40
+ cannot use the all-direct fast path with this codec.
41
+ ]]
19
42
  InstanceCodec.inst = table.freeze({
20
43
  _size = 2,
21
44
  _typeCheck = "Instance",
22
-
23
- write = function(ch: Types.ChannelState, value: Instance): ()
24
- local idx = ch.refCount + 1
25
- if idx > 0xFFFF then
26
- error("[Lync] Instance: ref overflow (>65535)")
27
- end
28
-
29
- ch.refs[idx] = value
30
- ch.refCount = idx
31
-
32
- local cursor = ch.cursor
33
- if cursor + 2 > ch.size then
34
- alloc(ch, 2)
35
- end
36
- writeu16(ch.buff, cursor, idx)
37
- ch.cursor = cursor + 2
38
- end,
39
-
40
- read = function(src: buffer, pos: number, refs: { Instance }?): (Instance, number)
41
- if not refs then
42
- error("[Lync] Instance: read requires refs array")
43
- end
44
-
45
- local idx = readu16(src, pos)
46
- if idx < 1 or idx > #refs then
47
- error(`[Lync] Instance: ref index {idx} out of bounds`)
48
- end
49
-
50
- local inst = refs[idx]
51
- if typeof(inst) ~= "Instance" then
52
- error("[Lync] Instance: invalid ref (expected Instance)")
53
- end
54
-
55
- return inst, 2
56
- end,
57
- } :: any)
45
+ write = writeInstance,
46
+ read = readInstance,
47
+ })
58
48
 
59
49
  return table.freeze(InstanceCodec)