@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
@@ -1,54 +1,60 @@
1
1
  --!strict
2
- --!native
3
- -- Region3(24B) and Region3int16(12B) codecs.
2
+ --!optimize 2
3
+ -- Region3 (24 bytes) and Region3int16 (12 bytes) codecs.
4
4
 
5
5
  local Base = require(script.Parent.Parent.Base)
6
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)
7
+ -- Private -------------------------------------------------------------
8
+
9
+ local function writeI16(b: buffer, off: number, v: number): ()
10
+ buffer.writeu16(b, off, if v < 0 then v + 0x10000 else v)
20
11
  end
21
12
 
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(buffer.readf32(b, o + 12), buffer.readf32(b, o + 16), buffer.readf32(b, o + 20))
26
- )
13
+ local function readI16(b: buffer, off: number): number
14
+ local raw = buffer.readu16(b, off)
15
+ return if raw >= 0x8000 then raw - 0x10000 else raw
27
16
  end
28
17
 
29
- return table.freeze({
30
- region3 = Base.define(24, writeRegion3, readRegion3),
31
- region3int16 = Base.define(12, function(b: buffer, o: number, v: Region3int16): ()
32
- local minV = v.Min
33
- local maxV = v.Max
34
- buffer.writei16(b, o, minV.X)
35
- buffer.writei16(b, o + 2, minV.Y)
36
- buffer.writei16(b, o + 4, minV.Z)
37
- buffer.writei16(b, o + 6, maxV.X)
38
- buffer.writei16(b, o + 8, maxV.Y)
39
- buffer.writei16(b, o + 10, maxV.Z)
40
- end, function(b: buffer, o: number): Region3int16
41
- return Region3int16.new(
42
- Vector3int16.new(
43
- buffer.readi16(b, o),
44
- buffer.readi16(b, o + 2),
45
- buffer.readi16(b, o + 4)
46
- ),
47
- Vector3int16.new(
48
- buffer.readi16(b, o + 6),
49
- buffer.readi16(b, o + 8),
50
- buffer.readi16(b, o + 10)
51
- )
18
+ -- Public --------------------------------------------------------------
19
+
20
+ local RegionCodec = {}
21
+
22
+ RegionCodec.region3 = Base.define(24, function(b: buffer, off: number, value: Region3): ()
23
+ local pos = value.CFrame.Position
24
+ local half = value.Size * 0.5
25
+ local minV = pos - half
26
+ local maxV = pos + half
27
+ buffer.writef32(b, off, minV.X)
28
+ buffer.writef32(b, off + 4, minV.Y)
29
+ buffer.writef32(b, off + 8, minV.Z)
30
+ buffer.writef32(b, off + 12, maxV.X)
31
+ buffer.writef32(b, off + 16, maxV.Y)
32
+ buffer.writef32(b, off + 20, maxV.Z)
33
+ end, function(b: buffer, off: number): Region3
34
+ return Region3.new(
35
+ Vector3.new(buffer.readf32(b, off), buffer.readf32(b, off + 4), buffer.readf32(b, off + 8)),
36
+ Vector3.new(
37
+ buffer.readf32(b, off + 12),
38
+ buffer.readf32(b, off + 16),
39
+ buffer.readf32(b, off + 20)
52
40
  )
53
- end),
54
- })
41
+ )
42
+ end, { _typeCheck = "Region3" })
43
+
44
+ RegionCodec.region3int16 = Base.define(12, function(b: buffer, off: number, value: Region3int16): ()
45
+ local minV = value.Min
46
+ local maxV = value.Max
47
+ writeI16(b, off, minV.X)
48
+ writeI16(b, off + 2, minV.Y)
49
+ writeI16(b, off + 4, minV.Z)
50
+ writeI16(b, off + 6, maxV.X)
51
+ writeI16(b, off + 8, maxV.Y)
52
+ writeI16(b, off + 10, maxV.Z)
53
+ end, function(b: buffer, off: number): Region3int16
54
+ return Region3int16.new(
55
+ Vector3int16.new(readI16(b, off), readI16(b, off + 2), readI16(b, off + 4)),
56
+ Vector3int16.new(readI16(b, off + 6), readI16(b, off + 8), readI16(b, off + 10))
57
+ )
58
+ end, { _typeCheck = "Region3int16" })
59
+
60
+ return table.freeze(RegionCodec)
@@ -1,29 +1,25 @@
1
1
  --!strict
2
- --!native
3
- -- NumberSequence and ColorSequence codecs with varint length prefix.
2
+ --!optimize 2
3
+ -- NumberSequence and ColorSequence codecs with varint count.
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
  local Varint = require(script.Parent.Parent.primitive.Varint)
8
8
 
9
- type ChannelState = Types.ChannelState
10
-
11
- local alloc = Channel.alloc
12
-
13
- -- Constants -----------------------------------------------------------
14
-
15
- local NS_STRIDE = 12
16
- local CS_STRIDE = 7
9
+ -- Private -------------------------------------------------------------
17
10
 
11
+ local alloc = Base.alloc
18
12
  local round = math.round
19
13
  local clamp = math.clamp
20
14
 
21
15
  -- Public --------------------------------------------------------------
22
16
 
23
- local Sequence = {}
17
+ local SequenceCodec = {}
24
18
 
25
- Sequence.numberSequence = table.freeze({
26
- write = function(ch: ChannelState, value: NumberSequence): ()
19
+ SequenceCodec.numberSequence = table.freeze({
20
+ _typeCheck = "NumberSequence",
21
+
22
+ write = function(ch: Types.ChannelState, value: NumberSequence): ()
27
23
  local keypoints = value.Keypoints
28
24
  local count = #keypoints
29
25
  Varint.write(ch, count)
@@ -32,47 +28,54 @@ Sequence.numberSequence = table.freeze({
32
28
  return
33
29
  end
34
30
 
35
- local totalBytes = count * NS_STRIDE
36
- local c = ch.cursor
37
- if c + totalBytes > ch.size then
38
- alloc(ch, totalBytes)
31
+ local bytes = count * 12
32
+ local cursor = ch.cursor
33
+ if cursor + bytes > ch.size then
34
+ alloc(ch, bytes)
39
35
  end
40
-
41
36
  local b = ch.buff
37
+
42
38
  for i = 1, count do
43
39
  local kp = keypoints[i]
44
- buffer.writef32(b, c, kp.Time)
45
- buffer.writef32(b, c + 4, kp.Value)
46
- buffer.writef32(b, c + 8, kp.Envelope)
47
- c += NS_STRIDE
40
+ local off = cursor + (i - 1) * 12
41
+ buffer.writef32(b, off, kp.Time)
42
+ buffer.writef32(b, off + 4, kp.Value)
43
+ buffer.writef32(b, off + 8, kp.Envelope)
48
44
  end
49
45
 
50
- ch.cursor = c
46
+ ch.cursor = cursor + bytes
51
47
  end,
48
+
52
49
  read = function(src: buffer, pos: number, _refs: { Instance }?): (NumberSequence, number)
53
50
  local count, lenBytes = Varint.read(src, pos)
54
- local absPos = pos + lenBytes
55
-
56
51
  if count == 0 then
57
52
  return NumberSequence.new(0), lenBytes
58
53
  end
59
54
 
55
+ local bytes = count * 12
56
+ local dataStart = pos + lenBytes
57
+ if dataStart + bytes > buffer.len(src) then
58
+ error("[Lync] NumberSequence: data exceeds buffer")
59
+ end
60
+
60
61
  local keypoints = table.create(count)
61
62
  for i = 1, count do
63
+ local off = dataStart + (i - 1) * 12
62
64
  keypoints[i] = NumberSequenceKeypoint.new(
63
- buffer.readf32(src, absPos),
64
- buffer.readf32(src, absPos + 4),
65
- buffer.readf32(src, absPos + 8)
65
+ buffer.readf32(src, off),
66
+ buffer.readf32(src, off + 4),
67
+ buffer.readf32(src, off + 8)
66
68
  )
67
- absPos += NS_STRIDE
68
69
  end
69
70
 
70
- return NumberSequence.new(keypoints), absPos - pos
71
+ return NumberSequence.new(keypoints), lenBytes + bytes
71
72
  end,
72
- })
73
+ } :: any)
74
+
75
+ SequenceCodec.colorSequence = table.freeze({
76
+ _typeCheck = "ColorSequence",
73
77
 
74
- Sequence.colorSequence = table.freeze({
75
- write = function(ch: ChannelState, value: ColorSequence): ()
78
+ write = function(ch: Types.ChannelState, value: ColorSequence): ()
76
79
  local keypoints = value.Keypoints
77
80
  local count = #keypoints
78
81
  Varint.write(ch, count)
@@ -81,48 +84,53 @@ Sequence.colorSequence = table.freeze({
81
84
  return
82
85
  end
83
86
 
84
- local totalBytes = count * CS_STRIDE
85
- local c = ch.cursor
86
- if c + totalBytes > ch.size then
87
- alloc(ch, totalBytes)
87
+ local bytes = count * 7
88
+ local cursor = ch.cursor
89
+ if cursor + bytes > ch.size then
90
+ alloc(ch, bytes)
88
91
  end
89
-
90
92
  local b = ch.buff
93
+
91
94
  for i = 1, count do
92
95
  local kp = keypoints[i]
93
- local color = kp.Value
94
- buffer.writef32(b, c, kp.Time)
95
- buffer.writeu8(b, c + 4, round(clamp(color.R, 0, 1) * 255))
96
- buffer.writeu8(b, c + 5, round(clamp(color.G, 0, 1) * 255))
97
- buffer.writeu8(b, c + 6, round(clamp(color.B, 0, 1) * 255))
98
- c += CS_STRIDE
96
+ local off = cursor + (i - 1) * 7
97
+ local c = kp.Value
98
+ buffer.writef32(b, off, kp.Time)
99
+ buffer.writeu8(b, off + 4, round(clamp(c.R, 0, 1) * 255))
100
+ buffer.writeu8(b, off + 5, round(clamp(c.G, 0, 1) * 255))
101
+ buffer.writeu8(b, off + 6, round(clamp(c.B, 0, 1) * 255))
99
102
  end
100
103
 
101
- ch.cursor = c
104
+ ch.cursor = cursor + bytes
102
105
  end,
106
+
103
107
  read = function(src: buffer, pos: number, _refs: { Instance }?): (ColorSequence, number)
104
108
  local count, lenBytes = Varint.read(src, pos)
105
- local absPos = pos + lenBytes
106
-
107
109
  if count == 0 then
108
110
  return ColorSequence.new(Color3.new()), lenBytes
109
111
  end
110
112
 
113
+ local bytes = count * 7
114
+ local dataStart = pos + lenBytes
115
+ if dataStart + bytes > buffer.len(src) then
116
+ error("[Lync] ColorSequence: data exceeds buffer")
117
+ end
118
+
111
119
  local keypoints = table.create(count)
112
120
  for i = 1, count do
121
+ local off = dataStart + (i - 1) * 7
113
122
  keypoints[i] = ColorSequenceKeypoint.new(
114
- buffer.readf32(src, absPos),
123
+ buffer.readf32(src, off),
115
124
  Color3.fromRGB(
116
- buffer.readu8(src, absPos + 4),
117
- buffer.readu8(src, absPos + 5),
118
- buffer.readu8(src, absPos + 6)
125
+ buffer.readu8(src, off + 4),
126
+ buffer.readu8(src, off + 5),
127
+ buffer.readu8(src, off + 6)
119
128
  )
120
129
  )
121
- absPos += CS_STRIDE
122
130
  end
123
131
 
124
- return ColorSequence.new(keypoints), absPos - pos
132
+ return ColorSequence.new(keypoints), lenBytes + bytes
125
133
  end,
126
- })
134
+ } :: any)
127
135
 
128
- return table.freeze(Sequence)
136
+ return table.freeze(SequenceCodec)
@@ -1,84 +1,117 @@
1
1
  --!strict
2
2
  --!native
3
- -- Variable-length string codec with varint length prefix.
3
+ -- Variable-length string codec with __call for bounded variant.
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
  local Varint = require(script.Parent.Parent.primitive.Varint)
8
8
 
9
- type ChannelState = Types.ChannelState
9
+ -- Private -------------------------------------------------------------
10
10
 
11
- local alloc = Channel.alloc
11
+ local alloc = Base.alloc
12
+ local writeu8 = buffer.writeu8
13
+ local readu8 = buffer.readu8
14
+ local INLINE = Varint.INLINE_MAX
12
15
 
13
- -- Public --------------------------------------------------------------
14
-
15
- local String = {}
16
+ local function writeString(ch: Types.ChannelState, value: string): ()
17
+ local len = #value
18
+ local cursor = ch.cursor
16
19
 
17
- String.string = table.freeze({
18
- write = function(ch: ChannelState, value: string): ()
19
- local len = #value
20
-
21
- if len < 128 then
22
- local total = 1 + len
23
- local c = ch.cursor
24
- if c + total > ch.size then
25
- alloc(ch, total)
26
- end
27
- local b = ch.buff
28
- buffer.writeu8(b, c, len)
29
- if len > 0 then
30
- buffer.writestring(b, c + 1, value)
31
- end
32
- ch.cursor = c + total
33
- else
34
- Varint.write(ch, len)
35
- local c = ch.cursor
36
- if c + len > ch.size then
37
- alloc(ch, len)
38
- end
39
- buffer.writestring(ch.buff, c, value)
40
- ch.cursor = c + len
20
+ if len <= INLINE then
21
+ -- Short strings (<= 191 bytes) use a single-byte prefix, avoiding Varint.write overhead
22
+ if cursor + 1 + len > ch.size then
23
+ alloc(ch, 1 + len)
41
24
  end
42
- end,
43
- read = function(src: buffer, pos: number, _refs: { Instance }?): (string, number)
44
- local len, lenBytes = Varint.read(src, pos)
45
- if len == 0 then
46
- return "", lenBytes
25
+ local b = ch.buff
26
+ writeu8(b, cursor, len)
27
+ if len > 0 then
28
+ buffer.writestring(b, cursor + 1, value)
47
29
  end
48
-
49
- local remaining = buffer.len(src) - pos - lenBytes
50
- if len > remaining then
51
- error(`[Lync] String length exceeds buffer: {len} > {remaining}`)
30
+ ch.cursor = cursor + 1 + len
31
+ else
32
+ -- Long strings need multi-byte length encoding
33
+ Varint.write(ch, len)
34
+ cursor = ch.cursor
35
+ if cursor + len > ch.size then
36
+ alloc(ch, len)
52
37
  end
38
+ buffer.writestring(ch.buff, cursor, value)
39
+ ch.cursor = cursor + len
40
+ end
41
+ end
42
+
43
+ local function readString(src: buffer, pos: number, _refs: { Instance }?): (string, number)
44
+ local b0 = readu8(src, pos)
45
+ local len: number
46
+ local lenBytes: number
53
47
 
54
- return buffer.readstring(src, pos + lenBytes, len), lenBytes + len
55
- end,
56
- })
48
+ if b0 <= INLINE then
49
+ len = b0
50
+ lenBytes = 1
51
+ else
52
+ len, lenBytes = Varint.read(src, pos)
53
+ end
54
+
55
+ if len == 0 then
56
+ return "", lenBytes
57
+ end
57
58
 
58
- function String.bounded(maxLength: number): any
59
- if maxLength <= 0 or maxLength ~= math.floor(maxLength) then
60
- error(`[Lync] boundedString maxLength must be a positive integer, got {maxLength}`)
59
+ local dataStart = pos + lenBytes
60
+ if dataStart + len > buffer.len(src) then
61
+ error("[Lync] String: length exceeds remaining buffer")
62
+ end
63
+
64
+ return buffer.readstring(src, dataStart, len), lenBytes + len
65
+ end
66
+
67
+ -- Bounded variant shares the write function to avoid per-call closure allocation
68
+ local function boundedFactory(_self: any, maxLength: number): Types.InternalCodec<string>
69
+ if maxLength <= 0 then
70
+ error("[Lync] Lync.string: maxLength must be positive")
61
71
  end
62
72
 
63
73
  return table.freeze({
64
- write = String.string.write,
65
- read = function(src: buffer, pos: number, _refs: { Instance }?): (string, number)
66
- local len, lenBytes = Varint.read(src, pos)
74
+ write = writeString,
75
+
76
+ read = function(src: buffer, pos: number, refs: { Instance }?): (string, number)
77
+ local b0 = readu8(src, pos)
78
+ local len: number
79
+ local lenBytes: number
80
+
81
+ if b0 <= INLINE then
82
+ len = b0
83
+ lenBytes = 1
84
+ else
85
+ len, lenBytes = Varint.read(src, pos)
86
+ end
87
+
67
88
  if len > maxLength then
68
- error(`[Lync] String length {len} exceeds max {maxLength}`)
89
+ error(`[Lync] String: length {len} exceeds max {maxLength}`)
69
90
  end
91
+
70
92
  if len == 0 then
71
93
  return "", lenBytes
72
94
  end
73
95
 
74
- local remaining = buffer.len(src) - pos - lenBytes
75
- if len > remaining then
76
- error(`[Lync] String length exceeds buffer: {len} > {remaining}`)
96
+ local dataStart = pos + lenBytes
97
+ if dataStart + len > buffer.len(src) then
98
+ error("[Lync] String: length exceeds remaining buffer")
77
99
  end
78
100
 
79
- return buffer.readstring(src, pos + lenBytes, len), lenBytes + len
101
+ return buffer.readstring(src, dataStart, len), lenBytes + len
80
102
  end,
81
- })
103
+ } :: any)
82
104
  end
83
105
 
84
- return table.freeze(String)
106
+ -- Public --------------------------------------------------------------
107
+
108
+ local StringCodec = setmetatable(
109
+ {
110
+ write = writeString,
111
+ read = readString,
112
+ _typeCheck = "string",
113
+ } :: any,
114
+ { __call = boundedFactory }
115
+ )
116
+
117
+ return StringCodec
@@ -1,27 +1,45 @@
1
1
  --!strict
2
- --!native
3
- -- UDim(8B) and UDim2(16B) codecs.
2
+ --!optimize 2
3
+ -- UDim (8 bytes) and UDim2 (16 bytes) codecs.
4
4
 
5
5
  local Base = require(script.Parent.Parent.Base)
6
6
 
7
- return table.freeze({
8
- udim = Base.define(8, function(b: buffer, o: number, v: UDim): ()
9
- buffer.writef32(b, o, v.Scale)
10
- buffer.writei32(b, o + 4, v.Offset)
11
- end, function(b: buffer, o: number): UDim
12
- return UDim.new(buffer.readf32(b, o), buffer.readi32(b, o + 4))
13
- end),
14
- udim2 = Base.define(16, function(b: buffer, o: number, v: UDim2): ()
15
- local x = v.X
16
- local y = v.Y
17
- buffer.writef32(b, o, x.Scale)
18
- buffer.writei32(b, o + 4, x.Offset)
19
- buffer.writef32(b, o + 8, y.Scale)
20
- buffer.writei32(b, o + 12, y.Offset)
21
- end, function(b: buffer, o: number): UDim2
22
- return UDim2.new(
23
- UDim.new(buffer.readf32(b, o), buffer.readi32(b, o + 4)),
24
- UDim.new(buffer.readf32(b, o + 8), buffer.readi32(b, o + 12))
7
+ -- Public --------------------------------------------------------------
8
+
9
+ local UDimCodec = {}
10
+
11
+ UDimCodec.udim = Base.define(8, function(b: buffer, off: number, value: UDim): ()
12
+ buffer.writef32(b, off, value.Scale)
13
+ buffer.writeu32(
14
+ b,
15
+ off + 4,
16
+ if value.Offset < 0 then value.Offset + 0x100000000 else value.Offset
17
+ )
18
+ end, function(b: buffer, off: number): UDim
19
+ local rawOff = buffer.readu32(b, off + 4)
20
+ return UDim.new(
21
+ buffer.readf32(b, off),
22
+ if rawOff >= 0x80000000 then rawOff - 0x100000000 else rawOff
23
+ )
24
+ end, { _typeCheck = "UDim" })
25
+
26
+ UDimCodec.udim2 = Base.define(16, function(b: buffer, off: number, value: UDim2): ()
27
+ local x = value.X
28
+ local y = value.Y
29
+ buffer.writef32(b, off, x.Scale)
30
+ buffer.writeu32(b, off + 4, if x.Offset < 0 then x.Offset + 0x100000000 else x.Offset)
31
+ buffer.writef32(b, off + 8, y.Scale)
32
+ buffer.writeu32(b, off + 12, if y.Offset < 0 then y.Offset + 0x100000000 else y.Offset)
33
+ end, function(b: buffer, off: number): UDim2
34
+ local xOff = buffer.readu32(b, off + 4)
35
+ local yOff = buffer.readu32(b, off + 12)
36
+ return UDim2.new(
37
+ UDim.new(buffer.readf32(b, off), if xOff >= 0x80000000 then xOff - 0x100000000 else xOff),
38
+ UDim.new(
39
+ buffer.readf32(b, off + 8),
40
+ if yOff >= 0x80000000 then yOff - 0x100000000 else yOff
25
41
  )
26
- end),
27
- })
42
+ )
43
+ end, { _typeCheck = "UDim2" })
44
+
45
+ return table.freeze(UDimCodec)