@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
package/src/api/Signal.luau
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
--!strict
|
|
2
2
|
--!optimize 2
|
|
3
|
-
-- O(1) disconnect signal system with thread reuse.
|
|
3
|
+
-- O(1) disconnect signal system with thread reuse and reentrancy-safe fire.
|
|
4
4
|
|
|
5
5
|
local Types = require(script.Parent.Parent.Types)
|
|
6
6
|
|
|
7
|
-
-- Constants
|
|
7
|
+
-- Constants --------------------------------------------------------------
|
|
8
8
|
|
|
9
9
|
local STATE_DISCONNECTED = 0
|
|
10
10
|
local STATE_CONNECTED = 1
|
|
11
11
|
local STATE_ONCE = 2
|
|
12
12
|
|
|
13
|
-
-- State
|
|
13
|
+
-- State ------------------------------------------------------------------
|
|
14
14
|
|
|
15
15
|
local _freeThread: thread? = nil
|
|
16
16
|
|
|
17
|
-
-- Private
|
|
17
|
+
-- Private ----------------------------------------------------------------
|
|
18
18
|
|
|
19
19
|
local function passer(fn: (...any) -> (), ...): ()
|
|
20
20
|
local thread = _freeThread
|
|
@@ -37,7 +37,6 @@ local function spawnCallback(fn: (...any) -> (), ...: any): ()
|
|
|
37
37
|
task.spawn(_freeThread :: thread, fn, ...)
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
-- Entry type
|
|
41
40
|
type Entry = {
|
|
42
41
|
fn: (...any) -> (),
|
|
43
42
|
_state: number,
|
|
@@ -47,57 +46,49 @@ type Entry = {
|
|
|
47
46
|
_index: number,
|
|
48
47
|
}
|
|
49
48
|
|
|
50
|
-
-- EntryImpl metatable
|
|
51
49
|
local EntryImpl = {}
|
|
52
50
|
EntryImpl.__index = EntryImpl
|
|
53
51
|
|
|
54
|
-
function
|
|
55
|
-
if
|
|
52
|
+
local function disconnectEntry(entry: Entry): ()
|
|
53
|
+
if entry._state == STATE_DISCONNECTED then
|
|
56
54
|
return
|
|
57
55
|
end
|
|
58
56
|
|
|
59
|
-
|
|
60
|
-
|
|
57
|
+
entry._state = STATE_DISCONNECTED
|
|
58
|
+
entry.connected = false
|
|
61
59
|
|
|
62
|
-
local entries =
|
|
63
|
-
local countRef =
|
|
64
|
-
local idx =
|
|
65
|
-
local last = countRef[1]
|
|
60
|
+
local entries = entry._signalEntries
|
|
61
|
+
local countRef = entry._signalCount
|
|
62
|
+
local idx, last = entry._index, countRef[1]
|
|
66
63
|
|
|
67
64
|
if idx < last then
|
|
68
|
-
-- Swap-remove: move last entry to this slot
|
|
69
65
|
local moved = entries[last]
|
|
70
66
|
entries[idx] = moved
|
|
71
67
|
moved._index = idx
|
|
72
68
|
end
|
|
73
|
-
|
|
74
|
-
entries[last] = nil :: any
|
|
69
|
+
entries[last] = nil
|
|
75
70
|
countRef[1] = last - 1
|
|
76
71
|
end
|
|
77
72
|
|
|
73
|
+
EntryImpl.disconnect = disconnectEntry
|
|
78
74
|
table.freeze(EntryImpl)
|
|
79
75
|
|
|
80
|
-
-- Public --------------------------------------------------------------
|
|
81
|
-
|
|
82
|
-
local SignalModule = {}
|
|
83
|
-
|
|
84
76
|
type SignalFields = {
|
|
85
77
|
_entries: { Entry },
|
|
86
78
|
_count: { number },
|
|
87
|
-
_snapshot: { Entry },
|
|
88
79
|
}
|
|
89
80
|
|
|
90
81
|
local SignalImpl = {}
|
|
91
82
|
SignalImpl.__index = SignalImpl
|
|
92
83
|
|
|
93
|
-
function
|
|
84
|
+
local function newEntry(self: SignalFields, callback: (...any) -> (), state: number): Entry
|
|
94
85
|
local countRef = self._count
|
|
95
86
|
local idx = countRef[1] + 1
|
|
96
87
|
countRef[1] = idx
|
|
97
88
|
|
|
98
89
|
local entry: Entry = setmetatable({
|
|
99
90
|
fn = callback,
|
|
100
|
-
_state =
|
|
91
|
+
_state = state,
|
|
101
92
|
connected = true,
|
|
102
93
|
_signalEntries = self._entries,
|
|
103
94
|
_signalCount = countRef,
|
|
@@ -105,40 +96,28 @@ function SignalImpl.connect(self: SignalFields, callback: (...any) -> ()): Types
|
|
|
105
96
|
}, EntryImpl) :: any
|
|
106
97
|
|
|
107
98
|
self._entries[idx] = entry
|
|
108
|
-
return entry
|
|
99
|
+
return entry
|
|
109
100
|
end
|
|
110
101
|
|
|
111
|
-
function SignalImpl.
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
countRef[1] = idx
|
|
115
|
-
|
|
116
|
-
local entry: Entry = setmetatable({
|
|
117
|
-
fn = callback,
|
|
118
|
-
_state = STATE_ONCE,
|
|
119
|
-
connected = true,
|
|
120
|
-
_signalEntries = self._entries,
|
|
121
|
-
_signalCount = countRef,
|
|
122
|
-
_index = idx,
|
|
123
|
-
}, EntryImpl) :: any
|
|
102
|
+
function SignalImpl.connect(self: SignalFields, callback: (...any) -> ()): Types.Connection
|
|
103
|
+
return newEntry(self, callback, STATE_CONNECTED) :: any
|
|
104
|
+
end
|
|
124
105
|
|
|
125
|
-
|
|
126
|
-
return
|
|
106
|
+
function SignalImpl.once(self: SignalFields, callback: (...any) -> ()): Types.Connection
|
|
107
|
+
return newEntry(self, callback, STATE_ONCE) :: any
|
|
127
108
|
end
|
|
128
109
|
|
|
129
110
|
function SignalImpl.wait(self: SignalFields): ...any
|
|
130
111
|
local running = coroutine.running()
|
|
131
112
|
local conn: Types.Connection
|
|
132
|
-
|
|
133
113
|
conn = SignalImpl.connect(self, function(...: any)
|
|
134
114
|
conn:disconnect()
|
|
135
115
|
task.spawn(running, ...)
|
|
136
116
|
end)
|
|
137
|
-
|
|
138
117
|
return coroutine.yield()
|
|
139
118
|
end
|
|
140
119
|
|
|
141
|
-
function
|
|
120
|
+
local function dispatch(self: SignalFields, sync: boolean, ...: any): ()
|
|
142
121
|
local count = self._count[1]
|
|
143
122
|
if count == 0 then
|
|
144
123
|
return
|
|
@@ -146,78 +125,73 @@ function SignalImpl.fire(self: SignalFields, ...: any): ()
|
|
|
146
125
|
|
|
147
126
|
local entries = self._entries
|
|
148
127
|
|
|
149
|
-
-- Single-listener fast path
|
|
150
128
|
if count == 1 then
|
|
151
129
|
local entry = entries[1]
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
130
|
+
local state = entry._state
|
|
131
|
+
if state == STATE_ONCE then
|
|
132
|
+
disconnectEntry(entry)
|
|
133
|
+
if sync then
|
|
134
|
+
entry.fn(...)
|
|
135
|
+
else
|
|
136
|
+
spawnCallback(entry.fn, ...)
|
|
137
|
+
end
|
|
138
|
+
elseif state == STATE_CONNECTED then
|
|
139
|
+
if sync then
|
|
140
|
+
entry.fn(...)
|
|
141
|
+
else
|
|
142
|
+
spawnCallback(entry.fn, ...)
|
|
143
|
+
end
|
|
157
144
|
end
|
|
158
145
|
return
|
|
159
146
|
end
|
|
160
147
|
|
|
161
|
-
|
|
162
|
-
local snapshot = self._snapshot
|
|
148
|
+
local snapshot = table.create(count)
|
|
163
149
|
table.move(entries, 1, count, 1, snapshot)
|
|
164
150
|
|
|
165
151
|
for i = 1, count do
|
|
166
152
|
local entry = snapshot[i]
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
153
|
+
local state = entry._state
|
|
154
|
+
if state == STATE_ONCE then
|
|
155
|
+
disconnectEntry(entry)
|
|
156
|
+
if sync then
|
|
157
|
+
entry.fn(...)
|
|
158
|
+
else
|
|
159
|
+
spawnCallback(entry.fn, ...)
|
|
160
|
+
end
|
|
161
|
+
elseif state == STATE_CONNECTED then
|
|
162
|
+
if sync then
|
|
163
|
+
entry.fn(...)
|
|
164
|
+
else
|
|
165
|
+
spawnCallback(entry.fn, ...)
|
|
166
|
+
end
|
|
174
167
|
end
|
|
175
168
|
end
|
|
176
169
|
end
|
|
177
170
|
|
|
178
|
-
function SignalImpl.
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
return
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
local entries = self._entries
|
|
185
|
-
|
|
186
|
-
if count == 1 then
|
|
187
|
-
local entry = entries[1]
|
|
188
|
-
if entry._state == STATE_ONCE then
|
|
189
|
-
EntryImpl.disconnect(entry)
|
|
190
|
-
entry.fn(...)
|
|
191
|
-
elseif entry._state == STATE_CONNECTED then
|
|
192
|
-
entry.fn(...)
|
|
193
|
-
end
|
|
194
|
-
return
|
|
195
|
-
end
|
|
171
|
+
function SignalImpl.fire(self: SignalFields, ...: any): ()
|
|
172
|
+
dispatch(self, false, ...)
|
|
173
|
+
end
|
|
196
174
|
|
|
197
|
-
|
|
198
|
-
|
|
175
|
+
function SignalImpl.fireSync(self: SignalFields, ...: any): ()
|
|
176
|
+
dispatch(self, true, ...)
|
|
177
|
+
end
|
|
199
178
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
179
|
+
function SignalImpl.count(self: SignalFields): number
|
|
180
|
+
return self._count[1]
|
|
181
|
+
end
|
|
203
182
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
entry.fn(...)
|
|
207
|
-
elseif entry._state == STATE_CONNECTED then
|
|
208
|
-
entry.fn(...)
|
|
209
|
-
end
|
|
210
|
-
end
|
|
183
|
+
function SignalImpl.hasListeners(self: SignalFields): boolean
|
|
184
|
+
return self._count[1] > 0
|
|
211
185
|
end
|
|
212
186
|
|
|
213
187
|
table.freeze(SignalImpl)
|
|
214
188
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
}, SignalImpl)
|
|
189
|
+
-- Public -----------------------------------------------------------------
|
|
190
|
+
|
|
191
|
+
local SignalModule = {}
|
|
192
|
+
|
|
193
|
+
function SignalModule.create(): Types.SignalLike
|
|
194
|
+
return (setmetatable({ _entries = {}, _count = { 0 } }, SignalImpl) :: any) :: Types.SignalLike
|
|
221
195
|
end
|
|
222
196
|
|
|
223
197
|
return table.freeze(SignalModule)
|
package/src/codec/Base.luau
CHANGED
|
@@ -1,23 +1,33 @@
|
|
|
1
1
|
--!strict
|
|
2
2
|
--!optimize 2
|
|
3
|
-
-- Fixed-size codec factory
|
|
3
|
+
-- Fixed-size codec factory and channel buffer-grow primitive.
|
|
4
4
|
|
|
5
5
|
local Types = require(script.Parent.Parent.Types)
|
|
6
6
|
|
|
7
|
-
-- Constants
|
|
7
|
+
-- Constants --------------------------------------------------------------
|
|
8
8
|
|
|
9
9
|
local DEFAULT_MAX = 262144
|
|
10
|
+
local INITIAL_BUF = 1024
|
|
10
11
|
|
|
11
|
-
-- State
|
|
12
|
+
-- State ------------------------------------------------------------------
|
|
12
13
|
|
|
13
14
|
local _maxSize = DEFAULT_MAX
|
|
14
15
|
|
|
15
|
-
-- Private
|
|
16
|
+
-- Private ----------------------------------------------------------------
|
|
16
17
|
|
|
17
18
|
local countlz = bit32.countlz
|
|
18
19
|
local lshift = bit32.lshift
|
|
19
20
|
|
|
21
|
+
--[[
|
|
22
|
+
Grow `ch.buff` so `ch.cursor + bytes` fits. New size is the next
|
|
23
|
+
power of two >= needed, capped by _maxSize. Caller MUST re-read
|
|
24
|
+
`ch.buff` after this returns: the buffer reference is replaced.
|
|
25
|
+
]]
|
|
20
26
|
local function alloc(ch: Types.ChannelState, bytes: number): ()
|
|
27
|
+
if bytes == 0 then
|
|
28
|
+
return
|
|
29
|
+
end
|
|
30
|
+
|
|
21
31
|
local needed = ch.cursor + bytes
|
|
22
32
|
if needed <= ch.size then
|
|
23
33
|
return
|
|
@@ -25,11 +35,10 @@ local function alloc(ch: Types.ChannelState, bytes: number): ()
|
|
|
25
35
|
|
|
26
36
|
if needed > _maxSize then
|
|
27
37
|
local name = ch.currentPacket
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
)
|
|
38
|
+
if name then
|
|
39
|
+
error(`[Lync] Channel.alloc("{name}"): buffer overflow ({needed}B, max {_maxSize}B)`)
|
|
40
|
+
end
|
|
41
|
+
error(`[Lync] Channel.alloc: buffer overflow ({needed}B, max {_maxSize}B)`)
|
|
33
42
|
end
|
|
34
43
|
|
|
35
44
|
local newSize = lshift(1, 32 - countlz(needed - 1))
|
|
@@ -39,25 +48,31 @@ local function alloc(ch: Types.ChannelState, bytes: number): ()
|
|
|
39
48
|
ch.size = newSize
|
|
40
49
|
end
|
|
41
50
|
|
|
42
|
-
-- Public
|
|
51
|
+
-- Public -----------------------------------------------------------------
|
|
43
52
|
|
|
44
53
|
local Base = {}
|
|
45
54
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
55
|
+
Base.INITIAL_BUF = INITIAL_BUF
|
|
56
|
+
|
|
57
|
+
Base.alloc = alloc
|
|
49
58
|
|
|
50
59
|
function Base.setMaxSize(bytes: number): ()
|
|
51
60
|
_maxSize = bytes
|
|
52
61
|
end
|
|
53
62
|
|
|
63
|
+
--[[
|
|
64
|
+
Build a fixed-size codec. The codec exposes channel-level write/read
|
|
65
|
+
(which handles buffer growth and cursor bookkeeping) plus the raw
|
|
66
|
+
_directWrite / _directRead primitives composite codecs use in their
|
|
67
|
+
pre-sized fast paths.
|
|
68
|
+
]]
|
|
54
69
|
function Base.define<T>(
|
|
55
70
|
size: number,
|
|
56
71
|
directWrite: (b: buffer, offset: number, value: T) -> (),
|
|
57
72
|
directRead: (b: buffer, offset: number) -> T,
|
|
58
73
|
meta: { [string]: any }?
|
|
59
74
|
): Types.InternalCodec<T>
|
|
60
|
-
local codec: any = {
|
|
75
|
+
local codec: { [string]: any } = {
|
|
61
76
|
_size = size,
|
|
62
77
|
_directWrite = directWrite,
|
|
63
78
|
_directRead = directRead,
|
|
@@ -82,21 +97,26 @@ function Base.define<T>(
|
|
|
82
97
|
end
|
|
83
98
|
end
|
|
84
99
|
|
|
85
|
-
return table.freeze(codec)
|
|
100
|
+
return table.freeze(codec) :: Types.InternalCodec<T>
|
|
86
101
|
end
|
|
87
102
|
|
|
88
|
-
|
|
103
|
+
--[[
|
|
104
|
+
Constant codec: zero bytes wire, decode always returns `value`. Used
|
|
105
|
+
by quantizers when min == max so the wire emits nothing yet decode
|
|
106
|
+
still produces a type-faithful value.
|
|
107
|
+
]]
|
|
108
|
+
function Base.constant<T>(value: T): Types.InternalCodec<T>
|
|
89
109
|
return table.freeze({
|
|
90
110
|
_size = 0,
|
|
91
|
-
_directWrite = function(_b: buffer, _off: number, _v:
|
|
92
|
-
_directRead = function(_b: buffer, _off: number):
|
|
93
|
-
return
|
|
111
|
+
_directWrite = function(_b: buffer, _off: number, _v: T): () end,
|
|
112
|
+
_directRead = function(_b: buffer, _off: number): T
|
|
113
|
+
return value
|
|
94
114
|
end,
|
|
95
|
-
write = function(_ch: Types.ChannelState, _v:
|
|
96
|
-
read = function(_src: buffer, _pos: number, _refs: { Instance }?): (
|
|
97
|
-
return
|
|
115
|
+
write = function(_ch: Types.ChannelState, _v: T): () end,
|
|
116
|
+
read = function(_src: buffer, _pos: number, _refs: { Instance }?): (T, number)
|
|
117
|
+
return value, 0
|
|
98
118
|
end,
|
|
99
|
-
} ::
|
|
119
|
+
}) :: Types.InternalCodec<T>
|
|
100
120
|
end
|
|
101
121
|
|
|
102
122
|
return table.freeze(Base)
|