@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.
- package/README.md +300 -0
- package/package.json +38 -0
- package/src/Types.luau +63 -0
- package/src/api/Group.luau +126 -0
- package/src/api/Namespace.luau +226 -0
- package/src/api/Packet.luau +147 -0
- package/src/api/Query.luau +295 -0
- package/src/api/Signal.luau +224 -0
- package/src/codec/Base.luau +49 -0
- package/src/codec/composite/Array.luau +275 -0
- package/src/codec/composite/Map.luau +395 -0
- package/src/codec/composite/Optional.luau +47 -0
- package/src/codec/composite/Shared.luau +151 -0
- package/src/codec/composite/Struct.luau +440 -0
- package/src/codec/composite/Tagged.luau +222 -0
- package/src/codec/composite/Tuple.luau +143 -0
- package/src/codec/datatype/Buffer.luau +44 -0
- package/src/codec/datatype/CFrame.luau +51 -0
- package/src/codec/datatype/Color.luau +22 -0
- package/src/codec/datatype/Instance.luau +48 -0
- package/src/codec/datatype/IntVector.luau +25 -0
- package/src/codec/datatype/NumberRange.luau +14 -0
- package/src/codec/datatype/Ray.luau +27 -0
- package/src/codec/datatype/Rect.luau +21 -0
- package/src/codec/datatype/Region.luau +58 -0
- package/src/codec/datatype/Sequence.luau +129 -0
- package/src/codec/datatype/String.luau +87 -0
- package/src/codec/datatype/UDim.luau +27 -0
- package/src/codec/datatype/Vector.luau +25 -0
- package/src/codec/meta/Auto.luau +353 -0
- package/src/codec/meta/Bitfield.luau +191 -0
- package/src/codec/meta/Custom.luau +27 -0
- package/src/codec/meta/Enum.luau +80 -0
- package/src/codec/meta/Nothing.luau +9 -0
- package/src/codec/meta/Quantized.luau +170 -0
- package/src/codec/meta/Unknown.luau +35 -0
- package/src/codec/primitive/Bool.luau +30 -0
- package/src/codec/primitive/Float16.luau +111 -0
- package/src/codec/primitive/Number.luau +48 -0
- package/src/codec/primitive/Varint.luau +76 -0
- package/src/index.d.ts +279 -0
- package/src/init.luau +161 -0
- package/src/internal/Baseline.luau +41 -0
- package/src/internal/Channel.luau +235 -0
- package/src/internal/Middleware.luau +109 -0
- package/src/internal/Pool.luau +68 -0
- package/src/internal/Registry.luau +146 -0
- package/src/transport/Bridge.luau +66 -0
- package/src/transport/Client.luau +151 -0
- package/src/transport/Gate.luau +222 -0
- package/src/transport/Reader.luau +175 -0
- 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)
|