@rbxts/tether 1.1.5 → 1.1.6
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 +3 -1
- package/out/builtin-middlewares.d.ts +6 -15
- package/out/builtin-middlewares.luau +54 -32
- package/out/message-emitter.luau +18 -9
- package/out/middleware.d.ts +4 -4
- package/out/middleware.luau +36 -3
- package/out/structs.d.ts +1 -1
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -52,6 +52,7 @@ Drop, delay, or modify requests
|
|
|
52
52
|
|
|
53
53
|
### Creating middleware
|
|
54
54
|
|
|
55
|
+
**Note:** These client/server middlewares can be implemented as shared middlewares. This is strictly an example.
|
|
55
56
|
#### Client
|
|
56
57
|
```ts
|
|
57
58
|
import type { ClientMiddleware } from "@rbxts/tether";
|
|
@@ -109,10 +110,11 @@ messaging.middleware
|
|
|
109
110
|
.useServer(Message.Test, [BuiltinMiddlewares.rateLimit(5)])
|
|
110
111
|
// automatically validates that the data sent through the remote matches
|
|
111
112
|
// the data associated with the message at runtime using type guards
|
|
112
|
-
.useServer(Message.Test, [BuiltinMiddlewares.
|
|
113
|
+
.useServer(Message.Test, [BuiltinMiddlewares.validate()])
|
|
113
114
|
// logs every message fired
|
|
114
115
|
.useServerGlobal([logServer()])
|
|
115
116
|
.useClientGlobal([logClient()])
|
|
117
|
+
.useSharedGlobal([BuiltinMiddlewares.debug()]); // verbosely logs every packet sent
|
|
116
118
|
.useServer(Message.Test, [incrementNumberData()]) // error! - data for Message.Test is not a number
|
|
117
119
|
.useServerGlobal([incrementNumberData()]); // error! - global data type is always 'unknown', we cannot guarantee a number
|
|
118
120
|
|
|
@@ -1,33 +1,24 @@
|
|
|
1
1
|
import { Modding } from "@flamework/core";
|
|
2
|
-
import { type SharedMiddleware
|
|
2
|
+
import { type SharedMiddleware } from "./middleware";
|
|
3
3
|
type Guard<T> = (value: unknown) => value is T;
|
|
4
4
|
export declare namespace BuiltinMiddlewares {
|
|
5
5
|
/**
|
|
6
|
-
* Creates a shared
|
|
6
|
+
* Creates a shared middleware that will drop any message that occurs within the given interval of the previous message
|
|
7
7
|
*
|
|
8
8
|
* @param interval The interval in seconds that the middleware should wait before allowing a new request
|
|
9
9
|
* @returns A middleware that will drop any message that occurs within the given interval
|
|
10
10
|
*/
|
|
11
11
|
function rateLimit(interval: number): SharedMiddleware;
|
|
12
12
|
/**
|
|
13
|
-
* Creates a
|
|
14
|
-
*
|
|
15
|
-
* If the guard fails, the middleware will drop the message
|
|
16
|
-
*
|
|
17
|
-
* @param guard The guard to use to validate the data
|
|
18
|
-
* @returns A server middleware that validates the data with the given guard
|
|
19
|
-
* @metadata macro
|
|
20
|
-
*/
|
|
21
|
-
function validateServer<T>(guard?: Guard<T> | Modding.Generic<T, "guard">): ServerMiddleware<T>;
|
|
22
|
-
/**
|
|
23
|
-
* Creates a client middleware that validates the data with the given guard (or a generated guard if none was provided)
|
|
13
|
+
* Creates a shared middleware that validates the data with the given guard (or a generated guard if none was provided)
|
|
24
14
|
*
|
|
25
15
|
* If the guard fails, the middleware will drop the message
|
|
26
16
|
*
|
|
27
17
|
* @param guard The guard to use to validate the data.
|
|
28
|
-
* @returns A
|
|
18
|
+
* @returns A shared middleware that validates the data with the given guard.
|
|
29
19
|
* @metadata macro
|
|
30
20
|
*/
|
|
31
|
-
function
|
|
21
|
+
function validate<T>(guard?: Guard<T> | Modding.Generic<T, "guard">): SharedMiddleware;
|
|
22
|
+
function debug(): SharedMiddleware;
|
|
32
23
|
}
|
|
33
24
|
export {};
|
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
-- Compiled with roblox-ts v3.0.0
|
|
2
2
|
local TS = _G[script]
|
|
3
3
|
local DropRequest = TS.import(script, script.Parent, "middleware").DropRequest
|
|
4
|
+
local RunService = TS.import(script, TS.getModule(script, "@rbxts", "services")).RunService
|
|
4
5
|
local noOp = function()
|
|
5
6
|
return function() end
|
|
6
7
|
end
|
|
7
|
-
local validationGuardGenerationFailed = function(
|
|
8
|
-
return `[@rbxts/tether]: Failed to generate guard for validate
|
|
8
|
+
local validationGuardGenerationFailed = function()
|
|
9
|
+
return `[@rbxts/tether]: Failed to generate guard for validate<T>() builtin middleware - skipping validation`
|
|
9
10
|
end
|
|
10
11
|
local guardFailed = function(message)
|
|
11
|
-
return `[@rbxts/tether]: Type validation failed for message '{tostring(message)}' - check your sent data`
|
|
12
|
+
return `[@rbxts/tether]: Type validation guard failed for message '{tostring(message)}' - check your sent data`
|
|
12
13
|
end
|
|
14
|
+
local BLOB_SIZE = 5
|
|
13
15
|
local BuiltinMiddlewares = {}
|
|
14
16
|
do
|
|
15
17
|
local _container = BuiltinMiddlewares
|
|
16
18
|
--[[
|
|
17
19
|
*
|
|
18
|
-
* Creates a shared
|
|
20
|
+
* Creates a shared middleware that will drop any message that occurs within the given interval of the previous message
|
|
19
21
|
*
|
|
20
22
|
* @param interval The interval in seconds that the middleware should wait before allowing a new request
|
|
21
23
|
* @returns A middleware that will drop any message that occurs within the given interval
|
|
@@ -35,18 +37,18 @@ do
|
|
|
35
37
|
_container.rateLimit = rateLimit
|
|
36
38
|
--[[
|
|
37
39
|
*
|
|
38
|
-
* Creates a
|
|
40
|
+
* Creates a shared middleware that validates the data with the given guard (or a generated guard if none was provided)
|
|
39
41
|
*
|
|
40
42
|
* If the guard fails, the middleware will drop the message
|
|
41
43
|
*
|
|
42
|
-
* @param guard The guard to use to validate the data
|
|
43
|
-
* @returns A
|
|
44
|
+
* @param guard The guard to use to validate the data.
|
|
45
|
+
* @returns A shared middleware that validates the data with the given guard.
|
|
44
46
|
* @metadata macro
|
|
45
47
|
|
|
46
48
|
]]
|
|
47
|
-
local function
|
|
49
|
+
local function validate(guard)
|
|
48
50
|
if guard == nil then
|
|
49
|
-
warn(validationGuardGenerationFailed(
|
|
51
|
+
warn(validationGuardGenerationFailed())
|
|
50
52
|
return noOp
|
|
51
53
|
end
|
|
52
54
|
return function(message)
|
|
@@ -59,34 +61,54 @@ do
|
|
|
59
61
|
end
|
|
60
62
|
end
|
|
61
63
|
end
|
|
62
|
-
_container.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
64
|
+
_container.validate = validate
|
|
65
|
+
local function toStringBuffer(buf)
|
|
66
|
+
local s = { "{ " }
|
|
67
|
+
do
|
|
68
|
+
local i = 0
|
|
69
|
+
local _shouldIncrement = false
|
|
70
|
+
while true do
|
|
71
|
+
if _shouldIncrement then
|
|
72
|
+
i += 1
|
|
73
|
+
else
|
|
74
|
+
_shouldIncrement = true
|
|
75
|
+
end
|
|
76
|
+
if not (i < buffer.len(buf)) then
|
|
77
|
+
break
|
|
78
|
+
end
|
|
79
|
+
local byte = buffer.readu8(buf, i)
|
|
80
|
+
local _arg0 = tostring(byte)
|
|
81
|
+
table.insert(s, _arg0)
|
|
82
|
+
if i < buffer.len(buf) - 1 then
|
|
83
|
+
table.insert(s, ", ")
|
|
84
|
+
end
|
|
85
|
+
end
|
|
78
86
|
end
|
|
87
|
+
table.insert(s, " }")
|
|
88
|
+
return table.concat(s, "")
|
|
89
|
+
end
|
|
90
|
+
local line = "------------------------------------"
|
|
91
|
+
local function debug()
|
|
79
92
|
return function(message)
|
|
80
|
-
return function(
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
93
|
+
return function(data, _, getRawData)
|
|
94
|
+
local rawData = getRawData()
|
|
95
|
+
local bufferSize = buffer.len(rawData.buffer)
|
|
96
|
+
local blobsSize = #rawData.blobs * BLOB_SIZE
|
|
97
|
+
print(line)
|
|
98
|
+
print("Packet sent to", (if RunService:IsServer() then "client" else "server") .. "!")
|
|
99
|
+
print(" - Message:", message)
|
|
100
|
+
print(" - Data:", data)
|
|
101
|
+
print(" - Raw data:")
|
|
102
|
+
print(" - Buffer:", toStringBuffer(rawData.buffer))
|
|
103
|
+
print(" - Blobs:", rawData.blobs)
|
|
104
|
+
print(" - Packet size:", bufferSize + blobsSize, "bytes")
|
|
105
|
+
print(" - Buffer:", bufferSize, "bytes")
|
|
106
|
+
print(" - Blobs:", blobsSize, "bytes")
|
|
107
|
+
print(line)
|
|
86
108
|
end
|
|
87
109
|
end
|
|
88
110
|
end
|
|
89
|
-
_container.
|
|
111
|
+
_container.debug = debug
|
|
90
112
|
end
|
|
91
113
|
return {
|
|
92
114
|
BuiltinMiddlewares = BuiltinMiddlewares,
|
package/out/message-emitter.luau
CHANGED
|
@@ -114,20 +114,23 @@ do
|
|
|
114
114
|
data = newData
|
|
115
115
|
return nil
|
|
116
116
|
end
|
|
117
|
+
local getPacket = function()
|
|
118
|
+
return self:getPacket(message, data)
|
|
119
|
+
end
|
|
117
120
|
for _, globalMiddleware in self.middleware:getServerGlobal() do
|
|
118
|
-
local result = globalMiddleware(message)(data, updateData)
|
|
121
|
+
local result = globalMiddleware(message)(data, updateData, getPacket)
|
|
119
122
|
if result == DropRequest then
|
|
120
123
|
return nil
|
|
121
124
|
end
|
|
122
125
|
end
|
|
123
126
|
for _, middleware in self.middleware:getServer(message) do
|
|
124
|
-
local result = middleware(message)(data, updateData)
|
|
127
|
+
local result = middleware(message)(data, updateData, getPacket)
|
|
125
128
|
if result == DropRequest then
|
|
126
129
|
return nil
|
|
127
130
|
end
|
|
128
131
|
end
|
|
129
132
|
local send = if unreliable then self.clientEvents.sendUnreliableServerMessage else self.clientEvents.sendServerMessage
|
|
130
|
-
send(
|
|
133
|
+
send(getPacket())
|
|
131
134
|
end
|
|
132
135
|
function MessageEmitter:emitClient(player, message, data, unreliable)
|
|
133
136
|
if unreliable == nil then
|
|
@@ -137,20 +140,23 @@ do
|
|
|
137
140
|
data = newData
|
|
138
141
|
return nil
|
|
139
142
|
end
|
|
143
|
+
local getPacket = function()
|
|
144
|
+
return self:getPacket(message, data)
|
|
145
|
+
end
|
|
140
146
|
for _, globalMiddleware in self.middleware:getClientGlobal() do
|
|
141
|
-
local result = globalMiddleware(message)(player, data, updateData)
|
|
147
|
+
local result = globalMiddleware(message)(player, data, updateData, getPacket)
|
|
142
148
|
if result == DropRequest then
|
|
143
149
|
return nil
|
|
144
150
|
end
|
|
145
151
|
end
|
|
146
152
|
for _, middleware in self.middleware:getClient(message) do
|
|
147
|
-
local result = middleware(message)(player, data, updateData)
|
|
153
|
+
local result = middleware(message)(player, data, updateData, getPacket)
|
|
148
154
|
if result == DropRequest then
|
|
149
155
|
return nil
|
|
150
156
|
end
|
|
151
157
|
end
|
|
152
158
|
local send = if unreliable then self.serverEvents.sendUnreliableClientMessage else self.serverEvents.sendClientMessage
|
|
153
|
-
send(player,
|
|
159
|
+
send(player, getPacket())
|
|
154
160
|
end
|
|
155
161
|
function MessageEmitter:emitAllClients(message, data, unreliable)
|
|
156
162
|
if unreliable == nil then
|
|
@@ -160,9 +166,12 @@ do
|
|
|
160
166
|
data = newData
|
|
161
167
|
return nil
|
|
162
168
|
end
|
|
169
|
+
local getPacket = function()
|
|
170
|
+
return self:getPacket(message, data)
|
|
171
|
+
end
|
|
163
172
|
for _, globalMiddleware in self.middleware:getClientGlobal() do
|
|
164
173
|
for _1, player in Players:GetPlayers() do
|
|
165
|
-
local result = globalMiddleware(message)(player, data, updateData)
|
|
174
|
+
local result = globalMiddleware(message)(player, data, updateData, getPacket)
|
|
166
175
|
if result == DropRequest then
|
|
167
176
|
return nil
|
|
168
177
|
end
|
|
@@ -170,7 +179,7 @@ do
|
|
|
170
179
|
end
|
|
171
180
|
for _, middleware in self.middleware:getClient(message) do
|
|
172
181
|
for _1, player in Players:GetPlayers() do
|
|
173
|
-
local result = middleware(message)(player, data, updateData)
|
|
182
|
+
local result = middleware(message)(player, data, updateData, getPacket)
|
|
174
183
|
if result == DropRequest then
|
|
175
184
|
return nil
|
|
176
185
|
end
|
|
@@ -262,7 +271,7 @@ do
|
|
|
262
271
|
end
|
|
263
272
|
function MessageEmitter:getPacket(message, data)
|
|
264
273
|
local serializer = self:getSerializer(message)
|
|
265
|
-
if serializer ~= nil then
|
|
274
|
+
if serializer ~= nil and data ~= nil then
|
|
266
275
|
return serializer.serialize({
|
|
267
276
|
message = message,
|
|
268
277
|
data = data,
|
package/out/middleware.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import type { BaseMessage } from "./structs";
|
|
1
|
+
import type { BaseMessage, SerializedPacket } from "./structs";
|
|
2
2
|
type DropRequestSymbol = symbol & {
|
|
3
3
|
_drop_req?: undefined;
|
|
4
4
|
};
|
|
5
5
|
export declare const DropRequest: DropRequestSymbol;
|
|
6
6
|
type UpdateDataFn<T> = (newData: T) => void;
|
|
7
|
-
export type ClientMiddleware<Data = unknown> = (message: BaseMessage) => (player: Player | Player[], data: Readonly<Data>, updateData: UpdateDataFn<Data
|
|
8
|
-
export type ServerMiddleware<Data = unknown> = (message: BaseMessage) => (data: Readonly<Data>, updateData: UpdateDataFn<Data
|
|
9
|
-
export type SharedMiddleware = (message: BaseMessage) => () => DropRequestSymbol | void;
|
|
7
|
+
export type ClientMiddleware<Data = unknown> = (message: BaseMessage) => (player: Player | Player[], data: Readonly<Data>, updateData: UpdateDataFn<Data>, getRawData: () => SerializedPacket) => DropRequestSymbol | void;
|
|
8
|
+
export type ServerMiddleware<Data = unknown> = (message: BaseMessage) => (data: Readonly<Data>, updateData: UpdateDataFn<Data>, getRawData: () => SerializedPacket) => DropRequestSymbol | void;
|
|
9
|
+
export type SharedMiddleware = (message: BaseMessage) => (data: unknown, updateData: UpdateDataFn<unknown>, getRawData: () => SerializedPacket) => DropRequestSymbol | void;
|
|
10
10
|
export type Middleware<Data = unknown> = ServerMiddleware<Data> & ClientMiddleware<Data>;
|
|
11
11
|
export declare class MiddlewareProvider<MessageData> {
|
|
12
12
|
private readonly clientGlobalMiddlewares;
|
package/out/middleware.luau
CHANGED
|
@@ -71,8 +71,25 @@ do
|
|
|
71
71
|
return self
|
|
72
72
|
end
|
|
73
73
|
function MiddlewareProvider:useShared(message, middlewares, order)
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
local server = middlewares
|
|
75
|
+
local _middlewares = middlewares
|
|
76
|
+
local _exp = (if type(_middlewares) == "function" then { middlewares } else middlewares)
|
|
77
|
+
-- ▼ ReadonlyArray.map ▼
|
|
78
|
+
local _newValue = table.create(#_exp)
|
|
79
|
+
local _callback = function(middleware)
|
|
80
|
+
return function(message)
|
|
81
|
+
return function(_, data, updateData, getRawData)
|
|
82
|
+
return middleware(message)(data, updateData, getRawData)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
for _k, _v in _exp do
|
|
87
|
+
_newValue[_k] = _callback(_v, _k - 1, _exp)
|
|
88
|
+
end
|
|
89
|
+
-- ▲ ReadonlyArray.map ▲
|
|
90
|
+
local client = _newValue
|
|
91
|
+
self:useServer(message, server, order)
|
|
92
|
+
self:useClient(message, client, order)
|
|
76
93
|
return self
|
|
77
94
|
end
|
|
78
95
|
function MiddlewareProvider:useClientGlobal(middlewares, order)
|
|
@@ -110,7 +127,23 @@ do
|
|
|
110
127
|
return self
|
|
111
128
|
end
|
|
112
129
|
function MiddlewareProvider:useSharedGlobal(middlewares, order)
|
|
113
|
-
|
|
130
|
+
local _middlewares = middlewares
|
|
131
|
+
local _exp = (if type(_middlewares) == "function" then { middlewares } else middlewares)
|
|
132
|
+
-- ▼ ReadonlyArray.map ▼
|
|
133
|
+
local _newValue = table.create(#_exp)
|
|
134
|
+
local _callback = function(middleware)
|
|
135
|
+
return function(message)
|
|
136
|
+
return function(_, data, updateData, getRawData)
|
|
137
|
+
return middleware(message)(data, updateData, getRawData)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
for _k, _v in _exp do
|
|
142
|
+
_newValue[_k] = _callback(_v, _k - 1, _exp)
|
|
143
|
+
end
|
|
144
|
+
-- ▲ ReadonlyArray.map ▲
|
|
145
|
+
local client = _newValue
|
|
146
|
+
self:useClientGlobal(client, order)
|
|
114
147
|
self:useServerGlobal(middlewares, order)
|
|
115
148
|
return self
|
|
116
149
|
end
|
package/out/structs.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export interface SerializedPacket {
|
|
|
10
10
|
}
|
|
11
11
|
export interface TetherPacket<Data> {
|
|
12
12
|
readonly message: DataType.u8;
|
|
13
|
-
readonly data
|
|
13
|
+
readonly data: Data;
|
|
14
14
|
}
|
|
15
15
|
export type MessageEvent = (packet: SerializedPacket) => void;
|
|
16
16
|
export type UnreliableMessageEvent = Networking.Unreliable<MessageEvent>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rbxts/tether",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.6",
|
|
4
4
|
"main": "out/init.lua",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "rbxtsc",
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"@rbxts/types": "^1.0.835",
|
|
35
35
|
"rbxts-transformer-flamework": "^1.2.4",
|
|
36
36
|
"roblox-ts": "^3.0.0",
|
|
37
|
+
"ts-toolbelt": "^9.6.0",
|
|
37
38
|
"typescript": "^5.5.3"
|
|
38
39
|
},
|
|
39
40
|
"dependencies": {
|
|
@@ -41,6 +42,7 @@
|
|
|
41
42
|
"@flamework/networking": "^1.2.4",
|
|
42
43
|
"@rbxts/destroyable": "^1.0.1",
|
|
43
44
|
"@rbxts/flamework-binary-serializer": "^0.6.0",
|
|
45
|
+
"@rbxts/flamework-meta-utils": "^1.0.4",
|
|
44
46
|
"@rbxts/services": "^1.5.5"
|
|
45
47
|
}
|
|
46
48
|
}
|