@rbxts/tether 1.4.0 → 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
 
@@ -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,232 +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
- local responseCallback = function(data)
133
- returnValue = data
134
- return returnValue
135
- end
136
- functions[responseCallback] = true
137
- self.server.emit(message, data, unreliable)
138
- while returnValue == nil do
139
- RunService.Heartbeat:Wait()
140
- end
141
- -- Clean up the callback after receiving the response
142
- functions[responseCallback] = nil
143
- return returnValue
144
- end),
145
- setCallback = function(message, returnMessage, callback)
146
- if RunService:IsClient() then
147
- error(noServerListen)
148
- end
149
- return self.server.on(message, function(player, data)
150
- local returnValue = callback(player, data)
151
- -- Defer the response emission to end of frame and swap context to avoid context check issues
152
- -- task.defer guarantees response is sent by end of current frame, ensuring predictable timing in production
153
- task.defer(function()
154
- setLuneContext("server")
155
- self.client.emit(player, returnMessage, returnValue)
156
- setLuneContext("both")
157
- end)
158
- end)
159
- end,
160
- }
161
- self.client = {
162
- on = function(message, callback)
163
- if RunService:IsServer() then
164
- error(noClientListen)
165
- end
166
- return self:on(message, callback, self.clientCallbacks)
167
- end,
168
- once = function(message, callback)
169
- if RunService:IsServer() then
170
- error(noClientListen)
171
- end
172
- return self:once(message, callback, self.clientCallbacks)
173
- end,
174
- emit = function(player, message, data, unreliable)
175
- if unreliable == nil then
176
- unreliable = false
177
- end
178
- if RunService:IsClient() then
179
- error("[@rbxts/tether]: Cannot emit message to client from client")
180
- end
181
- task.spawn(function()
182
- local _binding = self:runClientMiddlewares(message, data)
183
- local dropRequest = _binding[1]
184
- local newData = _binding[2]
185
- if dropRequest then
186
- return nil
187
- end
188
- local _clientQueue = self.clientQueue
189
- local _arg0 = { player, message, newData, unreliable }
190
- table.insert(_clientQueue, _arg0)
191
- if not self.options.batchRemotes then
192
- self:update()
193
- end
194
- end)
195
- end,
196
- emitExcept = function(player, message, data, unreliable)
197
- if unreliable == nil then
198
- unreliable = false
199
- end
200
- local shouldSendTo = function(p)
201
- local _player = player
202
- local _result
203
- if typeof(_player) == "Instance" then
204
- _result = p ~= player
205
- else
206
- local _player_1 = player
207
- local _p = p
208
- _result = not (table.find(_player_1, _p) ~= nil)
209
- end
210
- return _result
211
- end
212
- local _client = self.client
213
- local _exp = Players:GetPlayers()
214
- -- ▼ ReadonlyArray.filter ▼
215
- local _newValue = {}
216
- local _length = 0
217
- for _k, _v in _exp do
218
- if shouldSendTo(_v, _k - 1, _exp) == true then
219
- _length += 1
220
- _newValue[_length] = _v
221
- end
222
- end
223
- -- ▲ ReadonlyArray.filter ▲
224
- _client.emit(_newValue, message, data, unreliable)
225
- end,
226
- emitAll = function(message, data, unreliable)
227
- if unreliable == nil then
228
- unreliable = false
229
- end
230
- if RunService:IsClient() then
231
- error("[@rbxts/tether]: Cannot emit message to all clients from client")
232
- end
233
- task.spawn(function()
234
- local _binding = self:runClientMiddlewares(message, data)
235
- local dropRequest = _binding[1]
236
- local newData = _binding[2]
237
- if dropRequest then
238
- return nil
239
- end
240
- local _clientBroadcastQueue = self.clientBroadcastQueue
241
- local _arg0 = { message, newData, unreliable }
242
- table.insert(_clientBroadcastQueue, _arg0)
243
- if not self.options.batchRemotes then
244
- self:update()
245
- end
246
- end)
247
- end,
248
- invoke = TS.async(function(message, returnMessage, player, data, unreliable)
249
- if unreliable == nil then
250
- unreliable = false
251
- end
252
- if RunService:IsClient() then
253
- error("[@rbxts/tether]: Cannot invoke client function from client")
254
- end
255
- local _serverFunctions = self.serverFunctions
256
- local _returnMessage = returnMessage
257
- if not (_serverFunctions[_returnMessage] ~= nil) then
258
- local _serverFunctions_1 = self.serverFunctions
259
- local _returnMessage_1 = returnMessage
260
- _serverFunctions_1[_returnMessage_1] = {}
261
- end
262
- local _serverFunctions_1 = self.serverFunctions
263
- local _returnMessage_1 = returnMessage
264
- local functions = _serverFunctions_1[_returnMessage_1]
265
- local returnValue
266
- local responseCallback = function(data)
267
- returnValue = data
268
- return returnValue
269
- end
270
- functions[responseCallback] = true
271
- self.client.emit(player, message, data, unreliable)
272
- while returnValue == nil do
273
- RunService.Heartbeat:Wait()
274
- end
275
- -- Clean up the callback after receiving the response
276
- functions[responseCallback] = nil
277
- return returnValue
278
- end),
279
- setCallback = function(message, returnMessage, callback)
280
- if RunService:IsServer() then
281
- error(noClientListen)
282
- end
283
- return self.client.on(message, function(data)
284
- local returnValue = callback(data)
285
- -- Defer the response emission to end of frame and swap context to avoid context check issues
286
- -- task.defer guarantees response is sent by end of current frame, ensuring predictable timing in production
287
- task.defer(function()
288
- setLuneContext("client")
289
- self.server.emit(returnMessage, returnValue)
290
- setLuneContext("both")
291
- end)
292
- end)
293
- end,
294
- }
295
83
  self.trash:add(function()
296
84
  self.clientCallbacks = {}
297
85
  self.serverCallbacks = {}
@@ -304,9 +92,10 @@ do
304
92
  function MessageEmitter:create(options, meta)
305
93
  local emitter = MessageEmitter.new(Object.assign({}, defaultMesssageEmitterOptions, options))
306
94
  if meta == nil then
307
- 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")
308
96
  return emitter:initialize()
309
97
  end
98
+ -- https://discord.com/channels/476080952636997633/506983834877689856/1363938149486821577
310
99
  for kind, _binding in pairs(meta) do
311
100
  local guard = _binding.guard
312
101
  local serializerMetadata = _binding.serializerMetadata
@@ -319,6 +108,87 @@ do
319
108
  end
320
109
  return emitter:initialize()
321
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
322
192
  function MessageEmitter:initialize()
323
193
  setLuneContext("client")
324
194
  if RunService:IsClient() then
@@ -355,11 +225,11 @@ do
355
225
  return nil
356
226
  end
357
227
  elapsed -= batchRate
358
- self:update()
228
+ self:relay()
359
229
  end))
360
230
  return self
361
231
  end
362
- function MessageEmitter:update()
232
+ function MessageEmitter:relay()
363
233
  local getPacket = function(info)
364
234
  return info.packet
365
235
  end
@@ -590,79 +460,6 @@ do
590
460
  self.clientQueue = {}
591
461
  end
592
462
  end
593
- function MessageEmitter:runClientMiddlewares(message, data, player)
594
- if not self:validateData(message, data) then
595
- return { true, data }
596
- end
597
- local players = player or Players:GetPlayers()
598
- local ctx = {
599
- message = message,
600
- data = data,
601
- getRawData = function()
602
- return self:getPacket(message, data)
603
- end,
604
- }
605
- for _, globalMiddleware in self.middleware:getClientGlobal() do
606
- local result = globalMiddleware(players, ctx)
607
- if not self:validateData(message, ctx.data, "Invalid data after global client middleware") then
608
- return { false, ctx.data }
609
- end
610
- if result == DropRequest then
611
- self.middleware:notifyRequestDropped(message, "Global client middleware")
612
- return { true, ctx.data }
613
- end
614
- end
615
- for _, middleware in self.middleware:getClient(message) do
616
- local result = middleware(players, ctx)
617
- if not self:validateData(message, ctx.data, "Invalid data after client middleware") then
618
- return { false, ctx.data }
619
- end
620
- if result == DropRequest then
621
- self.middleware:notifyRequestDropped(message, "Client middleware")
622
- return { true, ctx.data }
623
- end
624
- end
625
- if not self:validateData(message, ctx.data) then
626
- return { true, ctx.data }
627
- end
628
- return { false, ctx.data }
629
- end
630
- function MessageEmitter:runServerMiddlewares(message, data)
631
- if not self:validateData(message, data) then
632
- return { true, data }
633
- end
634
- local ctx = {
635
- message = message,
636
- data = data,
637
- getRawData = function()
638
- return self:getPacket(message, data)
639
- end,
640
- }
641
- for _, globalMiddleware in self.middleware:getServerGlobal() do
642
- if not self:validateData(message, ctx.data, "Invalid data after global server middleware") then
643
- return { false, ctx.data }
644
- end
645
- local result = globalMiddleware(ctx)
646
- if result == DropRequest then
647
- self.middleware:notifyRequestDropped(message, "Global server middleware")
648
- return { true, ctx.data }
649
- end
650
- end
651
- for _, middleware in self.middleware:getServer(message) do
652
- if not self:validateData(message, ctx.data, "Invalid data after server middleware") then
653
- return { false, ctx.data }
654
- end
655
- local result = middleware(ctx)
656
- if result == DropRequest then
657
- self.middleware:notifyRequestDropped(message, "Server middleware")
658
- return { true, ctx.data }
659
- end
660
- end
661
- if not self:validateData(message, ctx.data) then
662
- return { true, ctx.data }
663
- end
664
- return { false, ctx.data }
665
- end
666
463
  function MessageEmitter:validateData(message, data, requestDropReason)
667
464
  if requestDropReason == nil then
668
465
  requestDropReason = "Invalid data"
@@ -680,9 +477,9 @@ do
680
477
  function MessageEmitter:onRemoteFire(isServer, serializedPackets, player)
681
478
  for _, packet in serializedPackets do
682
479
  if buffer.len(packet.messageBuf) > 1 then
683
- 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")
684
481
  end
685
- local message = buffer.readu8(packet.messageBuf, 0)
482
+ local message = readMessage(packet)
686
483
  self:executeEventCallbacks(isServer, message, packet, player)
687
484
  self:executeFunctions(isServer, message, packet)
688
485
  end
@@ -715,6 +512,15 @@ do
715
512
  end
716
513
  end
717
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
718
524
  function MessageEmitter:deserializeAndValidate(message, serializedPacket)
719
525
  local serializer = self:getSerializer(message)
720
526
  local _packet = serializer
@@ -725,43 +531,10 @@ do
725
531
  self:validateData(message, packet)
726
532
  return packet
727
533
  end
728
- function MessageEmitter:once(message, callback, callbacksMap)
729
- local destructor
730
- destructor = self:on(message, function(player, data)
731
- destructor()
732
- callback(player, data)
733
- end, callbacksMap)
734
- return destructor
735
- end
736
- function MessageEmitter:on(message, callback, callbacksMap)
737
- local _callbacksMap = callbacksMap
738
- local _message = message
739
- if not (_callbacksMap[_message] ~= nil) then
740
- local _callbacksMap_1 = callbacksMap
741
- local _message_1 = message
742
- _callbacksMap_1[_message_1] = {}
743
- end
744
- local _callbacksMap_1 = callbacksMap
745
- local _message_1 = message
746
- local callbacks = _callbacksMap_1[_message_1]
747
- local _callback = callback
748
- callbacks[_callback] = true
749
- local _callbacksMap_2 = callbacksMap
750
- local _message_2 = message
751
- _callbacksMap_2[_message_2] = callbacks
752
- return function()
753
- local _callback_1 = callback
754
- -- ▼ Set.delete ▼
755
- local _valueExisted = callbacks[_callback_1] ~= nil
756
- callbacks[_callback_1] = nil
757
- -- ▲ Set.delete ▲
758
- return _valueExisted
759
- end
760
- end
761
534
  function MessageEmitter:getPacket(message, data)
762
535
  local serializer = self:getSerializer(message)
763
536
  local messageBuf = buffer.create(1)
764
- buffer.writeu8(messageBuf, 0, message)
537
+ writeMessage(messageBuf, message)
765
538
  if serializer == nil then
766
539
  return {
767
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.4.0",
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",
@@ -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 {};