@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/Query.luau
CHANGED
|
@@ -5,24 +5,33 @@
|
|
|
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
|
|
33
|
+
|
|
34
|
+
-- Public types -----------------------------------------------------------
|
|
26
35
|
|
|
27
36
|
export type QueryHandle = {
|
|
28
37
|
handle: (self: QueryHandle, fn: (request: any, player: Player?) -> any) -> Types.Connection,
|
|
@@ -31,249 +40,239 @@ export type QueryHandle = {
|
|
|
31
40
|
stats: (self: QueryHandle) -> Types.PacketStats,
|
|
32
41
|
}
|
|
33
42
|
|
|
43
|
+
-- Public -----------------------------------------------------------------
|
|
44
|
+
|
|
45
|
+
local Query = {}
|
|
46
|
+
|
|
34
47
|
function Query.define(
|
|
35
48
|
name: string,
|
|
36
49
|
requestCodec: Types.InternalCodec<any>,
|
|
37
50
|
responseCodec: Types.InternalCodec<any>,
|
|
38
51
|
options: Types.QueryOptions?
|
|
39
52
|
): QueryHandle
|
|
40
|
-
local opts = options or {} :: Types.QueryOptions
|
|
53
|
+
local opts = options or ({} :: Types.QueryOptions)
|
|
41
54
|
local timeout = opts.timeout or DEFAULT_TIMEOUT
|
|
42
55
|
|
|
43
|
-
-- Register request channel
|
|
44
56
|
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
|
-
|
|
57
|
+
local respName = `{name}\0resp`
|
|
58
|
+
local respOpenFn = Channel.resolveOpenFn(Channel.TS_NONE, respName)
|
|
59
|
+
|
|
60
|
+
local reqReg = Registry.register({
|
|
61
|
+
name = name,
|
|
62
|
+
kind = Registry.KIND_REQUEST,
|
|
63
|
+
codec = requestCodec,
|
|
64
|
+
isUnreliable = false,
|
|
65
|
+
partner = nil,
|
|
66
|
+
needsGate = IS_SERVER,
|
|
67
|
+
headerSize = 1,
|
|
68
|
+
timestampMode = Channel.TS_NONE,
|
|
69
|
+
openFn = reqOpenFn,
|
|
70
|
+
rateLimit = opts.rateLimit or { maxPerSecond = 30 },
|
|
71
|
+
validate = opts.validate,
|
|
72
|
+
maxPayloadBytes = nil,
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
local respReg = Registry.register({
|
|
76
|
+
name = respName,
|
|
77
|
+
kind = Registry.KIND_RESPONSE,
|
|
78
|
+
codec = responseCodec,
|
|
79
|
+
isUnreliable = false,
|
|
80
|
+
partner = reqReg.id,
|
|
81
|
+
needsGate = false,
|
|
82
|
+
headerSize = 1,
|
|
83
|
+
timestampMode = Channel.TS_NONE,
|
|
84
|
+
openFn = respOpenFn,
|
|
85
|
+
rateLimit = nil,
|
|
86
|
+
validate = nil,
|
|
87
|
+
maxPayloadBytes = nil,
|
|
88
|
+
})
|
|
76
89
|
|
|
77
90
|
reqReg.partner = respReg.id
|
|
78
91
|
|
|
79
|
-
|
|
80
|
-
local
|
|
81
|
-
local _client: any = nil
|
|
92
|
+
local activeHandler: ((any, Player?) -> any)? = nil
|
|
93
|
+
local handlerToken = 0
|
|
82
94
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
end
|
|
87
|
-
return _server
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
local function getClient(): any
|
|
91
|
-
if not _client then
|
|
92
|
-
_client = require(script.Parent.Parent.transport.Client)
|
|
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
|
|
95
|
+
respReg.signal:connect(function(value: any, _sender: Player?, corrId: number)
|
|
96
|
+
local single = _pending[corrId]
|
|
97
|
+
if single then
|
|
101
98
|
_pending[corrId] = nil
|
|
102
|
-
task.spawn(
|
|
99
|
+
task.spawn(single, value)
|
|
100
|
+
return
|
|
101
|
+
end
|
|
102
|
+
local gather = _pendingGather[corrId]
|
|
103
|
+
if gather then
|
|
104
|
+
_pendingGather[corrId] = nil
|
|
105
|
+
gather(value)
|
|
103
106
|
end
|
|
104
107
|
end)
|
|
105
108
|
|
|
106
|
-
-- Listen for incoming requests and auto-respond
|
|
107
|
-
local _handler: ((any, Player?) -> any)? = nil
|
|
108
|
-
|
|
109
109
|
reqReg.signal:connect(function(value: any, sender: Player?, corrId: number)
|
|
110
|
-
|
|
110
|
+
local handler = activeHandler
|
|
111
|
+
if not handler then
|
|
111
112
|
return
|
|
112
113
|
end
|
|
113
114
|
|
|
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
|
|
115
|
+
local ok, response = pcall(handler, value, sender)
|
|
116
|
+
local payloadCodec = if ok then responseCodec else nil
|
|
117
|
+
local payloadData = if ok then response else nil
|
|
118
|
+
|
|
119
|
+
local ch = if IS_SERVER and sender
|
|
120
|
+
then Transport.server().getChannel(sender, false)
|
|
121
|
+
elseif not IS_SERVER then Transport.client().getChannel(false)
|
|
122
|
+
else nil
|
|
123
|
+
if ch then
|
|
124
|
+
Channel.writeQuery(ch, respReg.id, corrId, payloadCodec, payloadData)
|
|
135
125
|
end
|
|
136
126
|
end)
|
|
137
127
|
|
|
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`)
|
|
128
|
+
local function scheduleSingleTimeout(corrId: number, running: thread): ()
|
|
129
|
+
task.delay(timeout, function()
|
|
130
|
+
if _pending[corrId] == running then
|
|
131
|
+
_pending[corrId] = nil
|
|
132
|
+
task.spawn(running, nil)
|
|
166
133
|
end
|
|
134
|
+
end)
|
|
135
|
+
end
|
|
167
136
|
|
|
168
|
-
|
|
137
|
+
local function requestSingle(target: Player, data: any): any
|
|
138
|
+
local corrId = nextCorr()
|
|
139
|
+
local ch = Transport.server().getChannel(target, false)
|
|
140
|
+
Channel.writeQuery(ch, reqReg.id, corrId, requestCodec, data)
|
|
169
141
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
142
|
+
local running = coroutine.running()
|
|
143
|
+
_pending[corrId] = running
|
|
144
|
+
scheduleSingleTimeout(corrId, running)
|
|
145
|
+
return coroutine.yield()
|
|
146
|
+
end
|
|
174
147
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
148
|
+
local function requestMulti(targets: { Player }, data: any): { [Player]: any }
|
|
149
|
+
if #targets == 0 then
|
|
150
|
+
return {}
|
|
151
|
+
end
|
|
178
152
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
end
|
|
184
|
-
end)
|
|
153
|
+
local results: { [Player]: any } = {}
|
|
154
|
+
local remaining = #targets
|
|
155
|
+
local running = coroutine.running()
|
|
156
|
+
local resolved = false
|
|
185
157
|
|
|
186
|
-
|
|
158
|
+
local function resolveOnce(): ()
|
|
159
|
+
if resolved then
|
|
160
|
+
return
|
|
187
161
|
end
|
|
162
|
+
resolved = true
|
|
163
|
+
task.spawn(running, results)
|
|
164
|
+
end
|
|
188
165
|
|
|
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
|
|
166
|
+
local Server = Transport.server()
|
|
167
|
+
for _, player in targets do
|
|
168
|
+
local pCorr = nextCorr()
|
|
169
|
+
local ch = Server.getChannel(player, false)
|
|
170
|
+
Channel.writeQuery(ch, reqReg.id, pCorr, requestCodec, data)
|
|
171
|
+
|
|
172
|
+
local pRef = player
|
|
173
|
+
_pendingGather[pCorr] = function(response: any)
|
|
174
|
+
results[pRef] = response
|
|
175
|
+
remaining -= 1
|
|
176
|
+
if remaining == 0 then
|
|
177
|
+
resolveOnce()
|
|
206
178
|
end
|
|
207
179
|
end
|
|
180
|
+
end
|
|
208
181
|
|
|
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
|
|
182
|
+
task.delay(timeout, resolveOnce)
|
|
183
|
+
return coroutine.yield()
|
|
184
|
+
end
|
|
232
185
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
end
|
|
239
|
-
end)
|
|
186
|
+
local function collectTargets(target: any): { Player }
|
|
187
|
+
local targets: { Player } = {}
|
|
188
|
+
if typeof(target) ~= "table" then
|
|
189
|
+
return targets
|
|
190
|
+
end
|
|
240
191
|
|
|
241
|
-
|
|
192
|
+
if target._lyncAll then
|
|
193
|
+
for _, p in Players:GetPlayers() do
|
|
194
|
+
table.insert(targets, p)
|
|
195
|
+
end
|
|
196
|
+
elseif target._lyncGroup then
|
|
197
|
+
for player in target._members do
|
|
198
|
+
table.insert(targets, player)
|
|
199
|
+
end
|
|
242
200
|
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)
|
|
201
|
+
for _, p in target do
|
|
202
|
+
if isPlayer(p) then
|
|
203
|
+
table.insert(targets, p :: Player)
|
|
255
204
|
end
|
|
256
|
-
end
|
|
257
|
-
|
|
258
|
-
return coroutine.yield()
|
|
205
|
+
end
|
|
259
206
|
end
|
|
207
|
+
return targets
|
|
260
208
|
end
|
|
261
209
|
|
|
262
|
-
function
|
|
263
|
-
|
|
210
|
+
local function requestFromServer(data: any, target: any): any
|
|
211
|
+
if target == nil then
|
|
212
|
+
error(`[Lync] Query.request: "{name}" server request requires a target`)
|
|
213
|
+
end
|
|
214
|
+
if isPlayer(target) then
|
|
215
|
+
return requestSingle(target :: Player, data)
|
|
216
|
+
end
|
|
217
|
+
return requestMulti(collectTargets(target), data)
|
|
264
218
|
end
|
|
265
219
|
|
|
266
|
-
function
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
220
|
+
local function requestFromClient(data: any): any
|
|
221
|
+
local corrId = nextCorr()
|
|
222
|
+
local ch = Transport.client().getChannel(false)
|
|
223
|
+
Channel.writeQuery(ch, reqReg.id, corrId, requestCodec, data)
|
|
224
|
+
|
|
225
|
+
local running = coroutine.running()
|
|
226
|
+
_pending[corrId] = running
|
|
227
|
+
scheduleSingleTimeout(corrId, running)
|
|
228
|
+
return coroutine.yield()
|
|
274
229
|
end
|
|
275
230
|
|
|
276
|
-
|
|
231
|
+
local handle = {
|
|
232
|
+
handle = function(
|
|
233
|
+
_self: QueryHandle,
|
|
234
|
+
fn: (request: any, player: Player?) -> any
|
|
235
|
+
): Types.Connection
|
|
236
|
+
handlerToken += 1
|
|
237
|
+
local myToken = handlerToken
|
|
238
|
+
activeHandler = fn
|
|
239
|
+
|
|
240
|
+
local conn = { connected = true }
|
|
241
|
+
function conn.disconnect(self): ()
|
|
242
|
+
if not self.connected then
|
|
243
|
+
return
|
|
244
|
+
end
|
|
245
|
+
self.connected = false
|
|
246
|
+
if handlerToken == myToken then
|
|
247
|
+
activeHandler = nil
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
return conn :: Types.Connection
|
|
251
|
+
end,
|
|
252
|
+
|
|
253
|
+
request = function(_self: QueryHandle, data: any, target: any?): any
|
|
254
|
+
if IS_SERVER then
|
|
255
|
+
return requestFromServer(data, target)
|
|
256
|
+
end
|
|
257
|
+
return requestFromClient(data)
|
|
258
|
+
end,
|
|
259
|
+
|
|
260
|
+
name = function(_self: QueryHandle): string
|
|
261
|
+
return name
|
|
262
|
+
end,
|
|
263
|
+
|
|
264
|
+
stats = function(_self: QueryHandle): Types.PacketStats
|
|
265
|
+
return {
|
|
266
|
+
bytesSent = reqReg.bytesSent + respReg.bytesSent,
|
|
267
|
+
bytesReceived = reqReg.bytesReceived + respReg.bytesReceived,
|
|
268
|
+
fires = reqReg.fires + respReg.fires,
|
|
269
|
+
recvFires = reqReg.recvFires + respReg.recvFires,
|
|
270
|
+
drops = reqReg.drops + respReg.drops,
|
|
271
|
+
}
|
|
272
|
+
end,
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return table.freeze(handle) :: QueryHandle
|
|
277
276
|
end
|
|
278
277
|
|
|
279
278
|
function Query.pendingCount(): number
|
|
@@ -281,6 +280,9 @@ function Query.pendingCount(): number
|
|
|
281
280
|
for _ in _pending do
|
|
282
281
|
count += 1
|
|
283
282
|
end
|
|
283
|
+
for _ in _pendingGather do
|
|
284
|
+
count += 1
|
|
285
|
+
end
|
|
284
286
|
return count
|
|
285
287
|
end
|
|
286
288
|
|
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)
|