@axpecter/lync 2.1.2 → 2.2.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 +241 -349
- package/package.json +1 -1
- package/src/Types.luau +53 -13
- package/src/api/Group.luau +41 -51
- package/src/api/Packet.luau +145 -143
- package/src/api/Query.luau +204 -202
- package/src/api/Scope.luau +36 -38
- package/src/api/Signal.luau +68 -94
- package/src/codec/Base.luau +43 -23
- package/src/codec/composite/Array.luau +161 -152
- package/src/codec/composite/Map.luau +37 -53
- package/src/codec/composite/Optional.luau +15 -21
- package/src/codec/composite/Shared.luau +78 -41
- package/src/codec/composite/Struct.luau +64 -125
- package/src/codec/composite/Tagged.luau +15 -25
- package/src/codec/composite/Tuple.luau +15 -20
- package/src/codec/datatype/Buffer.luau +44 -51
- package/src/codec/datatype/CFrame.luau +72 -104
- package/src/codec/datatype/Color.luau +14 -10
- package/src/codec/datatype/Instance.luau +32 -42
- package/src/codec/datatype/IntVector.luau +24 -22
- package/src/codec/datatype/NumberRange.luau +21 -12
- package/src/codec/datatype/Ray.luau +13 -7
- package/src/codec/datatype/Rect.luau +13 -7
- package/src/codec/datatype/Region.luau +25 -22
- package/src/codec/datatype/Sequence.luau +144 -118
- package/src/codec/datatype/String.luau +29 -59
- package/src/codec/datatype/UDim.luau +29 -30
- package/src/codec/datatype/Vector.luau +89 -105
- package/src/codec/meta/Auto.luau +194 -246
- package/src/codec/meta/Bitfield.luau +37 -36
- package/src/codec/meta/Custom.luau +10 -10
- package/src/codec/meta/Enum.luau +8 -11
- package/src/codec/meta/Float.luau +16 -20
- package/src/codec/meta/Nothing.luau +2 -2
- package/src/codec/meta/Unknown.luau +22 -32
- package/src/codec/primitive/Bool.luau +9 -5
- package/src/codec/primitive/Float16.luau +13 -23
- package/src/codec/primitive/Int.luau +20 -28
- package/src/codec/primitive/Number.luau +6 -10
- package/src/codec/primitive/Signed.luau +32 -0
- package/src/codec/primitive/Varint.luau +59 -28
- package/src/index.d.ts +8 -2
- package/src/init.luau +118 -143
- package/src/internal/Baseline.luau +17 -18
- package/src/internal/Channel.luau +143 -212
- package/src/internal/Middleware.luau +37 -47
- package/src/internal/Pool.luau +10 -10
- package/src/internal/Registry.luau +38 -34
- package/src/internal/Transport.luau +28 -0
- package/src/internal/Util.luau +26 -0
- package/src/transport/Bridge.luau +32 -24
- package/src/transport/Client.luau +51 -53
- package/src/transport/Gate.luau +183 -113
- package/src/transport/Reader.luau +95 -66
- package/src/transport/Server.luau +130 -125
package/src/index.d.ts
CHANGED
|
@@ -141,7 +141,7 @@ interface LyncModule {
|
|
|
141
141
|
|
|
142
142
|
configure(this: void, options: Lync.ConfigureOptions): void;
|
|
143
143
|
start(this: void): void;
|
|
144
|
-
|
|
144
|
+
isStarted(this: void): boolean;
|
|
145
145
|
|
|
146
146
|
// ── Definitions ─────────────────────────────────────────────────
|
|
147
147
|
|
|
@@ -253,7 +253,13 @@ interface LyncModule {
|
|
|
253
253
|
|
|
254
254
|
enum<T extends string[]>(this: void, ...values: T): Lync.Codec<T[number]>;
|
|
255
255
|
bitfield(this: void, schema: Record<string, { type: "bool" } | { type: "uint"; width: number } | { type: "int"; width: number }>): Lync.Codec<Record<string, boolean | number>>;
|
|
256
|
-
custom<T>(
|
|
256
|
+
custom<T>(
|
|
257
|
+
this: void,
|
|
258
|
+
size: number,
|
|
259
|
+
write: (b: buffer, offset: number, value: T) => void,
|
|
260
|
+
read: (b: buffer, offset: number) => T,
|
|
261
|
+
typeCheck?: string,
|
|
262
|
+
): Lync.Codec<T>;
|
|
257
263
|
readonly nothing: Lync.Codec<undefined>;
|
|
258
264
|
readonly unknown: Lync.Codec<unknown>;
|
|
259
265
|
readonly auto: Lync.Codec<unknown>;
|
package/src/init.luau
CHANGED
|
@@ -15,8 +15,8 @@ local Query = require(script.api.Query)
|
|
|
15
15
|
local Registry = require(script.internal.Registry)
|
|
16
16
|
local Scope = require(script.api.Scope)
|
|
17
17
|
local Types = require(script.Types)
|
|
18
|
+
local Util = require(script.internal.Util)
|
|
18
19
|
|
|
19
|
-
-- Codecs
|
|
20
20
|
local ArrayC = require(script.codec.composite.Array)
|
|
21
21
|
local AutoC = require(script.codec.meta.Auto)
|
|
22
22
|
local BitfieldC = require(script.codec.meta.Bitfield)
|
|
@@ -48,108 +48,123 @@ local UDimC = require(script.codec.datatype.UDim)
|
|
|
48
48
|
local UnknownC = require(script.codec.meta.Unknown)
|
|
49
49
|
local VectorC = require(script.codec.datatype.Vector)
|
|
50
50
|
|
|
51
|
-
-- Constants
|
|
51
|
+
-- Constants --------------------------------------------------------------
|
|
52
52
|
|
|
53
53
|
local IS_SERVER = RunService:IsServer()
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
local DROP = table.freeze({ _lyncDrop = true })
|
|
56
|
+
local ALL = table.freeze({ _lyncAll = true })
|
|
56
57
|
|
|
57
|
-
local
|
|
58
|
-
local
|
|
59
|
-
local
|
|
60
|
-
local
|
|
58
|
+
local CHANNEL_MAX_MIN, CHANNEL_MAX_MAX = 4096, 1048576
|
|
59
|
+
local DEPTH_MIN, DEPTH_MAX = 4, 32
|
|
60
|
+
local POOL_MIN, POOL_MAX = 2, 128
|
|
61
|
+
local FLUSH_HZ_MIN, FLUSH_HZ_MAX = 1, 60
|
|
61
62
|
|
|
62
|
-
--
|
|
63
|
+
-- State ------------------------------------------------------------------
|
|
63
64
|
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
--[[
|
|
66
|
+
Wrap mutable flush state in one table so the Heartbeat closure
|
|
67
|
+
captures a single LCT_VAL upvalue instead of separate LCT_REF
|
|
68
|
+
upvalues for each field.
|
|
69
|
+
]]
|
|
70
|
+
local _flushState = {
|
|
71
|
+
started = false,
|
|
72
|
+
rate = FLUSH_HZ_MAX,
|
|
73
|
+
accum = 0,
|
|
74
|
+
conn = nil :: RBXScriptConnection?,
|
|
75
|
+
transport = nil :: { flush: () -> () }?,
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
-- Private ----------------------------------------------------------------
|
|
79
|
+
|
|
80
|
+
local isPlayer = Util.isPlayer
|
|
66
81
|
|
|
67
82
|
local function except(...: any): any
|
|
68
83
|
local excluded: { [Player]: boolean } = {}
|
|
69
84
|
|
|
70
85
|
for i = 1, select("#", ...) do
|
|
71
86
|
local arg = select(i, ...)
|
|
72
|
-
if
|
|
87
|
+
if isPlayer(arg) then
|
|
73
88
|
excluded[arg :: Player] = true
|
|
74
|
-
elseif typeof(arg) == "table" and
|
|
75
|
-
for player in
|
|
89
|
+
elseif typeof(arg) == "table" and arg._lyncGroup then
|
|
90
|
+
for player in arg._members :: { [Player]: boolean } do
|
|
76
91
|
excluded[player] = true
|
|
77
92
|
end
|
|
78
93
|
else
|
|
79
|
-
error("[Lync] except: argument must be a Player or Group")
|
|
94
|
+
error("[Lync] Lync.except: argument must be a Player or Group")
|
|
80
95
|
end
|
|
81
96
|
end
|
|
82
97
|
|
|
83
|
-
return table.freeze({
|
|
98
|
+
return table.freeze({
|
|
99
|
+
_lyncExcept = true,
|
|
100
|
+
_excluded = table.freeze(excluded),
|
|
101
|
+
})
|
|
84
102
|
end
|
|
85
103
|
|
|
86
104
|
local function setupFlush(): ()
|
|
87
|
-
local
|
|
88
|
-
|
|
105
|
+
local transport: { flush: () -> () }
|
|
89
106
|
if IS_SERVER then
|
|
90
107
|
local Server = require(script.transport.Server)
|
|
91
|
-
|
|
92
|
-
Server.flush()
|
|
93
|
-
end
|
|
108
|
+
transport = { flush = Server.flush }
|
|
94
109
|
else
|
|
95
110
|
local Client = require(script.transport.Client)
|
|
96
|
-
|
|
97
|
-
Client.flush()
|
|
98
|
-
end
|
|
111
|
+
transport = { flush = Client.flush }
|
|
99
112
|
end
|
|
113
|
+
_flushState.transport = transport
|
|
100
114
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
+
_flushState.conn = RunService.Heartbeat:Connect(function(dt: number)
|
|
116
|
+
local rate = _flushState.rate
|
|
117
|
+
if rate >= FLUSH_HZ_MAX then
|
|
118
|
+
transport.flush()
|
|
119
|
+
return
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
local accum = _flushState.accum + dt
|
|
123
|
+
local interval = 1 / rate
|
|
124
|
+
if accum >= interval then
|
|
125
|
+
-- Drift guard: if we're more than one interval behind (frame
|
|
126
|
+
-- stall) zero the accumulator instead of catching up.
|
|
127
|
+
accum = if accum >= 2 * interval then 0 else accum - interval
|
|
128
|
+
transport.flush()
|
|
115
129
|
end
|
|
130
|
+
_flushState.accum = accum
|
|
116
131
|
end)
|
|
117
132
|
end
|
|
118
133
|
|
|
119
|
-
-- Public
|
|
120
|
-
|
|
121
|
-
local Lync: any = {}
|
|
134
|
+
-- Public -----------------------------------------------------------------
|
|
122
135
|
|
|
123
|
-
|
|
136
|
+
local Lync = {}
|
|
124
137
|
|
|
125
138
|
function Lync.configure(options: Types.ConfigureOptions): ()
|
|
126
|
-
if
|
|
139
|
+
if _flushState.started then
|
|
127
140
|
error("[Lync] Lync.configure: must be called before Lync.start()")
|
|
128
141
|
end
|
|
129
142
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if size <
|
|
133
|
-
error(
|
|
143
|
+
local size = options.channelMaxSize
|
|
144
|
+
if size then
|
|
145
|
+
if size < CHANNEL_MAX_MIN or size > CHANNEL_MAX_MAX then
|
|
146
|
+
error(
|
|
147
|
+
`[Lync] Lync.configure: channelMaxSize must be {CHANNEL_MAX_MIN}-{CHANNEL_MAX_MAX}`
|
|
148
|
+
)
|
|
134
149
|
end
|
|
135
150
|
Channel.setMaxSize(size)
|
|
136
151
|
Base.setMaxSize(size)
|
|
137
152
|
end
|
|
138
153
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
if depth <
|
|
142
|
-
error(
|
|
154
|
+
local depth = options.validationDepth
|
|
155
|
+
if depth then
|
|
156
|
+
if depth < DEPTH_MIN or depth > DEPTH_MAX then
|
|
157
|
+
error(`[Lync] Lync.configure: validationDepth must be {DEPTH_MIN}-{DEPTH_MAX}`)
|
|
143
158
|
end
|
|
144
159
|
Gate.setDepth(depth)
|
|
145
160
|
end
|
|
146
161
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
if
|
|
150
|
-
error(
|
|
162
|
+
local poolSize = options.poolSize
|
|
163
|
+
if poolSize then
|
|
164
|
+
if poolSize < POOL_MIN or poolSize > POOL_MAX then
|
|
165
|
+
error(`[Lync] Lync.configure: poolSize must be {POOL_MIN}-{POOL_MAX}`)
|
|
151
166
|
end
|
|
152
|
-
Pool.setMaxSize(
|
|
167
|
+
Pool.setMaxSize(poolSize)
|
|
153
168
|
end
|
|
154
169
|
|
|
155
170
|
if options.bandwidthLimit then
|
|
@@ -170,10 +185,10 @@ function Lync.configure(options: Types.ConfigureOptions): ()
|
|
|
170
185
|
end
|
|
171
186
|
|
|
172
187
|
function Lync.start(): ()
|
|
173
|
-
if
|
|
188
|
+
if _flushState.started then
|
|
174
189
|
error("[Lync] Lync.start: already started")
|
|
175
190
|
end
|
|
176
|
-
|
|
191
|
+
_flushState.started = true
|
|
177
192
|
|
|
178
193
|
if IS_SERVER then
|
|
179
194
|
local Server = require(script.transport.Server)
|
|
@@ -186,57 +201,40 @@ function Lync.start(): ()
|
|
|
186
201
|
setupFlush()
|
|
187
202
|
end
|
|
188
203
|
|
|
189
|
-
|
|
190
|
-
|
|
204
|
+
function Lync.flush(): ()
|
|
205
|
+
if not _flushState.started then
|
|
206
|
+
error("[Lync] Lync.flush: must call Lync.start() first")
|
|
207
|
+
end
|
|
208
|
+
-- start() is the only path that sets transport, so it's non-nil here.
|
|
209
|
+
(_flushState.transport :: { flush: () -> () }).flush()
|
|
210
|
+
_flushState.accum = 0
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
function Lync.flushRate(hz: number): ()
|
|
214
|
+
if hz < FLUSH_HZ_MIN or hz > FLUSH_HZ_MAX then
|
|
215
|
+
error(`[Lync] Lync.flushRate: hz must be {FLUSH_HZ_MIN}-{FLUSH_HZ_MAX}`)
|
|
216
|
+
end
|
|
217
|
+
_flushState.rate = hz
|
|
218
|
+
_flushState.accum = 0
|
|
219
|
+
end
|
|
191
220
|
|
|
192
|
-
|
|
221
|
+
function Lync.isStarted(): boolean
|
|
222
|
+
return _flushState.started
|
|
223
|
+
end
|
|
193
224
|
|
|
194
225
|
Lync.packet = Packet.define
|
|
195
226
|
Lync.query = Query.define
|
|
196
227
|
Lync.group = Group.create
|
|
197
228
|
Lync.scope = Scope.create
|
|
198
229
|
|
|
199
|
-
-- Targeting -----------------------------------------------------------
|
|
200
|
-
|
|
201
230
|
Lync.all = ALL
|
|
202
231
|
Lync.except = except
|
|
203
232
|
Lync.DROP = DROP
|
|
204
233
|
|
|
205
|
-
-- Middleware ----------------------------------------------------------
|
|
206
|
-
|
|
207
234
|
Lync.onSend = Middleware.onSend
|
|
208
235
|
Lync.onReceive = Middleware.onReceive
|
|
209
236
|
Lync.onDrop = Middleware.onDrop
|
|
210
237
|
|
|
211
|
-
-- Runtime control -----------------------------------------------------
|
|
212
|
-
|
|
213
|
-
function Lync.flush(): ()
|
|
214
|
-
if not _started then
|
|
215
|
-
error("[Lync] Lync.flush: must call Lync.start() first")
|
|
216
|
-
end
|
|
217
|
-
|
|
218
|
-
if IS_SERVER then
|
|
219
|
-
local Server = require(script.transport.Server)
|
|
220
|
-
Server.flush()
|
|
221
|
-
else
|
|
222
|
-
local Client = require(script.transport.Client)
|
|
223
|
-
Client.flush()
|
|
224
|
-
end
|
|
225
|
-
|
|
226
|
-
-- Reset accumulator to prevent double-flush
|
|
227
|
-
_flushAccum = 0
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
function Lync.flushRate(hz: number): ()
|
|
231
|
-
if hz < 1 or hz > 60 then
|
|
232
|
-
error("[Lync] Lync.flushRate: hz must be 1-60")
|
|
233
|
-
end
|
|
234
|
-
_flushRate = hz
|
|
235
|
-
_flushAccum = 0
|
|
236
|
-
end
|
|
237
|
-
|
|
238
|
-
-- Stats ---------------------------------------------------------------
|
|
239
|
-
|
|
240
238
|
Lync.stats = table.freeze({
|
|
241
239
|
player = function(player: Player): Types.PlayerStats?
|
|
242
240
|
if not IS_SERVER then
|
|
@@ -251,56 +249,48 @@ Lync.stats = table.freeze({
|
|
|
251
249
|
local Server = require(script.transport.Server)
|
|
252
250
|
Server.resetStats()
|
|
253
251
|
end
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
252
|
+
for i = 1, Registry.count() do
|
|
253
|
+
local reg = Registry.get(i)
|
|
254
|
+
if reg then
|
|
255
|
+
reg.bytesSent = 0
|
|
256
|
+
reg.bytesReceived = 0
|
|
257
|
+
reg.fires = 0
|
|
258
|
+
reg.recvFires = 0
|
|
259
|
+
reg.drops = 0
|
|
260
|
+
end
|
|
262
261
|
end
|
|
263
262
|
end,
|
|
264
263
|
})
|
|
265
264
|
|
|
266
|
-
-- Debug ---------------------------------------------------------------
|
|
267
|
-
|
|
268
265
|
Lync.debug = table.freeze({
|
|
269
|
-
capture = function(_label: string?): ()
|
|
270
|
-
|
|
271
|
-
end,
|
|
272
|
-
|
|
273
|
-
stop = function(): ()
|
|
274
|
-
-- TODO: Implement capture stop
|
|
275
|
-
end,
|
|
276
|
-
|
|
277
|
-
dump = function(): ()
|
|
278
|
-
-- TODO: Implement capture dump
|
|
279
|
-
end,
|
|
266
|
+
capture = function(_label: string?): () end,
|
|
267
|
+
stop = function(): () end,
|
|
268
|
+
dump = function(): () end,
|
|
280
269
|
|
|
281
270
|
pending = function(): number
|
|
282
271
|
return Query.pendingCount()
|
|
283
272
|
end,
|
|
284
273
|
|
|
285
274
|
registrations = function(): { any }
|
|
286
|
-
local
|
|
287
|
-
local result = table.create(
|
|
288
|
-
|
|
289
|
-
for i
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
275
|
+
local count = Registry.count()
|
|
276
|
+
local result = table.create(count)
|
|
277
|
+
local n = 0
|
|
278
|
+
for i = 1, count do
|
|
279
|
+
local reg = Registry.get(i)
|
|
280
|
+
if reg then
|
|
281
|
+
n += 1
|
|
282
|
+
result[n] = table.freeze({
|
|
283
|
+
name = reg.name,
|
|
284
|
+
id = reg.id,
|
|
285
|
+
kind = reg.kind,
|
|
286
|
+
isUnreliable = reg.isUnreliable,
|
|
287
|
+
})
|
|
288
|
+
end
|
|
296
289
|
end
|
|
297
|
-
|
|
298
290
|
return table.freeze(result)
|
|
299
291
|
end,
|
|
300
292
|
})
|
|
301
293
|
|
|
302
|
-
-- Codecs --------------------------------------------------------------
|
|
303
|
-
|
|
304
294
|
Lync.int = IntC.int
|
|
305
295
|
Lync.float = FloatC.float
|
|
306
296
|
Lync.f16 = Float16C.f16
|
|
@@ -341,19 +331,4 @@ Lync.nothing = NothingC.nothing
|
|
|
341
331
|
Lync.unknown = UnknownC.unknown
|
|
342
332
|
Lync.auto = AutoC.auto
|
|
343
333
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
local LyncProxy = setmetatable({}, {
|
|
347
|
-
__index = function(_self: any, key: string): any
|
|
348
|
-
if key == "started" then
|
|
349
|
-
return _started
|
|
350
|
-
end
|
|
351
|
-
return Lync[key]
|
|
352
|
-
end,
|
|
353
|
-
|
|
354
|
-
__newindex = function(): ()
|
|
355
|
-
error("[Lync] Lync: module table is read-only")
|
|
356
|
-
end,
|
|
357
|
-
})
|
|
358
|
-
|
|
359
|
-
return LyncProxy
|
|
334
|
+
return table.freeze(Lync)
|
|
@@ -2,40 +2,39 @@
|
|
|
2
2
|
--!optimize 2
|
|
3
3
|
-- Read-side delta cache for delta codecs.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
local Types = require(script.Parent.Parent.Types)
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
-- State ------------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
local _caches: { [number]: { [Types.BaselineKey]: any } } = {}
|
|
10
|
+
local _currentKey: Types.BaselineKey = false
|
|
9
11
|
local _hasDelta = false
|
|
10
12
|
|
|
11
|
-
-- Public
|
|
13
|
+
-- Public -----------------------------------------------------------------
|
|
12
14
|
|
|
13
15
|
local Baseline = {}
|
|
14
16
|
|
|
15
|
-
function Baseline.setReadKey(key:
|
|
17
|
+
function Baseline.setReadKey(key: Types.BaselineKey): ()
|
|
16
18
|
_currentKey = key
|
|
17
19
|
end
|
|
18
20
|
|
|
19
21
|
function Baseline.getCache(deltaId: number): any?
|
|
20
|
-
local
|
|
21
|
-
if
|
|
22
|
-
return nil
|
|
23
|
-
end
|
|
24
|
-
return perPlayer[_currentKey]
|
|
22
|
+
local perKey = _caches[deltaId]
|
|
23
|
+
return if perKey then perKey[_currentKey] else nil
|
|
25
24
|
end
|
|
26
25
|
|
|
27
26
|
function Baseline.setCache(deltaId: number, value: any): ()
|
|
28
|
-
local
|
|
29
|
-
if not
|
|
30
|
-
|
|
31
|
-
_caches[deltaId] =
|
|
27
|
+
local perKey = _caches[deltaId]
|
|
28
|
+
if not perKey then
|
|
29
|
+
perKey = {}
|
|
30
|
+
_caches[deltaId] = perKey
|
|
32
31
|
end
|
|
33
|
-
|
|
32
|
+
perKey[_currentKey] = value
|
|
34
33
|
end
|
|
35
34
|
|
|
36
|
-
function Baseline.clearPlayer(key:
|
|
37
|
-
for _,
|
|
38
|
-
|
|
35
|
+
function Baseline.clearPlayer(key: Types.BaselineKey): ()
|
|
36
|
+
for _, perKey in _caches do
|
|
37
|
+
perKey[key] = nil
|
|
39
38
|
end
|
|
40
39
|
end
|
|
41
40
|
|