@rbxts/tether 1.3.24 → 1.4.1

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
@@ -64,7 +64,7 @@ messaging.server.emit(Message.Test, {
64
64
  });
65
65
  ```
66
66
 
67
- ## Simulated Remote Functions
67
+ ## Function Messages
68
68
 
69
69
  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.
70
70
 
@@ -96,12 +96,12 @@ do
96
96
  local schemaString = if schema ~= nil then " " .. table.concat(string.split(repr(schema[1], {
97
97
  pretty = true,
98
98
  }), "\n"), "\n ") else "unknown"
99
- local text = { "\n", horizontalLine, "\n", "Packet sent to ", (if RunService:IsServer() then "client" else "server"), "!\n", " - Message: ", message, "\n", " - Data: ", repr(data, {
99
+ local text = { "\n", horizontalLine, "\n", "Packet sent to ", (if RunService:IsServer() then "client" else "server"), "!\n", " - Message: ", tostring(message), "\n", " - Data: ", repr(data, {
100
100
  pretty = true,
101
101
  }), "\n", " - Raw data:\n", " - Buffer: ", bufferToString(buf), "\n", " - Blobs: ", repr(blobs, {
102
102
  pretty = false,
103
103
  robloxClassName = true,
104
- }), "\n", " - Packet size: ", bufferSize + blobsSize, " bytes\n", " - Buffer: ", bufferSize, " bytes\n", " - Blobs: ", blobsSize, " bytes\n", " - Schema: ", schemaString, "\n", horizontalLine, "\n" }
104
+ }), "\n", " - Packet size: ", tostring(bufferSize + blobsSize), " bytes\n", " - Buffer: ", tostring(bufferSize), " bytes\n", " - Blobs: ", tostring(blobsSize), " bytes\n", " - Schema: ", schemaString, "\n", horizontalLine, "\n" }
105
105
  print(table.concat(text, ""))
106
106
  end
107
107
  end
@@ -0,0 +1,49 @@
1
+ import { ContextualEmitter } from "./contextual-emitter";
2
+ import type { BaseMessage, ClientMessageCallback, ClientFunctionMessageCallback } from "../structs";
3
+ export declare class ClientEmitter<MessageData> extends ContextualEmitter<MessageData> {
4
+ readonly context = "client";
5
+ readonly on: <K extends keyof MessageData>(this: ClientEmitter<MessageData>, message: K & BaseMessage, callback: ClientMessageCallback<MessageData[K]>) => () => void;
6
+ readonly once: <K extends keyof MessageData>(this: ClientEmitter<MessageData>, message: K & BaseMessage, callback: ClientMessageCallback<MessageData[K]>) => () => void;
7
+ /**
8
+ * Emits a message to a specific client or multiple clients
9
+ *
10
+ * @param player The player(s) to whom the message is sent
11
+ * @param message The message kind to be sent
12
+ * @param data The data associated with the message
13
+ * @param unreliable Whether the message should be sent unreliably
14
+ */
15
+ emit<K extends keyof MessageData>(player: Player | Player[], message: K & BaseMessage, data?: MessageData[K], unreliable?: boolean): void;
16
+ /**
17
+ * Emits a message to all clients except the specified client(s)
18
+ *
19
+ * @param player The player(s) to whom the message is not sent
20
+ * @param message The message kind to be sent
21
+ * @param data The data associated with the message
22
+ * @param unreliable Whether the message should be sent unreliably
23
+ */
24
+ emitExcept<K extends keyof MessageData>(player: Player | Player[], message: K & BaseMessage, data?: MessageData[K], unreliable?: boolean): void;
25
+ /**
26
+ * Emits a message to all connected clients
27
+ *
28
+ * @param message The message kind to be sent
29
+ * @param data The data associated with the message
30
+ * @param unreliable Whether the message should be sent unreliably
31
+ */
32
+ emitAll<K extends keyof MessageData>(message: K & BaseMessage, data?: MessageData[K], unreliable?: boolean): void;
33
+ /**
34
+ * Sets a callback for a simulated remote function
35
+ *
36
+ * @returns A destructor function that disconnects the callback from the message
37
+ */
38
+ setCallback<K extends keyof MessageData, R extends keyof MessageData>(message: K & BaseMessage, returnMessage: R & BaseMessage, callback: ClientFunctionMessageCallback<MessageData[K], MessageData[R]>): () => void;
39
+ /**
40
+ * Simulates a remote function invocation
41
+ *
42
+ * @param message The message kind to be sent
43
+ * @param returnMessage The message kind to be returned
44
+ * @param player The player to whom the function is sent
45
+ * @param data The data associated with the message
46
+ * @param unreliable Whether the message should be sent unreliably
47
+ */
48
+ invoke<K extends keyof MessageData, R extends keyof MessageData>(message: K & BaseMessage, returnMessage: R & BaseMessage, player: Player, data?: MessageData[K], unreliable?: boolean): Promise<MessageData[R]>;
49
+ }
@@ -0,0 +1,153 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local _services = TS.import(script, TS.getModule(script, "@rbxts", "services"))
4
+ local Players = _services.Players
5
+ local RunService = _services.RunService
6
+ local ContextualEmitter = TS.import(script, script.Parent, "contextual-emitter").ContextualEmitter
7
+ local ClientEmitter
8
+ do
9
+ local super = ContextualEmitter
10
+ ClientEmitter = setmetatable({}, {
11
+ __tostring = function()
12
+ return "ClientEmitter"
13
+ end,
14
+ __index = super,
15
+ })
16
+ ClientEmitter.__index = ClientEmitter
17
+ function ClientEmitter.new(...)
18
+ local self = setmetatable({}, ClientEmitter)
19
+ return self:constructor(...) or self
20
+ end
21
+ function ClientEmitter:constructor(...)
22
+ super.constructor(self, ...)
23
+ self.context = "client"
24
+ end
25
+ function ClientEmitter:emit(player, message, data, unreliable)
26
+ if unreliable == nil then
27
+ unreliable = false
28
+ end
29
+ if RunService:IsClient() then
30
+ error("[tether::error] Cannot emit message from client to client")
31
+ end
32
+ task.spawn(function()
33
+ local _binding = self.master:runClientMiddlewares(message, data, player)
34
+ local dropRequest = _binding[1]
35
+ local newData = _binding[2]
36
+ if dropRequest then
37
+ return nil
38
+ end
39
+ self.master:queueMessage(self.context, message, { player, message, newData, unreliable })
40
+ end)
41
+ end
42
+ function ClientEmitter:emitExcept(player, message, data, unreliable)
43
+ if unreliable == nil then
44
+ unreliable = false
45
+ end
46
+ local shouldSendTo = function(p)
47
+ local _player = player
48
+ local _result
49
+ if typeof(_player) == "Instance" then
50
+ _result = p ~= player
51
+ else
52
+ local _player_1 = player
53
+ local _p = p
54
+ _result = not (table.find(_player_1, _p) ~= nil)
55
+ end
56
+ return _result
57
+ end
58
+ local _self = self
59
+ local _exp = Players:GetPlayers()
60
+ -- ▼ ReadonlyArray.filter ▼
61
+ local _newValue = {}
62
+ local _length = 0
63
+ for _k, _v in _exp do
64
+ if shouldSendTo(_v, _k - 1, _exp) == true then
65
+ _length += 1
66
+ _newValue[_length] = _v
67
+ end
68
+ end
69
+ -- ▲ ReadonlyArray.filter ▲
70
+ _self:emit(_newValue, message, data, unreliable)
71
+ end
72
+ function ClientEmitter:emitAll(message, data, unreliable)
73
+ if unreliable == nil then
74
+ unreliable = false
75
+ end
76
+ if RunService:IsClient() then
77
+ error("[tether::error] Cannot emit message from client to all clients")
78
+ end
79
+ task.spawn(function()
80
+ local _binding = self.master:runClientMiddlewares(message, data)
81
+ local dropRequest = _binding[1]
82
+ local newData = _binding[2]
83
+ if dropRequest then
84
+ return nil
85
+ end
86
+ self.master:queueMessage(true, message, { message, newData, unreliable })
87
+ end)
88
+ end
89
+ function ClientEmitter:setCallback(message, returnMessage, callback)
90
+ if RunService:IsServer() then
91
+ error("[tether::error] Cannot listen to server message from client")
92
+ end
93
+ return self:on(message, function(data)
94
+ local returnValue = callback(data)
95
+ -- Defer the response emission to end of frame and swap context to avoid context check issues
96
+ -- task.defer guarantees response is sent by end of current frame, ensuring predictable timing in production
97
+ task.defer(function()
98
+ setLuneContext("client")
99
+ self.master.server:emit(returnMessage, returnValue)
100
+ setLuneContext("both")
101
+ end)
102
+ end)
103
+ end
104
+ function ClientEmitter:invoke(message, returnMessage, player, data, unreliable)
105
+ if unreliable == nil then
106
+ unreliable = false
107
+ end
108
+ if RunService:IsClient() then
109
+ error("[tether::error] Cannot invoke function from client to client")
110
+ end
111
+ local _binding = self.master
112
+ local serverFunctions = _binding.serverFunctions
113
+ local _returnMessage = returnMessage
114
+ if not (serverFunctions[_returnMessage] ~= nil) then
115
+ local _returnMessage_1 = returnMessage
116
+ serverFunctions[_returnMessage_1] = {}
117
+ end
118
+ local _returnMessage_1 = returnMessage
119
+ local functions = serverFunctions[_returnMessage_1]
120
+ local returnValue
121
+ local responseCallback = function(data)
122
+ returnValue = data
123
+ return returnValue
124
+ end
125
+ functions[responseCallback] = true
126
+ self:emit(player, message, data, unreliable)
127
+ return TS.Promise.new(function(resolve, reject)
128
+ -- awful
129
+ local frames = 0
130
+ while true do
131
+ local _condition = returnValue == nil
132
+ if _condition then
133
+ local _original = frames
134
+ frames += 1
135
+ _condition = _original < 400
136
+ end
137
+ if not _condition then
138
+ break
139
+ end
140
+ RunService.Heartbeat:Wait()
141
+ end
142
+ if frames == 400 then
143
+ return reject("[tether::error] Client function timed out (no response)")
144
+ end
145
+ -- clean up the callback after receiving the response
146
+ functions[responseCallback] = nil
147
+ resolve(returnValue)
148
+ end)
149
+ end
150
+ end
151
+ return {
152
+ ClientEmitter = ClientEmitter,
153
+ }
@@ -0,0 +1,26 @@
1
+ import type { MessageEmitter } from "./message-emitter";
2
+ import type { BaseMessage, MessageCallback, FunctionMessageCallback } from "../structs";
3
+ type Cleanup = () => void;
4
+ export declare abstract class ContextualEmitter<MessageData> {
5
+ protected readonly master: MessageEmitter<MessageData>;
6
+ abstract readonly context: "client" | "server";
7
+ /**
8
+ * Constructs a new ContextualEmitter.
9
+ * @param master The master emitter to which messages are delegated.
10
+ */
11
+ constructor(master: MessageEmitter<MessageData>);
12
+ abstract emit<K extends keyof MessageData>(player: (K & BaseMessage) | (Player | Player[]), message?: MessageData[K] | (K & BaseMessage), data?: MessageData[K] | boolean, unreliable?: boolean): void;
13
+ abstract setCallback<K extends keyof MessageData, R extends keyof MessageData>(message: K & BaseMessage, returnMessage: R & BaseMessage, callback: FunctionMessageCallback<MessageData[K], MessageData[R]>): Cleanup;
14
+ abstract invoke<K extends keyof MessageData, R extends keyof MessageData>(message: K & BaseMessage, returnMessage: R & BaseMessage, player?: Player | MessageData[K], data?: MessageData[K] | boolean, unreliable?: boolean): Promise<MessageData[R]>;
15
+ /**
16
+ * @returns A destructor function that disconnects the callback from the message
17
+ */
18
+ on<K extends keyof MessageData>(message: K & BaseMessage, callback: MessageCallback<MessageData[K]>): Cleanup;
19
+ /**
20
+ * Disconnects the callback as soon as it is called for the first time
21
+ *
22
+ * @returns A destructor function that disconnects the callback from the message
23
+ */
24
+ once<K extends keyof MessageData>(message: K & BaseMessage, callback: MessageCallback<MessageData[K]>): Cleanup;
25
+ }
26
+ export {};
@@ -0,0 +1,49 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local RunService = TS.import(script, TS.getModule(script, "@rbxts", "services")).RunService
4
+ local ContextualEmitter
5
+ do
6
+ ContextualEmitter = {}
7
+ function ContextualEmitter:constructor(master)
8
+ self.master = master
9
+ end
10
+ function ContextualEmitter:on(message, callback)
11
+ local isClient = self.context == "client"
12
+ if RunService:IsClient() and not isClient then
13
+ return error("[tether::error] Cannot listen to server message from client")
14
+ elseif RunService:IsServer() and isClient then
15
+ return error("[tether::error] Cannot listen to client message from server")
16
+ end
17
+ local callbacksMap = if isClient then self.master.clientCallbacks else self.master.serverCallbacks
18
+ local _message = message
19
+ if not (callbacksMap[_message] ~= nil) then
20
+ local _message_1 = message
21
+ callbacksMap[_message_1] = {}
22
+ end
23
+ local _message_1 = message
24
+ local callbacks = callbacksMap[_message_1]
25
+ local _callback = callback
26
+ callbacks[_callback] = true
27
+ local _message_2 = message
28
+ callbacksMap[_message_2] = callbacks
29
+ return function()
30
+ local _callback_1 = callback
31
+ -- ▼ Set.delete ▼
32
+ local _valueExisted = callbacks[_callback_1] ~= nil
33
+ callbacks[_callback_1] = nil
34
+ -- ▲ Set.delete ▲
35
+ return _valueExisted
36
+ end
37
+ end
38
+ function ContextualEmitter:once(message, callback)
39
+ local destructor
40
+ destructor = self:on(message, function(player, data)
41
+ destructor()
42
+ callback(player, data)
43
+ end)
44
+ return destructor
45
+ end
46
+ end
47
+ return {
48
+ ContextualEmitter = ContextualEmitter,
49
+ }
@@ -0,0 +1,52 @@
1
+ import { Modding } from "@flamework/core";
2
+ import Destroyable from "@rbxts/destroyable";
3
+ import { MiddlewareProvider } from "../middleware";
4
+ import type { ClientMessageCallback, ServerMessageCallback, BaseMessage, MessageEmitterMetadata } from "../structs";
5
+ import { ServerEmitter } from "./server-emitter";
6
+ import { ClientEmitter } from "./client-emitter";
7
+ interface MessageEmitterOptions<MessageData> {
8
+ readonly batchRemotes: boolean;
9
+ readonly batchRate: number;
10
+ readonly doNotBatch: Set<keyof MessageData>;
11
+ }
12
+ export declare class MessageEmitter<MessageData> extends Destroyable {
13
+ private readonly options;
14
+ readonly server: ServerEmitter<MessageData>;
15
+ readonly client: ClientEmitter<MessageData>;
16
+ readonly middleware: MiddlewareProvider<MessageData>;
17
+ /** @hidden */ clientCallbacks: Map<keyof MessageData, Set<ClientMessageCallback>>;
18
+ /** @hidden */ clientFunctions: Map<keyof MessageData, Set<(data: unknown) => void>>;
19
+ /** @hidden */ serverCallbacks: Map<keyof MessageData, Set<ServerMessageCallback>>;
20
+ /** @hidden */ serverFunctions: Map<keyof MessageData, Set<(data: unknown) => void>>;
21
+ private readonly guards;
22
+ private serializers;
23
+ private serverQueue;
24
+ private clientBroadcastQueue;
25
+ private clientQueue;
26
+ /** @metadata macro */
27
+ static create<MessageData>(options?: Partial<MessageEmitterOptions<MessageData>>, meta?: Modding.Many<MessageEmitterMetadata<MessageData>>): MessageEmitter<MessageData>;
28
+ private constructor();
29
+ /** @hidden */
30
+ queueMessage<K extends keyof MessageData>(context: "client" | "server" | true, message: K & BaseMessage, data: (MessageEmitter<MessageData>["clientQueue"] | MessageEmitter<MessageData>["serverQueue"])[number]): void;
31
+ /** @hidden */
32
+ runClientMiddlewares<Kind extends keyof MessageData>(message: Kind & BaseMessage, data?: MessageData[Kind], player?: Player | Player[]): [boolean, MessageData[Kind]];
33
+ /** @hidden */
34
+ runServerMiddlewares<Kind extends keyof MessageData>(message: Kind & BaseMessage, data?: MessageData[Kind]): [boolean, MessageData[Kind]];
35
+ /** Set up emitter connections */
36
+ private initialize;
37
+ /** Send all queued data across the network simultaneously */
38
+ private relay;
39
+ private validateData;
40
+ private onRemoteFire;
41
+ private executeFunctions;
42
+ private executeEventCallbacks;
43
+ private shouldBatch;
44
+ private deserializeAndValidate;
45
+ private getPacket;
46
+ /** @metadata macro */
47
+ private addSerializer;
48
+ /** @metadata macro */
49
+ private createMessageSerializer;
50
+ private getSerializer;
51
+ }
52
+ export {};
@@ -8,23 +8,25 @@ local Destroyable = TS.import(script, TS.getModule(script, "@rbxts", "destroyabl
8
8
  local Object = TS.import(script, TS.getModule(script, "@rbxts", "object-utils"))
9
9
  local createSerializer = TS.import(script, TS.getModule(script, "@rbxts", "serio").out).default
10
10
  local repr = TS.import(script, TS.getModule(script, "@rbxts", "repr").out)
11
- local _middleware = TS.import(script, script.Parent, "middleware")
11
+ local _middleware = TS.import(script, script.Parent.Parent, "middleware")
12
12
  local DropRequest = _middleware.DropRequest
13
13
  local MiddlewareProvider = _middleware.MiddlewareProvider
14
- local IS_LUNE = string.sub(_VERSION, 1, 4) == "Lune"
14
+ local ServerEmitter = TS.import(script, script.Parent, "server-emitter").ServerEmitter
15
+ local ClientEmitter = TS.import(script, script.Parent, "client-emitter").ClientEmitter
16
+ local _utility = TS.import(script, script.Parent.Parent, "utility")
17
+ local readMessage = _utility.readMessage
18
+ local writeMessage = _utility.writeMessage
15
19
  if setLuneContext == nil then
16
20
  setLuneContext = function() end
17
21
  end
18
22
  setLuneContext("both")
19
- local noServerListen = "[@rbxts/tether]: Cannot listen to server message from client"
20
- local noClientListen = "[@rbxts/tether]: Cannot listen to client message from server"
21
- 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"
22
23
  local guardFailed = function(message, data)
23
- return `[@rbxts/tether]: Type validation guard failed for message '{message}' - check your sent data\nSent data: {repr(data)}`
24
+ return `[tether::warning]: Type validation guard failed for message '{message}' - check your sent data\nSent data: {repr(data)}`
24
25
  end
25
26
  local defaultMesssageEmitterOptions = {
26
27
  batchRemotes = true,
27
28
  batchRate = 1 / 24,
29
+ doNotBatch = {},
28
30
  }
29
31
  local sendMessage
30
32
  do
@@ -66,214 +68,18 @@ do
66
68
  end
67
69
  super.constructor(self)
68
70
  self.options = options
71
+ self.server = ServerEmitter.new(self)
72
+ self.client = ClientEmitter.new(self)
69
73
  self.middleware = MiddlewareProvider.new()
70
- self.guards = {}
71
- self.serializers = {}
72
74
  self.clientCallbacks = {}
73
75
  self.clientFunctions = {}
74
76
  self.serverCallbacks = {}
75
77
  self.serverFunctions = {}
78
+ self.guards = {}
79
+ self.serializers = {}
76
80
  self.serverQueue = {}
77
81
  self.clientBroadcastQueue = {}
78
82
  self.clientQueue = {}
79
- self.server = {
80
- on = function(message, callback)
81
- if RunService:IsClient() then
82
- error(noServerListen)
83
- end
84
- return self:on(message, callback, self.serverCallbacks)
85
- end,
86
- once = function(message, callback)
87
- if RunService:IsClient() then
88
- error(noServerListen)
89
- end
90
- return self:once(message, callback, self.serverCallbacks)
91
- end,
92
- emit = function(message, data, unreliable)
93
- if unreliable == nil then
94
- unreliable = false
95
- end
96
- if RunService:IsServer() then
97
- error("[@rbxts/tether]: Cannot emit message to server from server")
98
- end
99
- task.spawn(function()
100
- local _binding = self:runServerMiddlewares(message, data)
101
- local dropRequest = _binding[1]
102
- local newData = _binding[2]
103
- if dropRequest then
104
- return nil
105
- end
106
- local _serverQueue = self.serverQueue
107
- local _arg0 = { message, newData, unreliable }
108
- table.insert(_serverQueue, _arg0)
109
- if not self.options.batchRemotes then
110
- self:update()
111
- end
112
- end)
113
- end,
114
- invoke = TS.async(function(message, returnMessage, data, unreliable)
115
- if unreliable == nil then
116
- unreliable = false
117
- end
118
- if RunService:IsServer() then
119
- error("[@rbxts/tether]: Cannot invoke server function from server")
120
- end
121
- local _clientFunctions = self.clientFunctions
122
- local _returnMessage = returnMessage
123
- if not (_clientFunctions[_returnMessage] ~= nil) then
124
- local _clientFunctions_1 = self.clientFunctions
125
- local _returnMessage_1 = returnMessage
126
- _clientFunctions_1[_returnMessage_1] = {}
127
- end
128
- local _clientFunctions_1 = self.clientFunctions
129
- local _returnMessage_1 = returnMessage
130
- local functions = _clientFunctions_1[_returnMessage_1]
131
- local returnValue
132
- functions[function(data)
133
- returnValue = data
134
- return returnValue
135
- end] = true
136
- self.server.emit(message, data, unreliable)
137
- while returnValue == nil do
138
- RunService.Heartbeat:Wait()
139
- end
140
- return returnValue
141
- end),
142
- setCallback = function(message, returnMessage, callback)
143
- if RunService:IsClient() then
144
- error(noServerListen)
145
- end
146
- return self.server.on(message, function(player, data)
147
- local returnValue = callback(player, data)
148
- self.client.emit(player, returnMessage, returnValue)
149
- end)
150
- end,
151
- }
152
- self.client = {
153
- on = function(message, callback)
154
- if RunService:IsServer() then
155
- error(noClientListen)
156
- end
157
- return self:on(message, callback, self.clientCallbacks)
158
- end,
159
- once = function(message, callback)
160
- if RunService:IsServer() then
161
- error(noClientListen)
162
- end
163
- return self:once(message, callback, self.clientCallbacks)
164
- end,
165
- emit = function(player, message, data, unreliable)
166
- if unreliable == nil then
167
- unreliable = false
168
- end
169
- if RunService:IsClient() then
170
- error("[@rbxts/tether]: Cannot emit message to client from client")
171
- end
172
- task.spawn(function()
173
- local _binding = self:runClientMiddlewares(message, data)
174
- local dropRequest = _binding[1]
175
- local newData = _binding[2]
176
- if dropRequest then
177
- return nil
178
- end
179
- local _clientQueue = self.clientQueue
180
- local _arg0 = { player, message, newData, unreliable }
181
- table.insert(_clientQueue, _arg0)
182
- if not self.options.batchRemotes then
183
- self:update()
184
- end
185
- end)
186
- end,
187
- emitExcept = function(player, message, data, unreliable)
188
- if unreliable == nil then
189
- unreliable = false
190
- end
191
- local shouldSendTo = function(p)
192
- local _player = player
193
- local _result
194
- if typeof(_player) == "Instance" then
195
- _result = p ~= player
196
- else
197
- local _player_1 = player
198
- local _p = p
199
- _result = not (table.find(_player_1, _p) ~= nil)
200
- end
201
- return _result
202
- end
203
- local _client = self.client
204
- local _exp = Players:GetPlayers()
205
- -- ▼ ReadonlyArray.filter ▼
206
- local _newValue = {}
207
- local _length = 0
208
- for _k, _v in _exp do
209
- if shouldSendTo(_v, _k - 1, _exp) == true then
210
- _length += 1
211
- _newValue[_length] = _v
212
- end
213
- end
214
- -- ▲ ReadonlyArray.filter ▲
215
- _client.emit(_newValue, message, data, unreliable)
216
- end,
217
- emitAll = function(message, data, unreliable)
218
- if unreliable == nil then
219
- unreliable = false
220
- end
221
- if RunService:IsClient() then
222
- error("[@rbxts/tether]: Cannot emit message to all clients from client")
223
- end
224
- task.spawn(function()
225
- local _binding = self:runClientMiddlewares(message, data)
226
- local dropRequest = _binding[1]
227
- local newData = _binding[2]
228
- if dropRequest then
229
- return nil
230
- end
231
- local _clientBroadcastQueue = self.clientBroadcastQueue
232
- local _arg0 = { message, newData, unreliable }
233
- table.insert(_clientBroadcastQueue, _arg0)
234
- if not self.options.batchRemotes then
235
- self:update()
236
- end
237
- end)
238
- end,
239
- invoke = TS.async(function(message, returnMessage, player, data, unreliable)
240
- if unreliable == nil then
241
- unreliable = false
242
- end
243
- if RunService:IsClient() then
244
- error("[@rbxts/tether]: Cannot invoke client function from client")
245
- end
246
- local _serverFunctions = self.serverFunctions
247
- local _returnMessage = returnMessage
248
- if not (_serverFunctions[_returnMessage] ~= nil) then
249
- local _serverFunctions_1 = self.serverFunctions
250
- local _returnMessage_1 = returnMessage
251
- _serverFunctions_1[_returnMessage_1] = {}
252
- end
253
- local _serverFunctions_1 = self.serverFunctions
254
- local _returnMessage_1 = returnMessage
255
- local functions = _serverFunctions_1[_returnMessage_1]
256
- local returnValue
257
- functions[function(data)
258
- returnValue = data
259
- return returnValue
260
- end] = true
261
- self.client.emit(player, message, data, unreliable)
262
- while returnValue == nil do
263
- RunService.Heartbeat:Wait()
264
- end
265
- return returnValue
266
- end),
267
- setCallback = function(message, returnMessage, callback)
268
- if RunService:IsServer() then
269
- error(noClientListen)
270
- end
271
- return self.client.on(message, function(data)
272
- local returnValue = callback(data)
273
- self.server.emit(returnMessage, returnValue)
274
- end)
275
- end,
276
- }
277
83
  self.trash:add(function()
278
84
  self.clientCallbacks = {}
279
85
  self.serverCallbacks = {}
@@ -286,9 +92,10 @@ do
286
92
  function MessageEmitter:create(options, meta)
287
93
  local emitter = MessageEmitter.new(Object.assign({}, defaultMesssageEmitterOptions, options))
288
94
  if meta == nil then
289
- warn(metaGenerationFailed)
95
+ warn("[tether::warning] Failed to generate message metadata - make sure you have the Flamework transformer and are using Flamework macro-friendly types in your schemas")
290
96
  return emitter:initialize()
291
97
  end
98
+ -- https://discord.com/channels/476080952636997633/506983834877689856/1363938149486821577
292
99
  for kind, _binding in pairs(meta) do
293
100
  local guard = _binding.guard
294
101
  local serializerMetadata = _binding.serializerMetadata
@@ -301,6 +108,87 @@ do
301
108
  end
302
109
  return emitter:initialize()
303
110
  end
111
+ function MessageEmitter:queueMessage(context, message, data)
112
+ local queue = if context == "client" then self.clientQueue elseif context == true then self.clientBroadcastQueue else self.serverQueue
113
+ local _data = data
114
+ table.insert(queue, _data)
115
+ if not self:shouldBatch(message) then
116
+ self:relay()
117
+ end
118
+ end
119
+ function MessageEmitter:runClientMiddlewares(message, data, player)
120
+ if not self:validateData(message, data) then
121
+ return { true, data }
122
+ end
123
+ local players = player or Players:GetPlayers()
124
+ local ctx = {
125
+ message = message,
126
+ data = data,
127
+ getRawData = function()
128
+ return self:getPacket(message, data)
129
+ end,
130
+ }
131
+ for _, globalMiddleware in self.middleware:getClientGlobal() do
132
+ local result = globalMiddleware(players, ctx)
133
+ if not self:validateData(message, ctx.data, "Invalid data after global client middleware") then
134
+ return { false, ctx.data }
135
+ end
136
+ if result == DropRequest then
137
+ self.middleware:notifyRequestDropped(message, "Global client middleware")
138
+ return { true, ctx.data }
139
+ end
140
+ end
141
+ for _, middleware in self.middleware:getClient(message) do
142
+ local result = middleware(players, ctx)
143
+ if not self:validateData(message, ctx.data, "Invalid data after client middleware") then
144
+ return { false, ctx.data }
145
+ end
146
+ if result == DropRequest then
147
+ self.middleware:notifyRequestDropped(message, "Client middleware")
148
+ return { true, ctx.data }
149
+ end
150
+ end
151
+ if not self:validateData(message, ctx.data) then
152
+ return { true, ctx.data }
153
+ end
154
+ return { false, ctx.data }
155
+ end
156
+ function MessageEmitter:runServerMiddlewares(message, data)
157
+ if not self:validateData(message, data) then
158
+ return { true, data }
159
+ end
160
+ local ctx = {
161
+ message = message,
162
+ data = data,
163
+ getRawData = function()
164
+ return self:getPacket(message, data)
165
+ end,
166
+ }
167
+ for _, globalMiddleware in self.middleware:getServerGlobal() do
168
+ if not self:validateData(message, ctx.data, "Invalid data after global server middleware") then
169
+ return { false, ctx.data }
170
+ end
171
+ local result = globalMiddleware(ctx)
172
+ if result == DropRequest then
173
+ self.middleware:notifyRequestDropped(message, "Global server middleware")
174
+ return { true, ctx.data }
175
+ end
176
+ end
177
+ for _, middleware in self.middleware:getServer(message) do
178
+ if not self:validateData(message, ctx.data, "Invalid data after server middleware") then
179
+ return { false, ctx.data }
180
+ end
181
+ local result = middleware(ctx)
182
+ if result == DropRequest then
183
+ self.middleware:notifyRequestDropped(message, "Server middleware")
184
+ return { true, ctx.data }
185
+ end
186
+ end
187
+ if not self:validateData(message, ctx.data) then
188
+ return { true, ctx.data }
189
+ end
190
+ return { false, ctx.data }
191
+ end
304
192
  function MessageEmitter:initialize()
305
193
  setLuneContext("client")
306
194
  if RunService:IsClient() then
@@ -337,11 +225,11 @@ do
337
225
  return nil
338
226
  end
339
227
  elapsed -= batchRate
340
- self:update()
228
+ self:relay()
341
229
  end))
342
230
  return self
343
231
  end
344
- function MessageEmitter:update()
232
+ function MessageEmitter:relay()
345
233
  local getPacket = function(info)
346
234
  return info.packet
347
235
  end
@@ -572,79 +460,6 @@ do
572
460
  self.clientQueue = {}
573
461
  end
574
462
  end
575
- function MessageEmitter:runClientMiddlewares(message, data, player)
576
- if not self:validateData(message, data) then
577
- return { true, data }
578
- end
579
- local players = player or Players:GetPlayers()
580
- local ctx = {
581
- message = message,
582
- data = data,
583
- getRawData = function()
584
- return self:getPacket(message, data)
585
- end,
586
- }
587
- for _, globalMiddleware in self.middleware:getClientGlobal() do
588
- local result = globalMiddleware(players, ctx)
589
- if not self:validateData(message, ctx.data, "Invalid data after global client middleware") then
590
- return { false, ctx.data }
591
- end
592
- if result == DropRequest then
593
- self.middleware:notifyRequestDropped(message, "Global client middleware")
594
- return { true, ctx.data }
595
- end
596
- end
597
- for _, middleware in self.middleware:getClient(message) do
598
- local result = middleware(players, ctx)
599
- if not self:validateData(message, ctx.data, "Invalid data after client middleware") then
600
- return { false, ctx.data }
601
- end
602
- if result == DropRequest then
603
- self.middleware:notifyRequestDropped(message, "Client middleware")
604
- return { true, ctx.data }
605
- end
606
- end
607
- if not self:validateData(message, ctx.data) then
608
- return { true, ctx.data }
609
- end
610
- return { false, ctx.data }
611
- end
612
- function MessageEmitter:runServerMiddlewares(message, data)
613
- if not self:validateData(message, data) then
614
- return { true, data }
615
- end
616
- local ctx = {
617
- message = message,
618
- data = data,
619
- getRawData = function()
620
- return self:getPacket(message, data)
621
- end,
622
- }
623
- for _, globalMiddleware in self.middleware:getServerGlobal() do
624
- if not self:validateData(message, ctx.data, "Invalid data after global server middleware") then
625
- return { false, ctx.data }
626
- end
627
- local result = globalMiddleware(ctx)
628
- if result == DropRequest then
629
- self.middleware:notifyRequestDropped(message, "Global server middleware")
630
- return { true, ctx.data }
631
- end
632
- end
633
- for _, middleware in self.middleware:getServer(message) do
634
- if not self:validateData(message, ctx.data, "Invalid data after server middleware") then
635
- return { false, ctx.data }
636
- end
637
- local result = middleware(ctx)
638
- if result == DropRequest then
639
- self.middleware:notifyRequestDropped(message, "Server middleware")
640
- return { true, ctx.data }
641
- end
642
- end
643
- if not self:validateData(message, ctx.data) then
644
- return { true, ctx.data }
645
- end
646
- return { false, ctx.data }
647
- end
648
463
  function MessageEmitter:validateData(message, data, requestDropReason)
649
464
  if requestDropReason == nil then
650
465
  requestDropReason = "Invalid data"
@@ -662,9 +477,9 @@ do
662
477
  function MessageEmitter:onRemoteFire(isServer, serializedPackets, player)
663
478
  for _, packet in serializedPackets do
664
479
  if buffer.len(packet.messageBuf) > 1 then
665
- return warn("[@rbxts/tether]: Rejected packet because message buffer was larger than one byte")
480
+ return warn("[tether::warning] Rejected packet because message buffer was larger than one byte")
666
481
  end
667
- local message = buffer.readu8(packet.messageBuf, 0)
482
+ local message = readMessage(packet)
668
483
  self:executeEventCallbacks(isServer, message, packet, player)
669
484
  self:executeFunctions(isServer, message, packet)
670
485
  end
@@ -697,6 +512,15 @@ do
697
512
  end
698
513
  end
699
514
  end
515
+ function MessageEmitter:shouldBatch(message)
516
+ local _condition = self.options.batchRemotes
517
+ if _condition then
518
+ local _doNotBatch = self.options.doNotBatch
519
+ local _message = message
520
+ _condition = not (_doNotBatch[_message] ~= nil)
521
+ end
522
+ return _condition
523
+ end
700
524
  function MessageEmitter:deserializeAndValidate(message, serializedPacket)
701
525
  local serializer = self:getSerializer(message)
702
526
  local _packet = serializer
@@ -707,43 +531,10 @@ do
707
531
  self:validateData(message, packet)
708
532
  return packet
709
533
  end
710
- function MessageEmitter:once(message, callback, callbacksMap)
711
- local destructor
712
- destructor = self:on(message, function(player, data)
713
- destructor()
714
- callback(player, data)
715
- end, callbacksMap)
716
- return destructor
717
- end
718
- function MessageEmitter:on(message, callback, callbacksMap)
719
- local _callbacksMap = callbacksMap
720
- local _message = message
721
- if not (_callbacksMap[_message] ~= nil) then
722
- local _callbacksMap_1 = callbacksMap
723
- local _message_1 = message
724
- _callbacksMap_1[_message_1] = {}
725
- end
726
- local _callbacksMap_1 = callbacksMap
727
- local _message_1 = message
728
- local callbacks = _callbacksMap_1[_message_1]
729
- local _callback = callback
730
- callbacks[_callback] = true
731
- local _callbacksMap_2 = callbacksMap
732
- local _message_2 = message
733
- _callbacksMap_2[_message_2] = callbacks
734
- return function()
735
- local _callback_1 = callback
736
- -- ▼ Set.delete ▼
737
- local _valueExisted = callbacks[_callback_1] ~= nil
738
- callbacks[_callback_1] = nil
739
- -- ▲ Set.delete ▲
740
- return _valueExisted
741
- end
742
- end
743
534
  function MessageEmitter:getPacket(message, data)
744
535
  local serializer = self:getSerializer(message)
745
536
  local messageBuf = buffer.create(1)
746
- buffer.writeu8(messageBuf, 0, message)
537
+ writeMessage(messageBuf, message)
747
538
  if serializer == nil then
748
539
  return {
749
540
  messageBuf = messageBuf,
@@ -0,0 +1,30 @@
1
+ import { ContextualEmitter } from "./contextual-emitter";
2
+ import type { BaseMessage, ServerMessageCallback, ServerFunctionMessageCallback } from "../structs";
3
+ export declare class ServerEmitter<MessageData> extends ContextualEmitter<MessageData> {
4
+ readonly context = "server";
5
+ readonly on: <K extends keyof MessageData>(this: ServerEmitter<MessageData>, message: K & BaseMessage, callback: ServerMessageCallback<MessageData[K]>) => () => void;
6
+ readonly once: <K extends keyof MessageData>(this: ServerEmitter<MessageData>, message: K & BaseMessage, callback: ServerMessageCallback<MessageData[K]>) => () => void;
7
+ /**
8
+ * Emits a message to the server
9
+ *
10
+ * @param message The message kind to be sent
11
+ * @param data The data associated with the message
12
+ * @param unreliable Whether the message should be sent unreliably
13
+ */
14
+ emit<K extends keyof MessageData>(message: K & BaseMessage, data?: MessageData[K], unreliable?: boolean): void;
15
+ /**
16
+ * Sets a callback for a simulated remote function
17
+ *
18
+ * @returns A destructor function that disconnects the callback from the message
19
+ */
20
+ setCallback<K extends keyof MessageData, R extends keyof MessageData>(message: K & BaseMessage, returnMessage: R & BaseMessage, callback: ServerFunctionMessageCallback<MessageData[K], MessageData[R]>): () => void;
21
+ /**
22
+ * Simulates a remote function invocation
23
+ *
24
+ * @param message The message kind to be sent
25
+ * @param returnMessage The message kind to be returned
26
+ * @param data The data associated with the message
27
+ * @param unreliable Whether the message should be sent unreliably
28
+ */
29
+ invoke<K extends keyof MessageData, R extends keyof MessageData>(message: K & BaseMessage, returnMessage: R & BaseMessage, data?: MessageData[K], unreliable?: boolean): Promise<MessageData[R]>;
30
+ }
@@ -0,0 +1,104 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local RunService = TS.import(script, TS.getModule(script, "@rbxts", "services")).RunService
4
+ local ContextualEmitter = TS.import(script, script.Parent, "contextual-emitter").ContextualEmitter
5
+ local ServerEmitter
6
+ do
7
+ local super = ContextualEmitter
8
+ ServerEmitter = setmetatable({}, {
9
+ __tostring = function()
10
+ return "ServerEmitter"
11
+ end,
12
+ __index = super,
13
+ })
14
+ ServerEmitter.__index = ServerEmitter
15
+ function ServerEmitter.new(...)
16
+ local self = setmetatable({}, ServerEmitter)
17
+ return self:constructor(...) or self
18
+ end
19
+ function ServerEmitter:constructor(...)
20
+ super.constructor(self, ...)
21
+ self.context = "server"
22
+ end
23
+ function ServerEmitter:emit(message, data, unreliable)
24
+ if unreliable == nil then
25
+ unreliable = false
26
+ end
27
+ if RunService:IsServer() then
28
+ error("[tether::error] Cannot emit message from server to server")
29
+ end
30
+ task.spawn(function()
31
+ local _binding = self.master:runServerMiddlewares(message, data)
32
+ local dropRequest = _binding[1]
33
+ local newData = _binding[2]
34
+ if dropRequest then
35
+ return nil
36
+ end
37
+ self.master:queueMessage(self.context, message, { message, newData, unreliable })
38
+ end)
39
+ end
40
+ function ServerEmitter:setCallback(message, returnMessage, callback)
41
+ if RunService:IsClient() then
42
+ error("[tether::error] Cannot listen to client message from server")
43
+ end
44
+ return self:on(message, function(player, data)
45
+ local returnValue = callback(player, data)
46
+ -- Defer the response emission to end of frame and swap context to avoid context check issues
47
+ -- task.defer guarantees response is sent by end of current frame, ensuring predictable timing in production
48
+ task.defer(function()
49
+ setLuneContext("server")
50
+ self.master.client:emit(player, returnMessage, returnValue)
51
+ setLuneContext("both")
52
+ end)
53
+ end)
54
+ end
55
+ function ServerEmitter:invoke(message, returnMessage, data, unreliable)
56
+ if unreliable == nil then
57
+ unreliable = false
58
+ end
59
+ if RunService:IsServer() then
60
+ error("[tether::error] Cannot invoke function from server to server")
61
+ end
62
+ local _binding = self.master
63
+ local clientFunctions = _binding.clientFunctions
64
+ local _returnMessage = returnMessage
65
+ if not (clientFunctions[_returnMessage] ~= nil) then
66
+ local _returnMessage_1 = returnMessage
67
+ clientFunctions[_returnMessage_1] = {}
68
+ end
69
+ local _returnMessage_1 = returnMessage
70
+ local functions = clientFunctions[_returnMessage_1]
71
+ local returnValue
72
+ local responseCallback = function(data)
73
+ returnValue = data
74
+ return returnValue
75
+ end
76
+ functions[responseCallback] = true
77
+ self:emit(message, data, unreliable)
78
+ return TS.Promise.new(function(resolve, reject)
79
+ -- awful
80
+ local frames = 0
81
+ while true do
82
+ local _condition = returnValue == nil
83
+ if _condition then
84
+ local _original = frames
85
+ frames += 1
86
+ _condition = _original < 400
87
+ end
88
+ if not _condition then
89
+ break
90
+ end
91
+ RunService.Heartbeat:Wait()
92
+ end
93
+ if frames == 400 then
94
+ return reject("[tether::error] Server function timed out (no response)")
95
+ end
96
+ -- clean up the callback after receiving the response
97
+ functions[responseCallback] = nil
98
+ resolve(returnValue)
99
+ end)
100
+ end
101
+ end
102
+ return {
103
+ ServerEmitter = ServerEmitter,
104
+ }
package/out/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { MessageEmitter } from "./message-emitter";
1
+ export { MessageEmitter } from "./emitters/message-emitter";
2
2
  export { BuiltinMiddlewares } from "./builtin-middlewares";
3
3
  export { DropRequest, type ClientMiddleware, type ServerMiddleware, type SharedMiddleware, type Middleware, type MiddlewareContext } from "./middleware";
package/out/init.luau CHANGED
@@ -1,7 +1,7 @@
1
1
  -- Compiled with roblox-ts v3.0.0
2
2
  local TS = _G[script]
3
3
  local exports = {}
4
- exports.MessageEmitter = TS.import(script, script, "message-emitter").MessageEmitter
4
+ exports.MessageEmitter = TS.import(script, script, "emitters", "message-emitter").MessageEmitter
5
5
  exports.BuiltinMiddlewares = TS.import(script, script, "builtin-middlewares").BuiltinMiddlewares
6
6
  exports.DropRequest = TS.import(script, script, "middleware").DropRequest
7
7
  return exports
@@ -0,0 +1,15 @@
1
+ export declare const enum Error {
2
+ NoServerListen = "[tether::error] Cannot listen to server message from client",
3
+ NoClientListen = "[tether::error] Cannot listen to client message from server",
4
+ NoServerToServer = "[tether::error] Cannot emit message from server to server",
5
+ NoClientToClient = "[tether::error] Cannot emit message from client to client",
6
+ NoClientToAllClients = "[tether::error] Cannot emit message from client to all clients",
7
+ NoServerToServerFunction = "[tether::error] Cannot invoke function from server to server",
8
+ NoClientToClientFunction = "[tether::error] Cannot invoke function from client to client",
9
+ ServerFunctionTimeout = "[tether::error] Server function timed out (no response)",
10
+ ClientFunctionTimeout = "[tether::error] Client function timed out (no response)"
11
+ }
12
+ export declare const enum Warning {
13
+ MessageBufferTooLong = "[tether::warning] Rejected packet because message buffer was larger than one byte",
14
+ MetaGenerationFailed = "[tether::warning] Failed to generate message metadata - make sure you have the Flamework transformer and are using Flamework macro-friendly types in your schemas"
15
+ }
@@ -0,0 +1,4 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local errorTag = "[tether::error] "
3
+ local warnTag = "[tether::warning] "
4
+ return nil
package/out/structs.d.ts CHANGED
@@ -1,10 +1,11 @@
1
1
  import type { Modding } from "@flamework/core";
2
2
  import type { SerializerMetadata, SerializedData, Transform, Vector, String, u8, u16, u24, u32, i8, i16, i24, i32, f16, f24, f32, f64 } from "@rbxts/serio";
3
3
  export type MessageCallback<T = unknown> = ServerMessageCallback<T> | ClientMessageCallback<T>;
4
+ export type FunctionMessageCallback<T = unknown, R = unknown> = ServerFunctionMessageCallback<T, R> | ClientFunctionMessageCallback<T, R>;
4
5
  export type ClientMessageCallback<T = unknown> = (data: T) => void;
5
- export type ClientMessageFunctionCallback<T = unknown, R = unknown> = (data: T) => R;
6
+ export type ClientFunctionMessageCallback<T = unknown, R = unknown> = (data: T) => R;
6
7
  export type ServerMessageCallback<T = unknown> = (player: Player, data: T) => void;
7
- export type ServerMessageFunctionCallback<T = unknown, R = unknown> = (player: Player, data: T) => R;
8
+ export type ServerFunctionMessageCallback<T = unknown, R = unknown> = (player: Player, data: T) => R;
8
9
  export type BaseMessage = number;
9
10
  export interface PacketInfo {
10
11
  readonly packet: SerializedPacket;
package/out/utility.d.ts CHANGED
@@ -1 +1,6 @@
1
+ import type { BaseMessage, SerializedPacket } from "./structs";
1
2
  export declare function bufferToString(buf?: buffer): string;
3
+ export declare function encodeMessage(message: BaseMessage): number;
4
+ export declare function decodeMessage(encoded: number): BaseMessage;
5
+ export declare function writeMessage(buf: buffer, message: BaseMessage): void;
6
+ export declare function readMessage(packet: SerializedPacket | buffer): BaseMessage;
package/out/utility.luau CHANGED
@@ -1,4 +1,5 @@
1
1
  -- Compiled with roblox-ts v3.0.0
2
+ local COEFF = 0xFA
2
3
  local function bufferToString(buf)
3
4
  local s = { "{ " }
4
5
  if buf ~= nil then
@@ -25,6 +26,28 @@ local function bufferToString(buf)
25
26
  table.insert(s, "}")
26
27
  return table.concat(s, "")
27
28
  end
29
+ local function encodeMessage(message)
30
+ message = bit32.band((bit32.bxor(message, COEFF)), 0xFF)
31
+ return bit32.bor((bit32.lshift(message, 3)), (bit32.arshift(message, 5)))
32
+ end
33
+ local function decodeMessage(encoded)
34
+ encoded = bit32.bor((bit32.arshift(encoded, 3)), (bit32.lshift(encoded, 5)))
35
+ return bit32.band((bit32.bxor(encoded, COEFF)), 0xFF)
36
+ end
37
+ local function writeMessage(buf, message)
38
+ local _arg0 = buffer.len(buf) == 1
39
+ assert(_arg0)
40
+ buffer.writeu8(buf, 0, encodeMessage(message))
41
+ end
42
+ local function readMessage(packet)
43
+ local _packet = packet
44
+ local buf = if type(_packet) == "buffer" then packet else packet.messageBuf
45
+ return decodeMessage(buffer.readu8(buf, 0))
46
+ end
28
47
  return {
29
48
  bufferToString = bufferToString,
49
+ encodeMessage = encodeMessage,
50
+ decodeMessage = decodeMessage,
51
+ writeMessage = writeMessage,
52
+ readMessage = readMessage,
30
53
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rbxts/tether",
3
- "version": "1.3.24",
3
+ "version": "1.4.1",
4
4
  "main": "out/init.luau",
5
5
  "scripts": {
6
6
  "setup-rokit": "rokit trust lune-org/lune && rokit trust rojo-rbx/rojo && rokit install",
@@ -32,20 +32,20 @@
32
32
  },
33
33
  "devDependencies": {
34
34
  "@rbxts/compiler-types": "^3.0.0-types.0",
35
- "@rbxts/types": "^1.0.856",
35
+ "@rbxts/types": "^1.0.896",
36
36
  "rbxts-transformer-flamework": "^1.3.2",
37
37
  "roblox-ts": "^3.0.0",
38
38
  "ts-toolbelt": "^9.6.0",
39
- "typescript": "^5.5.3"
39
+ "typescript": "^5"
40
40
  },
41
41
  "dependencies": {
42
42
  "@flamework/core": "^1.3.2",
43
43
  "@rbxts/destroyable": "^1.0.9",
44
- "@rbxts/flamework-meta-utils": "^1.0.6",
44
+ "@rbxts/flamework-meta-utils": "^1.0.7",
45
45
  "@rbxts/object-utils": "^1.0.4",
46
46
  "@rbxts/repr": "^1.0.3",
47
47
  "@rbxts/serio": "^1.0.19",
48
- "@rbxts/services": "^1.5.5",
48
+ "@rbxts/services": "^1.6.0",
49
49
  "@rbxts/trash": "^1.0.8"
50
50
  }
51
51
  }
@@ -1,128 +0,0 @@
1
- import { Modding } from "@flamework/core";
2
- import Destroyable from "@rbxts/destroyable";
3
- import { MiddlewareProvider } from "./middleware";
4
- import type { ClientMessageCallback, ServerMessageCallback, BaseMessage, MessageEmitterMetadata, ClientMessageFunctionCallback, ServerMessageFunctionCallback } from "./structs";
5
- interface MessageEmitterOptions {
6
- readonly batchRemotes: boolean;
7
- readonly batchRate: number;
8
- }
9
- export declare class MessageEmitter<MessageData> extends Destroyable {
10
- private readonly options;
11
- readonly middleware: MiddlewareProvider<MessageData>;
12
- private readonly guards;
13
- private serializers;
14
- private clientCallbacks;
15
- private clientFunctions;
16
- private serverCallbacks;
17
- private serverFunctions;
18
- private serverQueue;
19
- private clientBroadcastQueue;
20
- private clientQueue;
21
- /** @metadata macro */
22
- static create<MessageData>(options?: Partial<MessageEmitterOptions>, meta?: Modding.Many<MessageEmitterMetadata<MessageData>>): MessageEmitter<MessageData>;
23
- private constructor();
24
- readonly server: {
25
- /**
26
- * @returns A destructor function that disconnects the callback from the message
27
- */
28
- on: <Kind extends keyof MessageData>(message: Kind & BaseMessage, callback: ServerMessageCallback<MessageData[Kind]>) => () => void;
29
- /**
30
- * Disconnects the callback as soon as it is called for the first time
31
- *
32
- * @returns A destructor function that disconnects the callback from the message
33
- */
34
- once: <Kind extends keyof MessageData>(message: Kind & BaseMessage, callback: ServerMessageCallback<MessageData[Kind]>) => () => void;
35
- /**
36
- * Emits a message to the server
37
- *
38
- * @param message The message kind to be sent
39
- * @param data The data associated with the message
40
- * @param unreliable Whether the message should be sent unreliably
41
- */
42
- emit: <Kind extends keyof MessageData>(message: Kind & BaseMessage, data?: MessageData[Kind], unreliable?: boolean) => void;
43
- /**
44
- * Simulates a remote function invocation.
45
- *
46
- * @param message The message kind to be sent
47
- * @param data The data associated with the message
48
- * @param unreliable Whether the message should be sent unreliably
49
- */
50
- invoke: <Kind extends keyof MessageData, ReturnKind extends keyof MessageData>(message: Kind & BaseMessage, returnMessage: ReturnKind & BaseMessage, data?: MessageData[Kind], unreliable?: boolean) => Promise<MessageData[ReturnKind]>;
51
- /**
52
- * Sets a callback for a simulated remote function
53
- *
54
- * @returns A destructor function that disconnects the callback from the message
55
- */
56
- setCallback: <Kind extends keyof MessageData, ReturnKind extends keyof MessageData>(message: Kind & BaseMessage, returnMessage: ReturnKind & BaseMessage, callback: ServerMessageFunctionCallback<MessageData[Kind], MessageData[ReturnKind]>) => () => void;
57
- };
58
- readonly client: {
59
- /**
60
- * @returns A destructor function that disconnects the callback from the message
61
- */
62
- on: <Kind extends keyof MessageData>(message: Kind & BaseMessage, callback: ClientMessageCallback<MessageData[Kind]>) => () => void;
63
- /**
64
- * Disconnects the callback as soon as it is called for the first time
65
- *
66
- * @returns A destructor function that disconnects the callback from the message
67
- */
68
- once: <Kind extends keyof MessageData>(message: Kind & BaseMessage, callback: ClientMessageCallback<MessageData[Kind]>) => () => void;
69
- /**
70
- * Emits a message to a specific client or multiple clients
71
- *
72
- * @param player The player(s) to whom the message is sent
73
- * @param message The message kind to be sent
74
- * @param data The data associated with the message
75
- * @param unreliable Whether the message should be sent unreliably
76
- */
77
- emit: <Kind extends keyof MessageData>(player: Player | Player[], message: Kind & BaseMessage, data?: MessageData[Kind], unreliable?: boolean) => void;
78
- /**
79
- * Emits a message to all clients except the specified client(s)
80
- *
81
- * @param player The player(s) to whom the message is not sent
82
- * @param message The message kind to be sent
83
- * @param data The data associated with the message
84
- * @param unreliable Whether the message should be sent unreliably
85
- */
86
- emitExcept: <Kind extends keyof MessageData>(player: Player | Player[], message: Kind & BaseMessage, data?: MessageData[Kind], unreliable?: boolean) => void;
87
- /**
88
- * Emits a message to all connected clients
89
- *
90
- * @param message The message kind to be sent
91
- * @param data The data associated with the message
92
- * @param unreliable Whether the message should be sent unreliably
93
- */
94
- emitAll: <Kind extends keyof MessageData>(message: Kind & BaseMessage, data?: MessageData[Kind], unreliable?: boolean) => void;
95
- /**
96
- * Simulates a remote function invocation.
97
- *
98
- * @param message The message kind to be sent
99
- * @param data The data associated with the message
100
- * @param unreliable Whether the message should be sent unreliably
101
- */
102
- invoke: <Kind extends keyof MessageData, ReturnKind extends keyof MessageData>(message: Kind & BaseMessage, returnMessage: ReturnKind & BaseMessage, player: Player, data?: MessageData[Kind], unreliable?: boolean) => Promise<MessageData[ReturnKind]>;
103
- /**
104
- * Sets a callback for a simulated remote function
105
- *
106
- * @returns A destructor function that disconnects the callback from the message
107
- */
108
- setCallback: <Kind extends keyof MessageData, ReturnKind extends keyof MessageData>(message: Kind & BaseMessage, returnMessage: ReturnKind & BaseMessage, callback: ClientMessageFunctionCallback<MessageData[Kind], MessageData[ReturnKind]>) => () => void;
109
- };
110
- private initialize;
111
- private update;
112
- private runClientMiddlewares;
113
- private runServerMiddlewares;
114
- private validateData;
115
- private onRemoteFire;
116
- private executeFunctions;
117
- private executeEventCallbacks;
118
- private deserializeAndValidate;
119
- private once;
120
- private on;
121
- private getPacket;
122
- /** @metadata macro */
123
- private addSerializer;
124
- /** @metadata macro */
125
- private createMessageSerializer;
126
- private getSerializer;
127
- }
128
- export {};