@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 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,
@@ -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(self:getPacket(message, data))
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, self:getPacket(message, data))
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,
@@ -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.5",
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
  }