@flamework-experimental/networking 2.0.0-alpha.0 → 2.0.0-alpha.2
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 +6 -0
- 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 +2 -3
- 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
|
@@ -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
|
package/out/functions/types.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
} from "../types";
|
|
13
13
|
import { FunctionNetworkingEvents } from "../handlers";
|
|
14
14
|
import { FunctionMiddleware } from "../middleware/types";
|
|
15
|
+
import { SignalConnection } from "../util/signal";
|
|
15
16
|
import { Modding } from "@flamework-experimental/core";
|
|
16
17
|
|
|
17
18
|
/**
|
|
@@ -68,8 +69,8 @@ export interface ServerReceiver<I extends unknown[], O, F = unknown> extends Raw
|
|
|
68
69
|
/** @hidden The declared function type; its return type is what the transformer packs. */
|
|
69
70
|
readonly _flamework_fn?: F;
|
|
70
71
|
|
|
71
|
-
/** @hidden Registers a callback
|
|
72
|
-
_setCallback(callback: (player: Player, ...args: never[]) => unknown): void;
|
|
72
|
+
/** @hidden Registers a callback with `pack`, which turns a successful result into `[payload, blobs?]`. */
|
|
73
|
+
_setCallback(callback: (player: Player, ...args: never[]) => unknown, pack: (value: unknown) => unknown): void;
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
export interface RawClientSender<I extends unknown[], O> {
|
|
@@ -119,8 +120,8 @@ export interface ClientReceiver<I extends unknown[], O, F = unknown> extends Raw
|
|
|
119
120
|
/** @hidden The declared function type; its return type is what the transformer packs. */
|
|
120
121
|
readonly _flamework_fn?: F;
|
|
121
122
|
|
|
122
|
-
/** @hidden Registers a callback
|
|
123
|
-
_setCallback(callback: (...args: never[]) => unknown): void;
|
|
123
|
+
/** @hidden Registers a callback with `pack`, which turns a successful result into `[payload, blobs?]`. */
|
|
124
|
+
_setCallback(callback: (...args: never[]) => unknown, pack: (value: unknown) => unknown): void;
|
|
124
125
|
}
|
|
125
126
|
|
|
126
127
|
export type ServerHandler<E, R> = NetworkingObfuscationMarker & {
|
|
@@ -199,7 +200,7 @@ export interface GlobalFunction<S, C> {
|
|
|
199
200
|
registerHandler<K extends keyof FunctionNetworkingEvents>(
|
|
200
201
|
key: K,
|
|
201
202
|
callback: FunctionNetworkingEvents[K],
|
|
202
|
-
):
|
|
203
|
+
): SignalConnection;
|
|
203
204
|
}
|
|
204
205
|
|
|
205
206
|
export interface FunctionConfiguration {
|
|
@@ -250,22 +251,19 @@ export type NamespaceMetadata<R, S> = Modding.Emit<{
|
|
|
250
251
|
incoming: IntrinsicObfuscate<{ [k in keyof Functions<R>]: IntrinsicTupleGuards<Parameters<R[k]>> }>;
|
|
251
252
|
|
|
252
253
|
outgoingIds: ObfuscateNames<keyof Functions<S>>;
|
|
253
|
-
|
|
254
|
+
// A response carries the resolved value: a `Promise<T>` result is checked as `T`.
|
|
255
|
+
outgoing: IntrinsicObfuscate<{ [k in keyof Functions<S>]: Modding.Target.Guard<Awaited<ReturnType<S[k]>>> }>;
|
|
254
256
|
|
|
255
257
|
/**
|
|
256
258
|
* Decoders, present only with `networking.serialization` on and absent for raw functions: the
|
|
257
|
-
* argument lists of requests this realm receives
|
|
258
|
-
*
|
|
259
|
-
* where they are produced.
|
|
259
|
+
* argument lists of requests this realm receives and the responses to requests it sends. Requests
|
|
260
|
+
* and results are packed inline where they are produced.
|
|
260
261
|
*/
|
|
261
262
|
incomingSerializers: IntrinsicObfuscate<{
|
|
262
263
|
[k in keyof Functions<R>]: R[k] extends NetworkRaw<unknown>
|
|
263
264
|
? undefined
|
|
264
265
|
: IntrinsicNetworkDecoder<Parameters<R[k]>>;
|
|
265
266
|
}>;
|
|
266
|
-
incomingResults: IntrinsicObfuscate<{
|
|
267
|
-
[k in keyof Functions<R>]: R[k] extends NetworkRaw<unknown> ? undefined : IntrinsicNetworkResultDecoder<R[k]>;
|
|
268
|
-
}>;
|
|
269
267
|
outgoingResults: IntrinsicObfuscate<{
|
|
270
268
|
[k in keyof Functions<S>]: S[k] extends NetworkRaw<unknown> ? undefined : IntrinsicNetworkResultDecoder<S[k]>;
|
|
271
269
|
}>;
|
package/out/handlers.d.ts
CHANGED
|
@@ -1,36 +1,35 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
1
|
+
import { NetworkInfo } from "./types";
|
|
2
|
+
|
|
3
|
+
interface BaseEvent {
|
|
4
|
+
/**
|
|
5
|
+
* The event or function that was fired.
|
|
6
|
+
*/
|
|
7
|
+
networkInfo: NetworkInfo;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface BadRequestData extends BaseEvent {
|
|
11
|
+
/**
|
|
12
|
+
* The index of the argument that was incorrect, or -1 when a serialized payload could not be decoded.
|
|
13
|
+
*/
|
|
14
|
+
argIndex: number;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The value of the argument that was incorrect, or the decoding error for a malformed payload.
|
|
18
|
+
*/
|
|
19
|
+
argValue: unknown;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface BadResponseData extends BaseEvent {
|
|
23
|
+
/**
|
|
24
|
+
* The index of the argument that was incorrect.
|
|
25
|
+
*/
|
|
26
|
+
value: unknown;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface EventNetworkingEvents {
|
|
30
|
+
onBadRequest: (player: Player, data: BadRequestData) => void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface FunctionNetworkingEvents extends EventNetworkingEvents {
|
|
34
|
+
onBadResponse: (player: Player, data: BadResponseData) => void;
|
|
35
|
+
}
|
package/out/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { GlobalFunction } from "./functions/types";
|
|
|
3
3
|
import { Skip as NetworkingSkip } from "./middleware/skip";
|
|
4
4
|
import { NetworkingFunctionError } from "./function/errors";
|
|
5
5
|
import { EventMiddleware as _EventMiddleware, FunctionMiddleware as _FunctionMiddleware } from "./middleware/types";
|
|
6
|
+
import { SignalConnection as _SignalConnection } from "./util/signal";
|
|
6
7
|
import { NetworkRaw, NetworkUnreliable } from "./types";
|
|
7
8
|
import type { Modding } from "@flamework-experimental/core";
|
|
8
9
|
export declare namespace Networking {
|
|
@@ -51,5 +52,10 @@ export declare namespace Networking {
|
|
|
51
52
|
* A function that generates an event middleware.
|
|
52
53
|
*/
|
|
53
54
|
type FunctionMiddleware<I extends readonly unknown[] = unknown[], O = void> = _FunctionMiddleware<I, O>;
|
|
55
|
+
/**
|
|
56
|
+
* What `connect` and `registerHandler` return: `Connected`, `Disconnect()`, and `Destroy()` for
|
|
57
|
+
* maids and janitors. It is networking's own, not an engine `RBXScriptConnection`.
|
|
58
|
+
*/
|
|
59
|
+
type Connection = _SignalConnection;
|
|
54
60
|
}
|
|
55
61
|
export { NetworkingFunctionError };
|
package/out/init.luau
CHANGED
|
@@ -71,6 +71,12 @@ do
|
|
|
71
71
|
* A function that generates an event middleware.
|
|
72
72
|
|
|
73
73
|
]]
|
|
74
|
+
--[[
|
|
75
|
+
*
|
|
76
|
+
* What `connect` and `registerHandler` return: `Connected`, `Disconnect()`, and `Destroy()` for
|
|
77
|
+
* maids and janitors. It is networking's own, not an engine `RBXScriptConnection`.
|
|
78
|
+
|
|
79
|
+
]]
|
|
74
80
|
end
|
|
75
81
|
return {
|
|
76
82
|
Networking = Networking,
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { t } from "@rbxts/t";
|
|
2
|
+
import { SignalContainer } from "../util/createSignalContainer";
|
|
3
|
+
import { EventNetworkingEvents } from "../handlers";
|
|
4
|
+
import { NetworkInfo } from "../types";
|
|
5
|
+
import { Guards } from "./processor";
|
|
6
|
+
/**
|
|
7
|
+
* The generated argument checks of one event or function, for the receive pipeline, which runs them
|
|
8
|
+
* ahead of all user middleware. A failure warns (with `warnOnInvalid`) and fires `onBadRequest`.
|
|
9
|
+
*/
|
|
10
|
+
export declare function createGuards(name: string, fixedParameters: t.check<unknown>[], restParameter: t.check<unknown> | undefined, networkInfo: NetworkInfo, warnOnInvalid: boolean, signals: SignalContainer<EventNetworkingEvents>): Guards;
|