@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,9 @@
1
+ --!strict
2
+ --!native
3
+ -- Zero-byte codec. Reads nil. Good for fire-and-forget signals.
4
+
5
+ local Base = require (script.Parent.Parent.Base)
6
+
7
+ return table.freeze ({
8
+ nothing = Base.zero (),
9
+ })
@@ -0,0 +1,170 @@
1
+ --!strict
2
+ --!native
3
+ -- Fixed-point compression. Maps float ranges to integer ranges.
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
+ -- Constants --------------------------------------------------------------
13
+
14
+ local clamp = math.clamp
15
+ local round = math.round
16
+ local ceil = math.ceil
17
+ local min = math.min
18
+ local max = math.max
19
+
20
+ local U32_MAX = 4294967295
21
+
22
+ -- Private ----------------------------------------------------------------
23
+
24
+ local function selectIO (bytes: number): ((b: buffer, offset: number, v: number) -> (), (
25
+ b: buffer,
26
+ offset: number
27
+ ) -> number)
28
+ if bytes == 1 then
29
+ return buffer.writeu8, buffer.readu8
30
+ end
31
+ if bytes == 2 then
32
+ return buffer.writeu16, buffer.readu16
33
+ end
34
+ return buffer.writeu32, buffer.readu32
35
+ end
36
+
37
+ local function selectWidth (maxInt: number): number
38
+ if maxInt <= 255 then
39
+ return 1
40
+ end
41
+ if maxInt <= 65535 then
42
+ return 2
43
+ end
44
+ return 4
45
+ end
46
+
47
+ -- Public -----------------------------------------------------------------
48
+
49
+ local Quantized = {}
50
+
51
+ -- Shared validation and precomputation for both float and vec3 quantizers.
52
+ local function setup (minVal: number, maxVal: number, precision: number)
53
+ if minVal > maxVal then
54
+ error (`[Lync] Quantized min > max: {minVal} > {maxVal}`)
55
+ end
56
+ if precision <= 0 then
57
+ error (`[Lync] Quantized precision must be positive: {precision}`)
58
+ end
59
+
60
+ local delta = maxVal - minVal
61
+ if delta == 0 then
62
+ return nil
63
+ end
64
+
65
+ local maxInt = min (U32_MAX, max (1, ceil (delta / precision)))
66
+ local bytes = selectWidth (maxInt)
67
+ local wfn, rfn = selectIO (bytes)
68
+ local invDelta = 1 / delta
69
+ local invMaxInt = delta / maxInt
70
+
71
+ return delta, maxInt, bytes, wfn, rfn, invDelta, invMaxInt
72
+ end
73
+
74
+ function Quantized.float (minVal: number, maxVal: number, precision: number): Codec<number>
75
+ local delta, maxInt, bytes, wfn, rfn, invDelta, invMaxInt = setup (minVal, maxVal, precision)
76
+ if not delta then
77
+ return table.freeze ({
78
+ _size = 0,
79
+ _directWrite = function (_b: buffer, _offset: number, _value: number): () end,
80
+ _directRead = function (_b: buffer, _offset: number): number
81
+ return minVal
82
+ end,
83
+ write = function (_ch: ChannelState, _value: number): () end,
84
+ read = function (_src: buffer, _pos: number, _refs: { Instance }?): (number, number)
85
+ return minVal, 0
86
+ end,
87
+ })
88
+ end
89
+
90
+ return table.freeze ({
91
+ _size = bytes,
92
+ _directWrite = function (b: buffer, offset: number, value: number): ()
93
+ wfn (b, offset, round (clamp ((value - minVal) * invDelta, 0, 1) * maxInt))
94
+ end,
95
+ _directRead = function (b: buffer, offset: number): number
96
+ return rfn (b, offset) * invMaxInt + minVal
97
+ end,
98
+ write = function (ch: ChannelState, value: number): ()
99
+ local c = ch.cursor
100
+ if c + bytes > ch.size then
101
+ alloc (ch, bytes)
102
+ end
103
+ wfn (ch.buff, c, round (clamp ((value - minVal) * invDelta, 0, 1) * maxInt))
104
+ ch.cursor = c + bytes
105
+ end,
106
+ read = function (src: buffer, pos: number, _refs: { Instance }?): (number, number)
107
+ return rfn (src, pos) * invMaxInt + minVal, bytes
108
+ end,
109
+ })
110
+ end
111
+
112
+ function Quantized.vec3 (minVal: number, maxVal: number, precision: number): Codec<Vector3>
113
+ local delta, maxInt, compBytes, wfn, rfn, invDelta, invMaxInt =
114
+ setup (minVal, maxVal, precision)
115
+ if not delta then
116
+ local val = Vector3.new (minVal, minVal, minVal)
117
+ return table.freeze ({
118
+ _size = 0,
119
+ _directWrite = function (_b: buffer, _offset: number, _value: Vector3): () end,
120
+ _directRead = function (_b: buffer, _offset: number): Vector3
121
+ return val
122
+ end,
123
+ write = function (_ch: ChannelState, _value: Vector3): () end,
124
+ read = function (_src: buffer, _pos: number, _refs: { Instance }?): (Vector3, number)
125
+ return val, 0
126
+ end,
127
+ })
128
+ end
129
+
130
+ local totalBytes = compBytes * 3
131
+ local stride = compBytes
132
+ local stride2 = compBytes * 2
133
+
134
+ return table.freeze ({
135
+ _size = totalBytes,
136
+ _directWrite = function (b: buffer, offset: number, value: Vector3): ()
137
+ wfn (b, offset, round (clamp ((value.X - minVal) * invDelta, 0, 1) * maxInt))
138
+ wfn (b, offset + stride, round (clamp ((value.Y - minVal) * invDelta, 0, 1) * maxInt))
139
+ wfn (b, offset + stride2, round (clamp ((value.Z - minVal) * invDelta, 0, 1) * maxInt))
140
+ end,
141
+ _directRead = function (b: buffer, offset: number): Vector3
142
+ return Vector3.new (
143
+ rfn (b, offset) * invMaxInt + minVal,
144
+ rfn (b, offset + stride) * invMaxInt + minVal,
145
+ rfn (b, offset + stride2) * invMaxInt + minVal
146
+ )
147
+ end,
148
+ write = function (ch: ChannelState, value: Vector3): ()
149
+ local c = ch.cursor
150
+ if c + totalBytes > ch.size then
151
+ alloc (ch, totalBytes)
152
+ end
153
+ local b = ch.buff
154
+ wfn (b, c, round (clamp ((value.X - minVal) * invDelta, 0, 1) * maxInt))
155
+ wfn (b, c + stride, round (clamp ((value.Y - minVal) * invDelta, 0, 1) * maxInt))
156
+ wfn (b, c + stride2, round (clamp ((value.Z - minVal) * invDelta, 0, 1) * maxInt))
157
+ ch.cursor = c + totalBytes
158
+ end,
159
+ read = function (src: buffer, pos: number, _refs: { Instance }?): (Vector3, number)
160
+ return Vector3.new (
161
+ rfn (src, pos) * invMaxInt + minVal,
162
+ rfn (src, pos + stride) * invMaxInt + minVal,
163
+ rfn (src, pos + stride2) * invMaxInt + minVal
164
+ ),
165
+ totalBytes
166
+ end,
167
+ })
168
+ end
169
+
170
+ return table.freeze (Quantized)
@@ -0,0 +1,35 @@
1
+ --!strict
2
+ --!native
3
+ -- Sidecar codec. Bypasses buffer serialization, uses Roblox remote refs.
4
+
5
+ local Channel = require (script.Parent.Parent.Parent.internal.Channel)
6
+ local alloc = Channel.alloc
7
+
8
+ return table.freeze ({
9
+ unknown = table.freeze ({
10
+ write = function (ch: any, value: any): ()
11
+ local refs = ch.refs
12
+ local idx = #refs + 1
13
+ if idx > 65535 then
14
+ error (`[Lync] Unknown ref overflow: {idx} exceeds u16 max`)
15
+ end
16
+
17
+ refs[idx] = value
18
+
19
+ local c = ch.cursor
20
+ if c + 2 > ch.size then
21
+ alloc (ch, 2)
22
+ end
23
+ buffer.writeu16 (ch.buff, c, idx)
24
+ ch.cursor = c + 2
25
+ end,
26
+ read = function (src: buffer, pos: number, refs: { Instance }?): (any, number)
27
+ if not refs then
28
+ error ("[Lync] Unknown read requires refs array")
29
+ end
30
+
31
+ local idx = buffer.readu16 (src, pos)
32
+ return refs[idx], 2
33
+ end,
34
+ }),
35
+ })
@@ -0,0 +1,30 @@
1
+ --!strict
2
+ --!native
3
+ -- Boolean codec. _isBool tells struct to pack into bitfields.
4
+
5
+ local Channel = require (script.Parent.Parent.Parent.internal.Channel)
6
+ local alloc = Channel.alloc
7
+
8
+ return table.freeze ({
9
+ bool = table.freeze ({
10
+ _size = 1,
11
+ _isBool = true,
12
+ _directWrite = function (b: buffer, offset: number, value: boolean): ()
13
+ buffer.writeu8 (b, offset, if value then 1 else 0)
14
+ end,
15
+ _directRead = function (b: buffer, offset: number): boolean
16
+ return buffer.readu8 (b, offset) ~= 0
17
+ end,
18
+ write = function (ch: any, value: boolean): ()
19
+ local c = ch.cursor
20
+ if c + 1 > ch.size then
21
+ alloc (ch, 1)
22
+ end
23
+ buffer.writeu8 (ch.buff, c, if value then 1 else 0)
24
+ ch.cursor = c + 1
25
+ end,
26
+ read = function (src: buffer, pos: number, _refs: { Instance }?): (boolean, number)
27
+ return buffer.readu8 (src, pos) ~= 0, 1
28
+ end,
29
+ }),
30
+ })
@@ -0,0 +1,111 @@
1
+ --!strict
2
+ --!native
3
+ -- Half-precision float codec. 2 bytes, ±65504, ~3 decimal digits.
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
+ -- Constants --------------------------------------------------------------
12
+
13
+ local MAX_HALF = 65504
14
+ local BIAS = 15
15
+ local EXP_MAX = 31
16
+ local MANT_BITS = 10
17
+ local MANT_SCALE = 1024
18
+ local SIGN_BIT = 0x8000
19
+ local INF_BITS = 0x7C00
20
+ local NAN_BITS = 0x7E00
21
+
22
+ local band = bit32.band
23
+ local bor = bit32.bor
24
+ local lshift = bit32.lshift
25
+ local rshift = bit32.rshift
26
+ local frexp = math.frexp
27
+ local ldexp = math.ldexp
28
+ local round = math.round
29
+ local huge = math.huge
30
+
31
+ -- Private ----------------------------------------------------------------
32
+
33
+ local function encode (value: number): number
34
+ if value ~= value then
35
+ return NAN_BITS
36
+ end
37
+ if value == 0 then
38
+ return 0
39
+ end
40
+
41
+ local sign = 0
42
+ if value < 0 then
43
+ sign = SIGN_BIT
44
+ value = -value
45
+ end
46
+
47
+ if value > MAX_HALF then
48
+ return bor (sign, INF_BITS)
49
+ end
50
+
51
+ local mantissa, exponent = frexp (value)
52
+ exponent += 14
53
+
54
+ local bits: number
55
+ if exponent <= 0 then
56
+ local shift = exponent + MANT_BITS - 1
57
+ if shift < 0 then
58
+ return sign
59
+ end
60
+ bits = round (mantissa * lshift (1, shift))
61
+ elseif exponent >= EXP_MAX then
62
+ bits = INF_BITS
63
+ else
64
+ bits = bor (lshift (exponent, MANT_BITS), round ((mantissa * 2 - 1) * MANT_SCALE))
65
+ end
66
+
67
+ return bor (sign, bits)
68
+ end
69
+
70
+ local function decode (packed: number): number
71
+ local exponent = band (rshift (packed, MANT_BITS), 0x1F)
72
+ local mantissa = band (packed, 0x03FF)
73
+
74
+ local result: number
75
+ if exponent == 0 then
76
+ result = if mantissa == 0 then 0 else ldexp (mantissa / MANT_SCALE, 1 - BIAS)
77
+ elseif exponent == EXP_MAX then
78
+ result = if mantissa == 0 then huge else 0 / 0
79
+ else
80
+ result = ldexp (mantissa / MANT_SCALE + 1, exponent - BIAS)
81
+ end
82
+
83
+ return if band (packed, SIGN_BIT) ~= 0 then -result else result
84
+ end
85
+
86
+ -- Public -----------------------------------------------------------------
87
+
88
+ local Float16 = {}
89
+
90
+ Float16.f16 = table.freeze ({
91
+ _size = 2,
92
+ _directWrite = function (b: buffer, offset: number, value: number): ()
93
+ buffer.writeu16 (b, offset, encode (value))
94
+ end,
95
+ _directRead = function (b: buffer, offset: number): number
96
+ return decode (buffer.readu16 (b, offset))
97
+ end,
98
+ write = function (ch: ChannelState, value: number): ()
99
+ local c = ch.cursor
100
+ if c + 2 > ch.size then
101
+ alloc (ch, 2)
102
+ end
103
+ buffer.writeu16 (ch.buff, c, encode (value))
104
+ ch.cursor = c + 2
105
+ end,
106
+ read = function (src: buffer, pos: number, _refs: { Instance }?): (number, number)
107
+ return decode (buffer.readu16 (src, pos)), 2
108
+ end,
109
+ })
110
+
111
+ return table.freeze (Float16)
@@ -0,0 +1,48 @@
1
+ --!strict
2
+ --!native
3
+ -- u8, u16, u32, i8, i16, i32, f32, f64 codecs.
4
+
5
+ local Base = require (script.Parent.Parent.Base)
6
+
7
+ return table.freeze ({
8
+ u8 = Base.define (1, function (b: buffer, o: number, v: number): ()
9
+ buffer.writeu8 (b, o, v)
10
+ end, function (b: buffer, o: number): number
11
+ return buffer.readu8 (b, o)
12
+ end),
13
+ u16 = Base.define (2, function (b: buffer, o: number, v: number): ()
14
+ buffer.writeu16 (b, o, v)
15
+ end, function (b: buffer, o: number): number
16
+ return buffer.readu16 (b, o)
17
+ end),
18
+ u32 = Base.define (4, function (b: buffer, o: number, v: number): ()
19
+ buffer.writeu32 (b, o, v)
20
+ end, function (b: buffer, o: number): number
21
+ return buffer.readu32 (b, o)
22
+ end),
23
+ i8 = Base.define (1, function (b: buffer, o: number, v: number): ()
24
+ buffer.writei8 (b, o, v)
25
+ end, function (b: buffer, o: number): number
26
+ return buffer.readi8 (b, o)
27
+ end),
28
+ i16 = Base.define (2, function (b: buffer, o: number, v: number): ()
29
+ buffer.writei16 (b, o, v)
30
+ end, function (b: buffer, o: number): number
31
+ return buffer.readi16 (b, o)
32
+ end),
33
+ i32 = Base.define (4, function (b: buffer, o: number, v: number): ()
34
+ buffer.writei32 (b, o, v)
35
+ end, function (b: buffer, o: number): number
36
+ return buffer.readi32 (b, o)
37
+ end),
38
+ f32 = Base.define (4, function (b: buffer, o: number, v: number): ()
39
+ buffer.writef32 (b, o, v)
40
+ end, function (b: buffer, o: number): number
41
+ return buffer.readf32 (b, o)
42
+ end),
43
+ f64 = Base.define (8, function (b: buffer, o: number, v: number): ()
44
+ buffer.writef64 (b, o, v)
45
+ end, function (b: buffer, o: number): number
46
+ return buffer.readf64 (b, o)
47
+ end),
48
+ })
@@ -0,0 +1,76 @@
1
+ --!strict
2
+ --!native
3
+ -- LEB128 variable-length integer encoding and decoding.
4
+
5
+ local Channel = require (script.Parent.Parent.Parent.internal.Channel)
6
+ local Types = require (script.Parent.Parent.Parent.Types)
7
+
8
+ type ChannelState = Types.ChannelState
9
+
10
+ local alloc = Channel.alloc
11
+
12
+ -- Constants --------------------------------------------------------------
13
+
14
+ local CONT = 0x80
15
+ local MASK = 0x7F
16
+ local MAX_BYTES = 5
17
+ local MAX_VALUE = 4294967295
18
+
19
+ local band = bit32.band
20
+ local bor = bit32.bor
21
+ local rshift = bit32.rshift
22
+ local lshift = bit32.lshift
23
+
24
+ -- Public -----------------------------------------------------------------
25
+
26
+ local Varint = {}
27
+
28
+ function Varint.write (ch: ChannelState, value: number): ()
29
+ if value < 0 or value > MAX_VALUE then
30
+ error (`[Lync] Varint value out of range: {value}`)
31
+ end
32
+
33
+ -- Fast path: ~80%+ of real-world varints are single-byte (lengths, counts)
34
+ if value < CONT then
35
+ local c = ch.cursor
36
+ if c + 1 > ch.size then
37
+ alloc (ch, 1)
38
+ end
39
+ buffer.writeu8 (ch.buff, c, value)
40
+ ch.cursor = c + 1
41
+ return
42
+ end
43
+
44
+ local c = ch.cursor
45
+ if c + MAX_BYTES > ch.size then
46
+ alloc (ch, MAX_BYTES)
47
+ end
48
+ local b = ch.buff
49
+
50
+ while value >= CONT do
51
+ buffer.writeu8 (b, c, bor (band (value, MASK), CONT))
52
+ value = rshift (value, 7)
53
+ c += 1
54
+ end
55
+
56
+ buffer.writeu8 (b, c, value)
57
+ ch.cursor = c + 1
58
+ end
59
+
60
+ -- Reads rely on buffer.readu8 throwing on OOB. MAX_BYTES rejects malformed loops.
61
+ function Varint.read (src: buffer, pos: number): (number, number)
62
+ local result = 0
63
+
64
+ for offset = 0, MAX_BYTES - 1 do
65
+ local byte = buffer.readu8 (src, pos + offset)
66
+ result = bor (result, lshift (band (byte, MASK), offset * 7))
67
+
68
+ if band (byte, CONT) == 0 then
69
+ return result, offset + 1
70
+ end
71
+ end
72
+
73
+ error (`[Lync] Varint exceeds {MAX_BYTES} bytes at position {pos}`)
74
+ end
75
+
76
+ return table.freeze (Varint)