@rbxts/tether 1.0.34 → 1.1.0
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 +85 -11
- package/out/builtin-middlewares.d.ts +33 -0
- package/out/builtin-middlewares.luau +93 -0
- package/out/index.d.ts +3 -29
- package/out/init.luau +5 -210
- package/out/message-emitter.d.ts +59 -0
- package/out/message-emitter.luau +272 -0
- package/out/middleware.d.ts +31 -0
- package/out/middleware.luau +121 -0
- package/out/structs.d.ts +19 -0
- package/out/structs.luau +2 -0
- package/package.json +8 -5
package/README.md
CHANGED
|
@@ -1,19 +1,24 @@
|
|
|
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
|
+
> [!CAUTION]
|
|
5
|
+
> Depends on `rbxts-transformer-flamework`!
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
3
8
|
|
|
4
9
|
### In `shared/messaging.ts`
|
|
5
10
|
```ts
|
|
6
|
-
import { DataType } from "@rbxts/flamework-binary-serializer";
|
|
11
|
+
import type { DataType } from "@rbxts/flamework-binary-serializer";
|
|
12
|
+
import { MessageEmitter } from "@rbxts/tether";
|
|
7
13
|
|
|
8
|
-
export const
|
|
9
|
-
messageEmitter.initialize();
|
|
14
|
+
export const messaging = MessageEmitter.create<MessageData>();
|
|
10
15
|
|
|
11
16
|
export const enum Message {
|
|
12
|
-
|
|
17
|
+
Test
|
|
13
18
|
}
|
|
14
19
|
|
|
15
20
|
export interface MessageData {
|
|
16
|
-
[Message.
|
|
21
|
+
[Message.Test]: {
|
|
17
22
|
readonly foo: string;
|
|
18
23
|
readonly n: DataType.u8;
|
|
19
24
|
};
|
|
@@ -21,23 +26,92 @@ export interface MessageData {
|
|
|
21
26
|
```
|
|
22
27
|
|
|
23
28
|
> [!CAUTION]
|
|
24
|
-
> Every single message kind must implement an interface for it's data (in the example that would be the object with the `foo` and `bar` fields) as well as
|
|
29
|
+
> Every single message kind must implement an interface for it's data (in the example that would be the object with the `foo` and `bar` fields). Message serialization (as well as your message itself) will not work if you don't do this.
|
|
25
30
|
|
|
26
31
|
### Server
|
|
27
32
|
```ts
|
|
28
|
-
import { Message,
|
|
33
|
+
import { Message, messaging } from "shared/messaging";
|
|
29
34
|
|
|
30
|
-
|
|
35
|
+
messaging.onServerMessage(Message.Test, (player, data) => {
|
|
31
36
|
print(player, "sent data:", data);
|
|
32
37
|
});
|
|
33
38
|
```
|
|
34
39
|
|
|
35
40
|
### Client
|
|
36
41
|
```ts
|
|
37
|
-
import { Message,
|
|
42
|
+
import { Message, messaging } from "shared/messaging";
|
|
38
43
|
|
|
39
|
-
|
|
44
|
+
messaging.emitServer(Message.Test, {
|
|
40
45
|
foo: "bar",
|
|
41
46
|
n: 69
|
|
42
47
|
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Middleware
|
|
51
|
+
Drop, delay, or modify requests
|
|
52
|
+
|
|
53
|
+
### Creating middleware
|
|
54
|
+
|
|
55
|
+
#### Client
|
|
56
|
+
```ts
|
|
57
|
+
import type { ClientMiddleware } from "@rbxts/tether";
|
|
58
|
+
|
|
59
|
+
export function logClient(): ClientMiddleware {
|
|
60
|
+
return message => (player, data) => print(`[LOG]: Sent message '${message}' to player ${player} with data:`, data);
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
#### Server
|
|
65
|
+
```ts
|
|
66
|
+
import type { ServerMiddleware } from "@rbxts/tether";
|
|
67
|
+
|
|
68
|
+
export function logServer(): ServerMiddleware {
|
|
69
|
+
return message => data => print(`[LOG]: Sent message '${message}' to server with data:`, data);
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
#### Shared
|
|
74
|
+
```ts
|
|
75
|
+
import { type SharedMiddleware, DropRequest } from "@rbxts/tether";
|
|
76
|
+
|
|
77
|
+
export function rateLimit(interval: number): SharedMiddleware {
|
|
78
|
+
let lastRequest = 0;
|
|
79
|
+
|
|
80
|
+
return message => // message attempting to be sent
|
|
81
|
+
() => { // no data/player - it's a shared middleware
|
|
82
|
+
if (os.clock() - lastRequest < interval)
|
|
83
|
+
return DropRequest;
|
|
84
|
+
|
|
85
|
+
lastRequest = os.clock();
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Using middleware
|
|
91
|
+
```ts
|
|
92
|
+
import type { DataType } from "@rbxts/flamework-binary-serializer";
|
|
93
|
+
import { MessageEmitter, BuiltinMiddlewares } from "@rbxts/tether";
|
|
94
|
+
|
|
95
|
+
export const messaging = MessageEmitter.create<MessageData>();
|
|
96
|
+
messaging.middleware
|
|
97
|
+
// only allows requests to the server every 5 seconds,
|
|
98
|
+
// drops any requests that occur within 5 seconds of each other
|
|
99
|
+
.useServer(Message.Test, [BuiltinMiddlewares.rateLimit(5)])
|
|
100
|
+
// automatically validates that the data sent through the remote matches
|
|
101
|
+
// the data associated with the message at runtime using type guards
|
|
102
|
+
.useServer(Message.Test, [BuiltinMiddlewares.validateServer()])
|
|
103
|
+
// logs every message fired
|
|
104
|
+
.useServerGlobal([logServer()]);
|
|
105
|
+
.useClientGlobal([logClient()]);
|
|
106
|
+
|
|
107
|
+
export const enum Message {
|
|
108
|
+
Test
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export interface MessageData {
|
|
112
|
+
[Message.Test]: {
|
|
113
|
+
readonly foo: string;
|
|
114
|
+
readonly n: DataType.u8;
|
|
115
|
+
};
|
|
116
|
+
}
|
|
43
117
|
```
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Modding } from "@flamework/core";
|
|
2
|
+
import { type SharedMiddleware, type ServerMiddleware, type ClientMiddleware } from "./middleware";
|
|
3
|
+
type Guard<T> = (value: unknown) => value is T;
|
|
4
|
+
export declare namespace BuiltinMiddlewares {
|
|
5
|
+
/**
|
|
6
|
+
* Creates a shared per-message middleware that will drop any message that occurs within the given interval of the previous message
|
|
7
|
+
*
|
|
8
|
+
* @param interval The interval in seconds that the middleware should wait before allowing a new request
|
|
9
|
+
* @returns A middleware that will drop any message that occurs within the given interval
|
|
10
|
+
*/
|
|
11
|
+
function rateLimit(interval: number): SharedMiddleware;
|
|
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)
|
|
24
|
+
*
|
|
25
|
+
* If the guard fails, the middleware will drop the message
|
|
26
|
+
*
|
|
27
|
+
* @param guard The guard to use to validate the data.
|
|
28
|
+
* @returns A client middleware that validates the data with the given guard.
|
|
29
|
+
* @metadata macro
|
|
30
|
+
*/
|
|
31
|
+
function validateClient<T>(guard?: Guard<T> | Modding.Generic<T, "guard">): ClientMiddleware<T>;
|
|
32
|
+
}
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,93 @@
|
|
|
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()
|
|
5
|
+
return function() end
|
|
6
|
+
end
|
|
7
|
+
local validationGuardGenerationFailed = function(context)
|
|
8
|
+
return `[@rbxts/tether]: Failed to generate guard for validate{context}<T> builtin middleware - skipping validation`
|
|
9
|
+
end
|
|
10
|
+
local guardFailed = function(message)
|
|
11
|
+
return `[@rbxts/tether]: Type validation failed for message '{tostring(message)}' - check your sent data`
|
|
12
|
+
end
|
|
13
|
+
local BuiltinMiddlewares = {}
|
|
14
|
+
do
|
|
15
|
+
local _container = BuiltinMiddlewares
|
|
16
|
+
--[[
|
|
17
|
+
*
|
|
18
|
+
* Creates a shared per-message middleware that will drop any message that occurs within the given interval of the previous message
|
|
19
|
+
*
|
|
20
|
+
* @param interval The interval in seconds that the middleware should wait before allowing a new request
|
|
21
|
+
* @returns A middleware that will drop any message that occurs within the given interval
|
|
22
|
+
|
|
23
|
+
]]
|
|
24
|
+
local function rateLimit(interval)
|
|
25
|
+
local lastRequest = 0
|
|
26
|
+
return function()
|
|
27
|
+
return function()
|
|
28
|
+
if os.clock() - lastRequest < interval then
|
|
29
|
+
return DropRequest
|
|
30
|
+
end
|
|
31
|
+
lastRequest = os.clock()
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
_container.rateLimit = rateLimit
|
|
36
|
+
--[[
|
|
37
|
+
*
|
|
38
|
+
* Creates a server middleware that validates the data with the given guard (or a generated guard if none was provided)
|
|
39
|
+
*
|
|
40
|
+
* If the guard fails, the middleware will drop the message
|
|
41
|
+
*
|
|
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
|
+
* @metadata macro
|
|
45
|
+
|
|
46
|
+
]]
|
|
47
|
+
local function validateServer(guard)
|
|
48
|
+
if guard == nil then
|
|
49
|
+
warn(validationGuardGenerationFailed("Server"))
|
|
50
|
+
return noOp
|
|
51
|
+
end
|
|
52
|
+
return function(message)
|
|
53
|
+
return function(data)
|
|
54
|
+
if guard(data) then
|
|
55
|
+
return nil
|
|
56
|
+
end
|
|
57
|
+
warn(guardFailed(message))
|
|
58
|
+
return DropRequest
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
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
|
|
78
|
+
end
|
|
79
|
+
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
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
_container.validateClient = validateClient
|
|
90
|
+
end
|
|
91
|
+
return {
|
|
92
|
+
BuiltinMiddlewares = BuiltinMiddlewares,
|
|
93
|
+
}
|
package/out/index.d.ts
CHANGED
|
@@ -1,29 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
type
|
|
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 SharedMiddleware, 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
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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 Whether 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 Whether 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 Whether 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,272 @@
|
|
|
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("[@rbxts/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
|
+
local updateData = function(newData)
|
|
136
|
+
data = newData
|
|
137
|
+
return nil
|
|
138
|
+
end
|
|
139
|
+
for _, globalMiddleware in self.middleware:getServerGlobal() do
|
|
140
|
+
local result = globalMiddleware(message)(data, updateData)
|
|
141
|
+
if result == DropRequest then
|
|
142
|
+
return nil
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
for _, middleware in self.middleware:getServer(message) do
|
|
146
|
+
local result = middleware(message)(data, updateData)
|
|
147
|
+
if result == DropRequest then
|
|
148
|
+
return nil
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
local send = if unreliable then self.clientEvents.sendUnreliableServerMessage else self.clientEvents.sendServerMessage
|
|
152
|
+
send(message, self:getPacket(message, data))
|
|
153
|
+
end
|
|
154
|
+
function MessageEmitter:emitClient(player, message, data, unreliable)
|
|
155
|
+
if unreliable == nil then
|
|
156
|
+
unreliable = false
|
|
157
|
+
end
|
|
158
|
+
local updateData = function(newData)
|
|
159
|
+
data = newData
|
|
160
|
+
return nil
|
|
161
|
+
end
|
|
162
|
+
for _, globalMiddleware in self.middleware:getClientGlobal() do
|
|
163
|
+
local result = globalMiddleware(message)(player, data, updateData)
|
|
164
|
+
if result == DropRequest then
|
|
165
|
+
return nil
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
for _, middleware in self.middleware:getClient(message) do
|
|
169
|
+
local result = middleware(message)(player, data, updateData)
|
|
170
|
+
if result == DropRequest then
|
|
171
|
+
return nil
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
local send = if unreliable then self.serverEvents.sendUnreliableClientMessage else self.serverEvents.sendClientMessage
|
|
175
|
+
send(player, message, self:getPacket(message, data))
|
|
176
|
+
end
|
|
177
|
+
function MessageEmitter:emitAllClients(message, data, unreliable)
|
|
178
|
+
if unreliable == nil then
|
|
179
|
+
unreliable = false
|
|
180
|
+
end
|
|
181
|
+
local updateData = function(newData)
|
|
182
|
+
data = newData
|
|
183
|
+
return nil
|
|
184
|
+
end
|
|
185
|
+
for _, globalMiddleware in self.middleware:getClientGlobal() do
|
|
186
|
+
for _1, player in Players:GetPlayers() do
|
|
187
|
+
local result = globalMiddleware(message)(player, data, updateData)
|
|
188
|
+
if result == DropRequest then
|
|
189
|
+
return nil
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
for _, middleware in self.middleware:getClient(message) do
|
|
194
|
+
for _1, player in Players:GetPlayers() do
|
|
195
|
+
local result = middleware(message)(player, data, updateData)
|
|
196
|
+
if result == DropRequest then
|
|
197
|
+
return nil
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
local send = if unreliable then self.serverEvents.sendUnreliableClientMessage else self.serverEvents.sendClientMessage
|
|
202
|
+
send:broadcast(message, self:getPacket(message, data))
|
|
203
|
+
end
|
|
204
|
+
function MessageEmitter:initialize()
|
|
205
|
+
if RunService:IsClient() then
|
|
206
|
+
self.janitor:Add(self.clientEvents.sendClientMessage:connect(function(sentMessage, packet)
|
|
207
|
+
local _clientCallbacks = self.clientCallbacks
|
|
208
|
+
local _sentMessage = sentMessage
|
|
209
|
+
local _condition = _clientCallbacks[_sentMessage]
|
|
210
|
+
if _condition == nil then
|
|
211
|
+
_condition = {}
|
|
212
|
+
end
|
|
213
|
+
local messageCallbacks = _condition
|
|
214
|
+
-- ▼ ReadonlySet.size ▼
|
|
215
|
+
local _size = 0
|
|
216
|
+
for _ in messageCallbacks do
|
|
217
|
+
_size += 1
|
|
218
|
+
end
|
|
219
|
+
-- ▲ ReadonlySet.size ▲
|
|
220
|
+
if _size == 0 then
|
|
221
|
+
return nil
|
|
222
|
+
end
|
|
223
|
+
local serializer = self:getSerializer(sentMessage)
|
|
224
|
+
local data = if packet ~= nil then serializer.deserialize(packet.buffer, packet.blobs) else nil
|
|
225
|
+
for callback in messageCallbacks do
|
|
226
|
+
callback(data)
|
|
227
|
+
end
|
|
228
|
+
end))
|
|
229
|
+
else
|
|
230
|
+
self.janitor:Add(self.serverEvents.sendServerMessage:connect(function(player, sentMessage, packet)
|
|
231
|
+
local _serverCallbacks = self.serverCallbacks
|
|
232
|
+
local _sentMessage = sentMessage
|
|
233
|
+
local _condition = _serverCallbacks[_sentMessage]
|
|
234
|
+
if _condition == nil then
|
|
235
|
+
_condition = {}
|
|
236
|
+
end
|
|
237
|
+
local messageCallbacks = _condition
|
|
238
|
+
-- ▼ ReadonlySet.size ▼
|
|
239
|
+
local _size = 0
|
|
240
|
+
for _ in messageCallbacks do
|
|
241
|
+
_size += 1
|
|
242
|
+
end
|
|
243
|
+
-- ▲ ReadonlySet.size ▲
|
|
244
|
+
if _size == 0 then
|
|
245
|
+
return nil
|
|
246
|
+
end
|
|
247
|
+
local serializer = self:getSerializer(sentMessage)
|
|
248
|
+
local data = if packet ~= nil then serializer.deserialize(packet.buffer, packet.blobs) else nil
|
|
249
|
+
for callback in messageCallbacks do
|
|
250
|
+
callback(player, data)
|
|
251
|
+
end
|
|
252
|
+
end))
|
|
253
|
+
end
|
|
254
|
+
return self
|
|
255
|
+
end
|
|
256
|
+
function MessageEmitter:getPacket(message, data)
|
|
257
|
+
local serializer = self:getSerializer(message)
|
|
258
|
+
return if data == nil then nil else serializer.serialize(data)
|
|
259
|
+
end
|
|
260
|
+
function MessageEmitter:addSerializer(message, meta)
|
|
261
|
+
self.serializers[message] = self:createMessageSerializer(meta)
|
|
262
|
+
end
|
|
263
|
+
function MessageEmitter:createMessageSerializer(meta)
|
|
264
|
+
return createBinarySerializer(meta)
|
|
265
|
+
end
|
|
266
|
+
function MessageEmitter:getSerializer(message)
|
|
267
|
+
return self.serializers[tostring(message)]
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
return {
|
|
271
|
+
MessageEmitter = MessageEmitter,
|
|
272
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { BaseMessage } from "./structs";
|
|
2
|
+
type DropRequestSymbol = symbol & {
|
|
3
|
+
_drop_req?: undefined;
|
|
4
|
+
};
|
|
5
|
+
export declare const DropRequest: DropRequestSymbol;
|
|
6
|
+
type UpdateDataFn<T> = (newData: T) => void;
|
|
7
|
+
export type ClientMiddleware<Data = unknown> = (message: BaseMessage) => (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;
|
|
10
|
+
export type Middleware<Data = unknown> = ServerMiddleware<Data> & ClientMiddleware<Data>;
|
|
11
|
+
export declare class MiddlewareProvider<MessageData> {
|
|
12
|
+
private readonly clientGlobalMiddlewares;
|
|
13
|
+
private readonly serverGlobalMiddlewares;
|
|
14
|
+
private readonly clientMiddlewares;
|
|
15
|
+
private readonly serverMiddlewares;
|
|
16
|
+
/** @hidden */
|
|
17
|
+
getClient<Kind extends keyof MessageData>(message: Kind): ClientMiddleware<MessageData[Kind]>[];
|
|
18
|
+
/** @hidden */
|
|
19
|
+
getServer<Kind extends keyof MessageData>(message: Kind): ServerMiddleware<MessageData[Kind]>[];
|
|
20
|
+
/** @hidden */
|
|
21
|
+
getClientGlobal<Data>(): ClientMiddleware<Data>[];
|
|
22
|
+
/** @hidden */
|
|
23
|
+
getServerGlobal<Data>(): ServerMiddleware<Data>[];
|
|
24
|
+
useClient<Kind extends keyof MessageData>(message: Kind, middlewares: ClientMiddleware<MessageData[Kind]> | readonly ClientMiddleware<MessageData[Kind]>[], order?: number): this;
|
|
25
|
+
useServer<Kind extends keyof MessageData>(message: Kind, middlewares: ServerMiddleware<MessageData[Kind]> | readonly ServerMiddleware<MessageData[Kind]>[], order?: number): this;
|
|
26
|
+
useShared<Kind extends keyof MessageData>(message: Kind, middlewares: SharedMiddleware | readonly SharedMiddleware[], order?: number): this;
|
|
27
|
+
useClientGlobal<Data>(middlewares: ClientMiddleware<Data> | readonly ClientMiddleware<Data>[], order?: number): this;
|
|
28
|
+
useServerGlobal<Data>(middlewares: ServerMiddleware<Data> | readonly ServerMiddleware<Data>[], order?: number): this;
|
|
29
|
+
useSharedGlobal(middlewares: SharedMiddleware | readonly SharedMiddleware[], order?: number): this;
|
|
30
|
+
}
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,121 @@
|
|
|
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.clientGlobalMiddlewares = {}
|
|
17
|
+
self.serverGlobalMiddlewares = {}
|
|
18
|
+
self.clientMiddlewares = {}
|
|
19
|
+
self.serverMiddlewares = {}
|
|
20
|
+
end
|
|
21
|
+
function MiddlewareProvider:getClient(message)
|
|
22
|
+
if self.clientMiddlewares[message] == nil then
|
|
23
|
+
self.clientMiddlewares[message] = {}
|
|
24
|
+
end
|
|
25
|
+
return self.clientMiddlewares[message]
|
|
26
|
+
end
|
|
27
|
+
function MiddlewareProvider:getServer(message)
|
|
28
|
+
if self.serverMiddlewares[message] == nil then
|
|
29
|
+
self.serverMiddlewares[message] = {}
|
|
30
|
+
end
|
|
31
|
+
return self.serverMiddlewares[message]
|
|
32
|
+
end
|
|
33
|
+
function MiddlewareProvider:getClientGlobal()
|
|
34
|
+
return self.clientGlobalMiddlewares
|
|
35
|
+
end
|
|
36
|
+
function MiddlewareProvider:getServerGlobal()
|
|
37
|
+
return self.serverGlobalMiddlewares
|
|
38
|
+
end
|
|
39
|
+
function MiddlewareProvider:useClient(message, middlewares, order)
|
|
40
|
+
local messageMiddleware = self:getClient(message)
|
|
41
|
+
local _middlewares = middlewares
|
|
42
|
+
if type(_middlewares) == "function" then
|
|
43
|
+
local _condition = order
|
|
44
|
+
if _condition == nil then
|
|
45
|
+
_condition = #messageMiddleware - 1
|
|
46
|
+
end
|
|
47
|
+
local _middlewares_1 = middlewares
|
|
48
|
+
table.insert(messageMiddleware, _condition + 1, _middlewares_1)
|
|
49
|
+
else
|
|
50
|
+
for _, middleware in middlewares do
|
|
51
|
+
self:useClient(message, middleware, order)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
return self
|
|
55
|
+
end
|
|
56
|
+
function MiddlewareProvider:useServer(message, middlewares, order)
|
|
57
|
+
local messageMiddleware = self:getServer(message)
|
|
58
|
+
local _middlewares = middlewares
|
|
59
|
+
if type(_middlewares) == "function" then
|
|
60
|
+
local _condition = order
|
|
61
|
+
if _condition == nil then
|
|
62
|
+
_condition = #messageMiddleware - 1
|
|
63
|
+
end
|
|
64
|
+
local _middlewares_1 = middlewares
|
|
65
|
+
table.insert(messageMiddleware, _condition + 1, _middlewares_1)
|
|
66
|
+
else
|
|
67
|
+
for _, middleware in middlewares do
|
|
68
|
+
self:useServer(message, middleware, order)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
return self
|
|
72
|
+
end
|
|
73
|
+
function MiddlewareProvider:useShared(message, middlewares, order)
|
|
74
|
+
self:useClient(message, middlewares, order)
|
|
75
|
+
self:useServer(message, middlewares, order)
|
|
76
|
+
return self
|
|
77
|
+
end
|
|
78
|
+
function MiddlewareProvider:useClientGlobal(middlewares, order)
|
|
79
|
+
local globalMiddleware = self:getClientGlobal()
|
|
80
|
+
local _middlewares = middlewares
|
|
81
|
+
if type(_middlewares) == "function" then
|
|
82
|
+
local _condition = order
|
|
83
|
+
if _condition == nil then
|
|
84
|
+
_condition = #globalMiddleware - 1
|
|
85
|
+
end
|
|
86
|
+
local _middlewares_1 = middlewares
|
|
87
|
+
table.insert(globalMiddleware, _condition + 1, _middlewares_1)
|
|
88
|
+
else
|
|
89
|
+
for _, middleware in middlewares do
|
|
90
|
+
self:useClientGlobal(middleware, order)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
return self
|
|
94
|
+
end
|
|
95
|
+
function MiddlewareProvider:useServerGlobal(middlewares, order)
|
|
96
|
+
local globalMiddleware = self:getServerGlobal()
|
|
97
|
+
local _middlewares = middlewares
|
|
98
|
+
if type(_middlewares) == "function" then
|
|
99
|
+
local _condition = order
|
|
100
|
+
if _condition == nil then
|
|
101
|
+
_condition = #globalMiddleware - 1
|
|
102
|
+
end
|
|
103
|
+
local _middlewares_1 = middlewares
|
|
104
|
+
table.insert(globalMiddleware, _condition + 1, _middlewares_1)
|
|
105
|
+
else
|
|
106
|
+
for _, middleware in middlewares do
|
|
107
|
+
self:useServerGlobal(middleware, order)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
return self
|
|
111
|
+
end
|
|
112
|
+
function MiddlewareProvider:useSharedGlobal(middlewares, order)
|
|
113
|
+
self:useClientGlobal(middlewares, order)
|
|
114
|
+
self:useServerGlobal(middlewares, order)
|
|
115
|
+
return self
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
return {
|
|
119
|
+
DropRequest = DropRequest,
|
|
120
|
+
MiddlewareProvider = MiddlewareProvider,
|
|
121
|
+
}
|
package/out/structs.d.ts
ADDED
|
@@ -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
|
+
}
|
package/out/structs.luau
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rbxts/tether",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"main": "out/init.lua",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "rbxtsc",
|
|
@@ -11,7 +11,9 @@
|
|
|
11
11
|
"roblox",
|
|
12
12
|
"tether",
|
|
13
13
|
"networking",
|
|
14
|
-
"message"
|
|
14
|
+
"message",
|
|
15
|
+
"serialization",
|
|
16
|
+
"middleware"
|
|
15
17
|
],
|
|
16
18
|
"repository": {
|
|
17
19
|
"url": "git+https://github.com/R-unic/tether.git"
|
|
@@ -30,13 +32,14 @@
|
|
|
30
32
|
"devDependencies": {
|
|
31
33
|
"@rbxts/compiler-types": "^3.0.0-types.0",
|
|
32
34
|
"@rbxts/types": "^1.0.835",
|
|
33
|
-
"rbxts-transformer-flamework": "
|
|
35
|
+
"rbxts-transformer-flamework": "^1.2.4",
|
|
34
36
|
"roblox-ts": "^3.0.0",
|
|
35
37
|
"typescript": "^5.5.3"
|
|
36
38
|
},
|
|
37
39
|
"dependencies": {
|
|
38
|
-
"@flamework/core": "^1.2.
|
|
39
|
-
"@flamework/networking": "^1.2.
|
|
40
|
+
"@flamework/core": "^1.2.4",
|
|
41
|
+
"@flamework/networking": "^1.2.4",
|
|
42
|
+
"@rbxts/destroyable": "^1.0.1",
|
|
40
43
|
"@rbxts/flamework-binary-serializer": "^0.6.0",
|
|
41
44
|
"@rbxts/services": "^1.5.5"
|
|
42
45
|
}
|