@rbxts/tether 1.1.4 → 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 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.validateServer()])
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, type ServerMiddleware, type ClientMiddleware } from "./middleware";
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 per-message middleware that will drop any message that occurs within the given interval of the previous message
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 server middleware that validates the data with the given guard (or a generated guard if none was provided)
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 client middleware that validates the data with the given guard.
18
+ * @returns A shared middleware that validates the data with the given guard.
29
19
  * @metadata macro
30
20
  */
31
- function validateClient<T>(guard?: Guard<T> | Modding.Generic<T, "guard">): ClientMiddleware<T>;
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(context)
8
- return `[@rbxts/tether]: Failed to generate guard for validate{context}<T> builtin middleware - skipping validation`
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 per-message middleware that will drop any message that occurs within the given interval of the previous message
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 server middleware that validates the data with the given guard (or a generated guard if none was provided)
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 server middleware that validates the data with the given guard
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 validateServer(guard)
49
+ local function validate(guard)
48
50
  if guard == nil then
49
- warn(validationGuardGenerationFailed("Server"))
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.validateServer = validateServer
63
- --[[
64
- *
65
- * Creates a client middleware that validates the data with the given guard (or a generated guard if none was provided)
66
- *
67
- * If the guard fails, the middleware will drop the message
68
- *
69
- * @param guard The guard to use to validate the data.
70
- * @returns A client middleware that validates the data with the given guard.
71
- * @metadata macro
72
-
73
- ]]
74
- local function validateClient(guard)
75
- if guard == nil then
76
- warn(validationGuardGenerationFailed("Client"))
77
- return noOp
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(player, data)
81
- if guard(data) then
82
- return nil
83
- end
84
- warn(guardFailed(message))
85
- return DropRequest
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.validateClient = validateClient
111
+ _container.debug = debug
90
112
  end
91
113
  return {
92
114
  BuiltinMiddlewares = BuiltinMiddlewares,
@@ -23,7 +23,6 @@ export declare class MessageEmitter<MessageData> extends Destroyable {
23
23
  * @returns A destructor function that disconnects the callback from the message
24
24
  */
25
25
  onClientMessage<Kind extends keyof MessageData>(message: Kind & BaseMessage, callback: ClientMessageCallback<MessageData[Kind]>): () => void;
26
- private on;
27
26
  /**
28
27
  * Emits a message to all connected clients
29
28
  *
@@ -50,6 +49,7 @@ export declare class MessageEmitter<MessageData> extends Destroyable {
50
49
  */
51
50
  emitAllClients<Kind extends keyof MessageData>(message: Kind & BaseMessage, data?: MessageData[Kind], unreliable?: boolean): void;
52
51
  private initialize;
52
+ private on;
53
53
  private getPacket;
54
54
  /** @metadata macro */
55
55
  private addSerializer;
@@ -106,28 +106,6 @@ do
106
106
  function MessageEmitter:onClientMessage(message, callback)
107
107
  return self:on(message, callback)
108
108
  end
109
- function MessageEmitter:on(message, callback)
110
- local callbacksMap = if RunService:IsServer() then self.serverCallbacks else self.clientCallbacks
111
- local _message = message
112
- if not (callbacksMap[_message] ~= nil) then
113
- local _message_1 = message
114
- callbacksMap[_message_1] = {}
115
- end
116
- local _message_1 = message
117
- local callbacks = callbacksMap[_message_1]
118
- local _callback = callback
119
- callbacks[_callback] = true
120
- local _message_2 = message
121
- callbacksMap[_message_2] = callbacks
122
- return function()
123
- local _callback_1 = callback
124
- -- ▼ Set.delete ▼
125
- local _valueExisted = callbacks[_callback_1] ~= nil
126
- callbacks[_callback_1] = nil
127
- -- ▲ Set.delete ▲
128
- return _valueExisted
129
- end
130
- end
131
109
  function MessageEmitter:emitServer(message, data, unreliable)
132
110
  if unreliable == nil then
133
111
  unreliable = false
@@ -136,20 +114,23 @@ do
136
114
  data = newData
137
115
  return nil
138
116
  end
117
+ local getPacket = function()
118
+ return self:getPacket(message, data)
119
+ end
139
120
  for _, globalMiddleware in self.middleware:getServerGlobal() do
140
- local result = globalMiddleware(message)(data, updateData)
121
+ local result = globalMiddleware(message)(data, updateData, getPacket)
141
122
  if result == DropRequest then
142
123
  return nil
143
124
  end
144
125
  end
145
126
  for _, middleware in self.middleware:getServer(message) do
146
- local result = middleware(message)(data, updateData)
127
+ local result = middleware(message)(data, updateData, getPacket)
147
128
  if result == DropRequest then
148
129
  return nil
149
130
  end
150
131
  end
151
132
  local send = if unreliable then self.clientEvents.sendUnreliableServerMessage else self.clientEvents.sendServerMessage
152
- send(self:getPacket(message, data))
133
+ send(getPacket())
153
134
  end
154
135
  function MessageEmitter:emitClient(player, message, data, unreliable)
155
136
  if unreliable == nil then
@@ -159,20 +140,23 @@ do
159
140
  data = newData
160
141
  return nil
161
142
  end
143
+ local getPacket = function()
144
+ return self:getPacket(message, data)
145
+ end
162
146
  for _, globalMiddleware in self.middleware:getClientGlobal() do
163
- local result = globalMiddleware(message)(player, data, updateData)
147
+ local result = globalMiddleware(message)(player, data, updateData, getPacket)
164
148
  if result == DropRequest then
165
149
  return nil
166
150
  end
167
151
  end
168
152
  for _, middleware in self.middleware:getClient(message) do
169
- local result = middleware(message)(player, data, updateData)
153
+ local result = middleware(message)(player, data, updateData, getPacket)
170
154
  if result == DropRequest then
171
155
  return nil
172
156
  end
173
157
  end
174
158
  local send = if unreliable then self.serverEvents.sendUnreliableClientMessage else self.serverEvents.sendClientMessage
175
- send(player, self:getPacket(message, data))
159
+ send(player, getPacket())
176
160
  end
177
161
  function MessageEmitter:emitAllClients(message, data, unreliable)
178
162
  if unreliable == nil then
@@ -182,9 +166,12 @@ do
182
166
  data = newData
183
167
  return nil
184
168
  end
169
+ local getPacket = function()
170
+ return self:getPacket(message, data)
171
+ end
185
172
  for _, globalMiddleware in self.middleware:getClientGlobal() do
186
173
  for _1, player in Players:GetPlayers() do
187
- local result = globalMiddleware(message)(player, data, updateData)
174
+ local result = globalMiddleware(message)(player, data, updateData, getPacket)
188
175
  if result == DropRequest then
189
176
  return nil
190
177
  end
@@ -192,7 +179,7 @@ do
192
179
  end
193
180
  for _, middleware in self.middleware:getClient(message) do
194
181
  for _1, player in Players:GetPlayers() do
195
- local result = middleware(message)(player, data, updateData)
182
+ local result = middleware(message)(player, data, updateData, getPacket)
196
183
  if result == DropRequest then
197
184
  return nil
198
185
  end
@@ -216,9 +203,17 @@ do
216
203
  return nil
217
204
  end
218
205
  local serializer = self:getSerializer(sentMessage)
219
- local packet = serializer.deserialize(serializedPacket.buffer, serializedPacket.blobs)
206
+ local _packet = serializer
207
+ if _packet ~= nil then
208
+ _packet = _packet.deserialize(serializedPacket.buffer, serializedPacket.blobs)
209
+ end
210
+ local packet = _packet
220
211
  for callback in messageCallbacks do
221
- callback(packet.data)
212
+ local _result = packet
213
+ if _result ~= nil then
214
+ _result = _result.data
215
+ end
216
+ callback(_result)
222
217
  end
223
218
  end))
224
219
  else
@@ -235,20 +230,59 @@ do
235
230
  return nil
236
231
  end
237
232
  local serializer = self:getSerializer(sentMessage)
238
- local packet = serializer.deserialize(serializedPacket.buffer, serializedPacket.blobs)
233
+ local _packet = serializer
234
+ if _packet ~= nil then
235
+ _packet = _packet.deserialize(serializedPacket.buffer, serializedPacket.blobs)
236
+ end
237
+ local packet = _packet
239
238
  for callback in messageCallbacks do
240
- callback(player, packet.data)
239
+ local _exp = player
240
+ local _result = packet
241
+ if _result ~= nil then
242
+ _result = _result.data
243
+ end
244
+ callback(_exp, _result)
241
245
  end
242
246
  end))
243
247
  end
244
248
  return self
245
249
  end
250
+ function MessageEmitter:on(message, callback)
251
+ local callbacksMap = if RunService:IsServer() then self.serverCallbacks else self.clientCallbacks
252
+ local _message = message
253
+ if not (callbacksMap[_message] ~= nil) then
254
+ local _message_1 = message
255
+ callbacksMap[_message_1] = {}
256
+ end
257
+ local _message_1 = message
258
+ local callbacks = callbacksMap[_message_1]
259
+ local _callback = callback
260
+ callbacks[_callback] = true
261
+ local _message_2 = message
262
+ callbacksMap[_message_2] = callbacks
263
+ return function()
264
+ local _callback_1 = callback
265
+ -- ▼ Set.delete ▼
266
+ local _valueExisted = callbacks[_callback_1] ~= nil
267
+ callbacks[_callback_1] = nil
268
+ -- ▲ Set.delete ▲
269
+ return _valueExisted
270
+ end
271
+ end
246
272
  function MessageEmitter:getPacket(message, data)
247
273
  local serializer = self:getSerializer(message)
248
- return serializer.serialize({
249
- message = message,
250
- data = data,
251
- })
274
+ if serializer ~= nil and data ~= nil then
275
+ return serializer.serialize({
276
+ message = message,
277
+ data = data,
278
+ })
279
+ end
280
+ local buf = buffer.create(1)
281
+ buffer.writeu8(buf, 0, message)
282
+ return {
283
+ buffer = buf,
284
+ blobs = {},
285
+ }
252
286
  end
253
287
  function MessageEmitter:addSerializer(message, meta)
254
288
  self.serializers[message] = self:createMessageSerializer(meta)
@@ -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>) => DropRequestSymbol | void;
8
- export type ServerMiddleware<Data = unknown> = (message: BaseMessage) => (data: Readonly<Data>, updateData: UpdateDataFn<Data>) => DropRequestSymbol | void;
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;
@@ -71,8 +71,25 @@ do
71
71
  return self
72
72
  end
73
73
  function MiddlewareProvider:useShared(message, middlewares, order)
74
- self:useClient(message, middlewares, order)
75
- self:useServer(message, middlewares, order)
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
- self:useClientGlobal(middlewares, order)
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?: 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.4",
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
  }