@rbxts/tether 1.0.4 → 1.0.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
@@ -1,19 +1,21 @@
1
1
  # Tether
2
- A message-based networking solution for Roblox with automatic binary serialization
2
+ A message-based networking solution for Roblox with automatic binary serialization.
3
+
4
+ ## Usage
3
5
 
4
6
  ### In `shared/messaging.ts`
5
7
  ```ts
6
8
  import type { DataType } from "@rbxts/flamework-binary-serializer";
9
+ import { MessageEmitter } from "@rbxts/tether";
7
10
 
8
- export const messageEmitter = MessageEmitter.create<MessageData>();
9
- messageEmitter.initialize();
11
+ export const messaging = MessageEmitter.create<MessageData>();
10
12
 
11
13
  export const enum Message {
12
- TEST
14
+ Test
13
15
  }
14
16
 
15
17
  export interface MessageData {
16
- [Message.TEST]: {
18
+ [Message.Test]: {
17
19
  readonly foo: string;
18
20
  readonly n: DataType.u8;
19
21
  };
@@ -25,19 +27,61 @@ export interface MessageData {
25
27
 
26
28
  ### Server
27
29
  ```ts
28
- import { Message, messageEmitter } from "shared/messaging";
30
+ import { Message, messaging } from "shared/messaging";
29
31
 
30
- messageEmitter.onServerMessage(Message.TEST, (player, data) => {
32
+ messaging.onServerMessage(Message.TEST, (player, data) => {
31
33
  print(player, "sent data:", data);
32
34
  });
33
35
  ```
34
36
 
35
37
  ### Client
36
38
  ```ts
37
- import { Message, messageEmitter } from "shared/messaging";
39
+ import { Message, messaging } from "shared/messaging";
38
40
 
39
- messageEmitter.emitServer(Message.TEST, {
41
+ messaging.emitServer(Message.TEST, {
40
42
  foo: "bar",
41
43
  n: 69
42
44
  });
45
+ ```
46
+
47
+ ## Middleware
48
+
49
+ ### Creating middleware
50
+ ```ts
51
+ import { type Middleware, DropRequest } from "@rbxts/tether";
52
+
53
+ export function rateLimit(interval: number): Middleware {
54
+ let lastRequest = 0;
55
+
56
+ return () => {
57
+ if (os.clock() - lastRequest < interval)
58
+ return DropRequest;
59
+
60
+ lastRequest = os.clock();
61
+ };
62
+ }
63
+ ```
64
+
65
+ ### Using middleware
66
+ ```ts
67
+ import type { DataType } from "@rbxts/flamework-binary-serializer";
68
+ import { MessageEmitter, BuiltinMiddlewares } from "@rbxts/tether";
69
+
70
+ export const messaging = MessageEmitter.create<MessageData>();
71
+ messaging.middleware
72
+ .useUniversal(Message.Test, [BuiltinMiddlewares.rateLimit(5)]) // only allows requests every 5 seconds, drops any
73
+ // requests that occur within 5 seconds of each other
74
+ .useClient(Message.Test, [BuiltinMiddlewares.validateClient()]); // automatically validates that data sent through the remote
75
+ // matches the data associated with the message at runtime
76
+
77
+ export const enum Message {
78
+ Test
79
+ }
80
+
81
+ export interface MessageData {
82
+ [Message.Test]: {
83
+ readonly foo: string;
84
+ readonly n: DataType.u8;
85
+ };
86
+ }
43
87
  ```
@@ -0,0 +1,30 @@
1
+ import { Modding } from "@flamework/core";
2
+ import { type UniversalMiddleware, type ServerMiddleware, type ClientMiddleware } from "./middleware";
3
+ type Guard<T> = (value: unknown) => value is T;
4
+ export declare namespace BuiltinMiddlewares {
5
+ /**
6
+ * Creates a universal middleware that will drop any message that occurs within the given interval of the previous message.
7
+ * @param interval The interval in seconds that the middleware should wait before allowing a new request.
8
+ * @returns A middleware that will drop any message that occurs within the given interval.
9
+ */
10
+ function rateLimit(interval: number): UniversalMiddleware;
11
+ /**
12
+ * Creates a server middleware that validates the data with the given guard (or a generated guard if none was provided).
13
+ * If the guard fails, the middleware will drop the message.
14
+ *
15
+ * @param guard The guard to use to validate the data.
16
+ * @returns A server middleware that validates the data with the given guard.
17
+ * @macro
18
+ */
19
+ function validateServer<T>(guard?: Guard<T> | Modding.Generic<T, "guard">): ServerMiddleware<T>;
20
+ /**
21
+ * Creates a server middleware that validates the data with the given guard (or a generated guard if none was provided).
22
+ * If the guard fails, the middleware will drop the message.
23
+ *
24
+ * @param guard The guard to use to validate the data.
25
+ * @returns A server middleware that validates the data with the given guard.
26
+ * @macro
27
+ */
28
+ function validateClient<T>(guard?: Guard<T> | Modding.Generic<T, "guard">): ClientMiddleware<T>;
29
+ }
30
+ export {};
@@ -0,0 +1,77 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local DropRequest = TS.import(script, script.Parent, "middleware").DropRequest
4
+ local noOp = function() end
5
+ local validationGuardGenerationFailed = function(context)
6
+ return `[Tether]: Failed to generate guard for validate{context}<T> builtin middleware - skipping validation`
7
+ end
8
+ local BuiltinMiddlewares = {}
9
+ do
10
+ local _container = BuiltinMiddlewares
11
+ --[[
12
+ *
13
+ * Creates a universal middleware that will drop any message that occurs within the given interval of the previous message.
14
+ * @param interval The interval in seconds that the middleware should wait before allowing a new request.
15
+ * @returns A middleware that will drop any message that occurs within the given interval.
16
+
17
+ ]]
18
+ local function rateLimit(interval)
19
+ local lastRequest = 0
20
+ return function()
21
+ if os.clock() - lastRequest < interval then
22
+ return DropRequest
23
+ end
24
+ lastRequest = os.clock()
25
+ end
26
+ end
27
+ _container.rateLimit = rateLimit
28
+ --[[
29
+ *
30
+ * Creates a server middleware that validates the data with the given guard (or a generated guard if none was provided).
31
+ * If the guard fails, the middleware will drop the message.
32
+ *
33
+ * @param guard The guard to use to validate the data.
34
+ * @returns A server middleware that validates the data with the given guard.
35
+ * @macro
36
+
37
+ ]]
38
+ local function validateServer(guard)
39
+ if guard == nil then
40
+ warn(validationGuardGenerationFailed("Server"))
41
+ return noOp
42
+ end
43
+ return function(data)
44
+ if guard(data) then
45
+ return nil
46
+ end
47
+ return DropRequest
48
+ end
49
+ end
50
+ _container.validateServer = validateServer
51
+ --[[
52
+ *
53
+ * Creates a server middleware that validates the data with the given guard (or a generated guard if none was provided).
54
+ * If the guard fails, the middleware will drop the message.
55
+ *
56
+ * @param guard The guard to use to validate the data.
57
+ * @returns A server middleware that validates the data with the given guard.
58
+ * @macro
59
+
60
+ ]]
61
+ local function validateClient(guard)
62
+ if guard == nil then
63
+ warn(validationGuardGenerationFailed("Client"))
64
+ return noOp
65
+ end
66
+ return function(data)
67
+ if guard(data) then
68
+ return nil
69
+ end
70
+ return DropRequest
71
+ end
72
+ end
73
+ _container.validateClient = validateClient
74
+ end
75
+ return {
76
+ BuiltinMiddlewares = BuiltinMiddlewares,
77
+ }
package/out/index.d.ts CHANGED
@@ -1,29 +1,3 @@
1
- import { Modding } from "@flamework/core";
2
- import { type SerializerMetadata } from "@rbxts/flamework-binary-serializer";
3
- type ClientMessageCallback<T = unknown> = (data: T) => void;
4
- type ServerMessageCallback<T = unknown> = (player: Player, data: T) => void;
5
- export declare class MessageEmitter<MessageData> {
6
- private readonly clientCallbacks;
7
- private readonly serverCallbacks;
8
- private readonly serializers;
9
- private readonly serverEvents;
10
- private readonly clientEvents;
11
- /** @metadata macro */
12
- static create<MessageData>(metaForEachMessage?: Modding.Many<{
13
- [Kind in keyof MessageData]: Modding.Many<SerializerMetadata<MessageData[Kind]>>;
14
- }>): MessageEmitter<MessageData>;
15
- private constructor();
16
- /** @metadata macro */
17
- addSerializer<Kind extends keyof MessageData>(message: Kind, meta?: Modding.Many<SerializerMetadata<MessageData[Kind]>>): void;
18
- initialize(): RBXScriptConnection;
19
- onServerMessage<Kind extends keyof MessageData>(message: Kind, callback: ServerMessageCallback<MessageData[Kind]>): () => void;
20
- onClientMessage<Kind extends keyof MessageData>(message: Kind, callback: ClientMessageCallback<MessageData[Kind]>): () => void;
21
- emitServer<Kind extends keyof MessageData>(message: Kind, data: MessageData[Kind], unreliable?: boolean): void;
22
- emitClient<Kind extends keyof MessageData>(player: Player, message: Kind, data: MessageData[Kind], unreliable?: boolean): void;
23
- emitAllClients<Kind extends keyof MessageData>(message: Kind, data: MessageData[Kind], unreliable?: boolean): void;
24
- private getPacket;
25
- /** @metadata macro */
26
- private createMessageSerializer;
27
- private getSerializer;
28
- }
29
- export {};
1
+ export { MessageEmitter } from "./message-emitter";
2
+ export { BuiltinMiddlewares } from "./builtin-middlewares";
3
+ export { DropRequest, type ClientMiddleware, type ServerMiddleware, type Middleware } from "./middleware";
package/out/init.luau CHANGED
@@ -1,212 +1,7 @@
1
1
  -- Compiled with roblox-ts v3.0.0
2
2
  local TS = _G[script]
3
- local t = TS.import(script, TS.getModule(script, "@rbxts", "t").lib.ts).t
4
- local Networking = TS.import(script, TS.getModule(script, "@flamework", "networking").out).Networking
5
- local createBinarySerializer = TS.import(script, TS.getModule(script, "@rbxts", "flamework-binary-serializer").out).createBinarySerializer
6
- local RunService = TS.import(script, TS.getModule(script, "@rbxts", "services")).RunService
7
- local GlobalEvents = Networking.createEvent("@rbxts/tether:init@GlobalEvents")
8
- local MessageEmitter
9
- do
10
- MessageEmitter = setmetatable({}, {
11
- __tostring = function()
12
- return "MessageEmitter"
13
- end,
14
- })
15
- MessageEmitter.__index = MessageEmitter
16
- function MessageEmitter.new(...)
17
- local self = setmetatable({}, MessageEmitter)
18
- return self:constructor(...) or self
19
- end
20
- function MessageEmitter:constructor()
21
- self.clientCallbacks = {}
22
- self.serverCallbacks = {}
23
- self.serializers = {}
24
- if RunService:IsServer() then
25
- self.serverEvents = GlobalEvents:createServer({}, {
26
- incomingIds = { "sendServerMessage", "sendUnreliableServerMessage" },
27
- incoming = {
28
- sendServerMessage = { { t.union(t.string, t.number), t.interface({
29
- buffer = t.typeof("buffer"),
30
- blobs = t.array(t.any),
31
- }) }, nil },
32
- sendUnreliableServerMessage = { { t.union(t.string, t.number), t.interface({
33
- buffer = t.typeof("buffer"),
34
- blobs = t.array(t.any),
35
- }) }, nil },
36
- },
37
- incomingUnreliable = {
38
- sendUnreliableServerMessage = true,
39
- },
40
- outgoingIds = { "sendClientMessage", "sendUnreliableClientMessage" },
41
- outgoingUnreliable = {
42
- sendUnreliableClientMessage = true,
43
- },
44
- namespaceIds = {},
45
- namespaces = {},
46
- })
47
- else
48
- self.clientEvents = GlobalEvents:createClient({}, {
49
- incomingIds = { "sendClientMessage", "sendUnreliableClientMessage" },
50
- incoming = {
51
- sendClientMessage = { { t.union(t.string, t.number), t.interface({
52
- buffer = t.typeof("buffer"),
53
- blobs = t.array(t.any),
54
- }) }, nil },
55
- sendUnreliableClientMessage = { { t.union(t.string, t.number), t.interface({
56
- buffer = t.typeof("buffer"),
57
- blobs = t.array(t.any),
58
- }) }, nil },
59
- },
60
- incomingUnreliable = {
61
- sendUnreliableClientMessage = true,
62
- },
63
- outgoingIds = { "sendServerMessage", "sendUnreliableServerMessage" },
64
- outgoingUnreliable = {
65
- sendUnreliableServerMessage = true,
66
- },
67
- namespaceIds = {},
68
- namespaces = {},
69
- })
70
- end
71
- end
72
- function MessageEmitter:create(metaForEachMessage)
73
- if metaForEachMessage == nil then
74
- warn("[Tether]: Failed to generate serializer metadata for MessageEmitter")
75
- end
76
- local emitter = MessageEmitter.new()
77
- if metaForEachMessage == nil then
78
- return emitter
79
- end
80
- for kind, meta in pairs(metaForEachMessage) do
81
- emitter:addSerializer(kind, meta)
82
- end
83
- return emitter
84
- end
85
- function MessageEmitter:addSerializer(message, meta)
86
- self.serializers[message] = self:createMessageSerializer(meta)
87
- end
88
- function MessageEmitter:initialize()
89
- if RunService:IsClient() then
90
- return self.clientEvents.sendClientMessage:connect(function(sentMessage, _param)
91
- local buffer = _param.buffer
92
- local blobs = _param.blobs
93
- local _clientCallbacks = self.clientCallbacks
94
- local _sentMessage = sentMessage
95
- local _condition = _clientCallbacks[_sentMessage]
96
- if _condition == nil then
97
- _condition = {}
98
- end
99
- local messageCallbacks = _condition
100
- if #messageCallbacks == 0 then
101
- return nil
102
- end
103
- local serializer = self:getSerializer(sentMessage)
104
- local data = serializer.deserialize(buffer, blobs)
105
- for _, callback in messageCallbacks do
106
- callback(data)
107
- end
108
- end)
109
- else
110
- return self.serverEvents.sendServerMessage:connect(function(player, sentMessage, _param)
111
- local buffer = _param.buffer
112
- local blobs = _param.blobs
113
- local _serverCallbacks = self.serverCallbacks
114
- local _sentMessage = sentMessage
115
- local _condition = _serverCallbacks[_sentMessage]
116
- if _condition == nil then
117
- _condition = {}
118
- end
119
- local messageCallbacks = _condition
120
- if #messageCallbacks == 0 then
121
- return nil
122
- end
123
- local serializer = self:getSerializer(sentMessage)
124
- local data = serializer.deserialize(buffer, blobs)
125
- for _, callback in messageCallbacks do
126
- callback(player, data)
127
- end
128
- end)
129
- end
130
- end
131
- function MessageEmitter:onServerMessage(message, callback)
132
- local _serverCallbacks = self.serverCallbacks
133
- local _message = message
134
- if not (_serverCallbacks[_message] ~= nil) then
135
- local _serverCallbacks_1 = self.serverCallbacks
136
- local _message_1 = message
137
- _serverCallbacks_1[_message_1] = {}
138
- end
139
- local _serverCallbacks_1 = self.serverCallbacks
140
- local _message_1 = message
141
- local callbacks = _serverCallbacks_1[_message_1]
142
- local _callback = callback
143
- table.insert(callbacks, _callback)
144
- local _serverCallbacks_2 = self.serverCallbacks
145
- local _message_2 = message
146
- _serverCallbacks_2[_message_2] = callbacks
147
- return function()
148
- local _callback_1 = callback
149
- local _arg0 = (table.find(callbacks, _callback_1) or 0) - 1
150
- return table.remove(callbacks, _arg0 + 1)
151
- end
152
- end
153
- function MessageEmitter:onClientMessage(message, callback)
154
- local _clientCallbacks = self.clientCallbacks
155
- local _message = message
156
- if not (_clientCallbacks[_message] ~= nil) then
157
- local _clientCallbacks_1 = self.clientCallbacks
158
- local _message_1 = message
159
- _clientCallbacks_1[_message_1] = {}
160
- end
161
- local _clientCallbacks_1 = self.clientCallbacks
162
- local _message_1 = message
163
- local callbacks = _clientCallbacks_1[_message_1]
164
- local _callback = callback
165
- table.insert(callbacks, _callback)
166
- local _clientCallbacks_2 = self.clientCallbacks
167
- local _message_2 = message
168
- _clientCallbacks_2[_message_2] = callbacks
169
- return function()
170
- local _callback_1 = callback
171
- local _arg0 = (table.find(callbacks, _callback_1) or 0) - 1
172
- return table.remove(callbacks, _arg0 + 1)
173
- end
174
- end
175
- function MessageEmitter:emitServer(message, data, unreliable)
176
- if unreliable == nil then
177
- unreliable = false
178
- end
179
- local send = if unreliable then self.clientEvents.sendUnreliableServerMessage else self.clientEvents.sendServerMessage
180
- send(message, self:getPacket(message, data))
181
- end
182
- function MessageEmitter:emitClient(player, message, data, unreliable)
183
- if unreliable == nil then
184
- unreliable = false
185
- end
186
- local send = if unreliable then self.serverEvents.sendUnreliableClientMessage else self.serverEvents.sendClientMessage
187
- send(player, message, self:getPacket(message, data))
188
- end
189
- function MessageEmitter:emitAllClients(message, data, unreliable)
190
- if unreliable == nil then
191
- unreliable = false
192
- end
193
- local send = if unreliable then self.serverEvents.sendUnreliableClientMessage else self.serverEvents.sendClientMessage
194
- send:broadcast(message, self:getPacket(message, data))
195
- end
196
- function MessageEmitter:getPacket(message, data, unreliable)
197
- if unreliable == nil then
198
- unreliable = false
199
- end
200
- local serializer = self:getSerializer(message)
201
- return serializer.serialize(data)
202
- end
203
- function MessageEmitter:createMessageSerializer(meta)
204
- return createBinarySerializer(meta)
205
- end
206
- function MessageEmitter:getSerializer(message)
207
- return self.serializers[tostring(message)]
208
- end
209
- end
210
- return {
211
- MessageEmitter = MessageEmitter,
212
- }
3
+ local exports = {}
4
+ exports.MessageEmitter = TS.import(script, script, "message-emitter").MessageEmitter
5
+ exports.BuiltinMiddlewares = TS.import(script, script, "builtin-middlewares").BuiltinMiddlewares
6
+ exports.DropRequest = TS.import(script, script, "middleware").DropRequest
7
+ return exports
@@ -0,0 +1,59 @@
1
+ import { Modding } from "@flamework/core";
2
+ import { type SerializerMetadata } from "@rbxts/flamework-binary-serializer";
3
+ import Destroyable from "@rbxts/destroyable";
4
+ import { MiddlewareProvider } from "./middleware";
5
+ import type { ClientMessageCallback, ServerMessageCallback } from "./structs";
6
+ export declare class MessageEmitter<MessageData> extends Destroyable {
7
+ readonly middleware: MiddlewareProvider<MessageData>;
8
+ private readonly clientCallbacks;
9
+ private readonly serverCallbacks;
10
+ private serializers;
11
+ private serverEvents;
12
+ private clientEvents;
13
+ /** @metadata macro */
14
+ static create<MessageData>(metaForEachMessage?: Modding.Many<{
15
+ [Kind in keyof MessageData]: MessageData[Kind] extends undefined ? undefined : Modding.Many<SerializerMetadata<MessageData[Kind]>>;
16
+ }>): MessageEmitter<MessageData>;
17
+ private constructor();
18
+ /**.
19
+ * @returns A destructor function that disconnects the callback from the message.
20
+ */
21
+ onServerMessage<Kind extends keyof MessageData>(message: Kind, callback: ServerMessageCallback<MessageData[Kind]>): () => void;
22
+ /**.
23
+ * @returns A destructor function that disconnects the callback from the message.
24
+ */
25
+ onClientMessage<Kind extends keyof MessageData>(message: Kind, callback: ClientMessageCallback<MessageData[Kind]>): () => void;
26
+ private on;
27
+ /**
28
+ * Emits a message to all connected clients.
29
+ *
30
+ * @param message - The message kind to be sent.
31
+ * @param data - The data associated with the message.
32
+ * @param unreliable - Optional flag indicating if the message should be sent unreliably.
33
+ */
34
+ emitServer<Kind extends keyof MessageData>(message: Kind, data?: MessageData[Kind], unreliable?: boolean): void;
35
+ /**
36
+ * Emits a message to a specific client.
37
+ *
38
+ * @param player - The player to whom the message is sent.
39
+ * @param message - The message kind to be sent.
40
+ * @param data - The data associated with the message.
41
+ * @param unreliable - Optional flag indicating if the message should be sent unreliably.
42
+ */
43
+ emitClient<Kind extends keyof MessageData>(player: Player, message: Kind, data?: MessageData[Kind], unreliable?: boolean): void;
44
+ /**
45
+ * Emits a message to all connected clients.
46
+ *
47
+ * @param message - The message kind to be sent.
48
+ * @param data - The data associated with the message.
49
+ * @param unreliable - Optional flag indicating if the message should be sent unreliably.
50
+ */
51
+ emitAllClients<Kind extends keyof MessageData>(message: Kind, data?: MessageData[Kind], unreliable?: boolean): void;
52
+ private initialize;
53
+ private getPacket;
54
+ /** @metadata macro */
55
+ private addSerializer;
56
+ /** @metadata macro */
57
+ private createMessageSerializer;
58
+ private getSerializer;
59
+ }
@@ -0,0 +1,246 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local t = TS.import(script, TS.getModule(script, "@rbxts", "t").lib.ts).t
4
+ local Networking = TS.import(script, TS.getModule(script, "@flamework", "networking").out).Networking
5
+ local createBinarySerializer = TS.import(script, TS.getModule(script, "@rbxts", "flamework-binary-serializer").out).createBinarySerializer
6
+ local _services = TS.import(script, TS.getModule(script, "@rbxts", "services"))
7
+ local Players = _services.Players
8
+ local RunService = _services.RunService
9
+ local Destroyable = TS.import(script, TS.getModule(script, "@rbxts", "destroyable").out).default
10
+ local _middleware = TS.import(script, script.Parent, "middleware")
11
+ local DropRequest = _middleware.DropRequest
12
+ local MiddlewareProvider = _middleware.MiddlewareProvider
13
+ local GlobalEvents = Networking.createEvent("@rbxts/tether:message-emitter@GlobalEvents")
14
+ local MessageEmitter
15
+ do
16
+ local super = Destroyable
17
+ MessageEmitter = setmetatable({}, {
18
+ __tostring = function()
19
+ return "MessageEmitter"
20
+ end,
21
+ __index = super,
22
+ })
23
+ MessageEmitter.__index = MessageEmitter
24
+ function MessageEmitter.new(...)
25
+ local self = setmetatable({}, MessageEmitter)
26
+ return self:constructor(...) or self
27
+ end
28
+ function MessageEmitter:constructor()
29
+ super.constructor(self)
30
+ self.middleware = MiddlewareProvider.new()
31
+ self.clientCallbacks = {}
32
+ self.serverCallbacks = {}
33
+ self.serializers = {}
34
+ self.janitor:Add(function()
35
+ table.clear(self.clientCallbacks)
36
+ table.clear(self.serverCallbacks)
37
+ self.serializers = nil
38
+ self.serverEvents = nil
39
+ self.clientEvents = nil
40
+ end)
41
+ if RunService:IsServer() then
42
+ self.serverEvents = GlobalEvents:createServer({}, {
43
+ incomingIds = { "sendServerMessage", "sendUnreliableServerMessage" },
44
+ incoming = {
45
+ sendServerMessage = { { t.union(t.string, t.number), t.optional(t.interface({
46
+ buffer = t.typeof("buffer"),
47
+ blobs = t.array(t.any),
48
+ })) }, nil },
49
+ sendUnreliableServerMessage = { { t.union(t.string, t.number), t.optional(t.interface({
50
+ buffer = t.typeof("buffer"),
51
+ blobs = t.array(t.any),
52
+ })) }, nil },
53
+ },
54
+ incomingUnreliable = {
55
+ sendUnreliableServerMessage = true,
56
+ },
57
+ outgoingIds = { "sendClientMessage", "sendUnreliableClientMessage" },
58
+ outgoingUnreliable = {
59
+ sendUnreliableClientMessage = true,
60
+ },
61
+ namespaceIds = {},
62
+ namespaces = {},
63
+ })
64
+ else
65
+ self.clientEvents = GlobalEvents:createClient({}, {
66
+ incomingIds = { "sendClientMessage", "sendUnreliableClientMessage" },
67
+ incoming = {
68
+ sendClientMessage = { { t.union(t.string, t.number), t.optional(t.interface({
69
+ buffer = t.typeof("buffer"),
70
+ blobs = t.array(t.any),
71
+ })) }, nil },
72
+ sendUnreliableClientMessage = { { t.union(t.string, t.number), t.optional(t.interface({
73
+ buffer = t.typeof("buffer"),
74
+ blobs = t.array(t.any),
75
+ })) }, nil },
76
+ },
77
+ incomingUnreliable = {
78
+ sendUnreliableClientMessage = true,
79
+ },
80
+ outgoingIds = { "sendServerMessage", "sendUnreliableServerMessage" },
81
+ outgoingUnreliable = {
82
+ sendUnreliableServerMessage = true,
83
+ },
84
+ namespaceIds = {},
85
+ namespaces = {},
86
+ })
87
+ end
88
+ end
89
+ function MessageEmitter:create(metaForEachMessage)
90
+ local emitter = MessageEmitter.new()
91
+ if metaForEachMessage == nil then
92
+ warn("[Tether]: Failed to generate serializer metadata for MessageEmitter")
93
+ return emitter:initialize()
94
+ end
95
+ for kind, meta in pairs(metaForEachMessage) do
96
+ if meta == nil then
97
+ continue
98
+ end
99
+ emitter:addSerializer(kind, meta)
100
+ end
101
+ return emitter:initialize()
102
+ end
103
+ function MessageEmitter:onServerMessage(message, callback)
104
+ return self:on(message, callback)
105
+ end
106
+ function MessageEmitter:onClientMessage(message, callback)
107
+ return self:on(message, callback)
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
+ function MessageEmitter:emitServer(message, data, unreliable)
132
+ if unreliable == nil then
133
+ unreliable = false
134
+ end
135
+ if self.middleware ~= nil then
136
+ for _, middleware in self.middleware:getServer(message) do
137
+ local result = middleware(data)
138
+ if result == DropRequest then
139
+ return nil
140
+ end
141
+ end
142
+ end
143
+ local send = if unreliable then self.clientEvents.sendUnreliableServerMessage else self.clientEvents.sendServerMessage
144
+ send(message, self:getPacket(message, data))
145
+ end
146
+ function MessageEmitter:emitClient(player, message, data, unreliable)
147
+ if unreliable == nil then
148
+ unreliable = false
149
+ end
150
+ if self.middleware ~= nil then
151
+ for _, middleware in self.middleware:getClient(message) do
152
+ local result = middleware(player, data)
153
+ if result == DropRequest then
154
+ return nil
155
+ end
156
+ end
157
+ end
158
+ local send = if unreliable then self.serverEvents.sendUnreliableClientMessage else self.serverEvents.sendClientMessage
159
+ send(player, message, self:getPacket(message, data))
160
+ end
161
+ function MessageEmitter:emitAllClients(message, data, unreliable)
162
+ if unreliable == nil then
163
+ unreliable = false
164
+ end
165
+ if self.middleware ~= nil then
166
+ for _, middleware in self.middleware:getClient(message) do
167
+ for _1, player in Players:GetPlayers() do
168
+ local result = middleware(player, data)
169
+ if result == DropRequest then
170
+ return nil
171
+ end
172
+ end
173
+ end
174
+ end
175
+ local send = if unreliable then self.serverEvents.sendUnreliableClientMessage else self.serverEvents.sendClientMessage
176
+ send:broadcast(message, self:getPacket(message, data))
177
+ end
178
+ function MessageEmitter:initialize()
179
+ if RunService:IsClient() then
180
+ self.janitor:Add(self.clientEvents.sendClientMessage:connect(function(sentMessage, packet)
181
+ local _clientCallbacks = self.clientCallbacks
182
+ local _sentMessage = sentMessage
183
+ local _condition = _clientCallbacks[_sentMessage]
184
+ if _condition == nil then
185
+ _condition = {}
186
+ end
187
+ local messageCallbacks = _condition
188
+ -- ▼ ReadonlySet.size ▼
189
+ local _size = 0
190
+ for _ in messageCallbacks do
191
+ _size += 1
192
+ end
193
+ -- ▲ ReadonlySet.size ▲
194
+ if _size == 0 then
195
+ return nil
196
+ end
197
+ local serializer = self:getSerializer(sentMessage)
198
+ local data = if packet ~= nil then serializer.deserialize(packet.buffer, packet.blobs) else nil
199
+ for callback in messageCallbacks do
200
+ callback(data)
201
+ end
202
+ end))
203
+ else
204
+ self.janitor:Add(self.serverEvents.sendServerMessage:connect(function(player, sentMessage, packet)
205
+ local _serverCallbacks = self.serverCallbacks
206
+ local _sentMessage = sentMessage
207
+ local _condition = _serverCallbacks[_sentMessage]
208
+ if _condition == nil then
209
+ _condition = {}
210
+ end
211
+ local messageCallbacks = _condition
212
+ -- ▼ ReadonlySet.size ▼
213
+ local _size = 0
214
+ for _ in messageCallbacks do
215
+ _size += 1
216
+ end
217
+ -- ▲ ReadonlySet.size ▲
218
+ if _size == 0 then
219
+ return nil
220
+ end
221
+ local serializer = self:getSerializer(sentMessage)
222
+ local data = if packet ~= nil then serializer.deserialize(packet.buffer, packet.blobs) else nil
223
+ for callback in messageCallbacks do
224
+ callback(player, data)
225
+ end
226
+ end))
227
+ end
228
+ return self
229
+ end
230
+ function MessageEmitter:getPacket(message, data)
231
+ local serializer = self:getSerializer(message)
232
+ return if data == nil then nil else serializer.serialize(data)
233
+ end
234
+ function MessageEmitter:addSerializer(message, meta)
235
+ self.serializers[message] = self:createMessageSerializer(meta)
236
+ end
237
+ function MessageEmitter:createMessageSerializer(meta)
238
+ return createBinarySerializer(meta)
239
+ end
240
+ function MessageEmitter:getSerializer(message)
241
+ return self.serializers[tostring(message)]
242
+ end
243
+ end
244
+ return {
245
+ MessageEmitter = MessageEmitter,
246
+ }
@@ -0,0 +1,20 @@
1
+ type DropRequestSymbol = symbol & {
2
+ _skip_middleware?: undefined;
3
+ };
4
+ export declare const DropRequest: DropRequestSymbol;
5
+ export type ClientMiddleware<Data = unknown> = (player: Player, data: Readonly<Data> | undefined) => DropRequestSymbol | void;
6
+ export type ServerMiddleware<Data = unknown> = (data: Readonly<Data> | undefined) => DropRequestSymbol | void;
7
+ export type UniversalMiddleware = () => DropRequestSymbol | void;
8
+ export type Middleware<Data = unknown> = ServerMiddleware<Data> & ClientMiddleware<Data>;
9
+ export declare class MiddlewareProvider<MessageData> {
10
+ private readonly clientMiddlewares;
11
+ private readonly serverMiddlewares;
12
+ /** @hidden */
13
+ getClient<Kind extends keyof MessageData>(message: Kind): ClientMiddleware<MessageData[Kind]>[];
14
+ /** @hidden */
15
+ getServer<Kind extends keyof MessageData>(message: Kind): ServerMiddleware<MessageData[Kind]>[];
16
+ useClient<Kind extends keyof MessageData>(message: Kind, middlewares: ClientMiddleware<MessageData[Kind]> | ClientMiddleware<MessageData[Kind]>[], order?: number): this;
17
+ useServer<Kind extends keyof MessageData>(message: Kind, middlewares: ServerMiddleware<MessageData[Kind]> | ServerMiddleware<MessageData[Kind]>[], order?: number): this;
18
+ useUniversal<Kind extends keyof MessageData>(message: Kind, middlewares: UniversalMiddleware | UniversalMiddleware[], order?: number): this;
19
+ }
20
+ export {};
@@ -0,0 +1,74 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local DropRequest = newproxy()
3
+ local MiddlewareProvider
4
+ do
5
+ MiddlewareProvider = setmetatable({}, {
6
+ __tostring = function()
7
+ return "MiddlewareProvider"
8
+ end,
9
+ })
10
+ MiddlewareProvider.__index = MiddlewareProvider
11
+ function MiddlewareProvider.new(...)
12
+ local self = setmetatable({}, MiddlewareProvider)
13
+ return self:constructor(...) or self
14
+ end
15
+ function MiddlewareProvider:constructor()
16
+ self.clientMiddlewares = {}
17
+ self.serverMiddlewares = {}
18
+ end
19
+ function MiddlewareProvider:getClient(message)
20
+ if self.clientMiddlewares[message] == nil then
21
+ self.clientMiddlewares[message] = {}
22
+ end
23
+ return self.clientMiddlewares[message]
24
+ end
25
+ function MiddlewareProvider:getServer(message)
26
+ if self.serverMiddlewares[message] == nil then
27
+ self.serverMiddlewares[message] = {}
28
+ end
29
+ return self.serverMiddlewares[message]
30
+ end
31
+ function MiddlewareProvider:useClient(message, middlewares, order)
32
+ local messageMiddleware = self:getClient(message)
33
+ local _middlewares = middlewares
34
+ if typeof(_middlewares) == "function" then
35
+ local _condition = order
36
+ if _condition == nil then
37
+ _condition = #messageMiddleware - 1
38
+ end
39
+ local _middlewares_1 = middlewares
40
+ table.insert(messageMiddleware, _condition + 1, _middlewares_1)
41
+ else
42
+ for _, middleware in middlewares do
43
+ self:useClient(message, middleware)
44
+ end
45
+ end
46
+ return self
47
+ end
48
+ function MiddlewareProvider:useServer(message, middlewares, order)
49
+ local messageMiddleware = self:getServer(message)
50
+ local _middlewares = middlewares
51
+ if typeof(_middlewares) == "function" then
52
+ local _condition = order
53
+ if _condition == nil then
54
+ _condition = #messageMiddleware - 1
55
+ end
56
+ local _middlewares_1 = middlewares
57
+ table.insert(messageMiddleware, _condition + 1, _middlewares_1)
58
+ else
59
+ for _, middleware in middlewares do
60
+ self:useServer(message, middleware)
61
+ end
62
+ end
63
+ return self
64
+ end
65
+ function MiddlewareProvider:useUniversal(message, middlewares, order)
66
+ self:useClient(message, middlewares, order)
67
+ self:useServer(message, middlewares, order)
68
+ return self
69
+ end
70
+ end
71
+ return {
72
+ DropRequest = DropRequest,
73
+ MiddlewareProvider = MiddlewareProvider,
74
+ }
@@ -0,0 +1,19 @@
1
+ import type { Networking } from "@flamework/networking";
2
+ export type MessageCallback<T = unknown> = ServerMessageCallback<T> | ClientMessageCallback<T>;
3
+ export type ClientMessageCallback<T = unknown> = (data: T) => void;
4
+ export type ServerMessageCallback<T = unknown> = (player: Player, data: T) => void;
5
+ export type BaseMessage = number | string | symbol;
6
+ export interface SerializedPacket {
7
+ readonly buffer: buffer;
8
+ readonly blobs: defined[];
9
+ }
10
+ export type MessageEvent = (kind: BaseMessage, packet?: SerializedPacket) => void;
11
+ export type UnreliableMessageEvent = Networking.Unreliable<MessageEvent>;
12
+ export interface ServerEvents {
13
+ sendServerMessage: MessageEvent;
14
+ sendUnreliableServerMessage: UnreliableMessageEvent;
15
+ }
16
+ export interface ClientEvents {
17
+ sendClientMessage: MessageEvent;
18
+ sendUnreliableClientMessage: UnreliableMessageEvent;
19
+ }
@@ -0,0 +1,2 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ return nil
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rbxts/tether",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "main": "out/init.lua",
5
5
  "scripts": {
6
6
  "build": "rbxtsc",
@@ -37,6 +37,7 @@
37
37
  "dependencies": {
38
38
  "@flamework/core": "^1.2.3",
39
39
  "@flamework/networking": "^1.2.3",
40
+ "@rbxts/destroyable": "^1.0.1",
40
41
  "@rbxts/flamework-binary-serializer": "^0.6.0",
41
42
  "@rbxts/services": "^1.5.5"
42
43
  }