@axpecter/lync 2.1.2 → 2.2.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 +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 +163 -152
- package/src/api/Query.luau +224 -211
- 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 +133 -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/Query.luau
CHANGED
|
@@ -5,275 +5,285 @@
|
|
|
5
5
|
local Players = game:GetService("Players")
|
|
6
6
|
local RunService = game:GetService("RunService")
|
|
7
7
|
|
|
8
|
-
local Baseline = require(script.Parent.Parent.internal.Baseline)
|
|
9
8
|
local Channel = require(script.Parent.Parent.internal.Channel)
|
|
10
9
|
local Registry = require(script.Parent.Parent.internal.Registry)
|
|
10
|
+
local Transport = require(script.Parent.Parent.internal.Transport)
|
|
11
11
|
local Types = require(script.Parent.Parent.Types)
|
|
12
|
+
local Util = require(script.Parent.Parent.internal.Util)
|
|
12
13
|
|
|
13
|
-
-- Constants
|
|
14
|
+
-- Constants --------------------------------------------------------------
|
|
14
15
|
|
|
15
16
|
local IS_SERVER = RunService:IsServer()
|
|
16
17
|
local DEFAULT_TIMEOUT = 5
|
|
17
18
|
|
|
18
|
-
-- State
|
|
19
|
+
-- State ------------------------------------------------------------------
|
|
19
20
|
|
|
20
21
|
local _nextCorrId = 0
|
|
21
22
|
local _pending: { [number]: thread } = {}
|
|
23
|
+
local _pendingGather: { [number]: (any) -> () } = {}
|
|
22
24
|
|
|
23
|
-
--
|
|
25
|
+
-- Private ----------------------------------------------------------------
|
|
24
26
|
|
|
25
|
-
local
|
|
27
|
+
local isPlayer = Util.isPlayer
|
|
28
|
+
|
|
29
|
+
local function nextCorr(): number
|
|
30
|
+
_nextCorrId += 1
|
|
31
|
+
return _nextCorrId
|
|
32
|
+
end
|
|
26
33
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
-- Public types -----------------------------------------------------------
|
|
35
|
+
|
|
36
|
+
--[[
|
|
37
|
+
Server `request(data, target)` returns `Resp?` for a Player target and
|
|
38
|
+
`{ [Player]: Resp? }` for a multi-target table; Luau doesn't support
|
|
39
|
+
overloads on table fields, so the type advertises `Resp?` (the common
|
|
40
|
+
single-target case). Multi-target callers must cast the return.
|
|
41
|
+
]]
|
|
42
|
+
export type QueryHandle<Req, Resp> = {
|
|
43
|
+
handle: (
|
|
44
|
+
self: QueryHandle<Req, Resp>,
|
|
45
|
+
fn: (request: Req, player: Player?) -> Resp?
|
|
46
|
+
) -> Types.Connection,
|
|
47
|
+
request: (self: QueryHandle<Req, Resp>, data: Req, target: any?) -> Resp?,
|
|
48
|
+
name: (self: QueryHandle<Req, Resp>) -> string,
|
|
49
|
+
stats: (self: QueryHandle<Req, Resp>) -> Types.PacketStats,
|
|
32
50
|
}
|
|
33
51
|
|
|
34
|
-
|
|
52
|
+
-- Public -----------------------------------------------------------------
|
|
53
|
+
|
|
54
|
+
local Query = {}
|
|
55
|
+
|
|
56
|
+
function Query.define<Req, Resp>(
|
|
35
57
|
name: string,
|
|
36
|
-
requestCodec: Types.InternalCodec<
|
|
37
|
-
responseCodec: Types.InternalCodec<
|
|
58
|
+
requestCodec: Types.InternalCodec<Req>,
|
|
59
|
+
responseCodec: Types.InternalCodec<Resp>,
|
|
38
60
|
options: Types.QueryOptions?
|
|
39
|
-
): QueryHandle
|
|
40
|
-
local opts = options or {} :: Types.QueryOptions
|
|
61
|
+
): QueryHandle<Req, Resp>
|
|
62
|
+
local opts = options or ({} :: Types.QueryOptions)
|
|
41
63
|
local timeout = opts.timeout or DEFAULT_TIMEOUT
|
|
42
64
|
|
|
43
|
-
-- Register request channel
|
|
44
65
|
local reqOpenFn = Channel.resolveOpenFn(Channel.TS_NONE, name)
|
|
45
|
-
local
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
nil,
|
|
74
|
-
nil
|
|
75
|
-
|
|
66
|
+
local respName = `{name}\0resp`
|
|
67
|
+
local respOpenFn = Channel.resolveOpenFn(Channel.TS_NONE, respName)
|
|
68
|
+
|
|
69
|
+
local reqReg = Registry.register({
|
|
70
|
+
name = name,
|
|
71
|
+
kind = Registry.KIND_REQUEST,
|
|
72
|
+
codec = requestCodec,
|
|
73
|
+
isUnreliable = false,
|
|
74
|
+
partner = nil,
|
|
75
|
+
needsGate = IS_SERVER,
|
|
76
|
+
headerSize = 1,
|
|
77
|
+
timestampMode = Channel.TS_NONE,
|
|
78
|
+
openFn = reqOpenFn,
|
|
79
|
+
rateLimit = opts.rateLimit or { maxPerSecond = 30 },
|
|
80
|
+
validate = opts.validate,
|
|
81
|
+
maxPayloadBytes = nil,
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
local respReg = Registry.register({
|
|
85
|
+
name = respName,
|
|
86
|
+
kind = Registry.KIND_RESPONSE,
|
|
87
|
+
codec = responseCodec,
|
|
88
|
+
isUnreliable = false,
|
|
89
|
+
partner = reqReg.id,
|
|
90
|
+
needsGate = false,
|
|
91
|
+
headerSize = 1,
|
|
92
|
+
timestampMode = Channel.TS_NONE,
|
|
93
|
+
openFn = respOpenFn,
|
|
94
|
+
rateLimit = nil,
|
|
95
|
+
validate = nil,
|
|
96
|
+
maxPayloadBytes = nil,
|
|
97
|
+
})
|
|
76
98
|
|
|
77
99
|
reqReg.partner = respReg.id
|
|
78
100
|
|
|
79
|
-
|
|
80
|
-
local
|
|
81
|
-
local _client: any = nil
|
|
82
|
-
|
|
83
|
-
local function getServer(): any
|
|
84
|
-
if not _server then
|
|
85
|
-
_server = require(script.Parent.Parent.transport.Server)
|
|
86
|
-
end
|
|
87
|
-
return _server
|
|
88
|
-
end
|
|
101
|
+
local activeHandler: ((any, Player?) -> any)? = nil
|
|
102
|
+
local handlerToken = 0
|
|
89
103
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
end
|
|
94
|
-
return _client
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
-- Listen for incoming responses and resolve pending requests
|
|
98
|
-
respReg.signal:connect(function(value: any, sender: Player?, corrId: number)
|
|
99
|
-
local waiting = _pending[corrId]
|
|
100
|
-
if waiting then
|
|
104
|
+
respReg.signal:connect(function(value: any, _sender: Player?, corrId: number)
|
|
105
|
+
local single = _pending[corrId]
|
|
106
|
+
if single then
|
|
101
107
|
_pending[corrId] = nil
|
|
102
|
-
task.spawn(
|
|
108
|
+
task.spawn(single, value)
|
|
109
|
+
return
|
|
110
|
+
end
|
|
111
|
+
local gather = _pendingGather[corrId]
|
|
112
|
+
if gather then
|
|
113
|
+
_pendingGather[corrId] = nil
|
|
114
|
+
gather(value)
|
|
103
115
|
end
|
|
104
116
|
end)
|
|
105
117
|
|
|
106
|
-
-- Listen for incoming requests and auto-respond
|
|
107
|
-
local _handler: ((any, Player?) -> any)? = nil
|
|
108
|
-
|
|
109
118
|
reqReg.signal:connect(function(value: any, sender: Player?, corrId: number)
|
|
110
|
-
|
|
119
|
+
local handler = activeHandler
|
|
120
|
+
if not handler then
|
|
111
121
|
return
|
|
112
122
|
end
|
|
113
123
|
|
|
114
|
-
|
|
115
|
-
local
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
Channel.writeQuery(ch, respReg.id, corrId, nil, nil)
|
|
125
|
-
end
|
|
126
|
-
end
|
|
127
|
-
else
|
|
128
|
-
local Client = getClient()
|
|
129
|
-
local ch = Client.getChannel(false)
|
|
130
|
-
if ok then
|
|
131
|
-
Channel.writeQuery(ch, respReg.id, corrId, responseCodec, response)
|
|
132
|
-
else
|
|
133
|
-
Channel.writeQuery(ch, respReg.id, corrId, nil, nil)
|
|
134
|
-
end
|
|
124
|
+
local ok, response = pcall(handler, value, sender)
|
|
125
|
+
local payloadCodec = if ok then responseCodec else nil
|
|
126
|
+
local payloadData = if ok then response else nil
|
|
127
|
+
|
|
128
|
+
local ch = if IS_SERVER and sender
|
|
129
|
+
then Transport.server().getChannel(sender, false)
|
|
130
|
+
elseif not IS_SERVER then Transport.client().getChannel(false)
|
|
131
|
+
else nil
|
|
132
|
+
if ch then
|
|
133
|
+
Channel.writeQuery(ch, respReg.id, corrId, payloadCodec, payloadData)
|
|
135
134
|
end
|
|
136
135
|
end)
|
|
137
136
|
|
|
138
|
-
local
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
): Types.Connection
|
|
144
|
-
_handler = fn
|
|
145
|
-
|
|
146
|
-
-- Return a connection that clears the handler on disconnect
|
|
147
|
-
return table.freeze({
|
|
148
|
-
connected = true,
|
|
149
|
-
disconnect = function(self: Types.Connection): ()
|
|
150
|
-
if not (self :: any).connected then
|
|
151
|
-
return
|
|
152
|
-
end
|
|
153
|
-
(self :: any).connected = false
|
|
154
|
-
_handler = nil
|
|
155
|
-
end,
|
|
156
|
-
} :: any)
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
function handle.request(_self: QueryHandle, data: any, target: any?): any
|
|
160
|
-
_nextCorrId += 1
|
|
161
|
-
local corrId = _nextCorrId
|
|
162
|
-
|
|
163
|
-
if IS_SERVER then
|
|
164
|
-
if target == nil then
|
|
165
|
-
error(`[Lync] Query "{name}": server request requires a target`)
|
|
137
|
+
local function scheduleSingleTimeout(corrId: number, running: thread): ()
|
|
138
|
+
task.delay(timeout, function()
|
|
139
|
+
if _pending[corrId] == running then
|
|
140
|
+
_pending[corrId] = nil
|
|
141
|
+
task.spawn(running, nil)
|
|
166
142
|
end
|
|
143
|
+
end)
|
|
144
|
+
end
|
|
167
145
|
|
|
168
|
-
|
|
146
|
+
local function requestSingle(target: Player, data: any): any
|
|
147
|
+
local corrId = nextCorr()
|
|
148
|
+
local ch = Transport.server().getChannel(target, false)
|
|
149
|
+
Channel.writeQuery(ch, reqReg.id, corrId, requestCodec, data)
|
|
169
150
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
151
|
+
local running = coroutine.running()
|
|
152
|
+
_pending[corrId] = running
|
|
153
|
+
scheduleSingleTimeout(corrId, running)
|
|
154
|
+
return coroutine.yield()
|
|
155
|
+
end
|
|
174
156
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
157
|
+
local function requestMulti(targets: { Player }, data: any): { [Player]: any }
|
|
158
|
+
if #targets == 0 then
|
|
159
|
+
return {}
|
|
160
|
+
end
|
|
178
161
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
end
|
|
184
|
-
end)
|
|
162
|
+
local results: { [Player]: any } = {}
|
|
163
|
+
local remaining = #targets
|
|
164
|
+
local running = coroutine.running()
|
|
165
|
+
local resolved = false
|
|
185
166
|
|
|
186
|
-
|
|
167
|
+
local function resolveOnce(): ()
|
|
168
|
+
if resolved then
|
|
169
|
+
return
|
|
187
170
|
end
|
|
171
|
+
resolved = true
|
|
172
|
+
task.spawn(running, results)
|
|
173
|
+
end
|
|
188
174
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
for _, p in tbl do
|
|
202
|
-
if typeof(p) == "Instance" and (p :: Instance):IsA("Player") then
|
|
203
|
-
table.insert(targets, p :: Player)
|
|
204
|
-
end
|
|
205
|
-
end
|
|
175
|
+
local Server = Transport.server()
|
|
176
|
+
for _, player in targets do
|
|
177
|
+
local pCorr = nextCorr()
|
|
178
|
+
local ch = Server.getChannel(player, false)
|
|
179
|
+
Channel.writeQuery(ch, reqReg.id, pCorr, requestCodec, data)
|
|
180
|
+
|
|
181
|
+
local pRef = player
|
|
182
|
+
_pendingGather[pCorr] = function(response: any)
|
|
183
|
+
results[pRef] = response
|
|
184
|
+
remaining -= 1
|
|
185
|
+
if remaining == 0 then
|
|
186
|
+
resolveOnce()
|
|
206
187
|
end
|
|
207
188
|
end
|
|
189
|
+
end
|
|
208
190
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
local results: { [Player]: any } = {}
|
|
214
|
-
local remaining = #targets
|
|
215
|
-
local running = coroutine.running()
|
|
216
|
-
|
|
217
|
-
for _, player in targets do
|
|
218
|
-
_nextCorrId += 1
|
|
219
|
-
local playerCorrId = _nextCorrId
|
|
220
|
-
local ch = Server.getChannel(player, false)
|
|
221
|
-
Channel.writeQuery(ch, reqReg.id, playerCorrId, requestCodec, data)
|
|
222
|
-
|
|
223
|
-
local playerRef = player
|
|
224
|
-
_pending[playerCorrId] = coroutine.create(function(response: any)
|
|
225
|
-
results[playerRef] = response
|
|
226
|
-
remaining -= 1
|
|
227
|
-
if remaining == 0 then
|
|
228
|
-
task.spawn(running, results)
|
|
229
|
-
end
|
|
230
|
-
end)
|
|
231
|
-
end
|
|
191
|
+
task.delay(timeout, resolveOnce)
|
|
192
|
+
return coroutine.yield()
|
|
193
|
+
end
|
|
232
194
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
end
|
|
239
|
-
end)
|
|
195
|
+
local function collectTargets(target: any): { Player }
|
|
196
|
+
local targets: { Player } = {}
|
|
197
|
+
if typeof(target) ~= "table" then
|
|
198
|
+
return targets
|
|
199
|
+
end
|
|
240
200
|
|
|
241
|
-
|
|
201
|
+
if target._lyncAll then
|
|
202
|
+
for _, p in Players:GetPlayers() do
|
|
203
|
+
table.insert(targets, p)
|
|
204
|
+
end
|
|
205
|
+
elseif target._lyncGroup then
|
|
206
|
+
for player in target._members do
|
|
207
|
+
table.insert(targets, player)
|
|
208
|
+
end
|
|
242
209
|
else
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
Channel.writeQuery(ch, reqReg.id, corrId, requestCodec, data)
|
|
247
|
-
|
|
248
|
-
local running = coroutine.running()
|
|
249
|
-
_pending[corrId] = running
|
|
250
|
-
|
|
251
|
-
task.delay(timeout, function()
|
|
252
|
-
if _pending[corrId] == running then
|
|
253
|
-
_pending[corrId] = nil
|
|
254
|
-
task.spawn(running, nil)
|
|
210
|
+
for _, p in target do
|
|
211
|
+
if isPlayer(p) then
|
|
212
|
+
table.insert(targets, p :: Player)
|
|
255
213
|
end
|
|
256
|
-
end
|
|
257
|
-
|
|
258
|
-
return coroutine.yield()
|
|
214
|
+
end
|
|
259
215
|
end
|
|
216
|
+
return targets
|
|
260
217
|
end
|
|
261
218
|
|
|
262
|
-
function
|
|
263
|
-
|
|
219
|
+
local function requestFromServer(data: any, target: any): any
|
|
220
|
+
if target == nil then
|
|
221
|
+
error(`[Lync] Query.request: "{name}" server request requires a target`)
|
|
222
|
+
end
|
|
223
|
+
if isPlayer(target) then
|
|
224
|
+
return requestSingle(target :: Player, data)
|
|
225
|
+
end
|
|
226
|
+
return requestMulti(collectTargets(target), data)
|
|
264
227
|
end
|
|
265
228
|
|
|
266
|
-
function
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
229
|
+
local function requestFromClient(data: any): any
|
|
230
|
+
local corrId = nextCorr()
|
|
231
|
+
local ch = Transport.client().getChannel(false)
|
|
232
|
+
Channel.writeQuery(ch, reqReg.id, corrId, requestCodec, data)
|
|
233
|
+
|
|
234
|
+
local running = coroutine.running()
|
|
235
|
+
_pending[corrId] = running
|
|
236
|
+
scheduleSingleTimeout(corrId, running)
|
|
237
|
+
return coroutine.yield()
|
|
274
238
|
end
|
|
275
239
|
|
|
276
|
-
|
|
240
|
+
type Self = QueryHandle<Req, Resp>
|
|
241
|
+
|
|
242
|
+
local handle = {
|
|
243
|
+
handle = function(
|
|
244
|
+
_self: Self,
|
|
245
|
+
fn: (request: Req, player: Player?) -> Resp?
|
|
246
|
+
): Types.Connection
|
|
247
|
+
handlerToken += 1
|
|
248
|
+
local myToken = handlerToken
|
|
249
|
+
activeHandler = fn :: any
|
|
250
|
+
|
|
251
|
+
local conn = { connected = true }
|
|
252
|
+
function conn.disconnect(self): ()
|
|
253
|
+
if not self.connected then
|
|
254
|
+
return
|
|
255
|
+
end
|
|
256
|
+
self.connected = false
|
|
257
|
+
if handlerToken == myToken then
|
|
258
|
+
activeHandler = nil
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
return conn :: Types.Connection
|
|
262
|
+
end,
|
|
263
|
+
|
|
264
|
+
request = function(_self: Self, data: Req, target: any?): Resp?
|
|
265
|
+
if IS_SERVER then
|
|
266
|
+
return requestFromServer(data, target)
|
|
267
|
+
end
|
|
268
|
+
return requestFromClient(data)
|
|
269
|
+
end,
|
|
270
|
+
|
|
271
|
+
name = function(_self: Self): string
|
|
272
|
+
return name
|
|
273
|
+
end,
|
|
274
|
+
|
|
275
|
+
stats = function(_self: Self): Types.PacketStats
|
|
276
|
+
return {
|
|
277
|
+
bytesSent = reqReg.bytesSent + respReg.bytesSent,
|
|
278
|
+
bytesReceived = reqReg.bytesReceived + respReg.bytesReceived,
|
|
279
|
+
fires = reqReg.fires + respReg.fires,
|
|
280
|
+
recvFires = reqReg.recvFires + respReg.recvFires,
|
|
281
|
+
drops = reqReg.drops + respReg.drops,
|
|
282
|
+
}
|
|
283
|
+
end,
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return table.freeze(handle) :: QueryHandle<Req, Resp>
|
|
277
287
|
end
|
|
278
288
|
|
|
279
289
|
function Query.pendingCount(): number
|
|
@@ -281,6 +291,9 @@ function Query.pendingCount(): number
|
|
|
281
291
|
for _ in _pending do
|
|
282
292
|
count += 1
|
|
283
293
|
end
|
|
294
|
+
for _ in _pendingGather do
|
|
295
|
+
count += 1
|
|
296
|
+
end
|
|
284
297
|
return count
|
|
285
298
|
end
|
|
286
299
|
|
package/src/api/Scope.luau
CHANGED
|
@@ -1,76 +1,74 @@
|
|
|
1
1
|
--!strict
|
|
2
2
|
--!optimize 2
|
|
3
|
-
-- Batched connection lifecycle
|
|
3
|
+
-- Batched connection lifecycle. Accepts Lync Connection,
|
|
4
|
+
-- RBXScriptConnection, and any object with :disconnect() / :Disconnect().
|
|
4
5
|
|
|
5
6
|
local Types = require(script.Parent.Parent.Types)
|
|
6
7
|
|
|
7
|
-
-- Public
|
|
8
|
-
|
|
9
|
-
local Scope = {}
|
|
8
|
+
-- Public types -----------------------------------------------------------
|
|
10
9
|
|
|
11
10
|
export type ScopeHandle = {
|
|
12
11
|
on: (self: ScopeHandle, source: any, fn: (...any) -> ()) -> Types.Connection,
|
|
13
12
|
once: (self: ScopeHandle, source: any, fn: (...any) -> ()) -> Types.Connection,
|
|
14
13
|
add: (self: ScopeHandle, connection: any) -> (),
|
|
14
|
+
size: (self: ScopeHandle) -> number,
|
|
15
15
|
destroy: (self: ScopeHandle) -> (),
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
type Scope = ScopeHandle & { _connections: { any }, _count: number }
|
|
19
|
+
|
|
20
|
+
-- Private ----------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
local function pushConnection(self: Scope, conn: any): ()
|
|
23
|
+
local idx = self._count + 1
|
|
24
|
+
self._count = idx
|
|
25
|
+
self._connections[idx] = conn
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
-- Public -----------------------------------------------------------------
|
|
29
|
+
|
|
18
30
|
local ScopeMeta = {}
|
|
19
31
|
ScopeMeta.__index = ScopeMeta
|
|
20
32
|
|
|
21
|
-
function ScopeMeta.on(self:
|
|
33
|
+
function ScopeMeta.on(self: Scope, source: any, fn: (...any) -> ()): Types.Connection
|
|
22
34
|
local conn = source:on(fn)
|
|
23
|
-
|
|
24
|
-
local idx = self._count + 1
|
|
25
|
-
self._count = idx
|
|
26
|
-
conns[idx] = conn
|
|
35
|
+
pushConnection(self, conn)
|
|
27
36
|
return conn
|
|
28
37
|
end
|
|
29
38
|
|
|
30
|
-
function ScopeMeta.once(self:
|
|
39
|
+
function ScopeMeta.once(self: Scope, source: any, fn: (...any) -> ()): Types.Connection
|
|
31
40
|
local conn = source:once(fn)
|
|
32
|
-
|
|
33
|
-
local idx = self._count + 1
|
|
34
|
-
self._count = idx
|
|
35
|
-
conns[idx] = conn
|
|
41
|
+
pushConnection(self, conn)
|
|
36
42
|
return conn
|
|
37
43
|
end
|
|
38
44
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
self.
|
|
45
|
+
ScopeMeta.add = pushConnection
|
|
46
|
+
|
|
47
|
+
function ScopeMeta.size(self: Scope): number
|
|
48
|
+
return self._count
|
|
43
49
|
end
|
|
44
50
|
|
|
45
|
-
function ScopeMeta.destroy(self:
|
|
51
|
+
function ScopeMeta.destroy(self: Scope): ()
|
|
46
52
|
local conns = self._connections
|
|
47
53
|
local count = self._count
|
|
48
|
-
|
|
49
54
|
for i = 1, count do
|
|
50
55
|
local conn = conns[i]
|
|
51
|
-
conns[i] = nil
|
|
52
|
-
|
|
53
|
-
--[[
|
|
54
|
-
Support both Lync Connection (conn:disconnect())
|
|
55
|
-
and RBXScriptConnection (conn:Disconnect()).
|
|
56
|
-
]]
|
|
57
|
-
if typeof(conn) == "table" then
|
|
58
|
-
if conn.disconnect then
|
|
59
|
-
conn:disconnect()
|
|
60
|
-
end
|
|
61
|
-
elseif typeof(conn) == "RBXScriptConnection" then
|
|
56
|
+
conns[i] = nil
|
|
57
|
+
if typeof(conn) == "RBXScriptConnection" then
|
|
62
58
|
(conn :: RBXScriptConnection):Disconnect()
|
|
59
|
+
elseif typeof(conn) == "table" and conn.disconnect then
|
|
60
|
+
conn:disconnect()
|
|
63
61
|
end
|
|
64
62
|
end
|
|
65
|
-
|
|
66
63
|
self._count = 0
|
|
67
64
|
end
|
|
68
65
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
66
|
+
table.freeze(ScopeMeta)
|
|
67
|
+
|
|
68
|
+
local ScopeModule = {}
|
|
69
|
+
|
|
70
|
+
function ScopeModule.create(): ScopeHandle
|
|
71
|
+
return (setmetatable({ _connections = {}, _count = 0 }, ScopeMeta) :: any) :: ScopeHandle
|
|
74
72
|
end
|
|
75
73
|
|
|
76
|
-
return table.freeze(
|
|
74
|
+
return table.freeze(ScopeModule)
|