@flamework-experimental/networking 2.0.0-alpha.1 → 2.0.0-alpha.3
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 +35 -27
- package/flamework.build +1 -1
- package/out/event/createEvent.d.ts +22 -4
- package/out/event/createEvent.luau +47 -49
- package/out/events/createClientMethod.luau +7 -3
- package/out/events/createGenericHandler.luau +6 -3
- package/out/events/createNetworkingEvent.luau +7 -5
- package/out/events/createServerMethod.d.ts +1 -1
- package/out/events/createServerMethod.luau +8 -3
- package/out/events/types.d.ts +4 -3
- package/out/function/createFunctionReceiver.d.ts +10 -9
- package/out/function/createFunctionReceiver.luau +53 -55
- package/out/function/createFunctionSender.d.ts +3 -2
- package/out/function/createFunctionSender.luau +118 -40
- package/out/functions/createClientMethod.d.ts +1 -1
- package/out/functions/createClientMethod.luau +9 -8
- package/out/functions/createGenericHandler.luau +8 -12
- package/out/functions/createNetworkingFunction.luau +7 -5
- package/out/functions/createServerMethod.d.ts +1 -1
- package/out/functions/createServerMethod.luau +9 -8
- package/out/functions/types.d.ts +10 -12
- package/out/handlers.d.ts +35 -36
- package/out/index.d.ts +6 -0
- package/out/init.luau +6 -0
- package/out/middleware/createGuards.d.ts +10 -0
- package/out/middleware/createGuards.luau +32 -0
- package/out/middleware/processor.d.ts +62 -0
- package/out/middleware/processor.luau +214 -0
- package/out/middleware/types.d.ts +26 -15
- package/out/util/createOnce.d.ts +14 -0
- package/out/util/createOnce.luau +66 -0
- package/out/util/createSignalContainer.d.ts +2 -1
- package/out/util/createSignalContainer.luau +2 -2
- package/out/util/signal.d.ts +27 -0
- package/out/util/signal.luau +135 -0
- package/out/util/trimArguments.d.ts +12 -0
- package/out/util/trimArguments.luau +48 -0
- package/package.json +3 -4
- package/out/middleware/createGuardMiddleware.d.ts +0 -6
- package/out/middleware/createGuardMiddleware.luau +0 -44
- package/out/middleware/createMiddlewareProcessor.d.ts +0 -3
- package/out/middleware/createMiddlewareProcessor.luau +0 -24
- package/out/util/timeoutPromise.d.ts +0 -1
- package/out/util/timeoutPromise.luau +0 -10
|
@@ -5,10 +5,12 @@ local _createEvent = TS.import(script, script.Parent.Parent, "event", "createEve
|
|
|
5
5
|
local createEvent = _createEvent.createEvent
|
|
6
6
|
local decodeArguments = _createEvent.decodeArguments
|
|
7
7
|
local NetworkingFunctionError = TS.import(script, script.Parent, "errors").NetworkingFunctionError
|
|
8
|
-
local createMiddlewareProcessor = TS.import(script, script.Parent.Parent, "middleware", "createMiddlewareProcessor").createMiddlewareProcessor
|
|
9
8
|
local _skip = TS.import(script, script.Parent.Parent, "middleware", "skip")
|
|
10
9
|
local Skip = _skip.Skip
|
|
11
10
|
local SkipBadRequest = _skip.SkipBadRequest
|
|
11
|
+
local _processor = TS.import(script, script.Parent.Parent, "middleware", "processor")
|
|
12
|
+
local createProcessor = _processor.createProcessor
|
|
13
|
+
local withoutPlayer = _processor.withoutPlayer
|
|
12
14
|
local getProcessResult
|
|
13
15
|
local function createFunctionReceiver(options)
|
|
14
16
|
local event = createEvent({
|
|
@@ -17,57 +19,63 @@ local function createFunctionReceiver(options)
|
|
|
17
19
|
id = options.id,
|
|
18
20
|
networkInfo = options.networkInfo,
|
|
19
21
|
})
|
|
20
|
-
|
|
21
|
-
local
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return newCallback(unpack(args))
|
|
30
|
-
end
|
|
31
|
-
end)
|
|
22
|
+
-- Which half this function is, fixed when it is made: the server's callback is given the sender.
|
|
23
|
+
local isServer = RunService:IsServer()
|
|
24
|
+
-- Guards, then middleware, then the callback: plain calls in the thread handling the request, a
|
|
25
|
+
-- Promise any of them returns waited for in place and a cancelled one read as `Networking.Skip`.
|
|
26
|
+
local process
|
|
27
|
+
local packResult
|
|
28
|
+
local setCallback = function(newCallback, pack)
|
|
29
|
+
packResult = pack
|
|
30
|
+
process = createProcessor(options.incomingMiddleware, options.networkInfo, if isServer then newCallback else withoutPlayer(newCallback), options.incomingGuards, SkipBadRequest, Skip)
|
|
32
31
|
end
|
|
33
32
|
--[[
|
|
34
33
|
*
|
|
35
|
-
*
|
|
36
|
-
* type carries nothing (`void`); errors always go back as they are.
|
|
34
|
+
* With serialization, a successful value leaves packed as `[payload, blobs?]`, or nothing at all
|
|
35
|
+
* when the result type carries nothing (`void`); errors always go back as they are. It is packed
|
|
36
|
+
* here, after the middleware, so a value a middleware returns is packed like the callback's own.
|
|
37
37
|
|
|
38
38
|
]]
|
|
39
39
|
local respond = function(player, id, processResult, value)
|
|
40
|
-
if processResult == true and
|
|
41
|
-
local
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
if processResult == true and packResult then
|
|
41
|
+
local packed = packResult(value)
|
|
42
|
+
if packed ~= nil then
|
|
43
|
+
event:fireEither(player, id, processResult, packed[1], packed[2])
|
|
44
|
+
else
|
|
45
|
+
event:fireEither(player, id, processResult)
|
|
46
|
+
end
|
|
45
47
|
else
|
|
46
48
|
event:fireEither(player, id, processResult, value)
|
|
47
49
|
end
|
|
48
50
|
end
|
|
51
|
+
--* A request that raised -- in a guard, a middleware, the callback, its Promise or the packing -- is answered `false`.
|
|
52
|
+
local fail = function(player, id, reason)
|
|
53
|
+
warn(`Failed to process request to '{options.debugName}'`)
|
|
54
|
+
warn(reason)
|
|
55
|
+
event:fireEither(player, id, false)
|
|
56
|
+
end
|
|
57
|
+
-- Runs on the thread the event's signal gives this handler, one per request.
|
|
49
58
|
local processRequest = function(player, id, ...)
|
|
50
59
|
local args = { ... }
|
|
51
|
-
|
|
60
|
+
local current = process
|
|
61
|
+
if not current then
|
|
52
62
|
return event:fireEither(player, id, NetworkingFunctionError.Unprocessed)
|
|
53
63
|
end
|
|
54
64
|
local decoded = decodeArguments(options.argsDecoder, player, args, options.onMalformed)
|
|
55
65
|
if not decoded then
|
|
56
66
|
return event:fireEither(player, id, NetworkingFunctionError.BadRequest)
|
|
57
67
|
end
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
68
|
+
local ok, value = pcall(current, player, unpack(decoded))
|
|
69
|
+
if not ok then
|
|
70
|
+
return fail(player, id, value)
|
|
71
|
+
end
|
|
72
|
+
local sent, reason = pcall(respond, player, id, getProcessResult(value), value)
|
|
73
|
+
if not sent then
|
|
74
|
+
fail(player, id, reason)
|
|
75
|
+
end
|
|
65
76
|
end
|
|
66
|
-
if
|
|
67
|
-
event:connectServer(
|
|
68
|
-
local args = { ... }
|
|
69
|
-
return processRequest(player, id, unpack(args))
|
|
70
|
-
end)
|
|
77
|
+
if isServer then
|
|
78
|
+
event:connectServer(processRequest)
|
|
71
79
|
else
|
|
72
80
|
event:connectClient(function(id, ...)
|
|
73
81
|
local args = { ... }
|
|
@@ -75,37 +83,27 @@ local function createFunctionReceiver(options)
|
|
|
75
83
|
end)
|
|
76
84
|
end
|
|
77
85
|
return {
|
|
78
|
-
setServerCallback = function(self, callback,
|
|
79
|
-
|
|
80
|
-
packed = false
|
|
81
|
-
end
|
|
82
|
-
setCallback(callback, packed)
|
|
86
|
+
setServerCallback = function(self, callback, pack)
|
|
87
|
+
setCallback(callback, pack)
|
|
83
88
|
end,
|
|
84
|
-
setClientCallback = function(self, callback,
|
|
85
|
-
|
|
86
|
-
packed = false
|
|
87
|
-
end
|
|
88
|
-
setCallback(callback, packed)
|
|
89
|
+
setClientCallback = function(self, callback, pack)
|
|
90
|
+
setCallback(callback, pack)
|
|
89
91
|
end,
|
|
90
92
|
invoke = function(self, player, ...)
|
|
91
93
|
local args = { ... }
|
|
92
|
-
|
|
94
|
+
local current = process
|
|
95
|
+
if not current then
|
|
93
96
|
return TS.Promise.reject(NetworkingFunctionError.Unprocessed)
|
|
94
97
|
end
|
|
95
|
-
|
|
98
|
+
-- The Promise `predict` returns is the only one: the chain runs in its executor's thread.
|
|
99
|
+
return TS.Promise.new(function(resolve, reject)
|
|
100
|
+
local value = current(player, unpack(args))
|
|
96
101
|
local processResult = getProcessResult(value)
|
|
97
102
|
if processResult ~= true then
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
local decoder = options.resultDecoder
|
|
102
|
-
if not packedResults or not decoder then
|
|
103
|
-
return value
|
|
103
|
+
reject(processResult)
|
|
104
|
+
else
|
|
105
|
+
resolve(value)
|
|
104
106
|
end
|
|
105
|
-
local _binding = value
|
|
106
|
-
local payload = _binding[1]
|
|
107
|
-
local blobs = _binding[2]
|
|
108
|
-
return decoder(payload, blobs or {})[1]
|
|
109
107
|
end)
|
|
110
108
|
end,
|
|
111
109
|
}
|
|
@@ -39,7 +39,8 @@ export interface RequestInfo {
|
|
|
39
39
|
requests: Map<number, (value: unknown, rejection?: NetworkingFunctionError) => void>;
|
|
40
40
|
}
|
|
41
41
|
export interface FunctionSenderInterface {
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
/** `timeout` in seconds; `math.huge` waits for good. */
|
|
43
|
+
invokeServer(timeout: number, ...args: unknown[]): Promise<unknown>;
|
|
44
|
+
invokeClient(player: Player, timeout: number, ...args: unknown[]): Promise<unknown>;
|
|
44
45
|
}
|
|
45
46
|
export declare function createFunctionSender(options: CreateFunctionSenderOptions): FunctionSenderInterface;
|
|
@@ -3,14 +3,13 @@ local TS = _G[script]
|
|
|
3
3
|
local _services = TS.import(script, TS.getModule(script, "@rbxts", "services"))
|
|
4
4
|
local Players = _services.Players
|
|
5
5
|
local RunService = _services.RunService
|
|
6
|
-
local
|
|
7
|
-
local
|
|
8
|
-
local decodeArguments = _createEvent.decodeArguments
|
|
6
|
+
local createEvent = TS.import(script, script.Parent.Parent, "event", "createEvent").createEvent
|
|
7
|
+
local decode = TS.import(script, script.Parent.Parent, "middleware", "processor").decode
|
|
9
8
|
local _errors = TS.import(script, script.Parent, "errors")
|
|
10
9
|
local NetworkingFunctionError = _errors.NetworkingFunctionError
|
|
11
10
|
local getFunctionError = _errors.getFunctionError
|
|
12
11
|
local t = TS.import(script, TS.getModule(script, "@rbxts", "t").lib.ts).t
|
|
13
|
-
local createRequestInfo
|
|
12
|
+
local createRequestInfo, hasLeft
|
|
14
13
|
local function createFunctionSender(options)
|
|
15
14
|
local event = createEvent({
|
|
16
15
|
namespace = options.namespace,
|
|
@@ -18,8 +17,8 @@ local function createFunctionSender(options)
|
|
|
18
17
|
id = options.id,
|
|
19
18
|
networkInfo = options.networkInfo,
|
|
20
19
|
})
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
--* A response is `(id, processResult, value)`, or `(id, true, payload, blobs?)` with a result decoder.
|
|
21
|
+
local processResponse = function(player, requestInfo, id, processResult, value, blobs)
|
|
23
22
|
if not t.number(id) then
|
|
24
23
|
return nil
|
|
25
24
|
end
|
|
@@ -34,12 +33,16 @@ local function createFunctionSender(options)
|
|
|
34
33
|
end
|
|
35
34
|
local rejection = getFunctionError(processResult)
|
|
36
35
|
if rejection ~= nil or not options.resultDecoder then
|
|
37
|
-
request(
|
|
36
|
+
request(value, rejection)
|
|
38
37
|
return nil
|
|
39
38
|
end
|
|
40
39
|
-- A successful response carries the packed value: `(buffer, blobs?)`.
|
|
41
|
-
local decoded =
|
|
42
|
-
if not
|
|
40
|
+
local ok, decoded = decode(options.resultDecoder, value, blobs)
|
|
41
|
+
if not ok then
|
|
42
|
+
local _result = options.onMalformed
|
|
43
|
+
if _result ~= nil then
|
|
44
|
+
_result(player, decoded)
|
|
45
|
+
end
|
|
43
46
|
request(nil, NetworkingFunctionError.InvalidResult)
|
|
44
47
|
return nil
|
|
45
48
|
end
|
|
@@ -48,21 +51,27 @@ local function createFunctionSender(options)
|
|
|
48
51
|
-- We don't need to defer here because we only accept responses to our explicit invocations.
|
|
49
52
|
local requestInfoServer = {}
|
|
50
53
|
local requestInfoClient = createRequestInfo()
|
|
54
|
+
-- A player who has left never answers: `FireClient` drops the request without a word, and only a
|
|
55
|
+
-- finite timeout would ever settle it. Weak, so that remembering them does not keep them alive.
|
|
56
|
+
local departedPlayers = setmetatable({}, {
|
|
57
|
+
__mode = "k",
|
|
58
|
+
})
|
|
51
59
|
if RunService:IsServer() then
|
|
52
|
-
event:connectServer(function(player, id, processResult,
|
|
53
|
-
local response = { ... }
|
|
60
|
+
event:connectServer(function(player, id, processResult, value, blobs)
|
|
54
61
|
local _player = player
|
|
55
62
|
local requestInfo = requestInfoServer[_player]
|
|
56
63
|
if not requestInfo then
|
|
57
64
|
return nil
|
|
58
65
|
end
|
|
59
|
-
processResponse(player, requestInfo, id, processResult,
|
|
66
|
+
processResponse(player, requestInfo, id, processResult, value, blobs)
|
|
60
67
|
end)
|
|
61
68
|
Players.PlayerRemoving:Connect(function(player)
|
|
62
69
|
local _player = player
|
|
63
|
-
|
|
70
|
+
departedPlayers[_player] = true
|
|
64
71
|
local _player_1 = player
|
|
65
|
-
requestInfoServer[_player_1]
|
|
72
|
+
local requestInfo = requestInfoServer[_player_1]
|
|
73
|
+
local _player_2 = player
|
|
74
|
+
requestInfoServer[_player_2] = nil
|
|
66
75
|
if requestInfo then
|
|
67
76
|
-- Cancel all existing requests from this player.
|
|
68
77
|
for _, request in requestInfo.requests do
|
|
@@ -71,16 +80,48 @@ local function createFunctionSender(options)
|
|
|
71
80
|
end
|
|
72
81
|
end)
|
|
73
82
|
else
|
|
74
|
-
event:connectClient(function(id, processResult,
|
|
75
|
-
|
|
76
|
-
processResponse(nil, requestInfoClient, id, processResult, unpack(response))
|
|
83
|
+
event:connectClient(function(id, processResult, value, blobs)
|
|
84
|
+
processResponse(nil, requestInfoClient, id, processResult, value, blobs)
|
|
77
85
|
end)
|
|
78
86
|
end
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
87
|
+
--[[
|
|
88
|
+
*
|
|
89
|
+
* The one Promise of a request, which its executor registers and sends; nothing else is made for
|
|
90
|
+
* it. The timeout is a `task.delay`, cancelled when the answer arrives (none at all for
|
|
91
|
+
* `math.huge`), and cancelling the Promise drops both the request and the timer.
|
|
92
|
+
*
|
|
93
|
+
* A send that raises (an argument the engine cannot carry) raises to the caller, as it did when
|
|
94
|
+
* the request was sent ahead of its Promise, and leaves nothing pending.
|
|
95
|
+
|
|
96
|
+
]]
|
|
97
|
+
local createInvocation = function(player, requestInfo, timeout, send)
|
|
98
|
+
local _original = requestInfoClient.nextId
|
|
99
|
+
requestInfoClient.nextId += 1
|
|
100
|
+
local id = _original
|
|
101
|
+
local failed = false
|
|
102
|
+
local failure
|
|
103
|
+
local invocation = TS.Promise.new(function(resolve, reject, onCancel)
|
|
104
|
+
local timer
|
|
105
|
+
-- A player's entry lives only while requests to them are in flight, so a request that is
|
|
106
|
+
-- never answered holds the Player only until it times out or is cancelled.
|
|
107
|
+
local settle = function()
|
|
108
|
+
requestInfo.requests[id] = nil
|
|
109
|
+
local _condition = player and next(requestInfo.requests) == nil
|
|
110
|
+
if _condition then
|
|
111
|
+
local _player = player
|
|
112
|
+
_condition = requestInfoServer[_player] == requestInfo
|
|
113
|
+
end
|
|
114
|
+
if _condition then
|
|
115
|
+
local _player = player
|
|
116
|
+
requestInfoServer[_player] = nil
|
|
117
|
+
end
|
|
118
|
+
if timer ~= nil then
|
|
119
|
+
task.cancel(timer)
|
|
120
|
+
timer = nil
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
requestInfo.requests[id] = function(value, rejection)
|
|
124
|
+
settle()
|
|
84
125
|
if rejection then
|
|
85
126
|
return reject(rejection)
|
|
86
127
|
end
|
|
@@ -90,40 +131,77 @@ local function createFunctionSender(options)
|
|
|
90
131
|
resolve(value)
|
|
91
132
|
end
|
|
92
133
|
end
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
134
|
+
if timeout ~= math.huge then
|
|
135
|
+
timer = task.delay(timeout, function()
|
|
136
|
+
-- Running now: the timer is spent, and a thread cannot cancel itself.
|
|
137
|
+
timer = nil
|
|
138
|
+
settle()
|
|
139
|
+
reject(NetworkingFunctionError.Timeout)
|
|
140
|
+
end)
|
|
141
|
+
end
|
|
142
|
+
onCancel(settle)
|
|
143
|
+
local sent, reason = pcall(send, id)
|
|
144
|
+
if not sent then
|
|
145
|
+
settle()
|
|
146
|
+
failed = true
|
|
147
|
+
failure = reason
|
|
148
|
+
end
|
|
98
149
|
end)
|
|
150
|
+
if failed then
|
|
151
|
+
-- Never settled, so cancelling it is silent; the raise is the caller's.
|
|
152
|
+
invocation:cancel()
|
|
153
|
+
error(failure, 0)
|
|
154
|
+
end
|
|
155
|
+
return invocation
|
|
99
156
|
end
|
|
100
157
|
return {
|
|
101
|
-
invokeServer = function(self, ...)
|
|
158
|
+
invokeServer = function(self, timeout, ...)
|
|
102
159
|
local args = { ... }
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
event:fireServer(id, unpack(args))
|
|
107
|
-
return createInvocation(nil, id, requestInfoClient)
|
|
160
|
+
return createInvocation(nil, requestInfoClient, timeout, function(id)
|
|
161
|
+
return event:fireServer(id, unpack(args))
|
|
162
|
+
end)
|
|
108
163
|
end,
|
|
109
|
-
invokeClient = function(self, player, ...)
|
|
164
|
+
invokeClient = function(self, player, timeout, ...)
|
|
110
165
|
local args = { ... }
|
|
111
166
|
local _player = player
|
|
112
|
-
local
|
|
167
|
+
local _condition = departedPlayers[_player] ~= nil
|
|
168
|
+
if not _condition then
|
|
169
|
+
_condition = hasLeft(player)
|
|
170
|
+
end
|
|
171
|
+
if _condition then
|
|
172
|
+
return TS.Promise.reject(NetworkingFunctionError.Cancelled)
|
|
173
|
+
end
|
|
174
|
+
local _player_1 = player
|
|
175
|
+
local requestInfo = requestInfoServer[_player_1]
|
|
113
176
|
if not requestInfo then
|
|
114
177
|
local _exp = player
|
|
115
178
|
requestInfo = createRequestInfo()
|
|
116
179
|
local _requestInfo = requestInfo
|
|
117
180
|
requestInfoServer[_exp] = _requestInfo
|
|
118
181
|
end
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
event:fireClient(player, id, unpack(args))
|
|
123
|
-
return createInvocation(player, id, requestInfo)
|
|
182
|
+
return createInvocation(player, requestInfo, timeout, function(id)
|
|
183
|
+
return event:fireClient(player, id, unpack(args))
|
|
184
|
+
end)
|
|
124
185
|
end,
|
|
125
186
|
}
|
|
126
187
|
end
|
|
188
|
+
--[[
|
|
189
|
+
*
|
|
190
|
+
* A player who left before this sender existed, whom its `PlayerRemoving` never saw: a player in the
|
|
191
|
+
* game is parented to `Players`. Inside `PlayerRemoving` the leaving player may still read as
|
|
192
|
+
* parented (Immediate signals), which is what `departedPlayers` is for. A Player that never was
|
|
193
|
+
* parented there (the Lune harness's) counts as present while `Players` lists it.
|
|
194
|
+
|
|
195
|
+
]]
|
|
196
|
+
function hasLeft(player)
|
|
197
|
+
local _condition = player.Parent ~= Players
|
|
198
|
+
if _condition then
|
|
199
|
+
local _exp = Players:GetPlayers()
|
|
200
|
+
local _player = player
|
|
201
|
+
_condition = not (table.find(_exp, _player) ~= nil)
|
|
202
|
+
end
|
|
203
|
+
return _condition
|
|
204
|
+
end
|
|
127
205
|
function createRequestInfo()
|
|
128
206
|
return {
|
|
129
207
|
nextId = 0,
|
|
@@ -9,7 +9,7 @@ export declare function createClientMethod(config: FunctionCreateConfiguration<u
|
|
|
9
9
|
invokeWithTimeout: (timeout: number, ...args: unknown[]) => Promise<unknown>;
|
|
10
10
|
readonly _flamework_receive?: unknown[] | undefined;
|
|
11
11
|
readonly _flamework_fn?: unknown;
|
|
12
|
-
_setCallback: (callback: (...args: never[]) => unknown) => void;
|
|
12
|
+
_setCallback: (callback: (...args: never[]) => unknown, pack: (value: unknown) => unknown) => void;
|
|
13
13
|
setCallback: (callback: (...args: unknown[]) => unknown | Promise<unknown>) => void;
|
|
14
14
|
predict: (...args: unknown[]) => Promise<unknown>;
|
|
15
15
|
};
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
-- Compiled with roblox-ts v3.0.0
|
|
2
2
|
local TS = _G[script]
|
|
3
|
-
local
|
|
4
|
-
local timeoutPromise = TS.import(script, script.Parent.Parent, "util", "timeoutPromise").timeoutPromise
|
|
3
|
+
local trimArguments = TS.import(script, script.Parent.Parent, "util", "trimArguments").trimArguments
|
|
5
4
|
local function createClientMethod(config, receiver, sender)
|
|
5
|
+
-- A method that takes an argument list trims it before spreading it, or an explicit trailing
|
|
6
|
+
-- `undefined` would lose the arguments after a gap (see `trimArguments`).
|
|
6
7
|
local method = {
|
|
7
8
|
invoke = function(self, ...)
|
|
8
9
|
local args = { ... }
|
|
9
|
-
return self:invokeWithTimeout(config.defaultTimeout, unpack(args))
|
|
10
|
+
return self:invokeWithTimeout(config.defaultTimeout, unpack(trimArguments(args)))
|
|
10
11
|
end,
|
|
11
12
|
invokeWithTimeout = function(self, timeout, ...)
|
|
12
13
|
local args = { ... }
|
|
13
14
|
local _sender = sender
|
|
14
15
|
assert(_sender, "This is not a sender remote.")
|
|
15
|
-
return
|
|
16
|
+
return sender:invokeServer(timeout, unpack(trimArguments(args)))
|
|
16
17
|
end,
|
|
17
18
|
_invoke = function(self, ...)
|
|
18
19
|
local packed = { ... }
|
|
@@ -27,22 +28,22 @@ local function createClientMethod(config, receiver, sender)
|
|
|
27
28
|
assert(_receiver, "This is not a receiver remote.")
|
|
28
29
|
receiver:setClientCallback(callback)
|
|
29
30
|
end,
|
|
30
|
-
_setCallback = function(self, callback)
|
|
31
|
+
_setCallback = function(self, callback, pack)
|
|
31
32
|
local _receiver = receiver
|
|
32
33
|
assert(_receiver, "This is not a receiver remote.")
|
|
33
|
-
receiver:setClientCallback(callback,
|
|
34
|
+
receiver:setClientCallback(callback, pack)
|
|
34
35
|
end,
|
|
35
36
|
predict = function(self, ...)
|
|
36
37
|
local args = { ... }
|
|
37
38
|
local _receiver = receiver
|
|
38
39
|
assert(_receiver, "This is not a receiver remote.")
|
|
39
|
-
return receiver:invoke(nil, unpack(args))
|
|
40
|
+
return receiver:invoke(nil, unpack(trimArguments(args)))
|
|
40
41
|
end,
|
|
41
42
|
}
|
|
42
43
|
setmetatable(method, {
|
|
43
44
|
__call = function(method, ...)
|
|
44
45
|
local args = { ... }
|
|
45
|
-
return method:invoke(unpack(args))
|
|
46
|
+
return method:invoke(unpack(trimArguments(args)))
|
|
46
47
|
end,
|
|
47
48
|
})
|
|
48
49
|
return method
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
-- Compiled with roblox-ts v3.0.0
|
|
2
2
|
local TS = _G[script]
|
|
3
3
|
local NetworkingFunctionError = TS.import(script, script.Parent.Parent, "function", "errors").NetworkingFunctionError
|
|
4
|
-
local SkipBadRequest = TS.import(script, script.Parent.Parent, "middleware", "skip").SkipBadRequest
|
|
5
4
|
local createFunctionReceiver = TS.import(script, script.Parent.Parent, "function", "createFunctionReceiver").createFunctionReceiver
|
|
6
5
|
local createFunctionSender = TS.import(script, script.Parent.Parent, "function", "createFunctionSender").createFunctionSender
|
|
7
|
-
local
|
|
6
|
+
local createGuards = TS.import(script, script.Parent.Parent, "middleware", "createGuards").createGuards
|
|
8
7
|
local Players = TS.import(script, TS.getModule(script, "@rbxts", "services")).Players
|
|
9
8
|
local getNamespaceConfig = TS.import(script, script.Parent.Parent, "util", "getNamespaceConfig").getNamespaceConfig
|
|
10
9
|
local function createGenericHandler(globalName, namespaceName, receiverPrefix, senderPrefix, metadata, config, signals, createMethod)
|
|
@@ -42,11 +41,13 @@ local function createGenericHandler(globalName, namespaceName, receiverPrefix, s
|
|
|
42
41
|
name = effectiveName,
|
|
43
42
|
globalName = globalName,
|
|
44
43
|
}
|
|
44
|
+
-- The generated guards run ahead of all user middleware; a request that fails them is answered
|
|
45
|
+
-- `BadRequest`.
|
|
46
|
+
local incomingGuards
|
|
45
47
|
if not config.disableIncomingGuards and isReceiver then
|
|
46
48
|
local guards = metadata.incoming[name]
|
|
47
49
|
assert(guards)
|
|
48
|
-
|
|
49
|
-
table.insert(incomingMiddleware, 1, _arg0)
|
|
50
|
+
incomingGuards = createGuards(name, guards[1], guards[2], networkInfo, config.warnOnInvalidGuards, signals)
|
|
50
51
|
end
|
|
51
52
|
-- A malformed serialized payload is reported like a failed guard, with no argument index.
|
|
52
53
|
local onMalformed = function(player, message)
|
|
@@ -65,9 +66,10 @@ local function createGenericHandler(globalName, namespaceName, receiverPrefix, s
|
|
|
65
66
|
local _object = {
|
|
66
67
|
namespace = globalName,
|
|
67
68
|
debugName = name,
|
|
68
|
-
id =
|
|
69
|
+
id = `{receiverPrefix}{effectiveName}`,
|
|
69
70
|
networkInfo = networkInfo,
|
|
70
71
|
incomingMiddleware = incomingMiddleware,
|
|
72
|
+
incomingGuards = incomingGuards,
|
|
71
73
|
}
|
|
72
74
|
local _left = "argsDecoder"
|
|
73
75
|
local _result_1 = metadata.incomingSerializers
|
|
@@ -75,12 +77,6 @@ local function createGenericHandler(globalName, namespaceName, receiverPrefix, s
|
|
|
75
77
|
_result_1 = _result_1[name]
|
|
76
78
|
end
|
|
77
79
|
_object[_left] = _result_1
|
|
78
|
-
local _left_1 = "resultDecoder"
|
|
79
|
-
local _result_2 = metadata.incomingResults
|
|
80
|
-
if _result_2 ~= nil then
|
|
81
|
-
_result_2 = _result_2[name]
|
|
82
|
-
end
|
|
83
|
-
_object[_left_1] = _result_2
|
|
84
80
|
_object.onMalformed = onMalformed
|
|
85
81
|
_result = createFunctionReceiver(_object)
|
|
86
82
|
else
|
|
@@ -92,7 +88,7 @@ local function createGenericHandler(globalName, namespaceName, receiverPrefix, s
|
|
|
92
88
|
local _object = {
|
|
93
89
|
namespace = globalName,
|
|
94
90
|
debugName = name,
|
|
95
|
-
id =
|
|
91
|
+
id = `{senderPrefix}{effectiveName}`,
|
|
96
92
|
networkInfo = networkInfo,
|
|
97
93
|
}
|
|
98
94
|
local _left = "resultDecoder"
|
|
@@ -5,6 +5,7 @@ local createSignalContainer = TS.import(script, script.Parent.Parent, "util", "c
|
|
|
5
5
|
local createGenericHandler = TS.import(script, script.Parent, "createGenericHandler").createGenericHandler
|
|
6
6
|
local createServerMethod = TS.import(script, script.Parent, "createServerMethod").createServerMethod
|
|
7
7
|
local createClientMethod = TS.import(script, script.Parent, "createClientMethod").createClientMethod
|
|
8
|
+
local createOnce = TS.import(script, script.Parent.Parent, "util", "createOnce").createOnce
|
|
8
9
|
local SERVER_PREFIX = "$"
|
|
9
10
|
local CLIENT_PREFIX = "@"
|
|
10
11
|
local function getDefaultConfiguration(config)
|
|
@@ -38,7 +39,9 @@ end
|
|
|
38
39
|
local function createNetworkingFunction(globalName)
|
|
39
40
|
local signals = createSignalContainer()
|
|
40
41
|
local server
|
|
41
|
-
|
|
42
|
+
-- Built once: building it waits for the server's remotes, and a thread that asks meanwhile gets the
|
|
43
|
+
-- same handler rather than a second one wired to the same remotes.
|
|
44
|
+
local client = createOnce()
|
|
42
45
|
return {
|
|
43
46
|
createServer = function(self, config, meta)
|
|
44
47
|
if RunService:IsRunning() and not RunService:IsServer() then
|
|
@@ -53,10 +56,9 @@ local function createNetworkingFunction(globalName)
|
|
|
53
56
|
if RunService:IsRunning() and not RunService:IsClient() then
|
|
54
57
|
return nil
|
|
55
58
|
end
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
end
|
|
59
|
-
return client
|
|
59
|
+
return client(function()
|
|
60
|
+
return createGenericHandler(globalName, nil, CLIENT_PREFIX, SERVER_PREFIX, meta, getDefaultConfiguration(config), signals, createClientMethod)
|
|
61
|
+
end)
|
|
60
62
|
end,
|
|
61
63
|
registerHandler = function(self, key, callback)
|
|
62
64
|
return signals:connect(key, callback)
|
|
@@ -9,7 +9,7 @@ export declare function createServerMethod(config: FunctionCreateConfiguration<u
|
|
|
9
9
|
invokeWithTimeout: (player: Player, timeout: number, ...args: unknown[]) => Promise<unknown>;
|
|
10
10
|
readonly _flamework_receive?: unknown[] | undefined;
|
|
11
11
|
readonly _flamework_fn?: unknown;
|
|
12
|
-
_setCallback: (callback: (player: Player, ...args: never[]) => unknown) => void;
|
|
12
|
+
_setCallback: (callback: (player: Player, ...args: never[]) => unknown, pack: (value: unknown) => unknown) => void;
|
|
13
13
|
setCallback: (callback: (player: Player, ...args: unknown[]) => unknown | Promise<unknown>) => void;
|
|
14
14
|
predict: (player: Player, ...args: unknown[]) => Promise<unknown>;
|
|
15
15
|
};
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
-- Compiled with roblox-ts v3.0.0
|
|
2
2
|
local TS = _G[script]
|
|
3
|
-
local
|
|
4
|
-
local timeoutPromise = TS.import(script, script.Parent.Parent, "util", "timeoutPromise").timeoutPromise
|
|
3
|
+
local trimArguments = TS.import(script, script.Parent.Parent, "util", "trimArguments").trimArguments
|
|
5
4
|
local function createServerMethod(config, receiver, sender)
|
|
5
|
+
-- A method that takes an argument list trims it before spreading it, or an explicit trailing
|
|
6
|
+
-- `undefined` would lose the arguments after a gap (see `trimArguments`).
|
|
6
7
|
local method = {
|
|
7
8
|
invoke = function(self, player, ...)
|
|
8
9
|
local args = { ... }
|
|
9
|
-
return self:invokeWithTimeout(player, config.defaultTimeout, unpack(args))
|
|
10
|
+
return self:invokeWithTimeout(player, config.defaultTimeout, unpack(trimArguments(args)))
|
|
10
11
|
end,
|
|
11
12
|
invokeWithTimeout = function(self, player, timeout, ...)
|
|
12
13
|
local args = { ... }
|
|
13
14
|
local _sender = sender
|
|
14
15
|
assert(_sender, "This is not a sender remote.")
|
|
15
|
-
return
|
|
16
|
+
return sender:invokeClient(player, timeout, unpack(trimArguments(args)))
|
|
16
17
|
end,
|
|
17
18
|
_invoke = function(self, player, ...)
|
|
18
19
|
local packed = { ... }
|
|
@@ -27,22 +28,22 @@ local function createServerMethod(config, receiver, sender)
|
|
|
27
28
|
assert(_receiver, "This is not a receiver remote.")
|
|
28
29
|
receiver:setServerCallback(callback)
|
|
29
30
|
end,
|
|
30
|
-
_setCallback = function(self, callback)
|
|
31
|
+
_setCallback = function(self, callback, pack)
|
|
31
32
|
local _receiver = receiver
|
|
32
33
|
assert(_receiver, "This is not a receiver remote.")
|
|
33
|
-
receiver:setServerCallback(callback,
|
|
34
|
+
receiver:setServerCallback(callback, pack)
|
|
34
35
|
end,
|
|
35
36
|
predict = function(self, player, ...)
|
|
36
37
|
local args = { ... }
|
|
37
38
|
local _receiver = receiver
|
|
38
39
|
assert(_receiver, "This is not a receiver remote.")
|
|
39
|
-
return receiver:invoke(player, unpack(args))
|
|
40
|
+
return receiver:invoke(player, unpack(trimArguments(args)))
|
|
40
41
|
end,
|
|
41
42
|
}
|
|
42
43
|
setmetatable(method, {
|
|
43
44
|
__call = function(method, player, ...)
|
|
44
45
|
local args = { ... }
|
|
45
|
-
return method:invoke(player, unpack(args))
|
|
46
|
+
return method:invoke(player, unpack(trimArguments(args)))
|
|
46
47
|
end,
|
|
47
48
|
})
|
|
48
49
|
return method
|