@rbxts/tether 1.2.6 → 1.2.8
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 +54 -23
- package/out/builtin-middlewares.d.ts +1 -2
- package/out/builtin-middlewares.luau +27 -34
- package/out/message-emitter.d.ts +15 -2
- package/out/message-emitter.luau +234 -163
- package/out/middleware.d.ts +9 -3
- package/out/middleware.luau +27 -12
- package/out/structs.d.ts +3 -6
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# Tether
|
|
2
|
+
|
|
2
3
|
A message-based networking solution for Roblox with automatic binary serialization and type validation.
|
|
3
4
|
|
|
4
5
|
> [!CAUTION]
|
|
@@ -7,15 +8,16 @@ A message-based networking solution for Roblox with automatic binary serializati
|
|
|
7
8
|
## Usage
|
|
8
9
|
|
|
9
10
|
### In `shared/messaging.ts`
|
|
11
|
+
|
|
10
12
|
```ts
|
|
11
|
-
import type { DataType } from "@rbxts/flamework-binary-serializer";
|
|
12
13
|
import { MessageEmitter } from "@rbxts/tether";
|
|
14
|
+
import type { DataType } from "@rbxts/flamework-binary-serializer";
|
|
13
15
|
|
|
14
16
|
export const messaging = MessageEmitter.create<MessageData>();
|
|
15
17
|
|
|
16
18
|
export const enum Message {
|
|
17
19
|
Test,
|
|
18
|
-
Packed
|
|
20
|
+
Packed,
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
export interface MessageData {
|
|
@@ -40,37 +42,43 @@ export interface MessageData {
|
|
|
40
42
|
> Every single message kind must implement an interface for it's data (in the example that would be the object types in `MessageData`). Message serialization (as well as your message itself) will not work if you don't do this.
|
|
41
43
|
|
|
42
44
|
### Server
|
|
45
|
+
|
|
43
46
|
```ts
|
|
44
47
|
import { Message, messaging } from "shared/messaging";
|
|
45
48
|
|
|
46
|
-
messaging.server.on(Message.Test, (player, data) =>
|
|
49
|
+
messaging.server.on(Message.Test, (player, data) =>
|
|
50
|
+
print(player, "sent data:", data)
|
|
51
|
+
);
|
|
47
52
|
```
|
|
48
53
|
|
|
49
54
|
### Client
|
|
55
|
+
|
|
50
56
|
```ts
|
|
51
57
|
import { Message, messaging } from "shared/messaging";
|
|
52
58
|
|
|
53
59
|
messaging.server.emit(Message.Test, {
|
|
54
60
|
foo: "bar",
|
|
55
|
-
n: 69
|
|
61
|
+
n: 69,
|
|
56
62
|
});
|
|
57
63
|
```
|
|
58
64
|
|
|
59
65
|
## Simulated Remote Functions
|
|
66
|
+
|
|
60
67
|
Tether does not directly use RemoteFunctions since it's based on the MessageEmitter structure. However I have created a small framework to simulate remote functions, as shown below.
|
|
61
68
|
|
|
62
69
|
For each function you will need two messages. One to invoke the function, and one to send the return value back (which is done automatically).
|
|
63
70
|
|
|
64
71
|
### In `shared/messaging.ts`
|
|
72
|
+
|
|
65
73
|
```ts
|
|
66
|
-
import type { DataType } from "@rbxts/flamework-binary-serializer";
|
|
67
74
|
import { MessageEmitter } from "@rbxts/tether";
|
|
75
|
+
import type { DataType } from "@rbxts/flamework-binary-serializer";
|
|
68
76
|
|
|
69
77
|
export const messaging = MessageEmitter.create<MessageData>();
|
|
70
78
|
|
|
71
79
|
export const enum Message {
|
|
72
80
|
Increment,
|
|
73
|
-
IncrementReturn
|
|
81
|
+
IncrementReturn,
|
|
74
82
|
}
|
|
75
83
|
|
|
76
84
|
export interface MessageData {
|
|
@@ -80,13 +88,19 @@ export interface MessageData {
|
|
|
80
88
|
```
|
|
81
89
|
|
|
82
90
|
### Server
|
|
91
|
+
|
|
83
92
|
```ts
|
|
84
93
|
import { Message, messaging } from "shared/messaging";
|
|
85
94
|
|
|
86
|
-
messaging.server.setCallback(
|
|
95
|
+
messaging.server.setCallback(
|
|
96
|
+
Message.Increment,
|
|
97
|
+
Message.IncrementReturn,
|
|
98
|
+
(_, n) => n + 1
|
|
99
|
+
);
|
|
87
100
|
```
|
|
88
101
|
|
|
89
102
|
### Client
|
|
103
|
+
|
|
90
104
|
```ts
|
|
91
105
|
import { Message, messaging } from "shared/messaging";
|
|
92
106
|
|
|
@@ -96,81 +110,98 @@ messaging.server
|
|
|
96
110
|
|
|
97
111
|
// or use await style
|
|
98
112
|
async function main(): Promise<void> {
|
|
99
|
-
const value = await messaging.server.invoke(
|
|
100
|
-
|
|
113
|
+
const value = await messaging.server.invoke(
|
|
114
|
+
Message.Increment,
|
|
115
|
+
Message.IncrementReturn,
|
|
116
|
+
69
|
|
117
|
+
);
|
|
118
|
+
print(value); // 70
|
|
101
119
|
}
|
|
102
120
|
|
|
103
121
|
main();
|
|
104
122
|
```
|
|
105
123
|
|
|
106
124
|
## Middleware
|
|
125
|
+
|
|
107
126
|
Drop, delay, or modify requests
|
|
108
127
|
|
|
109
128
|
### Creating middleware
|
|
110
129
|
|
|
111
130
|
**Note:** These client/server middlewares can be implemented as shared middlewares. This is strictly an example.
|
|
131
|
+
|
|
112
132
|
#### Client
|
|
133
|
+
|
|
113
134
|
```ts
|
|
114
135
|
import type { ClientMiddleware } from "@rbxts/tether";
|
|
115
136
|
|
|
116
137
|
export function logClient(): ClientMiddleware {
|
|
117
|
-
return
|
|
138
|
+
return (player, ctx) =>
|
|
139
|
+
print(
|
|
140
|
+
`[LOG]: Sent message '${ctx.message}' to player ${player} with data:`,
|
|
141
|
+
ctx.data
|
|
142
|
+
);
|
|
118
143
|
}
|
|
119
144
|
```
|
|
120
145
|
|
|
121
146
|
#### Server
|
|
147
|
+
|
|
122
148
|
```ts
|
|
123
149
|
import type { ServerMiddleware } from "@rbxts/tether";
|
|
124
150
|
|
|
125
151
|
export function logServer(): ServerMiddleware {
|
|
126
|
-
return
|
|
152
|
+
return (ctx) =>
|
|
153
|
+
print(
|
|
154
|
+
`[LOG]: Sent message '${ctx.message}' to server with data:`,
|
|
155
|
+
ctx.data
|
|
156
|
+
);
|
|
127
157
|
}
|
|
128
158
|
```
|
|
129
159
|
|
|
130
160
|
#### Shared
|
|
161
|
+
|
|
131
162
|
```ts
|
|
132
163
|
import { type SharedMiddleware, DropRequest } from "@rbxts/tether";
|
|
133
164
|
|
|
134
165
|
export function rateLimit(interval: number): SharedMiddleware {
|
|
135
166
|
let lastRequest = 0;
|
|
136
167
|
|
|
137
|
-
return
|
|
138
|
-
()
|
|
139
|
-
if (os.clock() - lastRequest < interval)
|
|
140
|
-
return DropRequest;
|
|
168
|
+
return () => {
|
|
169
|
+
if (os.clock() - lastRequest < interval) return DropRequest;
|
|
141
170
|
|
|
142
|
-
|
|
143
|
-
|
|
171
|
+
lastRequest = os.clock();
|
|
172
|
+
};
|
|
144
173
|
}
|
|
145
174
|
```
|
|
146
175
|
|
|
147
176
|
#### Transforming data
|
|
177
|
+
|
|
148
178
|
```ts
|
|
149
179
|
import type { ServerMiddleware } from "@rbxts/tether";
|
|
150
180
|
|
|
151
181
|
export function incrementNumberData(): ServerMiddleware<number> {
|
|
152
182
|
// sets the data to be used by the any subsequent middlewares as well as sent through the remote
|
|
153
|
-
return (
|
|
183
|
+
return ({ data, updateData }) => updateData(data + 1);
|
|
154
184
|
}
|
|
155
185
|
```
|
|
156
186
|
|
|
157
187
|
### Using middleware
|
|
188
|
+
|
|
158
189
|
```ts
|
|
159
|
-
import type { DataType } from "@rbxts/flamework-binary-serializer";
|
|
160
190
|
import { MessageEmitter, BuiltinMiddlewares } from "@rbxts/tether";
|
|
191
|
+
import type { DataType } from "@rbxts/flamework-binary-serializer";
|
|
161
192
|
|
|
162
193
|
export const messaging = MessageEmitter.create<MessageData>();
|
|
163
194
|
messaging.middleware
|
|
164
195
|
// only allows requests to the server every 5 seconds,
|
|
165
196
|
// drops any requests that occur within 5 seconds of each other
|
|
166
|
-
.useServer(Message.Test, BuiltinMiddlewares.rateLimit(5))
|
|
197
|
+
.useServer(Message.Test, BuiltinMiddlewares.rateLimit(5))
|
|
167
198
|
// will be just one byte!
|
|
168
|
-
.useShared(Message.Packed,
|
|
199
|
+
.useShared(Message.Packed, ctx => print("Packed object size:", buffer.len(ctx.getRawData().buffer)));
|
|
169
200
|
// logs every message fired
|
|
170
201
|
.useServerGlobal(logServer())
|
|
171
202
|
.useClientGlobal(logClient())
|
|
172
203
|
.useSharedGlobal(BuiltinMiddlewares.debug()); // verbosely logs every packet sent
|
|
173
|
-
.useServer(Message.Test, incrementNumberData()) // error! - data for Message.Test is not a number
|
|
204
|
+
.useServer(Message.Test, incrementNumberData()) // error! - data for Message.Test is not a number
|
|
174
205
|
.useServerGlobal(incrementNumberData()); // error! - global data type is always 'unknown', we cannot guarantee a number
|
|
175
206
|
|
|
176
207
|
export const enum Message {
|
|
@@ -194,4 +225,4 @@ export interface MessageData {
|
|
|
194
225
|
readonly boolean8: boolean;
|
|
195
226
|
}>;
|
|
196
227
|
}
|
|
197
|
-
```
|
|
228
|
+
```
|
|
@@ -2,7 +2,6 @@ import { Modding } from "@flamework/core";
|
|
|
2
2
|
import type { SerializerMetadata } from "@rbxts/flamework-binary-serializer";
|
|
3
3
|
import type { Any } from "ts-toolbelt";
|
|
4
4
|
import { type SharedMiddleware } from "./middleware";
|
|
5
|
-
import type { TetherPacket } from "./structs";
|
|
6
5
|
export declare namespace BuiltinMiddlewares {
|
|
7
6
|
/**
|
|
8
7
|
* Creates a shared middleware that will simulate a ping of the given amount when a message is sent
|
|
@@ -39,5 +38,5 @@ export declare namespace BuiltinMiddlewares {
|
|
|
39
38
|
* @returns A shared middleware that will log a message whenever a message is sent.
|
|
40
39
|
* @metadata macro
|
|
41
40
|
*/
|
|
42
|
-
function debug<T>(schema?: Modding.Many<Any.Equals<T, unknown> extends 1 ? undefined : SerializerMetadata<
|
|
41
|
+
function debug<T>(schema?: Modding.Many<Any.Equals<T, unknown> extends 1 ? undefined : SerializerMetadata<T>>): SharedMiddleware<T>;
|
|
43
42
|
}
|
|
@@ -18,10 +18,8 @@ do
|
|
|
18
18
|
]]
|
|
19
19
|
local function simulatePing(pingInMs)
|
|
20
20
|
return function()
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return nil
|
|
24
|
-
end
|
|
21
|
+
task.wait(pingInMs / 1000)
|
|
22
|
+
return nil
|
|
25
23
|
end
|
|
26
24
|
end
|
|
27
25
|
_container.simulatePing = simulatePing
|
|
@@ -38,13 +36,11 @@ do
|
|
|
38
36
|
if throwError == nil then
|
|
39
37
|
throwError = true
|
|
40
38
|
end
|
|
41
|
-
return function(
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if
|
|
46
|
-
return if throwError then error(`[@rbxts/tether]: Message '{message}' exceeded maximum packet size of {maxBytes} bytes`) else DropRequest
|
|
47
|
-
end
|
|
39
|
+
return function(ctx)
|
|
40
|
+
local rawData = ctx.getRawData()
|
|
41
|
+
local totalSize = buffer.len(rawData.buffer) + #rawData.blobs * BLOB_SIZE
|
|
42
|
+
if totalSize > maxBytes then
|
|
43
|
+
return if throwError then error(`[@rbxts/tether]: Message '{ctx.message}' exceeded maximum packet size of {maxBytes} bytes`) else DropRequest
|
|
48
44
|
end
|
|
49
45
|
end
|
|
50
46
|
end
|
|
@@ -60,12 +56,10 @@ do
|
|
|
60
56
|
local function rateLimit(interval)
|
|
61
57
|
local lastRequest = 0
|
|
62
58
|
return function()
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
return DropRequest
|
|
66
|
-
end
|
|
67
|
-
lastRequest = os.clock()
|
|
59
|
+
if os.clock() - lastRequest < interval then
|
|
60
|
+
return DropRequest
|
|
68
61
|
end
|
|
62
|
+
lastRequest = os.clock()
|
|
69
63
|
end
|
|
70
64
|
end
|
|
71
65
|
_container.rateLimit = rateLimit
|
|
@@ -86,24 +80,23 @@ do
|
|
|
86
80
|
|
|
87
81
|
]]
|
|
88
82
|
local function debug(schema)
|
|
89
|
-
return function(
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
end
|
|
83
|
+
return function(_param)
|
|
84
|
+
local message = _param.message
|
|
85
|
+
local data = _param.data
|
|
86
|
+
local getRawData = _param.getRawData
|
|
87
|
+
local rawData = getRawData()
|
|
88
|
+
local bufferSize = buffer.len(rawData.buffer)
|
|
89
|
+
local blobsSize = #rawData.blobs * BLOB_SIZE
|
|
90
|
+
local schemaString = if schema ~= nil then " " .. table.concat(string.split(repr(schema[1], {
|
|
91
|
+
pretty = true,
|
|
92
|
+
}), "\n"), "\n ") else "unknown"
|
|
93
|
+
local text = { "\n", horizontalLine, "\n", "Packet sent to ", (if RunService:IsServer() then "client" else "server"), "!\n", " - Message: ", message, "\n", " - Data: ", repr(data, {
|
|
94
|
+
pretty = true,
|
|
95
|
+
}), "\n", " - Raw data:\n", " - Buffer: ", bufferToString(rawData.buffer), "\n", " - Blobs: ", repr(rawData.blobs, {
|
|
96
|
+
pretty = false,
|
|
97
|
+
robloxClassName = true,
|
|
98
|
+
}), "\n", " - Packet size: ", bufferSize + blobsSize, " bytes\n", " - Buffer: ", bufferSize, " bytes\n", " - Blobs: ", blobsSize, " bytes\n", " - Schema: ", schemaString, "\n", horizontalLine, "\n" }
|
|
99
|
+
print(table.concat(text, ""))
|
|
107
100
|
end
|
|
108
101
|
end
|
|
109
102
|
_container.debug = debug
|
package/out/message-emitter.d.ts
CHANGED
|
@@ -2,18 +2,26 @@ import { Modding } from "@flamework/core";
|
|
|
2
2
|
import Destroyable from "@rbxts/destroyable";
|
|
3
3
|
import { MiddlewareProvider } from "./middleware";
|
|
4
4
|
import type { ClientMessageCallback, ServerMessageCallback, BaseMessage, MessageEmitterMetadata, ClientMessageFunctionCallback, ServerMessageFunctionCallback } from "./structs";
|
|
5
|
+
interface MessageEmitterOptions {
|
|
6
|
+
readonly batchRemotes: boolean;
|
|
7
|
+
readonly batchRate: number;
|
|
8
|
+
}
|
|
5
9
|
export declare class MessageEmitter<MessageData> extends Destroyable {
|
|
10
|
+
private readonly options;
|
|
6
11
|
readonly middleware: MiddlewareProvider<MessageData>;
|
|
7
12
|
private readonly clientCallbacks;
|
|
8
13
|
private readonly clientFunctions;
|
|
9
14
|
private readonly serverCallbacks;
|
|
10
15
|
private readonly serverFunctions;
|
|
16
|
+
private readonly clientQueue;
|
|
17
|
+
private readonly clientBroadcastQueue;
|
|
18
|
+
private readonly serverQueue;
|
|
11
19
|
private readonly guards;
|
|
12
20
|
private readonly serializers;
|
|
13
21
|
private serverEvents;
|
|
14
22
|
private clientEvents;
|
|
15
23
|
/** @metadata macro */
|
|
16
|
-
static create<MessageData>(meta?: Modding.Many<MessageEmitterMetadata<MessageData>>): MessageEmitter<MessageData>;
|
|
24
|
+
static create<MessageData>(options?: Partial<MessageEmitterOptions>, meta?: Modding.Many<MessageEmitterMetadata<MessageData>>): MessageEmitter<MessageData>;
|
|
17
25
|
private constructor();
|
|
18
26
|
readonly server: {
|
|
19
27
|
/**
|
|
@@ -101,11 +109,15 @@ export declare class MessageEmitter<MessageData> extends Destroyable {
|
|
|
101
109
|
*/
|
|
102
110
|
setCallback: <Kind extends keyof MessageData, ReturnKind extends keyof MessageData>(message: Kind & BaseMessage, returnMessage: ReturnKind & BaseMessage, callback: ClientMessageFunctionCallback<MessageData[Kind], MessageData[ReturnKind]>) => () => void;
|
|
103
111
|
};
|
|
104
|
-
private validateData;
|
|
105
112
|
private initialize;
|
|
113
|
+
private update;
|
|
114
|
+
private runClientMiddlewares;
|
|
115
|
+
private runServerMiddlewares;
|
|
116
|
+
private validateData;
|
|
106
117
|
private onRemoteFire;
|
|
107
118
|
private executeFunctions;
|
|
108
119
|
private executeEventCallbacks;
|
|
120
|
+
private deserializeAndValidate;
|
|
109
121
|
private once;
|
|
110
122
|
private on;
|
|
111
123
|
private getPacket;
|
|
@@ -115,3 +127,4 @@ export declare class MessageEmitter<MessageData> extends Destroyable {
|
|
|
115
127
|
private createMessageSerializer;
|
|
116
128
|
private getSerializer;
|
|
117
129
|
}
|
|
130
|
+
export {};
|
package/out/message-emitter.luau
CHANGED
|
@@ -11,13 +11,18 @@ local repr = TS.import(script, TS.getModule(script, "@rbxts", "repr").out)
|
|
|
11
11
|
local _middleware = TS.import(script, script.Parent, "middleware")
|
|
12
12
|
local DropRequest = _middleware.DropRequest
|
|
13
13
|
local MiddlewareProvider = _middleware.MiddlewareProvider
|
|
14
|
-
|
|
15
|
-
local messageSerializer = createBinarySerializer({ "object_raw", { { "message", { "u8" } }, { "data", { "literal", {}, -1 } } } })
|
|
14
|
+
local Object = TS.import(script, TS.getModule(script, "@rbxts", "object-utils"))
|
|
16
15
|
local remotes = Networking.createEvent("@rbxts/tether:message-emitter@remotes")
|
|
16
|
+
local noServerListen = "[@rbxts/tether]: Cannot listen to server message from client"
|
|
17
|
+
local noClientListen = "[@rbxts/tether]: Cannot listen to client message from server"
|
|
17
18
|
local metaGenerationFailed = "[@rbxts/tether]: Failed to generate message metadata - make sure you have the Flamework transformer and are using Flamework macro-friendly types in your schemas"
|
|
18
19
|
local guardFailed = function(message, data)
|
|
19
20
|
return `[@rbxts/tether]: Type validation guard failed for message '{message}' - check your sent data\nSent data: {repr(data)}`
|
|
20
21
|
end
|
|
22
|
+
local defaultMesssageEmitterOptions = {
|
|
23
|
+
batchRemotes = true,
|
|
24
|
+
batchRate = 1 / 24,
|
|
25
|
+
}
|
|
21
26
|
local MessageEmitter
|
|
22
27
|
do
|
|
23
28
|
local super = Destroyable
|
|
@@ -32,71 +37,61 @@ do
|
|
|
32
37
|
local self = setmetatable({}, MessageEmitter)
|
|
33
38
|
return self:constructor(...) or self
|
|
34
39
|
end
|
|
35
|
-
function MessageEmitter:constructor()
|
|
40
|
+
function MessageEmitter:constructor(options)
|
|
41
|
+
if options == nil then
|
|
42
|
+
options = defaultMesssageEmitterOptions
|
|
43
|
+
end
|
|
36
44
|
super.constructor(self)
|
|
45
|
+
self.options = options
|
|
37
46
|
self.middleware = MiddlewareProvider.new()
|
|
38
47
|
self.clientCallbacks = {}
|
|
39
48
|
self.clientFunctions = {}
|
|
40
49
|
self.serverCallbacks = {}
|
|
41
50
|
self.serverFunctions = {}
|
|
51
|
+
self.clientQueue = {}
|
|
52
|
+
self.clientBroadcastQueue = {}
|
|
53
|
+
self.serverQueue = {}
|
|
42
54
|
self.guards = {}
|
|
43
55
|
self.serializers = {}
|
|
44
56
|
self.server = {
|
|
45
57
|
on = function(message, callback)
|
|
58
|
+
if RunService:IsClient() then
|
|
59
|
+
error(noServerListen)
|
|
60
|
+
end
|
|
46
61
|
return self:on(message, callback, self.serverCallbacks)
|
|
47
62
|
end,
|
|
48
63
|
once = function(message, callback)
|
|
64
|
+
if RunService:IsClient() then
|
|
65
|
+
error(noServerListen)
|
|
66
|
+
end
|
|
49
67
|
return self:once(message, callback, self.serverCallbacks)
|
|
50
68
|
end,
|
|
51
69
|
emit = function(message, data, unreliable)
|
|
52
70
|
if unreliable == nil then
|
|
53
71
|
unreliable = false
|
|
54
72
|
end
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
return nil
|
|
58
|
-
end
|
|
59
|
-
local getPacket = function()
|
|
60
|
-
return self:getPacket(message, data)
|
|
61
|
-
end
|
|
62
|
-
if not self:validateData(message, data) then
|
|
63
|
-
return nil
|
|
73
|
+
if RunService:IsServer() then
|
|
74
|
+
error("[@rbxts/tether]: Cannot emit message to server from server")
|
|
64
75
|
end
|
|
65
76
|
task.spawn(function()
|
|
66
|
-
local
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
for _, globalMiddleware in self.middleware:getServerGlobal() do
|
|
72
|
-
if not self:validateData(message, data) then
|
|
73
|
-
return nil
|
|
74
|
-
end
|
|
75
|
-
local result = globalMiddleware(message)(ctx)
|
|
76
|
-
if result == DropRequest then
|
|
77
|
-
return nil
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
for _, middleware in self.middleware:getServer(message) do
|
|
81
|
-
if not self:validateData(message, data) then
|
|
82
|
-
return nil
|
|
83
|
-
end
|
|
84
|
-
local result = middleware(message)(ctx)
|
|
85
|
-
if result == DropRequest then
|
|
86
|
-
return nil
|
|
87
|
-
end
|
|
88
|
-
end
|
|
89
|
-
if not self:validateData(message, data) then
|
|
77
|
+
local _binding = self:runServerMiddlewares(message, data)
|
|
78
|
+
local dropRequest = _binding[1]
|
|
79
|
+
local newData = _binding[2]
|
|
80
|
+
if dropRequest then
|
|
90
81
|
return nil
|
|
91
82
|
end
|
|
92
|
-
local
|
|
93
|
-
|
|
83
|
+
local _serverQueue = self.serverQueue
|
|
84
|
+
local _arg0 = { message, newData, unreliable }
|
|
85
|
+
table.insert(_serverQueue, _arg0)
|
|
94
86
|
end)
|
|
95
87
|
end,
|
|
96
88
|
invoke = TS.async(function(message, returnMessage, data, unreliable)
|
|
97
89
|
if unreliable == nil then
|
|
98
90
|
unreliable = false
|
|
99
91
|
end
|
|
92
|
+
if RunService:IsServer() then
|
|
93
|
+
error("[@rbxts/tether]: Cannot invoke server function from server")
|
|
94
|
+
end
|
|
100
95
|
local _clientFunctions = self.clientFunctions
|
|
101
96
|
local _returnMessage = returnMessage
|
|
102
97
|
if not (_clientFunctions[_returnMessage] ~= nil) then
|
|
@@ -119,6 +114,9 @@ do
|
|
|
119
114
|
return returnValue
|
|
120
115
|
end),
|
|
121
116
|
setCallback = function(message, returnMessage, callback)
|
|
117
|
+
if RunService:IsClient() then
|
|
118
|
+
error(noServerListen)
|
|
119
|
+
end
|
|
122
120
|
return self.server.on(message, function(player, data)
|
|
123
121
|
local returnValue = callback(player, data)
|
|
124
122
|
self.client.emit(player, returnMessage, returnValue)
|
|
@@ -127,54 +125,34 @@ do
|
|
|
127
125
|
}
|
|
128
126
|
self.client = {
|
|
129
127
|
on = function(message, callback)
|
|
128
|
+
if RunService:IsServer() then
|
|
129
|
+
error(noClientListen)
|
|
130
|
+
end
|
|
130
131
|
return self:on(message, callback, self.clientCallbacks)
|
|
131
132
|
end,
|
|
132
133
|
once = function(message, callback)
|
|
134
|
+
if RunService:IsServer() then
|
|
135
|
+
error(noClientListen)
|
|
136
|
+
end
|
|
133
137
|
return self:once(message, callback, self.clientCallbacks)
|
|
134
138
|
end,
|
|
135
139
|
emit = function(player, message, data, unreliable)
|
|
136
140
|
if unreliable == nil then
|
|
137
141
|
unreliable = false
|
|
138
142
|
end
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
return nil
|
|
142
|
-
end
|
|
143
|
-
local getPacket = function()
|
|
144
|
-
return self:getPacket(message, data)
|
|
145
|
-
end
|
|
146
|
-
if not self:validateData(message, data) then
|
|
147
|
-
return nil
|
|
143
|
+
if RunService:IsClient() then
|
|
144
|
+
error("[@rbxts/tether]: Cannot emit message to client from client")
|
|
148
145
|
end
|
|
149
146
|
task.spawn(function()
|
|
150
|
-
local
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
155
|
-
for _, globalMiddleware in self.middleware:getClientGlobal() do
|
|
156
|
-
if not self:validateData(message, data) then
|
|
157
|
-
return nil
|
|
158
|
-
end
|
|
159
|
-
local result = globalMiddleware(message)(player, ctx)
|
|
160
|
-
if result == DropRequest then
|
|
161
|
-
return nil
|
|
162
|
-
end
|
|
163
|
-
end
|
|
164
|
-
for _, middleware in self.middleware:getClient(message) do
|
|
165
|
-
if not self:validateData(message, data) then
|
|
166
|
-
return nil
|
|
167
|
-
end
|
|
168
|
-
local result = middleware(message)(player, ctx)
|
|
169
|
-
if result == DropRequest then
|
|
170
|
-
return nil
|
|
171
|
-
end
|
|
172
|
-
end
|
|
173
|
-
if not self:validateData(message, data) then
|
|
147
|
+
local _binding = self:runClientMiddlewares(message, data)
|
|
148
|
+
local dropRequest = _binding[1]
|
|
149
|
+
local newData = _binding[2]
|
|
150
|
+
if dropRequest then
|
|
174
151
|
return nil
|
|
175
152
|
end
|
|
176
|
-
local
|
|
177
|
-
|
|
153
|
+
local _clientQueue = self.clientQueue
|
|
154
|
+
local _arg0 = { player, message, newData, unreliable }
|
|
155
|
+
table.insert(_clientQueue, _arg0)
|
|
178
156
|
end)
|
|
179
157
|
end,
|
|
180
158
|
emitExcept = function(player, message, data, unreliable)
|
|
@@ -211,56 +189,28 @@ do
|
|
|
211
189
|
if unreliable == nil then
|
|
212
190
|
unreliable = false
|
|
213
191
|
end
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
return nil
|
|
217
|
-
end
|
|
218
|
-
local getPacket = function()
|
|
219
|
-
return self:getPacket(message, data)
|
|
220
|
-
end
|
|
221
|
-
if not self:validateData(message, data) then
|
|
222
|
-
return nil
|
|
192
|
+
if RunService:IsClient() then
|
|
193
|
+
error("[@rbxts/tether]: Cannot emit message to all clients from client")
|
|
223
194
|
end
|
|
224
195
|
task.spawn(function()
|
|
225
|
-
local
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
}
|
|
230
|
-
local players = Players:GetPlayers()
|
|
231
|
-
for _, globalMiddleware in self.middleware:getClientGlobal() do
|
|
232
|
-
for _1, player in players do
|
|
233
|
-
if not self:validateData(message, data) then
|
|
234
|
-
return nil
|
|
235
|
-
end
|
|
236
|
-
local result = globalMiddleware(message)(player, ctx)
|
|
237
|
-
if result == DropRequest then
|
|
238
|
-
return nil
|
|
239
|
-
end
|
|
240
|
-
end
|
|
241
|
-
end
|
|
242
|
-
for _, middleware in self.middleware:getClient(message) do
|
|
243
|
-
for _1, player in players do
|
|
244
|
-
if not self:validateData(message, data) then
|
|
245
|
-
return nil
|
|
246
|
-
end
|
|
247
|
-
local result = middleware(message)(player, ctx)
|
|
248
|
-
if result == DropRequest then
|
|
249
|
-
return nil
|
|
250
|
-
end
|
|
251
|
-
end
|
|
252
|
-
end
|
|
253
|
-
if not self:validateData(message, data) then
|
|
196
|
+
local _binding = self:runClientMiddlewares(message, data)
|
|
197
|
+
local dropRequest = _binding[1]
|
|
198
|
+
local newData = _binding[2]
|
|
199
|
+
if dropRequest then
|
|
254
200
|
return nil
|
|
255
201
|
end
|
|
256
|
-
local
|
|
257
|
-
|
|
202
|
+
local _clientBroadcastQueue = self.clientBroadcastQueue
|
|
203
|
+
local _arg0 = { message, newData, unreliable }
|
|
204
|
+
table.insert(_clientBroadcastQueue, _arg0)
|
|
258
205
|
end)
|
|
259
206
|
end,
|
|
260
207
|
invoke = TS.async(function(message, returnMessage, player, data, unreliable)
|
|
261
208
|
if unreliable == nil then
|
|
262
209
|
unreliable = false
|
|
263
210
|
end
|
|
211
|
+
if RunService:IsClient() then
|
|
212
|
+
error("[@rbxts/tether]: Cannot invoke client function from client")
|
|
213
|
+
end
|
|
264
214
|
local _serverFunctions = self.serverFunctions
|
|
265
215
|
local _returnMessage = returnMessage
|
|
266
216
|
if not (_serverFunctions[_returnMessage] ~= nil) then
|
|
@@ -283,6 +233,9 @@ do
|
|
|
283
233
|
return returnValue
|
|
284
234
|
end),
|
|
285
235
|
setCallback = function(message, returnMessage, callback)
|
|
236
|
+
if RunService:IsServer() then
|
|
237
|
+
error(noClientListen)
|
|
238
|
+
end
|
|
286
239
|
return self.client.on(message, function(data)
|
|
287
240
|
local returnValue = callback(data)
|
|
288
241
|
self.server.emit(returnMessage, returnValue)
|
|
@@ -301,10 +254,12 @@ do
|
|
|
301
254
|
incomingIds = { "sendServerMessage", "sendUnreliableServerMessage" },
|
|
302
255
|
incoming = {
|
|
303
256
|
sendServerMessage = { { t.interface({
|
|
257
|
+
messageBuffer = t.typeof("buffer"),
|
|
304
258
|
buffer = t.typeof("buffer"),
|
|
305
259
|
blobs = t.array(t.any),
|
|
306
260
|
}) }, nil },
|
|
307
261
|
sendUnreliableServerMessage = { { t.interface({
|
|
262
|
+
messageBuffer = t.typeof("buffer"),
|
|
308
263
|
buffer = t.typeof("buffer"),
|
|
309
264
|
blobs = t.array(t.any),
|
|
310
265
|
}) }, nil },
|
|
@@ -324,10 +279,12 @@ do
|
|
|
324
279
|
incomingIds = { "sendClientMessage", "sendUnreliableClientMessage" },
|
|
325
280
|
incoming = {
|
|
326
281
|
sendClientMessage = { { t.interface({
|
|
282
|
+
messageBuffer = t.typeof("buffer"),
|
|
327
283
|
buffer = t.typeof("buffer"),
|
|
328
284
|
blobs = t.array(t.any),
|
|
329
285
|
}) }, nil },
|
|
330
286
|
sendUnreliableClientMessage = { { t.interface({
|
|
287
|
+
messageBuffer = t.typeof("buffer"),
|
|
331
288
|
buffer = t.typeof("buffer"),
|
|
332
289
|
blobs = t.array(t.any),
|
|
333
290
|
}) }, nil },
|
|
@@ -344,8 +301,8 @@ do
|
|
|
344
301
|
})
|
|
345
302
|
end
|
|
346
303
|
end
|
|
347
|
-
function MessageEmitter:create(meta)
|
|
348
|
-
local emitter = MessageEmitter.new()
|
|
304
|
+
function MessageEmitter:create(options, meta)
|
|
305
|
+
local emitter = MessageEmitter.new(Object.assign({}, options, defaultMesssageEmitterOptions))
|
|
349
306
|
if meta == nil then
|
|
350
307
|
warn(metaGenerationFailed)
|
|
351
308
|
return emitter:initialize()
|
|
@@ -356,23 +313,12 @@ do
|
|
|
356
313
|
local numberKind = tonumber(kind)
|
|
357
314
|
emitter.guards[numberKind] = guard
|
|
358
315
|
if serializerMetadata == nil then
|
|
359
|
-
emitter.serializers[numberKind] = messageSerializer
|
|
360
316
|
continue
|
|
361
317
|
end
|
|
362
318
|
emitter:addSerializer(numberKind, serializerMetadata)
|
|
363
319
|
end
|
|
364
320
|
return emitter:initialize()
|
|
365
321
|
end
|
|
366
|
-
function MessageEmitter:validateData(message, data)
|
|
367
|
-
local _guards = self.guards
|
|
368
|
-
local _message = message
|
|
369
|
-
local guard = _guards[_message]
|
|
370
|
-
local guardPassed = guard(data)
|
|
371
|
-
if not guardPassed then
|
|
372
|
-
warn(guardFailed(message, data))
|
|
373
|
-
end
|
|
374
|
-
return guardPassed
|
|
375
|
-
end
|
|
376
322
|
function MessageEmitter:initialize()
|
|
377
323
|
if RunService:IsClient() then
|
|
378
324
|
self.janitor:Add(self.clientEvents.sendClientMessage:connect(function(serializedPacket)
|
|
@@ -389,10 +335,143 @@ do
|
|
|
389
335
|
return self:onRemoteFire(serializedPacket, player)
|
|
390
336
|
end))
|
|
391
337
|
end
|
|
338
|
+
local elapsed = 0
|
|
339
|
+
RunService.Heartbeat:Connect(function(dt)
|
|
340
|
+
elapsed += dt
|
|
341
|
+
if elapsed >= self.options.batchRate then
|
|
342
|
+
elapsed -= self.options.batchRate
|
|
343
|
+
self:update()
|
|
344
|
+
end
|
|
345
|
+
end)
|
|
392
346
|
return self
|
|
393
347
|
end
|
|
348
|
+
function MessageEmitter:update()
|
|
349
|
+
for _, _binding in self.clientQueue do
|
|
350
|
+
local player = _binding[1]
|
|
351
|
+
local message = _binding[2]
|
|
352
|
+
local data = _binding[3]
|
|
353
|
+
local unreliable = _binding[4]
|
|
354
|
+
local remote = if unreliable then self.serverEvents.sendUnreliableClientMessage else self.serverEvents.sendClientMessage
|
|
355
|
+
remote(player, self:getPacket(message, data))
|
|
356
|
+
end
|
|
357
|
+
table.clear(self.clientQueue)
|
|
358
|
+
for _, _binding in self.clientBroadcastQueue do
|
|
359
|
+
local message = _binding[1]
|
|
360
|
+
local data = _binding[2]
|
|
361
|
+
local unreliable = _binding[3]
|
|
362
|
+
local remote = if unreliable then self.serverEvents.sendUnreliableClientMessage else self.serverEvents.sendClientMessage
|
|
363
|
+
remote:broadcast(self:getPacket(message, data))
|
|
364
|
+
end
|
|
365
|
+
table.clear(self.clientBroadcastQueue)
|
|
366
|
+
for _, _binding in self.serverQueue do
|
|
367
|
+
local message = _binding[1]
|
|
368
|
+
local data = _binding[2]
|
|
369
|
+
local unreliable = _binding[3]
|
|
370
|
+
local remote = if unreliable then self.clientEvents.sendUnreliableServerMessage else self.clientEvents.sendServerMessage
|
|
371
|
+
remote(self:getPacket(message, data))
|
|
372
|
+
end
|
|
373
|
+
table.clear(self.serverQueue)
|
|
374
|
+
end
|
|
375
|
+
function MessageEmitter:runClientMiddlewares(message, data, player)
|
|
376
|
+
if not self:validateData(message, data) then
|
|
377
|
+
return { true, data }
|
|
378
|
+
end
|
|
379
|
+
local players = player or Players:GetPlayers()
|
|
380
|
+
local ctx = {
|
|
381
|
+
message = message,
|
|
382
|
+
data = data,
|
|
383
|
+
updateData = function(newData)
|
|
384
|
+
data = newData
|
|
385
|
+
return nil
|
|
386
|
+
end,
|
|
387
|
+
getRawData = function()
|
|
388
|
+
return self:getPacket(message, data)
|
|
389
|
+
end,
|
|
390
|
+
}
|
|
391
|
+
for _, globalMiddleware in self.middleware:getClientGlobal() do
|
|
392
|
+
local result = globalMiddleware(players, ctx)
|
|
393
|
+
if not self:validateData(message, data, "Invalid data after global client middleware") then
|
|
394
|
+
return { false, data }
|
|
395
|
+
end
|
|
396
|
+
if result == DropRequest then
|
|
397
|
+
self.middleware:notifyRequestDropped(message, "Global client middleware")
|
|
398
|
+
return { true, data }
|
|
399
|
+
end
|
|
400
|
+
end
|
|
401
|
+
for _, middleware in self.middleware:getClient(message) do
|
|
402
|
+
local result = middleware(players, ctx)
|
|
403
|
+
if not self:validateData(message, data, "Invalid data after client middleware") then
|
|
404
|
+
return { false, data }
|
|
405
|
+
end
|
|
406
|
+
if result == DropRequest then
|
|
407
|
+
self.middleware:notifyRequestDropped(message, "Client middleware")
|
|
408
|
+
return { true, data }
|
|
409
|
+
end
|
|
410
|
+
end
|
|
411
|
+
if not self:validateData(message, data) then
|
|
412
|
+
return { true, data }
|
|
413
|
+
end
|
|
414
|
+
return { false, data }
|
|
415
|
+
end
|
|
416
|
+
function MessageEmitter:runServerMiddlewares(message, data)
|
|
417
|
+
if not self:validateData(message, data) then
|
|
418
|
+
return { true, data }
|
|
419
|
+
end
|
|
420
|
+
local ctx = {
|
|
421
|
+
message = message,
|
|
422
|
+
data = data,
|
|
423
|
+
updateData = function(newData)
|
|
424
|
+
data = newData
|
|
425
|
+
return nil
|
|
426
|
+
end,
|
|
427
|
+
getRawData = function()
|
|
428
|
+
return self:getPacket(message, data)
|
|
429
|
+
end,
|
|
430
|
+
}
|
|
431
|
+
for _, globalMiddleware in self.middleware:getServerGlobal() do
|
|
432
|
+
if not self:validateData(message, data, "Invalid data after global server middleware") then
|
|
433
|
+
return { false, data }
|
|
434
|
+
end
|
|
435
|
+
local result = globalMiddleware(ctx)
|
|
436
|
+
if result == DropRequest then
|
|
437
|
+
self.middleware:notifyRequestDropped(message, "Global server middleware")
|
|
438
|
+
return { true, data }
|
|
439
|
+
end
|
|
440
|
+
end
|
|
441
|
+
for _, middleware in self.middleware:getServer(message) do
|
|
442
|
+
if not self:validateData(message, data, "Invalid data after server middleware") then
|
|
443
|
+
return { false, data }
|
|
444
|
+
end
|
|
445
|
+
local result = middleware(ctx)
|
|
446
|
+
if result == DropRequest then
|
|
447
|
+
self.middleware:notifyRequestDropped(message, "Server middleware")
|
|
448
|
+
return { true, data }
|
|
449
|
+
end
|
|
450
|
+
end
|
|
451
|
+
if not self:validateData(message, data) then
|
|
452
|
+
return { true, data }
|
|
453
|
+
end
|
|
454
|
+
return { false, data }
|
|
455
|
+
end
|
|
456
|
+
function MessageEmitter:validateData(message, data, requestDropReason)
|
|
457
|
+
if requestDropReason == nil then
|
|
458
|
+
requestDropReason = "Invalid data"
|
|
459
|
+
end
|
|
460
|
+
local _guards = self.guards
|
|
461
|
+
local _message = message
|
|
462
|
+
local guard = _guards[_message]
|
|
463
|
+
local guardPassed = guard(data)
|
|
464
|
+
if not guardPassed then
|
|
465
|
+
warn(guardFailed(message, data))
|
|
466
|
+
self.middleware:notifyRequestDropped(message, requestDropReason)
|
|
467
|
+
end
|
|
468
|
+
return guardPassed
|
|
469
|
+
end
|
|
394
470
|
function MessageEmitter:onRemoteFire(serializedPacket, player)
|
|
395
|
-
|
|
471
|
+
if buffer.len(serializedPacket.messageBuffer) > 1 then
|
|
472
|
+
return warn("[@rbxts/tether]: Rejected message because message buffer was larger than one byte")
|
|
473
|
+
end
|
|
474
|
+
local message = buffer.readu8(serializedPacket.messageBuffer, 0)
|
|
396
475
|
self:executeEventCallbacks(message, serializedPacket, player)
|
|
397
476
|
self:executeFunctions(message, serializedPacket)
|
|
398
477
|
end
|
|
@@ -404,18 +483,9 @@ do
|
|
|
404
483
|
if functions == nil then
|
|
405
484
|
return nil
|
|
406
485
|
end
|
|
407
|
-
local
|
|
408
|
-
local _packet = serializer
|
|
409
|
-
if _packet ~= nil then
|
|
410
|
-
_packet = _packet.deserialize(serializedPacket.buffer, serializedPacket.blobs)
|
|
411
|
-
end
|
|
412
|
-
local packet = _packet
|
|
486
|
+
local packet = self:deserializeAndValidate(message, serializedPacket)
|
|
413
487
|
for callback in functions do
|
|
414
|
-
|
|
415
|
-
if _result ~= nil then
|
|
416
|
-
_result = _result.data
|
|
417
|
-
end
|
|
418
|
-
callback(_result)
|
|
488
|
+
callback(packet)
|
|
419
489
|
end
|
|
420
490
|
end
|
|
421
491
|
function MessageEmitter:executeEventCallbacks(message, serializedPacket, player)
|
|
@@ -426,28 +496,24 @@ do
|
|
|
426
496
|
if callbacks == nil then
|
|
427
497
|
return nil
|
|
428
498
|
end
|
|
499
|
+
local packet = self:deserializeAndValidate(message, serializedPacket)
|
|
500
|
+
for callback in callbacks do
|
|
501
|
+
if isServer then
|
|
502
|
+
callback(player, packet)
|
|
503
|
+
else
|
|
504
|
+
callback(packet)
|
|
505
|
+
end
|
|
506
|
+
end
|
|
507
|
+
end
|
|
508
|
+
function MessageEmitter:deserializeAndValidate(message, serializedPacket)
|
|
429
509
|
local serializer = self:getSerializer(message)
|
|
430
510
|
local _packet = serializer
|
|
431
511
|
if _packet ~= nil then
|
|
432
512
|
_packet = _packet.deserialize(serializedPacket.buffer, serializedPacket.blobs)
|
|
433
513
|
end
|
|
434
514
|
local packet = _packet
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
local _exp = player
|
|
438
|
-
local _result = packet
|
|
439
|
-
if _result ~= nil then
|
|
440
|
-
_result = _result.data
|
|
441
|
-
end
|
|
442
|
-
callback(_exp, _result)
|
|
443
|
-
else
|
|
444
|
-
local _result = packet
|
|
445
|
-
if _result ~= nil then
|
|
446
|
-
_result = _result.data
|
|
447
|
-
end
|
|
448
|
-
callback(_result)
|
|
449
|
-
end
|
|
450
|
-
end
|
|
515
|
+
self:validateData(message, packet)
|
|
516
|
+
return packet
|
|
451
517
|
end
|
|
452
518
|
function MessageEmitter:once(message, callback, callbacksMap)
|
|
453
519
|
local destructor
|
|
@@ -484,17 +550,22 @@ do
|
|
|
484
550
|
end
|
|
485
551
|
function MessageEmitter:getPacket(message, data)
|
|
486
552
|
local serializer = self:getSerializer(message)
|
|
553
|
+
local messageBuffer = buffer.create(1)
|
|
554
|
+
buffer.writeu8(messageBuffer, 0, message)
|
|
487
555
|
if serializer == nil then
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
}
|
|
556
|
+
return {
|
|
557
|
+
messageBuffer = messageBuffer,
|
|
558
|
+
buffer = buffer.create(0),
|
|
559
|
+
blobs = {},
|
|
560
|
+
}
|
|
493
561
|
end
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
562
|
+
local _object = {
|
|
563
|
+
messageBuffer = messageBuffer,
|
|
564
|
+
}
|
|
565
|
+
for _k, _v in serializer.serialize(data) do
|
|
566
|
+
_object[_k] = _v
|
|
567
|
+
end
|
|
568
|
+
return _object
|
|
498
569
|
end
|
|
499
570
|
function MessageEmitter:addSerializer(message, meta)
|
|
500
571
|
self.serializers[message] = self:createMessageSerializer(meta)
|
package/out/middleware.d.ts
CHANGED
|
@@ -5,22 +5,28 @@ type DropRequestSymbol = symbol & {
|
|
|
5
5
|
export declare const DropRequest: DropRequestSymbol;
|
|
6
6
|
export type ClientMiddleware<Data = unknown> = {
|
|
7
7
|
_client?: void;
|
|
8
|
-
} & ((
|
|
8
|
+
} & ((player: Player | Player[], ctx: MiddlewareContext<Data>) => DropRequestSymbol | void);
|
|
9
9
|
export type ServerMiddleware<Data = unknown> = {
|
|
10
10
|
_server?: void;
|
|
11
11
|
} & SharedMiddleware<Data>;
|
|
12
|
-
export type SharedMiddleware<Data = unknown> = (
|
|
12
|
+
export type SharedMiddleware<Data = unknown> = (ctx: MiddlewareContext<Data>) => DropRequestSymbol | void;
|
|
13
13
|
export type Middleware<Data = unknown> = ServerMiddleware<Data> & ClientMiddleware<Data> & SharedMiddleware<Data>;
|
|
14
|
-
export interface MiddlewareContext<Data = unknown> {
|
|
14
|
+
export interface MiddlewareContext<Data = unknown, Message extends BaseMessage = BaseMessage> {
|
|
15
|
+
readonly message: Message;
|
|
15
16
|
readonly data: Readonly<Data>;
|
|
16
17
|
updateData: (newData: Data) => void;
|
|
17
18
|
getRawData: () => SerializedPacket;
|
|
18
19
|
}
|
|
20
|
+
type RequestDropCallback = (message: BaseMessage, reason?: string) => void;
|
|
19
21
|
export declare class MiddlewareProvider<MessageData> {
|
|
20
22
|
private readonly clientGlobalMiddlewares;
|
|
21
23
|
private readonly serverGlobalMiddlewares;
|
|
22
24
|
private readonly clientMiddlewares;
|
|
23
25
|
private readonly serverMiddlewares;
|
|
26
|
+
private readonly requestDroppedCallbacks;
|
|
27
|
+
onRequestDropped(callback: RequestDropCallback): () => void;
|
|
28
|
+
/** @hidden */
|
|
29
|
+
notifyRequestDropped(message: BaseMessage, reason?: string): void;
|
|
24
30
|
/** @hidden */
|
|
25
31
|
getClient<Kind extends keyof MessageData>(message: Kind & BaseMessage): ClientMiddleware<MessageData[Kind]>[];
|
|
26
32
|
/** @hidden */
|
package/out/middleware.luau
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
-- Compiled with roblox-ts v3.0.0
|
|
2
2
|
local DropRequest = newproxy()
|
|
3
|
+
-- TODO: middlewares upon received message
|
|
3
4
|
local MiddlewareProvider
|
|
4
5
|
do
|
|
5
6
|
MiddlewareProvider = setmetatable({}, {
|
|
@@ -17,6 +18,26 @@ do
|
|
|
17
18
|
self.serverGlobalMiddlewares = {}
|
|
18
19
|
self.clientMiddlewares = {}
|
|
19
20
|
self.serverMiddlewares = {}
|
|
21
|
+
self.requestDroppedCallbacks = {}
|
|
22
|
+
end
|
|
23
|
+
function MiddlewareProvider:onRequestDropped(callback)
|
|
24
|
+
local _requestDroppedCallbacks = self.requestDroppedCallbacks
|
|
25
|
+
local _callback = callback
|
|
26
|
+
_requestDroppedCallbacks[_callback] = true
|
|
27
|
+
return function()
|
|
28
|
+
local _requestDroppedCallbacks_1 = self.requestDroppedCallbacks
|
|
29
|
+
local _callback_1 = callback
|
|
30
|
+
-- ▼ Set.delete ▼
|
|
31
|
+
local _valueExisted = _requestDroppedCallbacks_1[_callback_1] ~= nil
|
|
32
|
+
_requestDroppedCallbacks_1[_callback_1] = nil
|
|
33
|
+
-- ▲ Set.delete ▲
|
|
34
|
+
return _valueExisted
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
function MiddlewareProvider:notifyRequestDropped(message, reason)
|
|
38
|
+
for callback in self.requestDroppedCallbacks do
|
|
39
|
+
callback(message, reason)
|
|
40
|
+
end
|
|
20
41
|
end
|
|
21
42
|
function MiddlewareProvider:getClient(message)
|
|
22
43
|
if self.clientMiddlewares[message] == nil then
|
|
@@ -77,10 +98,8 @@ do
|
|
|
77
98
|
-- ▼ ReadonlyArray.map ▼
|
|
78
99
|
local _newValue = table.create(#_exp)
|
|
79
100
|
local _callback = function(middleware)
|
|
80
|
-
return function(
|
|
81
|
-
return
|
|
82
|
-
return middleware(message)(ctx)
|
|
83
|
-
end
|
|
101
|
+
return function(_, ctx)
|
|
102
|
+
return middleware(ctx)
|
|
84
103
|
end
|
|
85
104
|
end
|
|
86
105
|
for _k, _v in _exp do
|
|
@@ -89,8 +108,7 @@ do
|
|
|
89
108
|
-- ▲ ReadonlyArray.map ▲
|
|
90
109
|
local client = _newValue
|
|
91
110
|
self:useServer(message, server, order)
|
|
92
|
-
self:useClient(message, client, order)
|
|
93
|
-
return self
|
|
111
|
+
return self:useClient(message, client, order)
|
|
94
112
|
end
|
|
95
113
|
function MiddlewareProvider:useClientGlobal(middlewares, order)
|
|
96
114
|
local globalMiddleware = self:getClientGlobal()
|
|
@@ -132,10 +150,8 @@ do
|
|
|
132
150
|
-- ▼ ReadonlyArray.map ▼
|
|
133
151
|
local _newValue = table.create(#_exp)
|
|
134
152
|
local _callback = function(middleware)
|
|
135
|
-
return function(
|
|
136
|
-
return
|
|
137
|
-
return middleware(message)(ctx)
|
|
138
|
-
end
|
|
153
|
+
return function(_, ctx)
|
|
154
|
+
return middleware(ctx)
|
|
139
155
|
end
|
|
140
156
|
end
|
|
141
157
|
for _k, _v in _exp do
|
|
@@ -144,8 +160,7 @@ do
|
|
|
144
160
|
-- ▲ ReadonlyArray.map ▲
|
|
145
161
|
local client = _newValue
|
|
146
162
|
self:useClientGlobal(client, order)
|
|
147
|
-
self:useServerGlobal(middlewares, order)
|
|
148
|
-
return self
|
|
163
|
+
return self:useServerGlobal(middlewares, order)
|
|
149
164
|
end
|
|
150
165
|
end
|
|
151
166
|
return {
|
package/out/structs.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Modding } from "@flamework/core";
|
|
2
2
|
import type { Networking } from "@flamework/networking";
|
|
3
|
-
import type {
|
|
3
|
+
import type { SerializerMetadata } from "@rbxts/flamework-binary-serializer";
|
|
4
4
|
export type MessageCallback<T = unknown> = ServerMessageCallback<T> | ClientMessageCallback<T>;
|
|
5
5
|
export type ClientMessageCallback<T = unknown> = (data: T) => void;
|
|
6
6
|
export type ClientMessageFunctionCallback<T = unknown, R = unknown> = (data: T) => R;
|
|
@@ -8,13 +8,10 @@ export type ServerMessageCallback<T = unknown> = (player: Player, data: T) => vo
|
|
|
8
8
|
export type ServerMessageFunctionCallback<T = unknown, R = unknown> = (player: Player, data: T) => R;
|
|
9
9
|
export type BaseMessage = number;
|
|
10
10
|
export interface SerializedPacket {
|
|
11
|
+
readonly messageBuffer: buffer;
|
|
11
12
|
readonly buffer: buffer;
|
|
12
13
|
readonly blobs: defined[];
|
|
13
14
|
}
|
|
14
|
-
export interface TetherPacket<Data> {
|
|
15
|
-
readonly message: DataType.u8;
|
|
16
|
-
readonly data: Data;
|
|
17
|
-
}
|
|
18
15
|
export type MessageEvent = (packet: SerializedPacket) => void;
|
|
19
16
|
export type UnreliableMessageEvent = Networking.Unreliable<MessageEvent>;
|
|
20
17
|
export interface ServerEvents {
|
|
@@ -27,7 +24,7 @@ export interface ClientEvents {
|
|
|
27
24
|
}
|
|
28
25
|
export interface MessageMetadata<MessageData, Kind extends keyof MessageData> {
|
|
29
26
|
readonly guard: Modding.Generic<MessageData[Kind], "guard">;
|
|
30
|
-
readonly serializerMetadata: MessageData[Kind] extends undefined ? undefined : Modding.Many<SerializerMetadata<
|
|
27
|
+
readonly serializerMetadata: MessageData[Kind] extends undefined ? undefined : Modding.Many<SerializerMetadata<MessageData[Kind]>>;
|
|
31
28
|
}
|
|
32
29
|
export type Guard<T = unknown> = (value: unknown) => value is T;
|
|
33
30
|
export type MessageEmitterMetadata<MessageData> = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rbxts/tether",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.8",
|
|
4
4
|
"main": "out/init.lua",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "rbxtsc",
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
"@rbxts/destroyable": "^1.0.1",
|
|
43
43
|
"@rbxts/flamework-binary-serializer": "^0.6.0",
|
|
44
44
|
"@rbxts/flamework-meta-utils": "^1.0.4",
|
|
45
|
+
"@rbxts/object-utils": "^1.0.4",
|
|
45
46
|
"@rbxts/repr": "^1.0.1",
|
|
46
47
|
"@rbxts/services": "^1.5.5",
|
|
47
48
|
"ts-toolbelt": "^9.6.0"
|