@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,364 @@
1
+ --!strict
2
+ --!optimize 2
3
+ -- Server-side transport with batching, delta state, frame XOR.
4
+
5
+ local Players = game:GetService ("Players")
6
+ local RunService = game:GetService ("RunService")
7
+
8
+ local Baseline = require (script.Parent.Parent.internal.Baseline)
9
+ local Bridge = require (script.Parent.Bridge)
10
+ local Channel = require (script.Parent.Parent.internal.Channel)
11
+
12
+ local channelWriteBatch = Channel.writeBatch
13
+ local channelWriteBatchRaw = Channel.writeBatchRaw
14
+ local channelSetPacket = Channel.setCurrentPacket
15
+
16
+ local Gate = require (script.Parent.Gate)
17
+ local Group = require (script.Parent.Parent.api.Group)
18
+ local Middleware = require (script.Parent.Parent.internal.Middleware)
19
+ local Pool = require (script.Parent.Parent.internal.Pool)
20
+ local Reader = require (script.Parent.Reader)
21
+ local Types = require (script.Parent.Parent.Types)
22
+
23
+ type ChannelState = Types.ChannelState
24
+ type Codec<T> = Types.Codec<T>
25
+
26
+ -- State ------------------------------------------------------------------
27
+
28
+ type PlayerState = {
29
+ reliable: ChannelState,
30
+ unreliable: ChannelState,
31
+ }
32
+
33
+ local _players = {} :: { [Player]: PlayerState }
34
+ local _prevRecv = {} :: { [Player]: buffer }
35
+ local _isStarted = false
36
+ local _broadScratch = Channel.create ()
37
+
38
+ -- Private ----------------------------------------------------------------
39
+
40
+ local function onPlayerAdded (player: Player): ()
41
+ _players[player] = {
42
+ reliable = Pool.acquire (),
43
+ unreliable = Pool.acquire (),
44
+ }
45
+ Gate.onPlayerAdded (player)
46
+ end
47
+
48
+ local function onPlayerRemoving (player: Player): ()
49
+ local state = _players[player]
50
+ if not state then
51
+ return
52
+ end
53
+
54
+ Pool.release (state.reliable)
55
+ Pool.release (state.unreliable)
56
+ _players[player] = nil
57
+ _prevRecv[player] = nil
58
+
59
+ Gate.onPlayerRemoved (player)
60
+ Baseline.clearSource (player)
61
+ end
62
+
63
+ local function receive (player: Player, data: any, refs: any, applyXor: boolean): ()
64
+ local buf = Reader.decodeIncoming (data)
65
+ if not buf then
66
+ return
67
+ end
68
+
69
+ local raw: buffer
70
+ if applyXor then
71
+ raw = Channel.xorApply (buf, _prevRecv[player])
72
+ _prevRecv[player] = raw
73
+ else
74
+ raw = buf
75
+ end
76
+
77
+ local safeRefs: { Instance }? = if typeof (refs) == "table" then refs else nil
78
+ local ok, err = pcall (Reader.process, raw, safeRefs, player, 0)
79
+ if not ok then
80
+ warn (`[Lync] Read failed from {player.Name} ({player.UserId}): {err}`)
81
+ end
82
+ end
83
+
84
+ local function flushChannel (remote: any, ch: ChannelState, target: Player, applyXor: boolean): ()
85
+ if ch.cursor == 0 then
86
+ return
87
+ end
88
+
89
+ local raw, refs = Channel.sealAndDump (ch)
90
+
91
+ if applyXor then
92
+ local xored = Channel.xorApply (raw, ch.prevDump)
93
+ ch.prevDump = raw
94
+ remote:FireClient (target, xored, refs)
95
+ else
96
+ remote:FireClient (target, raw, refs)
97
+ end
98
+
99
+ Channel.reset (ch)
100
+ end
101
+
102
+ local function flush (): ()
103
+ local reliable = Bridge.reliable
104
+ local unreliable = Bridge.unreliable
105
+
106
+ for player, state in _players do
107
+ local ok, err = pcall (flushChannel, reliable, state.reliable, player, true)
108
+ if not ok then
109
+ warn (`[Lync] Flush failed for {player.Name} ({player.UserId}): {err}`)
110
+ Channel.reset (state.reliable)
111
+ end
112
+
113
+ local ok2, err2 = pcall (flushChannel, unreliable, state.unreliable, player, false)
114
+ if not ok2 then
115
+ warn (`[Lync] Flush failed for {player.Name} ({player.UserId}): {err2}`)
116
+ Channel.reset (state.unreliable)
117
+ end
118
+ end
119
+ end
120
+
121
+ -- Serialize data once into shared scratch, return raw bytes + refs snapshot.
122
+ local function serializeBroadcast (
123
+ name: string,
124
+ codec: Codec<any>,
125
+ data: any
126
+ ): (number, { Instance }?)
127
+ local scratch = _broadScratch
128
+ scratch.cursor = 0
129
+ table.clear (scratch.refs)
130
+ channelSetPacket (name)
131
+ codec.write (scratch, data)
132
+
133
+ local snapRefs = if #scratch.refs > 0 then table.clone (scratch.refs) else nil
134
+ return scratch.cursor, snapRefs
135
+ end
136
+
137
+ -- Run middleware and serialize once. Returns nil if middleware dropped.
138
+ local function prepareBroadcast (
139
+ name: string,
140
+ codec: Codec<any>,
141
+ data: any
142
+ ): (any, number, { Instance }?)
143
+ local final: any
144
+ if Middleware.hasSend then
145
+ final = Middleware.runSend (data, name, nil)
146
+ if final == nil then
147
+ return nil, 0, nil
148
+ end
149
+ else
150
+ final = data
151
+ end
152
+
153
+ local snapLen, snapRefs = serializeBroadcast (name, codec, final)
154
+ return final, snapLen, snapRefs
155
+ end
156
+
157
+ -- Fan out to all connected players via _players map.
158
+ local function broadcastToAll (
159
+ id: number,
160
+ name: string,
161
+ codec: Codec<any>,
162
+ data: any,
163
+ isUnreliable: boolean,
164
+ except: Player?
165
+ ): ()
166
+ local final, snapLen, snapRefs = prepareBroadcast (name, codec, data)
167
+ if final == nil and data ~= nil then
168
+ return
169
+ end
170
+
171
+ local scratchBuff = _broadScratch.buff
172
+ for player, state in _players do
173
+ if except and player == except then
174
+ continue
175
+ end
176
+ local ch = if isUnreliable then state.unreliable else state.reliable
177
+ channelWriteBatchRaw (ch, id, scratchBuff, snapLen, snapRefs)
178
+ end
179
+ end
180
+
181
+ -- Fan out to a specific player list.
182
+ local function broadcastToList (
183
+ players: { Player },
184
+ id: number,
185
+ name: string,
186
+ codec: Codec<any>,
187
+ data: any,
188
+ isUnreliable: boolean
189
+ ): ()
190
+ local final, snapLen, snapRefs = prepareBroadcast (name, codec, data)
191
+ if final == nil and data ~= nil then
192
+ return
193
+ end
194
+
195
+ local scratchBuff = _broadScratch.buff
196
+ for _, player in players do
197
+ local state = _players[player]
198
+ if not state then
199
+ continue
200
+ end
201
+ local ch = if isUnreliable then state.unreliable else state.reliable
202
+ channelWriteBatchRaw (ch, id, scratchBuff, snapLen, snapRefs)
203
+ end
204
+ end
205
+
206
+ -- Fan out to a group set { [Player]: true }.
207
+ local function broadcastToSet (
208
+ set: { [Player]: true },
209
+ id: number,
210
+ name: string,
211
+ codec: Codec<any>,
212
+ data: any,
213
+ isUnreliable: boolean
214
+ ): ()
215
+ local final, snapLen, snapRefs = prepareBroadcast (name, codec, data)
216
+ if final == nil and data ~= nil then
217
+ return
218
+ end
219
+
220
+ local scratchBuff = _broadScratch.buff
221
+ for player in set do
222
+ local state = _players[player]
223
+ if not state then
224
+ continue
225
+ end
226
+ local ch = if isUnreliable then state.unreliable else state.reliable
227
+ channelWriteBatchRaw (ch, id, scratchBuff, snapLen, snapRefs)
228
+ end
229
+ end
230
+
231
+ -- Public -----------------------------------------------------------------
232
+
233
+ local Server = {}
234
+
235
+ function Server.start (): ()
236
+ if _isStarted then
237
+ return
238
+ end
239
+ _isStarted = true
240
+
241
+ Bridge.setup ()
242
+
243
+ Players.PlayerAdded:Connect (onPlayerAdded)
244
+ Players.PlayerRemoving:Connect (onPlayerRemoving)
245
+ for _, player in Players:GetPlayers () do
246
+ onPlayerAdded (player)
247
+ end
248
+
249
+ Bridge.reliable.OnServerEvent:Connect (function (player: Player, data: any, refs: any): ()
250
+ receive (player, data, refs, true)
251
+ end)
252
+ Bridge.unreliable.OnServerEvent:Connect (function (player: Player, data: any, refs: any): ()
253
+ receive (player, data, refs, false)
254
+ end)
255
+ RunService.Heartbeat:Connect (flush)
256
+ end
257
+
258
+ function Server.writeTo (
259
+ player: Player,
260
+ id: number,
261
+ name: string,
262
+ codec: Codec<any>,
263
+ data: any,
264
+ isUnreliable: boolean
265
+ ): ()
266
+ local state = _players[player]
267
+ if not state then
268
+ return
269
+ end
270
+
271
+ local final: any
272
+ if Middleware.hasSend then
273
+ final = Middleware.runSend (data, name, player)
274
+ if final == nil then
275
+ return
276
+ end
277
+ else
278
+ final = data
279
+ end
280
+
281
+ local ch = if isUnreliable then state.unreliable else state.reliable
282
+ channelWriteBatch (ch, id, name, codec, final)
283
+ end
284
+
285
+ function Server.writeToAll (
286
+ id: number,
287
+ name: string,
288
+ codec: Codec<any>,
289
+ data: any,
290
+ isUnreliable: boolean
291
+ ): ()
292
+ broadcastToAll (id, name, codec, data, isUnreliable, nil)
293
+ end
294
+
295
+ function Server.writeToList (
296
+ players: { Player },
297
+ id: number,
298
+ name: string,
299
+ codec: Codec<any>,
300
+ data: any,
301
+ isUnreliable: boolean
302
+ ): ()
303
+ broadcastToList (players, id, name, codec, data, isUnreliable)
304
+ end
305
+
306
+ function Server.writeToAllExcept (
307
+ except: Player,
308
+ id: number,
309
+ name: string,
310
+ codec: Codec<any>,
311
+ data: any,
312
+ isUnreliable: boolean
313
+ ): ()
314
+ broadcastToAll (id, name, codec, data, isUnreliable, except)
315
+ end
316
+
317
+ function Server.writeToGroup (
318
+ groupName: string,
319
+ id: number,
320
+ name: string,
321
+ codec: Codec<any>,
322
+ data: any,
323
+ isUnreliable: boolean
324
+ ): ()
325
+ broadcastToSet (Group.getSet (groupName), id, name, codec, data, isUnreliable)
326
+ end
327
+
328
+ -- Shared for both query requests and responses (identical wire format).
329
+ function Server.writeQuery (
330
+ player: Player,
331
+ id: number,
332
+ name: string,
333
+ correlationId: number,
334
+ codec: Codec<any>,
335
+ data: any
336
+ ): ()
337
+ local state = _players[player]
338
+ if not state then
339
+ return
340
+ end
341
+
342
+ local final: any
343
+ if Middleware.hasSend then
344
+ final = Middleware.runSend (data, name, player)
345
+ if final == nil then
346
+ return
347
+ end
348
+ else
349
+ final = data
350
+ end
351
+
352
+ Channel.writeQuery (state.reliable, id, name, correlationId, codec, final)
353
+ end
354
+
355
+ function Server.writeQueryNil (player: Player, id: number, name: string, correlationId: number): ()
356
+ local state = _players[player]
357
+ if not state then
358
+ return
359
+ end
360
+
361
+ Channel.writeQuery (state.reliable, id, name, correlationId, nil, nil)
362
+ end
363
+
364
+ return table.freeze (Server)