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