@axpecter/lync 2.1.1 → 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 +9 -3
- 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
|
@@ -5,28 +5,29 @@
|
|
|
5
5
|
local Signal = require(script.Parent.Parent.api.Signal)
|
|
6
6
|
local Types = require(script.Parent.Parent.Types)
|
|
7
7
|
|
|
8
|
-
-- Constants
|
|
8
|
+
-- Constants --------------------------------------------------------------
|
|
9
9
|
|
|
10
10
|
local KIND_PACKET = 0
|
|
11
11
|
local KIND_REQUEST = 1
|
|
12
12
|
local KIND_RESPONSE = 2
|
|
13
13
|
local MAX_ID = 127
|
|
14
14
|
|
|
15
|
-
-- State
|
|
15
|
+
-- State ------------------------------------------------------------------
|
|
16
16
|
|
|
17
17
|
local _nextId = 0
|
|
18
18
|
local _byId: { [number]: Types.Registration } = {}
|
|
19
19
|
local _byName: { [string]: Types.Registration } = {}
|
|
20
20
|
|
|
21
|
-
-- Public
|
|
21
|
+
-- Public -----------------------------------------------------------------
|
|
22
22
|
|
|
23
23
|
local Registry = {}
|
|
24
24
|
|
|
25
25
|
Registry.KIND_PACKET = KIND_PACKET
|
|
26
26
|
Registry.KIND_REQUEST = KIND_REQUEST
|
|
27
27
|
Registry.KIND_RESPONSE = KIND_RESPONSE
|
|
28
|
+
Registry.MAX_ID = MAX_ID
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
export type RegisterOptions = {
|
|
30
31
|
name: string,
|
|
31
32
|
kind: number,
|
|
32
33
|
codec: Types.InternalCodec<any>,
|
|
@@ -37,35 +38,36 @@ function Registry.register(
|
|
|
37
38
|
timestampMode: number,
|
|
38
39
|
openFn: (ch: Types.ChannelState, id: number) -> (),
|
|
39
40
|
rateLimit: Types.RateLimitConfig?,
|
|
40
|
-
validate:
|
|
41
|
-
maxPayloadBytes: number
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if
|
|
47
|
-
error(`[Lync] Registry: ID limit exceeded ({MAX_ID})
|
|
41
|
+
validate: Types.ValidateFn?,
|
|
42
|
+
maxPayloadBytes: number?,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function Registry.register(opts: RegisterOptions): Types.Registration
|
|
46
|
+
local nextCandidate = _nextId + 1
|
|
47
|
+
if nextCandidate > MAX_ID then
|
|
48
|
+
error(`[Lync] Registry.register: ID limit exceeded ({MAX_ID})`)
|
|
48
49
|
end
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
error(`[Lync] Registry: duplicate name "{name}"`)
|
|
50
|
+
if opts.kind ~= KIND_RESPONSE and _byName[opts.name] then
|
|
51
|
+
error(`[Lync] Registry.register: duplicate name "{opts.name}"`)
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
+
_nextId = nextCandidate
|
|
55
|
+
|
|
54
56
|
local reg: Types.Registration = {
|
|
55
|
-
id =
|
|
56
|
-
name = name,
|
|
57
|
-
kind = kind,
|
|
58
|
-
codec = codec,
|
|
59
|
-
isUnreliable = isUnreliable,
|
|
60
|
-
partner = partner,
|
|
57
|
+
id = nextCandidate,
|
|
58
|
+
name = opts.name,
|
|
59
|
+
kind = opts.kind,
|
|
60
|
+
codec = opts.codec,
|
|
61
|
+
isUnreliable = opts.isUnreliable,
|
|
62
|
+
partner = opts.partner,
|
|
61
63
|
signal = Signal.create(),
|
|
62
|
-
rateLimit = rateLimit,
|
|
63
|
-
validate = validate,
|
|
64
|
-
needsGate = needsGate,
|
|
65
|
-
maxPayloadBytes = maxPayloadBytes,
|
|
66
|
-
timestampMode = timestampMode,
|
|
67
|
-
headerSize = headerSize,
|
|
68
|
-
_openFn = openFn,
|
|
64
|
+
rateLimit = opts.rateLimit,
|
|
65
|
+
validate = opts.validate,
|
|
66
|
+
needsGate = opts.needsGate,
|
|
67
|
+
maxPayloadBytes = opts.maxPayloadBytes,
|
|
68
|
+
timestampMode = opts.timestampMode,
|
|
69
|
+
headerSize = opts.headerSize,
|
|
70
|
+
_openFn = opts.openFn,
|
|
69
71
|
bytesSent = 0,
|
|
70
72
|
bytesReceived = 0,
|
|
71
73
|
fires = 0,
|
|
@@ -73,11 +75,10 @@ function Registry.register(
|
|
|
73
75
|
drops = 0,
|
|
74
76
|
}
|
|
75
77
|
|
|
76
|
-
_byId[
|
|
77
|
-
if kind ~= KIND_RESPONSE then
|
|
78
|
-
_byName[name] = reg
|
|
78
|
+
_byId[nextCandidate] = reg
|
|
79
|
+
if opts.kind ~= KIND_RESPONSE then
|
|
80
|
+
_byName[opts.name] = reg
|
|
79
81
|
end
|
|
80
|
-
|
|
81
82
|
return reg
|
|
82
83
|
end
|
|
83
84
|
|
|
@@ -95,9 +96,12 @@ end
|
|
|
95
96
|
|
|
96
97
|
function Registry.all(): { Types.Registration }
|
|
97
98
|
local result = table.create(_nextId)
|
|
99
|
+
local n = 0
|
|
98
100
|
for i = 1, _nextId do
|
|
99
|
-
|
|
100
|
-
|
|
101
|
+
local reg = _byId[i]
|
|
102
|
+
if reg then
|
|
103
|
+
n += 1
|
|
104
|
+
result[n] = reg
|
|
101
105
|
end
|
|
102
106
|
end
|
|
103
107
|
return result
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!optimize 2
|
|
3
|
+
-- Lazy-require Server/Client to break the api -> transport import cycle.
|
|
4
|
+
|
|
5
|
+
-- State ------------------------------------------------------------------
|
|
6
|
+
|
|
7
|
+
local _server: any = nil
|
|
8
|
+
local _client: any = nil
|
|
9
|
+
|
|
10
|
+
-- Public -----------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
local Transport = {}
|
|
13
|
+
|
|
14
|
+
function Transport.server(): any
|
|
15
|
+
if not _server then
|
|
16
|
+
_server = require(script.Parent.Parent.transport.Server)
|
|
17
|
+
end
|
|
18
|
+
return _server
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
function Transport.client(): any
|
|
22
|
+
if not _client then
|
|
23
|
+
_client = require(script.Parent.Parent.transport.Client)
|
|
24
|
+
end
|
|
25
|
+
return _client
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
return table.freeze(Transport)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!optimize 2
|
|
3
|
+
-- Cross-module utility helpers.
|
|
4
|
+
|
|
5
|
+
-- Public -----------------------------------------------------------------
|
|
6
|
+
|
|
7
|
+
local Util = {}
|
|
8
|
+
|
|
9
|
+
function Util.isPlayer(value: any): boolean
|
|
10
|
+
return typeof(value) == "Instance" and (value :: Instance):IsA("Player")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
--[[
|
|
14
|
+
O(1) array remove via swap with last element. The slot becomes nil
|
|
15
|
+
after this returns; callers that track an external count must
|
|
16
|
+
decrement it themselves.
|
|
17
|
+
]]
|
|
18
|
+
function Util.swapRemove<T>(arr: { T }, idx: number): ()
|
|
19
|
+
local last = #arr
|
|
20
|
+
if idx ~= last then
|
|
21
|
+
arr[idx] = arr[last]
|
|
22
|
+
end
|
|
23
|
+
arr[last] = nil
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
return table.freeze(Util)
|
|
@@ -1,21 +1,39 @@
|
|
|
1
1
|
--!strict
|
|
2
2
|
--!optimize 2
|
|
3
|
-
--
|
|
3
|
+
-- RemoteEvent / UnreliableRemoteEvent thin wrapper.
|
|
4
4
|
|
|
5
5
|
local RunService = game:GetService("RunService")
|
|
6
6
|
|
|
7
|
-
-- Constants
|
|
7
|
+
-- Constants --------------------------------------------------------------
|
|
8
8
|
|
|
9
9
|
local RELIABLE_NAME = "LyncReliable"
|
|
10
10
|
local UNRELIABLE_NAME = "LyncUnreliable"
|
|
11
11
|
|
|
12
|
-
-- State
|
|
12
|
+
-- State ------------------------------------------------------------------
|
|
13
13
|
|
|
14
14
|
local _reliable: RemoteEvent? = nil
|
|
15
15
|
local _unreliable: UnreliableRemoteEvent? = nil
|
|
16
16
|
local _isServer = RunService:IsServer()
|
|
17
17
|
|
|
18
|
-
--
|
|
18
|
+
-- Private ----------------------------------------------------------------
|
|
19
|
+
|
|
20
|
+
local function ensureReliable(): RemoteEvent
|
|
21
|
+
local re = _reliable
|
|
22
|
+
if not re then
|
|
23
|
+
error("[Lync] Bridge.reliable: not set up; call Lync.start() first")
|
|
24
|
+
end
|
|
25
|
+
return re
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
local function ensureUnreliable(): UnreliableRemoteEvent
|
|
29
|
+
local ure = _unreliable
|
|
30
|
+
if not ure then
|
|
31
|
+
error("[Lync] Bridge.unreliable: not set up; call Lync.start() first")
|
|
32
|
+
end
|
|
33
|
+
return ure
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
-- Public -----------------------------------------------------------------
|
|
19
37
|
|
|
20
38
|
local Bridge = {}
|
|
21
39
|
|
|
@@ -29,28 +47,18 @@ function Bridge.setup(container: Instance): ()
|
|
|
29
47
|
ure.Name = UNRELIABLE_NAME
|
|
30
48
|
ure.Parent = container
|
|
31
49
|
|
|
32
|
-
_reliable = re
|
|
33
|
-
|
|
34
|
-
else
|
|
35
|
-
_reliable = container:WaitForChild(RELIABLE_NAME) :: RemoteEvent
|
|
36
|
-
_unreliable = container:WaitForChild(UNRELIABLE_NAME) :: UnreliableRemoteEvent
|
|
50
|
+
_reliable, _unreliable = re, ure
|
|
51
|
+
return
|
|
37
52
|
end
|
|
53
|
+
_reliable = container:WaitForChild(RELIABLE_NAME) :: RemoteEvent
|
|
54
|
+
_unreliable = container:WaitForChild(UNRELIABLE_NAME) :: UnreliableRemoteEvent
|
|
38
55
|
end
|
|
39
56
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
function Bridge.unreliable(): UnreliableRemoteEvent
|
|
45
|
-
return _unreliable :: UnreliableRemoteEvent
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
function Bridge.isServer(): boolean
|
|
49
|
-
return _isServer
|
|
50
|
-
end
|
|
57
|
+
Bridge.reliable = ensureReliable
|
|
58
|
+
Bridge.unreliable = ensureUnreliable
|
|
51
59
|
|
|
52
60
|
function Bridge.fireServer(data: buffer, refs: { Instance }): ()
|
|
53
|
-
local re =
|
|
61
|
+
local re = ensureReliable()
|
|
54
62
|
if #refs > 0 then
|
|
55
63
|
re:FireServer(data, refs)
|
|
56
64
|
else
|
|
@@ -59,7 +67,7 @@ function Bridge.fireServer(data: buffer, refs: { Instance }): ()
|
|
|
59
67
|
end
|
|
60
68
|
|
|
61
69
|
function Bridge.fireClient(player: Player, data: buffer, refs: { Instance }): ()
|
|
62
|
-
local re =
|
|
70
|
+
local re = ensureReliable()
|
|
63
71
|
if #refs > 0 then
|
|
64
72
|
re:FireClient(player, data, refs)
|
|
65
73
|
else
|
|
@@ -68,7 +76,7 @@ function Bridge.fireClient(player: Player, data: buffer, refs: { Instance }): ()
|
|
|
68
76
|
end
|
|
69
77
|
|
|
70
78
|
function Bridge.fireServerUnreliable(data: buffer, refs: { Instance }): ()
|
|
71
|
-
local ure =
|
|
79
|
+
local ure = ensureUnreliable()
|
|
72
80
|
if #refs > 0 then
|
|
73
81
|
ure:FireServer(data, refs)
|
|
74
82
|
else
|
|
@@ -77,7 +85,7 @@ function Bridge.fireServerUnreliable(data: buffer, refs: { Instance }): ()
|
|
|
77
85
|
end
|
|
78
86
|
|
|
79
87
|
function Bridge.fireClientUnreliable(player: Player, data: buffer, refs: { Instance }): ()
|
|
80
|
-
local ure =
|
|
88
|
+
local ure = ensureUnreliable()
|
|
81
89
|
if #refs > 0 then
|
|
82
90
|
ure:FireClient(player, data, refs)
|
|
83
91
|
else
|
|
@@ -1,20 +1,59 @@
|
|
|
1
1
|
--!strict
|
|
2
2
|
--!optimize 2
|
|
3
|
-
-- Client-side transport: single channel pair, flush.
|
|
3
|
+
-- Client-side transport: single channel pair, flush, with outbound XOR delta.
|
|
4
|
+
|
|
5
|
+
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|
4
6
|
|
|
5
7
|
local Bridge = require(script.Parent.Parent.transport.Bridge)
|
|
6
8
|
local Channel = require(script.Parent.Parent.internal.Channel)
|
|
7
9
|
local Reader = require(script.Parent.Parent.transport.Reader)
|
|
8
10
|
local Types = require(script.Parent.Parent.Types)
|
|
9
11
|
|
|
10
|
-
--
|
|
12
|
+
-- Constants --------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
local CONTAINER_NAME = "LyncRemotes"
|
|
15
|
+
|
|
16
|
+
-- State ------------------------------------------------------------------
|
|
11
17
|
|
|
12
18
|
local _reliable: Types.ChannelState = Channel.create()
|
|
13
19
|
local _unreliable: Types.ChannelState = Channel.create()
|
|
14
20
|
local _prevIncoming: buffer? = nil
|
|
15
21
|
local _prevIncomingLen = 0
|
|
16
22
|
|
|
17
|
-
--
|
|
23
|
+
-- Private ----------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
local function handleIncomingReliable(data: any, refs: any?): ()
|
|
26
|
+
local incoming = Reader.decodeIncoming(data)
|
|
27
|
+
if not incoming then
|
|
28
|
+
return
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
local incomingLen = buffer.len(incoming)
|
|
32
|
+
local decoded = if _prevIncoming
|
|
33
|
+
then Channel.xorApply(incoming, incomingLen, _prevIncoming, _prevIncomingLen)
|
|
34
|
+
else incoming
|
|
35
|
+
_prevIncoming = decoded
|
|
36
|
+
_prevIncomingLen = incomingLen
|
|
37
|
+
|
|
38
|
+
local ok, err = pcall(Reader.process, decoded, incomingLen, refs, nil, false)
|
|
39
|
+
if not ok then
|
|
40
|
+
warn(`[Lync] Client.receive: {err}`)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
local function handleIncomingUnreliable(data: any, refs: any?): ()
|
|
45
|
+
local incoming = Reader.decodeIncoming(data)
|
|
46
|
+
if not incoming then
|
|
47
|
+
return
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
local ok, err = pcall(Reader.process, incoming, buffer.len(incoming), refs, nil, false)
|
|
51
|
+
if not ok then
|
|
52
|
+
warn(`[Lync] Client.receiveUnreliable: {err}`)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
-- Public -----------------------------------------------------------------
|
|
18
57
|
|
|
19
58
|
local Client = {}
|
|
20
59
|
|
|
@@ -27,69 +66,28 @@ function Client.flush(): ()
|
|
|
27
66
|
Channel.updateTimestamp()
|
|
28
67
|
end
|
|
29
68
|
|
|
30
|
-
-- Reliable
|
|
31
69
|
if _reliable.cursor > 0 then
|
|
32
|
-
local snapshot, refs, byteLen
|
|
33
|
-
|
|
70
|
+
local snapshot, refs, byteLen = Channel.sealAndDump(_reliable)
|
|
71
|
+
local xored = Channel.xorApply(snapshot, byteLen, _reliable.prevDump, _reliable.prevDumpLen)
|
|
72
|
+
_reliable.prevDump = snapshot
|
|
73
|
+
_reliable.prevDumpLen = byteLen
|
|
74
|
+
Bridge.fireServer(xored, refs)
|
|
34
75
|
Channel.reset(_reliable)
|
|
35
76
|
end
|
|
36
77
|
|
|
37
|
-
-- Unreliable
|
|
38
78
|
if _unreliable.cursor > 0 then
|
|
39
|
-
local snapshot, refs
|
|
79
|
+
local snapshot, refs = Channel.sealAndDump(_unreliable)
|
|
40
80
|
Bridge.fireServerUnreliable(snapshot, refs)
|
|
41
81
|
Channel.reset(_unreliable)
|
|
42
82
|
end
|
|
43
83
|
end
|
|
44
84
|
|
|
45
85
|
function Client.start(): ()
|
|
46
|
-
local container =
|
|
86
|
+
local container = ReplicatedStorage:WaitForChild(CONTAINER_NAME)
|
|
47
87
|
Bridge.setup(container)
|
|
48
88
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
-- Incoming reliable (XOR encoded)
|
|
53
|
-
reliable.OnClientEvent:Connect(function(data: any, refs: any?)
|
|
54
|
-
local incoming = Reader.decodeIncoming(data)
|
|
55
|
-
if not incoming then
|
|
56
|
-
return
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
local incomingLen = buffer.len(incoming)
|
|
60
|
-
|
|
61
|
-
-- XOR decode against previous frame
|
|
62
|
-
local decoded: buffer
|
|
63
|
-
if _prevIncoming then
|
|
64
|
-
decoded = Channel.xorApply(incoming, incomingLen, _prevIncoming, _prevIncomingLen)
|
|
65
|
-
else
|
|
66
|
-
decoded = incoming
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
-- Store decoded (not XOR'd) for next frame's XOR base
|
|
70
|
-
_prevIncoming = decoded
|
|
71
|
-
_prevIncomingLen = incomingLen
|
|
72
|
-
|
|
73
|
-
local ok, err = pcall(Reader.process, decoded, incomingLen, refs :: any, nil, false)
|
|
74
|
-
if not ok then
|
|
75
|
-
warn(`[Lync] Client: read error: {err}`)
|
|
76
|
-
end
|
|
77
|
-
end)
|
|
78
|
-
|
|
79
|
-
-- Incoming unreliable (no XOR)
|
|
80
|
-
unreliable.OnClientEvent:Connect(function(data: any, refs: any?)
|
|
81
|
-
local incoming = Reader.decodeIncoming(data)
|
|
82
|
-
if not incoming then
|
|
83
|
-
return
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
local incomingLen = buffer.len(incoming)
|
|
87
|
-
|
|
88
|
-
local ok, err = pcall(Reader.process, incoming, incomingLen, refs :: any, nil, false)
|
|
89
|
-
if not ok then
|
|
90
|
-
warn(`[Lync] Client: read error (unreliable): {err}`)
|
|
91
|
-
end
|
|
92
|
-
end)
|
|
89
|
+
Bridge.reliable().OnClientEvent:Connect(handleIncomingReliable)
|
|
90
|
+
Bridge.unreliable().OnClientEvent:Connect(handleIncomingUnreliable)
|
|
93
91
|
end
|
|
94
92
|
|
|
95
93
|
return table.freeze(Client)
|