@axpecter/lync 2.2.1 → 2.3.1
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 +95 -143
- package/package.json +1 -1
- package/src/Types.luau +22 -16
- package/src/api/Group.luau +40 -33
- package/src/api/Packet.luau +68 -54
- package/src/api/Query.luau +103 -65
- package/src/api/Scope.luau +26 -10
- package/src/api/Signal.luau +43 -52
- package/src/codec/Base.luau +21 -14
- package/src/codec/composite/Array.luau +56 -134
- package/src/codec/composite/Map.luau +103 -72
- package/src/codec/composite/Optional.luau +6 -2
- package/src/codec/composite/Shared.luau +160 -69
- package/src/codec/composite/Struct.luau +283 -42
- package/src/codec/composite/Tagged.luau +13 -16
- package/src/codec/composite/Tuple.luau +21 -15
- package/src/codec/datatype/Buffer.luau +6 -4
- package/src/codec/datatype/CFrame.luau +56 -44
- package/src/codec/datatype/Color.luau +10 -10
- package/src/codec/datatype/Instance.luau +15 -12
- package/src/codec/datatype/IntVector.luau +2 -2
- package/src/codec/datatype/NumberRange.luau +15 -8
- package/src/codec/datatype/Ray.luau +13 -14
- package/src/codec/datatype/Rect.luau +12 -12
- package/src/codec/datatype/Region.luau +13 -14
- package/src/codec/datatype/Sequence.luau +87 -65
- package/src/codec/datatype/String.luau +17 -10
- package/src/codec/datatype/UDim.luau +10 -8
- package/src/codec/datatype/Vector.luau +20 -59
- package/src/codec/meta/Auto.luau +94 -126
- package/src/codec/meta/Bitfield.luau +12 -14
- package/src/codec/meta/Custom.luau +3 -1
- package/src/codec/meta/Enum.luau +9 -9
- package/src/codec/meta/Float.luau +6 -28
- package/src/codec/meta/Nothing.luau +1 -1
- package/src/codec/meta/Unknown.luau +10 -7
- package/src/codec/primitive/Bool.luau +6 -4
- package/src/codec/primitive/Float16.luau +5 -2
- package/src/codec/primitive/Int.luau +5 -6
- package/src/codec/primitive/Number.luau +6 -4
- package/src/codec/primitive/Signed.luau +2 -2
- package/src/codec/primitive/Varint.luau +69 -38
- package/src/index.d.ts +161 -53
- package/src/init.luau +109 -103
- package/src/internal/Baseline.luau +9 -1
- package/src/internal/Channel.luau +153 -154
- package/src/internal/Middleware.luau +22 -6
- package/src/internal/Pool.luau +12 -4
- package/src/internal/Registry.luau +25 -16
- package/src/internal/Transport.luau +1 -1
- package/src/transport/Bridge.luau +47 -33
- package/src/transport/Client.luau +25 -21
- package/src/transport/Gate.luau +227 -172
- package/src/transport/Reader.luau +162 -105
- package/src/transport/Server.luau +46 -57
- package/src/util/Array.luau +18 -0
- package/src/util/Buffer.luau +92 -0
- package/src/util/Constants.luau +30 -0
- package/src/util/Log.luau +68 -0
- package/src/util/Player.luau +14 -0
- package/src/util/Quantize.luau +60 -0
- package/src/internal/Util.luau +0 -26
package/src/init.luau
CHANGED
|
@@ -4,18 +4,20 @@
|
|
|
4
4
|
|
|
5
5
|
local RunService = game:GetService("RunService")
|
|
6
6
|
|
|
7
|
-
local
|
|
7
|
+
local Baseline = require(script.internal.Baseline)
|
|
8
|
+
local Bridge = require(script.transport.Bridge)
|
|
8
9
|
local Channel = require(script.internal.Channel)
|
|
9
10
|
local Gate = require(script.transport.Gate)
|
|
10
11
|
local Group = require(script.api.Group)
|
|
12
|
+
local Log = require(script.util.Log)
|
|
11
13
|
local Middleware = require(script.internal.Middleware)
|
|
12
14
|
local Packet = require(script.api.Packet)
|
|
15
|
+
local Player = require(script.util.Player)
|
|
13
16
|
local Pool = require(script.internal.Pool)
|
|
14
17
|
local Query = require(script.api.Query)
|
|
15
18
|
local Registry = require(script.internal.Registry)
|
|
16
19
|
local Scope = require(script.api.Scope)
|
|
17
20
|
local Types = require(script.Types)
|
|
18
|
-
local Util = require(script.internal.Util)
|
|
19
21
|
|
|
20
22
|
local ArrayC = require(script.codec.composite.Array)
|
|
21
23
|
local AutoC = require(script.codec.meta.Auto)
|
|
@@ -48,7 +50,7 @@ local UDimC = require(script.codec.datatype.UDim)
|
|
|
48
50
|
local UnknownC = require(script.codec.meta.Unknown)
|
|
49
51
|
local VectorC = require(script.codec.datatype.Vector)
|
|
50
52
|
|
|
51
|
-
-- Public types
|
|
53
|
+
-- Public types -----------------------------------------------------------
|
|
52
54
|
|
|
53
55
|
export type Codec<T> = Types.Codec<T>
|
|
54
56
|
export type Connection = Types.Connection
|
|
@@ -67,8 +69,12 @@ export type PlayerStats = Types.PlayerStats
|
|
|
67
69
|
|
|
68
70
|
local IS_SERVER = RunService:IsServer()
|
|
69
71
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
+
--[[
|
|
73
|
+
Singleton-string brand on a single `_lyncKind` field: dispatch is one
|
|
74
|
+
hash lookup at the same predicted slot, and the literals narrow on `==`.
|
|
75
|
+
]]
|
|
76
|
+
local ALL = table.freeze({ _lyncKind = "all" :: "all" })
|
|
77
|
+
local DROP = table.freeze({ _lyncKind = "drop" :: "drop" })
|
|
72
78
|
|
|
73
79
|
local CHANNEL_MAX_MIN, CHANNEL_MAX_MAX = 4096, 1048576
|
|
74
80
|
local DEPTH_MIN, DEPTH_MAX = 4, 32
|
|
@@ -77,12 +83,8 @@ local FLUSH_HZ_MIN, FLUSH_HZ_MAX = 1, 60
|
|
|
77
83
|
|
|
78
84
|
-- State ------------------------------------------------------------------
|
|
79
85
|
|
|
80
|
-
--
|
|
81
|
-
|
|
82
|
-
captures a single LCT_VAL upvalue instead of separate LCT_REF
|
|
83
|
-
upvalues for each field.
|
|
84
|
-
]]
|
|
85
|
-
local _flushState = {
|
|
86
|
+
-- Single table so the Heartbeat closure captures one LCT_VAL, not N LCT_REFs.
|
|
87
|
+
local flushState = {
|
|
86
88
|
started = false,
|
|
87
89
|
rate = FLUSH_HZ_MAX,
|
|
88
90
|
accum = 0,
|
|
@@ -92,95 +94,86 @@ local _flushState = {
|
|
|
92
94
|
|
|
93
95
|
-- Private ----------------------------------------------------------------
|
|
94
96
|
|
|
95
|
-
local isPlayer =
|
|
97
|
+
local isPlayer = Player.is
|
|
98
|
+
|
|
99
|
+
type ExceptValue = { _lyncKind: "except", _excluded: { [Player]: boolean } }
|
|
96
100
|
|
|
97
|
-
local function except(...: any):
|
|
101
|
+
local function except(...: any): ExceptValue
|
|
98
102
|
local excluded: { [Player]: boolean } = {}
|
|
99
103
|
|
|
100
104
|
for i = 1, select("#", ...) do
|
|
101
105
|
local arg = select(i, ...)
|
|
102
106
|
if isPlayer(arg) then
|
|
103
107
|
excluded[arg :: Player] = true
|
|
104
|
-
elseif typeof(arg) == "table" and arg.
|
|
105
|
-
for player in arg._members
|
|
108
|
+
elseif typeof(arg) == "table" and arg._lyncKind == "group" then
|
|
109
|
+
for player in arg._members do
|
|
106
110
|
excluded[player] = true
|
|
107
111
|
end
|
|
108
112
|
else
|
|
109
|
-
error(
|
|
113
|
+
Log.error(`argument must be a Player or Group, got {typeof(arg)}`)
|
|
110
114
|
end
|
|
111
115
|
end
|
|
112
116
|
|
|
113
117
|
return table.freeze({
|
|
114
|
-
|
|
118
|
+
_lyncKind = "except" :: "except",
|
|
115
119
|
_excluded = table.freeze(excluded),
|
|
116
120
|
})
|
|
117
121
|
end
|
|
118
122
|
|
|
119
|
-
local function setupFlush(): ()
|
|
120
|
-
|
|
121
|
-
if IS_SERVER then
|
|
122
|
-
local Server = require(script.transport.Server)
|
|
123
|
-
transport = { flush = Server.flush }
|
|
124
|
-
else
|
|
125
|
-
local Client = require(script.transport.Client)
|
|
126
|
-
transport = { flush = Client.flush }
|
|
127
|
-
end
|
|
128
|
-
_flushState.transport = transport
|
|
123
|
+
local function setupFlush(transport: { flush: () -> () }): ()
|
|
124
|
+
flushState.transport = transport
|
|
129
125
|
|
|
130
|
-
|
|
131
|
-
local rate =
|
|
126
|
+
flushState.conn = RunService.Heartbeat:Connect(function(dt: number)
|
|
127
|
+
local rate = flushState.rate
|
|
132
128
|
if rate >= FLUSH_HZ_MAX then
|
|
133
129
|
transport.flush()
|
|
134
130
|
return
|
|
135
131
|
end
|
|
136
132
|
|
|
137
|
-
local accum =
|
|
133
|
+
local accum = flushState.accum + dt
|
|
138
134
|
local interval = 1 / rate
|
|
139
135
|
if accum >= interval then
|
|
140
|
-
--
|
|
141
|
-
-- stall) zero the accumulator instead of catching up.
|
|
136
|
+
-- Frame-stall guard: zero the accumulator instead of catching up.
|
|
142
137
|
accum = if accum >= 2 * interval then 0 else accum - interval
|
|
143
138
|
transport.flush()
|
|
144
139
|
end
|
|
145
|
-
|
|
140
|
+
flushState.accum = accum
|
|
146
141
|
end)
|
|
147
142
|
end
|
|
148
143
|
|
|
144
|
+
-- Validates `value` in [min, max] and applies it; errors with a configure-formatted message.
|
|
145
|
+
local function applyOption(
|
|
146
|
+
name: string,
|
|
147
|
+
value: number?,
|
|
148
|
+
min: number,
|
|
149
|
+
max: number,
|
|
150
|
+
apply: (number) -> ()
|
|
151
|
+
): ()
|
|
152
|
+
if value == nil then
|
|
153
|
+
return
|
|
154
|
+
end
|
|
155
|
+
if value < min or value > max then
|
|
156
|
+
Log.error(`{name} must be {min}-{max}`)
|
|
157
|
+
end
|
|
158
|
+
apply(value)
|
|
159
|
+
end
|
|
160
|
+
|
|
149
161
|
-- Public -----------------------------------------------------------------
|
|
150
162
|
|
|
151
163
|
local Lync = {}
|
|
152
164
|
|
|
153
165
|
function Lync.configure(options: Types.ConfigureOptions): ()
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
Channel.setMaxSize(size)
|
|
166
|
-
Base.setMaxSize(size)
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
local depth = options.validationDepth
|
|
170
|
-
if depth then
|
|
171
|
-
if depth < DEPTH_MIN or depth > DEPTH_MAX then
|
|
172
|
-
error(`[Lync] Lync.configure: validationDepth must be {DEPTH_MIN}-{DEPTH_MAX}`)
|
|
173
|
-
end
|
|
174
|
-
Gate.setDepth(depth)
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
local poolSize = options.poolSize
|
|
178
|
-
if poolSize then
|
|
179
|
-
if poolSize < POOL_MIN or poolSize > POOL_MAX then
|
|
180
|
-
error(`[Lync] Lync.configure: poolSize must be {POOL_MIN}-{POOL_MAX}`)
|
|
181
|
-
end
|
|
182
|
-
Pool.setMaxSize(poolSize)
|
|
183
|
-
end
|
|
166
|
+
Log.assert(not flushState.started, "must be called before Lync.start()")
|
|
167
|
+
|
|
168
|
+
applyOption(
|
|
169
|
+
"channelMaxSize",
|
|
170
|
+
options.channelMaxSize,
|
|
171
|
+
CHANNEL_MAX_MIN,
|
|
172
|
+
CHANNEL_MAX_MAX,
|
|
173
|
+
Channel.setMaxSize
|
|
174
|
+
)
|
|
175
|
+
applyOption("validationDepth", options.validationDepth, DEPTH_MIN, DEPTH_MAX, Gate.setDepth)
|
|
176
|
+
applyOption("poolSize", options.poolSize, POOL_MIN, POOL_MAX, Pool.setMaxSize)
|
|
184
177
|
|
|
185
178
|
if options.bandwidthLimit then
|
|
186
179
|
Gate.configureBandwidth(options.bandwidthLimit.softLimit, options.bandwidthLimit.maxStrikes)
|
|
@@ -192,49 +185,64 @@ function Lync.configure(options: Types.ConfigureOptions): ()
|
|
|
192
185
|
|
|
193
186
|
if options.stats then
|
|
194
187
|
Channel.enableStats()
|
|
195
|
-
if IS_SERVER then
|
|
196
|
-
local Server = require(script.transport.Server)
|
|
197
|
-
Server.enableStats()
|
|
198
|
-
end
|
|
199
188
|
end
|
|
200
189
|
end
|
|
201
190
|
|
|
202
191
|
function Lync.start(): ()
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
end
|
|
206
|
-
_flushState.started = true
|
|
192
|
+
Log.assert(not flushState.started, "already started")
|
|
193
|
+
flushState.started = true
|
|
207
194
|
|
|
208
195
|
if IS_SERVER then
|
|
209
196
|
local Server = require(script.transport.Server)
|
|
210
197
|
Server.start()
|
|
198
|
+
setupFlush(Server)
|
|
211
199
|
else
|
|
212
200
|
local Client = require(script.transport.Client)
|
|
213
201
|
Client.start()
|
|
202
|
+
setupFlush(Client)
|
|
214
203
|
end
|
|
215
|
-
|
|
216
|
-
setupFlush()
|
|
217
204
|
end
|
|
218
205
|
|
|
219
206
|
function Lync.flush(): ()
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
_flushState.accum = 0
|
|
207
|
+
Log.assert(flushState.started, "must call Lync.start() first")
|
|
208
|
+
-- Bind to a local: bare `(expr).flush()` after a statement parses as a continuation.
|
|
209
|
+
local transport = flushState.transport :: { flush: () -> () }
|
|
210
|
+
transport.flush()
|
|
211
|
+
flushState.accum = 0
|
|
226
212
|
end
|
|
227
213
|
|
|
228
214
|
function Lync.flushRate(hz: number): ()
|
|
229
215
|
if hz < FLUSH_HZ_MIN or hz > FLUSH_HZ_MAX then
|
|
230
|
-
error(`
|
|
216
|
+
Log.error(`hz must be {FLUSH_HZ_MIN}-{FLUSH_HZ_MAX}`)
|
|
231
217
|
end
|
|
232
|
-
|
|
233
|
-
|
|
218
|
+
flushState.rate = hz
|
|
219
|
+
flushState.accum = 0
|
|
234
220
|
end
|
|
235
221
|
|
|
236
222
|
function Lync.isStarted(): boolean
|
|
237
|
-
return
|
|
223
|
+
return flushState.started
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
-- Restore module state to post-require defaults. For tests / hot reload.
|
|
227
|
+
function Lync.reset(): ()
|
|
228
|
+
if flushState.conn then
|
|
229
|
+
flushState.conn:Disconnect()
|
|
230
|
+
flushState.conn = nil
|
|
231
|
+
end
|
|
232
|
+
flushState.started = false
|
|
233
|
+
flushState.rate = FLUSH_HZ_MAX
|
|
234
|
+
flushState.accum = 0
|
|
235
|
+
flushState.transport = nil
|
|
236
|
+
|
|
237
|
+
Baseline.reset()
|
|
238
|
+
Bridge.reset()
|
|
239
|
+
Channel.resetGlobals()
|
|
240
|
+
Gate.reset()
|
|
241
|
+
Group.reset()
|
|
242
|
+
Log.reset()
|
|
243
|
+
Middleware.reset()
|
|
244
|
+
Pool.reset()
|
|
245
|
+
Registry.reset()
|
|
238
246
|
end
|
|
239
247
|
|
|
240
248
|
Lync.packet = Packet.define
|
|
@@ -264,20 +272,21 @@ Lync.stats = table.freeze({
|
|
|
264
272
|
local Server = require(script.transport.Server)
|
|
265
273
|
Server.resetStats()
|
|
266
274
|
end
|
|
267
|
-
for
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
reg.recvFires = 0
|
|
274
|
-
reg.drops = 0
|
|
275
|
-
end
|
|
275
|
+
for _, reg in Registry.all() do
|
|
276
|
+
reg.bytesSent = 0
|
|
277
|
+
reg.bytesReceived = 0
|
|
278
|
+
reg.fires = 0
|
|
279
|
+
reg.recvFires = 0
|
|
280
|
+
reg.drops = 0
|
|
276
281
|
end
|
|
277
282
|
end,
|
|
278
283
|
})
|
|
279
284
|
|
|
280
285
|
Lync.debug = table.freeze({
|
|
286
|
+
--[[
|
|
287
|
+
Stubs reserved for capture/replay tooling. No-ops keep the API
|
|
288
|
+
surface stable across versions with or without the recorder.
|
|
289
|
+
]]
|
|
281
290
|
capture = function(_label: string?): () end,
|
|
282
291
|
stop = function(): () end,
|
|
283
292
|
dump = function(): () end,
|
|
@@ -287,20 +296,17 @@ Lync.debug = table.freeze({
|
|
|
287
296
|
end,
|
|
288
297
|
|
|
289
298
|
registrations = function(): { any }
|
|
299
|
+
-- Registry IDs are sequential 1..count and never freed, so get(i) is non-nil in range.
|
|
290
300
|
local count = Registry.count()
|
|
291
301
|
local result = table.create(count)
|
|
292
|
-
local n = 0
|
|
293
302
|
for i = 1, count do
|
|
294
|
-
local reg = Registry.get(i)
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
isUnreliable = reg.isUnreliable,
|
|
302
|
-
})
|
|
303
|
-
end
|
|
303
|
+
local reg = Registry.get(i) :: Types.Registration
|
|
304
|
+
result[i] = table.freeze({
|
|
305
|
+
name = reg.name,
|
|
306
|
+
id = reg.id,
|
|
307
|
+
kind = reg.kind,
|
|
308
|
+
isUnreliable = reg.isUnreliable,
|
|
309
|
+
})
|
|
304
310
|
end
|
|
305
311
|
return table.freeze(result)
|
|
306
312
|
end,
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
--!strict
|
|
2
2
|
--!optimize 2
|
|
3
|
-
-- Read-side delta cache
|
|
3
|
+
-- Read-side delta cache, keyed by (deltaId, peer).
|
|
4
4
|
|
|
5
5
|
local Types = require(script.Parent.Parent.Types)
|
|
6
6
|
|
|
7
7
|
-- State ------------------------------------------------------------------
|
|
8
8
|
|
|
9
|
+
-- Per-deltaId map keyed by BaselineKey: server uses Player, client uses `false`.
|
|
9
10
|
local _caches: { [number]: { [Types.BaselineKey]: any } } = {}
|
|
10
11
|
local _currentKey: Types.BaselineKey = false
|
|
11
12
|
local _hasDelta = false
|
|
@@ -38,6 +39,7 @@ function Baseline.clearPlayer(key: Types.BaselineKey): ()
|
|
|
38
39
|
end
|
|
39
40
|
end
|
|
40
41
|
|
|
42
|
+
-- Reader checks this once to skip baseline-key bookkeeping when no delta codec exists.
|
|
41
43
|
function Baseline.markHasDelta(): ()
|
|
42
44
|
_hasDelta = true
|
|
43
45
|
end
|
|
@@ -46,4 +48,10 @@ function Baseline.hasDelta(): boolean
|
|
|
46
48
|
return _hasDelta
|
|
47
49
|
end
|
|
48
50
|
|
|
51
|
+
function Baseline.reset(): ()
|
|
52
|
+
table.clear(_caches)
|
|
53
|
+
_currentKey = false
|
|
54
|
+
_hasDelta = false
|
|
55
|
+
end
|
|
56
|
+
|
|
49
57
|
return table.freeze(Baseline)
|