@rbxts/tether 1.4.0 → 1.4.2

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,156 @@
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
+ if setLuneContext == nil then
8
+ setLuneContext = function() end
9
+ end
10
+ local ClientEmitter
11
+ do
12
+ local super = ContextualEmitter
13
+ ClientEmitter = setmetatable({}, {
14
+ __tostring = function()
15
+ return "ClientEmitter"
16
+ end,
17
+ __index = super,
18
+ })
19
+ ClientEmitter.__index = ClientEmitter
20
+ function ClientEmitter.new(...)
21
+ local self = setmetatable({}, ClientEmitter)
22
+ return self:constructor(...) or self
23
+ end
24
+ function ClientEmitter:constructor(...)
25
+ super.constructor(self, ...)
26
+ self.context = "client"
27
+ end
28
+ function ClientEmitter:emit(player, message, data, unreliable)
29
+ if unreliable == nil then
30
+ unreliable = false
31
+ end
32
+ if RunService:IsClient() then
33
+ error("[tether::error] Cannot emit message from client to client")
34
+ end
35
+ task.spawn(function()
36
+ local _binding = self.master:runClientMiddlewares(message, data, player)
37
+ local dropRequest = _binding[1]
38
+ local newData = _binding[2]
39
+ if dropRequest then
40
+ return nil
41
+ end
42
+ self.master.relayer:queueMessage(self.context, message, { player, message, newData, unreliable })
43
+ end)
44
+ end
45
+ function ClientEmitter:emitExcept(player, message, data, unreliable)
46
+ if unreliable == nil then
47
+ unreliable = false
48
+ end
49
+ local shouldSendTo = function(p)
50
+ local _player = player
51
+ local _result
52
+ if typeof(_player) == "Instance" then
53
+ _result = p ~= player
54
+ else
55
+ local _player_1 = player
56
+ local _p = p
57
+ _result = not (table.find(_player_1, _p) ~= nil)
58
+ end
59
+ return _result
60
+ end
61
+ local _self = self
62
+ local _exp = Players:GetPlayers()
63
+ -- ▼ ReadonlyArray.filter ▼
64
+ local _newValue = {}
65
+ local _length = 0
66
+ for _k, _v in _exp do
67
+ if shouldSendTo(_v, _k - 1, _exp) == true then
68
+ _length += 1
69
+ _newValue[_length] = _v
70
+ end
71
+ end
72
+ -- ▲ ReadonlyArray.filter ▲
73
+ _self:emit(_newValue, message, data, unreliable)
74
+ end
75
+ function ClientEmitter:emitAll(message, data, unreliable)
76
+ if unreliable == nil then
77
+ unreliable = false
78
+ end
79
+ if RunService:IsClient() then
80
+ error("[tether::error] Cannot emit message from client to all clients")
81
+ end
82
+ task.spawn(function()
83
+ local _binding = self.master:runClientMiddlewares(message, data)
84
+ local dropRequest = _binding[1]
85
+ local newData = _binding[2]
86
+ if dropRequest then
87
+ return nil
88
+ end
89
+ self.master.relayer:queueMessage(true, message, { message, newData, unreliable })
90
+ end)
91
+ end
92
+ function ClientEmitter:setCallback(message, returnMessage, callback)
93
+ if RunService:IsServer() then
94
+ error("[tether::error] Cannot listen to server message from client")
95
+ end
96
+ return self:on(message, function(data)
97
+ local returnValue = callback(data)
98
+ -- Defer the response emission to end of frame and swap context to avoid context check issues
99
+ -- task.defer guarantees response is sent by end of current frame, ensuring predictable timing in production
100
+ task.defer(function()
101
+ setLuneContext("client")
102
+ self.master.server:emit(returnMessage, returnValue)
103
+ setLuneContext("both")
104
+ end)
105
+ end)
106
+ end
107
+ function ClientEmitter:invoke(message, returnMessage, player, data, unreliable)
108
+ if unreliable == nil then
109
+ unreliable = false
110
+ end
111
+ if RunService:IsClient() then
112
+ error("[tether::error] Cannot invoke function from client to client")
113
+ end
114
+ local _binding = self.master
115
+ local serverFunctions = _binding.serverFunctions
116
+ local _returnMessage = returnMessage
117
+ if not (serverFunctions[_returnMessage] ~= nil) then
118
+ local _returnMessage_1 = returnMessage
119
+ serverFunctions[_returnMessage_1] = {}
120
+ end
121
+ local _returnMessage_1 = returnMessage
122
+ local functions = serverFunctions[_returnMessage_1]
123
+ local returnValue
124
+ local responseCallback = function(data)
125
+ returnValue = data
126
+ return returnValue
127
+ end
128
+ functions[responseCallback] = true
129
+ self:emit(player, message, data, unreliable)
130
+ return TS.Promise.new(function(resolve, reject)
131
+ -- awful
132
+ local frames = 0
133
+ while true do
134
+ local _condition = returnValue == nil
135
+ if _condition then
136
+ local _original = frames
137
+ frames += 1
138
+ _condition = _original < 400
139
+ end
140
+ if not _condition then
141
+ break
142
+ end
143
+ RunService.Heartbeat:Wait()
144
+ end
145
+ if frames == 400 then
146
+ return reject("[tether::error] Client function timed out (no response)")
147
+ end
148
+ -- clean up the callback after receiving the response
149
+ functions[responseCallback] = nil
150
+ resolve(returnValue)
151
+ end)
152
+ end
153
+ end
154
+ return {
155
+ ClientEmitter = ClientEmitter,
156
+ }
@@ -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,41 @@
1
+ import { Modding } from "@flamework/core";
2
+ import { Trash } from "@rbxts/trash";
3
+ import Destroyable from "@rbxts/destroyable";
4
+ import { MiddlewareProvider } from "../middleware";
5
+ import type { SerializedPacket, ClientMessageCallback, ServerMessageCallback, BaseMessage, MessageEmitterMetadata } from "../structs";
6
+ import { ServerEmitter } from "./server-emitter";
7
+ import { ClientEmitter } from "./client-emitter";
8
+ import { Relayer } from "../relayer";
9
+ import { Serdes } from "../serdes";
10
+ export interface MessageEmitterOptions<MessageData> {
11
+ readonly batchRemotes: boolean;
12
+ readonly batchRate: number;
13
+ readonly doNotBatch: Set<keyof MessageData>;
14
+ }
15
+ export declare class MessageEmitter<MessageData> extends Destroyable {
16
+ readonly options: MessageEmitterOptions<MessageData>;
17
+ readonly server: ServerEmitter<MessageData>;
18
+ readonly client: ClientEmitter<MessageData>;
19
+ readonly middleware: MiddlewareProvider<MessageData>;
20
+ /** @hidden */ readonly trash: Trash;
21
+ /** @hidden */ readonly relayer: Relayer<MessageData>;
22
+ /** @hidden */ readonly serdes: Serdes<MessageData>;
23
+ /** @hidden */ clientCallbacks: Map<keyof MessageData, Set<ClientMessageCallback>>;
24
+ /** @hidden */ clientFunctions: Map<keyof MessageData, Set<(data: unknown) => void>>;
25
+ /** @hidden */ serverCallbacks: Map<keyof MessageData, Set<ServerMessageCallback>>;
26
+ /** @hidden */ serverFunctions: Map<keyof MessageData, Set<(data: unknown) => void>>;
27
+ private readonly guards;
28
+ /** @metadata macro */
29
+ static create<MessageData>(options?: Partial<MessageEmitterOptions<MessageData>>, meta?: Modding.Many<MessageEmitterMetadata<MessageData>>): MessageEmitter<MessageData>;
30
+ private constructor();
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
+ /** @hidden */
36
+ onRemoteFire(isServer: boolean, serializedPackets: SerializedPacket[], player?: Player): void;
37
+ private validateData;
38
+ private executeFunctions;
39
+ private executeEventCallbacks;
40
+ private deserializeAndValidate;
41
+ }
@@ -0,0 +1,222 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local Players = TS.import(script, TS.getModule(script, "@rbxts", "services")).Players
4
+ local Destroyable = TS.import(script, TS.getModule(script, "@rbxts", "destroyable").out).default
5
+ local Object = TS.import(script, TS.getModule(script, "@rbxts", "object-utils"))
6
+ local repr = TS.import(script, TS.getModule(script, "@rbxts", "repr").out)
7
+ local _middleware = TS.import(script, script.Parent.Parent, "middleware")
8
+ local DropRequest = _middleware.DropRequest
9
+ local MiddlewareProvider = _middleware.MiddlewareProvider
10
+ local ServerEmitter = TS.import(script, script.Parent, "server-emitter").ServerEmitter
11
+ local ClientEmitter = TS.import(script, script.Parent, "client-emitter").ClientEmitter
12
+ local Relayer = TS.import(script, script.Parent.Parent, "relayer").Relayer
13
+ local Serdes = TS.import(script, script.Parent.Parent, "serdes").Serdes
14
+ local readMessage = TS.import(script, script.Parent.Parent, "utility").readMessage
15
+ if setLuneContext == nil then
16
+ setLuneContext = function() end
17
+ end
18
+ setLuneContext("both")
19
+ local guardFailed = function(message, data)
20
+ return `[tether::warning]: Type validation guard failed for message '{message}' - check your sent data\nSent data: {repr(data)}`
21
+ end
22
+ local defaultMesssageEmitterOptions = {
23
+ batchRemotes = true,
24
+ batchRate = 1 / 24,
25
+ doNotBatch = {},
26
+ }
27
+ local MessageEmitter
28
+ do
29
+ local super = Destroyable
30
+ MessageEmitter = setmetatable({}, {
31
+ __tostring = function()
32
+ return "MessageEmitter"
33
+ end,
34
+ __index = super,
35
+ })
36
+ MessageEmitter.__index = MessageEmitter
37
+ function MessageEmitter.new(...)
38
+ local self = setmetatable({}, MessageEmitter)
39
+ return self:constructor(...) or self
40
+ end
41
+ function MessageEmitter:constructor(options)
42
+ if options == nil then
43
+ options = defaultMesssageEmitterOptions
44
+ end
45
+ super.constructor(self)
46
+ self.options = options
47
+ self.server = ServerEmitter.new(self)
48
+ self.client = ClientEmitter.new(self)
49
+ self.middleware = MiddlewareProvider.new()
50
+ self.relayer = Relayer.new(self)
51
+ self.serdes = Serdes.new()
52
+ self.clientCallbacks = {}
53
+ self.clientFunctions = {}
54
+ self.serverCallbacks = {}
55
+ self.serverFunctions = {}
56
+ self.guards = {}
57
+ self.trash:add(function()
58
+ self.clientCallbacks = {}
59
+ self.serverCallbacks = {}
60
+ self.clientFunctions = {}
61
+ self.clientCallbacks = {}
62
+ self.serdes.serializers = {}
63
+ self.relayer:relayAll()
64
+ setmetatable(self, nil)
65
+ end)
66
+ end
67
+ function MessageEmitter:create(options, meta)
68
+ local emitter = MessageEmitter.new(Object.assign({}, defaultMesssageEmitterOptions, options))
69
+ if meta == nil then
70
+ 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")
71
+ return emitter
72
+ end
73
+ -- lore
74
+ -- https://discord.com/channels/476080952636997633/506983834877689856/1363938149486821577
75
+ for kind, _binding in pairs(meta) do
76
+ local guard = _binding.guard
77
+ local serializerMetadata = _binding.serializerMetadata
78
+ local numberKind = tonumber(kind)
79
+ emitter.guards[numberKind] = guard
80
+ if serializerMetadata == nil then
81
+ continue
82
+ end
83
+ emitter.serdes:addSerializer(numberKind, serializerMetadata)
84
+ end
85
+ return emitter
86
+ end
87
+ function MessageEmitter:runClientMiddlewares(message, data, player)
88
+ if not self:validateData(message, data) then
89
+ return { true, data }
90
+ end
91
+ local players = player or Players:GetPlayers()
92
+ local ctx = {
93
+ message = message,
94
+ data = data,
95
+ getRawData = function()
96
+ return self.serdes:serializePacket(message, data)
97
+ end,
98
+ }
99
+ for _, globalMiddleware in self.middleware:getClientGlobal() do
100
+ local result = globalMiddleware(players, ctx)
101
+ if not self:validateData(message, ctx.data, "Invalid data after global client middleware") then
102
+ return { false, ctx.data }
103
+ end
104
+ if result == DropRequest then
105
+ self.middleware:notifyRequestDropped(message, "Global client middleware")
106
+ return { true, ctx.data }
107
+ end
108
+ end
109
+ for _, middleware in self.middleware:getClient(message) do
110
+ local result = middleware(players, ctx)
111
+ if not self:validateData(message, ctx.data, "Invalid data after client middleware") then
112
+ return { false, ctx.data }
113
+ end
114
+ if result == DropRequest then
115
+ self.middleware:notifyRequestDropped(message, "Client middleware")
116
+ return { true, ctx.data }
117
+ end
118
+ end
119
+ if not self:validateData(message, ctx.data) then
120
+ return { true, ctx.data }
121
+ end
122
+ return { false, ctx.data }
123
+ end
124
+ function MessageEmitter:runServerMiddlewares(message, data)
125
+ if not self:validateData(message, data) then
126
+ return { true, data }
127
+ end
128
+ local ctx = {
129
+ message = message,
130
+ data = data,
131
+ getRawData = function()
132
+ return self.serdes:serializePacket(message, data)
133
+ end,
134
+ }
135
+ for _, globalMiddleware in self.middleware:getServerGlobal() do
136
+ if not self:validateData(message, ctx.data, "Invalid data after global server middleware") then
137
+ return { false, ctx.data }
138
+ end
139
+ local result = globalMiddleware(ctx)
140
+ if result == DropRequest then
141
+ self.middleware:notifyRequestDropped(message, "Global server middleware")
142
+ return { true, ctx.data }
143
+ end
144
+ end
145
+ for _, middleware in self.middleware:getServer(message) do
146
+ if not self:validateData(message, ctx.data, "Invalid data after server middleware") then
147
+ return { false, ctx.data }
148
+ end
149
+ local result = middleware(ctx)
150
+ if result == DropRequest then
151
+ self.middleware:notifyRequestDropped(message, "Server middleware")
152
+ return { true, ctx.data }
153
+ end
154
+ end
155
+ if not self:validateData(message, ctx.data) then
156
+ return { true, ctx.data }
157
+ end
158
+ return { false, ctx.data }
159
+ end
160
+ function MessageEmitter:onRemoteFire(isServer, serializedPackets, player)
161
+ for _, packet in serializedPackets do
162
+ if buffer.len(packet.messageBuf) > 1 then
163
+ return warn("[tether::warning] Rejected packet because message buffer was larger than one byte")
164
+ end
165
+ local message = readMessage(packet)
166
+ self:executeEventCallbacks(isServer, message, packet, player)
167
+ self:executeFunctions(isServer, message, packet)
168
+ end
169
+ end
170
+ function MessageEmitter:validateData(message, data, requestDropReason)
171
+ if requestDropReason == nil then
172
+ requestDropReason = "Invalid data"
173
+ end
174
+ local _guards = self.guards
175
+ local _message = message
176
+ local guard = _guards[_message]
177
+ local guardPassed = guard(data)
178
+ if not guardPassed then
179
+ warn(guardFailed(message, data))
180
+ self.middleware:notifyRequestDropped(message, requestDropReason)
181
+ end
182
+ return guardPassed
183
+ end
184
+ function MessageEmitter:executeFunctions(isServer, message, serializedPacket)
185
+ local functionsMap = if isServer then self.serverFunctions else self.clientFunctions
186
+ local _message = message
187
+ local functions = functionsMap[_message]
188
+ if functions == nil then
189
+ return nil
190
+ end
191
+ local packet = self:deserializeAndValidate(message, serializedPacket)
192
+ for callback in functions do
193
+ callback(packet)
194
+ end
195
+ end
196
+ function MessageEmitter:executeEventCallbacks(isServer, message, serializedPacket, player)
197
+ local callbacksMap = if isServer then self.serverCallbacks else self.clientCallbacks
198
+ local _message = message
199
+ local callbacks = callbacksMap[_message]
200
+ if callbacks == nil then
201
+ return nil
202
+ end
203
+ local packet = self:deserializeAndValidate(message, serializedPacket)
204
+ for callback in callbacks do
205
+ if isServer then
206
+ local _arg0 = player ~= nil
207
+ assert(_arg0)
208
+ callback(player, packet)
209
+ else
210
+ callback(packet)
211
+ end
212
+ end
213
+ end
214
+ function MessageEmitter:deserializeAndValidate(message, serializedPacket)
215
+ local packet = self.serdes:deserializePacket(message, serializedPacket)
216
+ self:validateData(message, packet)
217
+ return packet
218
+ end
219
+ end
220
+ return {
221
+ MessageEmitter = MessageEmitter,
222
+ }
@@ -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
+ }