@axpecter/lync 2.0.0 → 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 +475 -426
  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 +249 -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,256 +0,0 @@
1
- --!strict
2
- --!optimize 2
3
- -- Scoped packet/query groups with batch ops and local middleware.
4
-
5
- local Middleware = require(script.Parent.Parent.internal.Middleware)
6
- local Packet = require(script.Parent.Packet)
7
- local Query = require(script.Parent.Query)
8
- local Types = require(script.Parent.Parent.Types)
9
-
10
- type Connection = Types.Connection
11
- type PacketConfig<T> = Types.PacketConfig<T>
12
- type QueryConfig<R, S> = Types.QueryConfig<R, S>
13
-
14
- -- Constants -----------------------------------------------------------
15
-
16
- local MAX_NAMESPACES = 64
17
-
18
- -- State ---------------------------------------------------------------
19
-
20
- local _registered = {} :: { [string]: true }
21
- local _count = 0
22
-
23
- -- Private -------------------------------------------------------------
24
-
25
- type NamespaceConfig = Types.NamespaceConfig
26
-
27
- type NamespaceFields = {
28
- _name: string,
29
- _packets: { [string]: Packet.Packet },
30
- _queries: { [string]: Query.Query },
31
- _tracked: { Connection },
32
- _removers: { () -> () },
33
- packets: { [string]: Packet.Packet },
34
- queries: { [string]: Query.Query },
35
- }
36
-
37
- local function prefixName(nsName: string, name: string): string
38
- return `{nsName}.{name}`
39
- end
40
-
41
- -- Wraps a middleware handler so it only fires for packets in this namespace.
42
- local function scopedHandler(
43
- nsName: string,
44
- handler: (data: any, name: string, player: Player?) -> any?
45
- ): (data: any, name: string, player: Player?) -> any?
46
- local prefix = nsName .. "."
47
- local prefixLen = #prefix
48
-
49
- return function(data: any, name: string, player: Player?): any?
50
- if string.sub(name, 1, prefixLen) ~= prefix then
51
- return data
52
- end
53
- return handler(data, name, player)
54
- end
55
- end
56
-
57
- local NamespaceImpl = {}
58
- NamespaceImpl.__index = NamespaceImpl
59
-
60
- export type Namespace = typeof(setmetatable({} :: NamespaceFields, NamespaceImpl))
61
-
62
- -- Listens on every packet in this namespace. Returns a Connection.
63
- function NamespaceImpl.listenAll(
64
- self: Namespace,
65
- callback: (
66
- name: string,
67
- data: any,
68
- sender: Player?,
69
- timestamp: number?
70
- ) -> ()
71
- ): Connection
72
- local connections = {} :: { Connection }
73
- local prefix = self._name .. "."
74
- local prefixLen = #prefix
75
-
76
- for fullName, packet in self._packets do
77
- local shortName = string.sub(fullName, prefixLen + 1)
78
- local conn = packet:listen(function(data: any, sender: Player?, timestamp: number?): ()
79
- callback(shortName, data, sender, timestamp)
80
- end)
81
- table.insert(connections, conn)
82
- table.insert(self._tracked, conn)
83
- end
84
-
85
- local bundle: Connection
86
- bundle = {
87
- connected = true,
88
- disconnect = function(self: Connection): ()
89
- if not self.connected then
90
- return
91
- end
92
- self.connected = false
93
-
94
- for _, inner in connections do
95
- if inner.connected then
96
- inner:disconnect()
97
- end
98
- end
99
- end,
100
- }
101
-
102
- return bundle
103
- end
104
-
105
- -- Disconnects every listenAll connection. Does not remove scoped middleware.
106
- function NamespaceImpl.disconnectAll(self: Namespace): ()
107
- for _, conn in self._tracked do
108
- if conn.connected then
109
- conn:disconnect()
110
- end
111
- end
112
- table.clear(self._tracked)
113
- end
114
-
115
- -- Registers send middleware scoped to this namespace. Returns a remover.
116
- function NamespaceImpl.onSend(
117
- self: Namespace,
118
- handler: (data: any, name: string, player: Player?) -> any?
119
- ): () -> ()
120
- local wrapped = scopedHandler(self._name, handler)
121
- local remover = Middleware.addSend(wrapped)
122
- table.insert(self._removers, remover)
123
-
124
- return function(): ()
125
- remover()
126
- local idx = table.find(self._removers, remover)
127
- if idx then
128
- table.remove(self._removers, idx)
129
- end
130
- end
131
- end
132
-
133
- -- Registers receive middleware scoped to this namespace. Returns a remover.
134
- function NamespaceImpl.onReceive(
135
- self: Namespace,
136
- handler: (data: any, name: string, player: Player?) -> any?
137
- ): () -> ()
138
- local wrapped = scopedHandler(self._name, handler)
139
- local remover = Middleware.addReceive(wrapped)
140
- table.insert(self._removers, remover)
141
-
142
- return function(): ()
143
- remover()
144
- local idx = table.find(self._removers, remover)
145
- if idx then
146
- table.remove(self._removers, idx)
147
- end
148
- end
149
- end
150
-
151
- -- Kills all listeners and removes all scoped middleware. Full cleanup.
152
- function NamespaceImpl.destroy(self: Namespace): ()
153
- self:disconnectAll()
154
-
155
- for _, remover in self._removers do
156
- remover()
157
- end
158
- table.clear(self._removers)
159
- end
160
-
161
- -- Returns every packet name(without prefix) in this namespace.
162
- function NamespaceImpl.packetNames(self: Namespace): { string }
163
- local result = {} :: { string }
164
- local prefix = self._name .. "."
165
- local prefixLen = #prefix
166
-
167
- for fullName in self._packets do
168
- table.insert(result, string.sub(fullName, prefixLen + 1))
169
- end
170
-
171
- table.sort(result)
172
- return result
173
- end
174
-
175
- -- Returns every query name(without prefix) in this namespace.
176
- function NamespaceImpl.queryNames(self: Namespace): { string }
177
- local result = {} :: { string }
178
- local prefix = self._name .. "."
179
- local prefixLen = #prefix
180
-
181
- for fullName in self._queries do
182
- table.insert(result, string.sub(fullName, prefixLen + 1))
183
- end
184
-
185
- table.sort(result)
186
- return result
187
- end
188
-
189
- table.freeze(NamespaceImpl)
190
-
191
- -- Public --------------------------------------------------------------
192
-
193
- local NamespaceModule = {}
194
-
195
- -- Defines a namespace. Names are auto-prefixed to prevent collisions.
196
- function NamespaceModule.define(name: string, config: NamespaceConfig): Namespace
197
- if #name == 0 then
198
- error("[Lync] Namespace name must not be empty")
199
- end
200
- if _registered[name] then
201
- error(`[Lync] Duplicate namespace: "{name}"`)
202
- end
203
- if _count >= MAX_NAMESPACES then
204
- error(`[Lync] Namespace limit reached: {MAX_NAMESPACES}`)
205
- end
206
-
207
- _count += 1
208
- _registered[name] = true
209
-
210
- local packets = {} :: { [string]: Packet.Packet }
211
- local queries = {} :: { [string]: Query.Query }
212
- local packetsByShort = {} :: { [string]: Packet.Packet }
213
- local queriesByShort = {} :: { [string]: Query.Query }
214
-
215
- if config.packets then
216
- for shortName, packetConfig in config.packets do
217
- local fullName = prefixName(name, shortName)
218
- local packet = Packet.define(fullName, packetConfig)
219
- packets[fullName] = packet
220
- packetsByShort[shortName] = packet
221
- end
222
- end
223
-
224
- if config.queries then
225
- for shortName, queryConfig in config.queries do
226
- local fullName = prefixName(name, shortName)
227
- local query = Query.define(fullName, queryConfig)
228
- queries[fullName] = query
229
- queriesByShort[shortName] = query
230
- end
231
- end
232
-
233
- local nsFields: NamespaceFields = {
234
- _name = name,
235
- _packets = packets,
236
- _queries = queries,
237
- _tracked = {} :: { Connection },
238
- _removers = {} :: { () -> () },
239
- packets = table.freeze(packetsByShort),
240
- queries = table.freeze(queriesByShort),
241
- }
242
-
243
- -- Inject dynamic short-name accessors (ns.Hit, ns.Stats, etc.)
244
- -- These bypass the typed fields; consumers should prefer ns.packets.X / ns.queries.X.
245
- local raw = nsFields :: any
246
- for shortName, packet in packetsByShort do
247
- raw[shortName] = packet
248
- end
249
- for shortName, query in queriesByShort do
250
- raw[shortName] = query
251
- end
252
-
253
- return setmetatable(nsFields, NamespaceImpl)
254
- end
255
-
256
- return table.freeze(NamespaceModule)
@@ -1,174 +0,0 @@
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 Types = require(script.Parent.Parent.Parent.Types)
7
-
8
- type ChannelState = Types.ChannelState
9
- type Codec<T> = Types.Codec<T>
10
-
11
- local alloc = Channel.alloc
12
-
13
- -- Constants -----------------------------------------------------------
14
-
15
- local clamp = math.clamp
16
- local round = math.round
17
- local ceil = math.ceil
18
- local min = math.min
19
- local max = math.max
20
-
21
- local U32_MAX = 4294967295
22
-
23
- -- Private -------------------------------------------------------------
24
-
25
- local function selectIO(bytes: number): ((b: buffer, offset: number, v: number) -> (), (
26
- b: buffer,
27
- offset: number
28
- ) -> number)
29
- if bytes == 1 then
30
- return buffer.writeu8, buffer.readu8
31
- end
32
- if bytes == 2 then
33
- return buffer.writeu16, buffer.readu16
34
- end
35
- return buffer.writeu32, buffer.readu32
36
- end
37
-
38
- local function selectWidth(maxInt: number): number
39
- if maxInt <= 255 then
40
- return 1
41
- end
42
- if maxInt <= 65535 then
43
- return 2
44
- end
45
- return 4
46
- end
47
-
48
- -- Public --------------------------------------------------------------
49
-
50
- local Quantized = {}
51
-
52
- -- Shared validation and precomputation for both float and vec3 quantizers.
53
- local function setup(
54
- minVal: number,
55
- maxVal: number,
56
- precision: number
57
- ): (number?, number?, number?, any?, any?, number?, number?)
58
- if minVal > maxVal then
59
- error(`[Lync] Quantized min > max: {minVal} > {maxVal}`)
60
- end
61
- if precision <= 0 then
62
- error(`[Lync] Quantized precision must be positive: {precision}`)
63
- end
64
-
65
- local delta = maxVal - minVal
66
- if delta == 0 then
67
- return nil
68
- end
69
-
70
- local maxInt = min(U32_MAX, max(1, ceil(delta / precision)))
71
- local bytes = selectWidth(maxInt)
72
- local wfn, rfn = selectIO(bytes)
73
- local invDelta = 1 / delta
74
- local invMaxInt = delta / maxInt
75
-
76
- return delta, maxInt, bytes, wfn, rfn, invDelta, invMaxInt
77
- end
78
-
79
- function Quantized.float(minVal: number, maxVal: number, precision: number): Codec<number>
80
- local delta, maxInt, bytes, wfn, rfn, invDelta, invMaxInt = setup(minVal, maxVal, precision)
81
- if not delta then
82
- return table.freeze({
83
- _size = 0,
84
- _directWrite = function(_b: buffer, _offset: number, _value: number): () end,
85
- _directRead = function(_b: buffer, _offset: number): number
86
- return minVal
87
- end,
88
- write = function(_ch: ChannelState, _value: number): () end,
89
- read = function(_src: buffer, _pos: number, _refs: { Instance }?): (number, number)
90
- return minVal, 0
91
- end,
92
- })
93
- end
94
-
95
- return table.freeze({
96
- _size = bytes,
97
- _directWrite = function(b: buffer, offset: number, value: number): ()
98
- wfn(b, offset, round(clamp((value - minVal) * invDelta, 0, 1) * maxInt))
99
- end,
100
- _directRead = function(b: buffer, offset: number): number
101
- return rfn(b, offset) * invMaxInt + minVal
102
- end,
103
- write = function(ch: ChannelState, value: number): ()
104
- local c = ch.cursor
105
- if c + bytes > ch.size then
106
- alloc(ch, bytes)
107
- end
108
- wfn(ch.buff, c, round(clamp((value - minVal) * invDelta, 0, 1) * maxInt))
109
- ch.cursor = c + bytes
110
- end,
111
- read = function(src: buffer, pos: number, _refs: { Instance }?): (number, number)
112
- return rfn(src, pos) * invMaxInt + minVal, bytes
113
- end,
114
- })
115
- end
116
-
117
- function Quantized.vec3(minVal: number, maxVal: number, precision: number): Codec<Vector3>
118
- local delta, maxInt, compBytes, wfn, rfn, invDelta, invMaxInt = setup(minVal, maxVal, precision)
119
- if not delta then
120
- local val = Vector3.new(minVal, minVal, minVal)
121
- return table.freeze({
122
- _size = 0,
123
- _directWrite = function(_b: buffer, _offset: number, _value: Vector3): () end,
124
- _directRead = function(_b: buffer, _offset: number): Vector3
125
- return val
126
- end,
127
- write = function(_ch: ChannelState, _value: Vector3): () end,
128
- read = function(_src: buffer, _pos: number, _refs: { Instance }?): (Vector3, number)
129
- return val, 0
130
- end,
131
- })
132
- end
133
-
134
- local totalBytes = compBytes * 3
135
- local stride = compBytes
136
- local stride2 = compBytes * 2
137
-
138
- return table.freeze({
139
- _size = totalBytes,
140
- _directWrite = function(b: buffer, offset: number, value: Vector3): ()
141
- wfn(b, offset, round(clamp((value.X - minVal) * invDelta, 0, 1) * maxInt))
142
- wfn(b, offset + stride, round(clamp((value.Y - minVal) * invDelta, 0, 1) * maxInt))
143
- wfn(b, offset + stride2, round(clamp((value.Z - minVal) * invDelta, 0, 1) * maxInt))
144
- end,
145
- _directRead = function(b: buffer, offset: number): Vector3
146
- return Vector3.new(
147
- rfn(b, offset) * invMaxInt + minVal,
148
- rfn(b, offset + stride) * invMaxInt + minVal,
149
- rfn(b, offset + stride2) * invMaxInt + minVal
150
- )
151
- end,
152
- write = function(ch: ChannelState, value: Vector3): ()
153
- local c = ch.cursor
154
- if c + totalBytes > ch.size then
155
- alloc(ch, totalBytes)
156
- end
157
- local b = ch.buff
158
- wfn(b, c, round(clamp((value.X - minVal) * invDelta, 0, 1) * maxInt))
159
- wfn(b, c + stride, round(clamp((value.Y - minVal) * invDelta, 0, 1) * maxInt))
160
- wfn(b, c + stride2, round(clamp((value.Z - minVal) * invDelta, 0, 1) * maxInt))
161
- ch.cursor = c + totalBytes
162
- end,
163
- read = function(src: buffer, pos: number, _refs: { Instance }?): (Vector3, number)
164
- return Vector3.new(
165
- rfn(src, pos) * invMaxInt + minVal,
166
- rfn(src, pos + stride) * invMaxInt + minVal,
167
- rfn(src, pos + stride2) * invMaxInt + minVal
168
- ),
169
- totalBytes
170
- end,
171
- })
172
- end
173
-
174
- return table.freeze(Quantized)