Introduction
Postie is a module that provides a safe alternative to RemoteFunctions, offering a time-out parameter when invoking another machine. The main benefit of this is that you avoid the issues that come with invoking the client with a RemoteFunction:
- If the client throws an error, the server will throw the error too.
- If the client disconnects while it’s being invoked, the InvokeClient call will error.
- If the client never returns a value, the server will hang forever.
Postie solves all three of these problems by replacing one RemoteFunction invocation with two RemoteEvent firings.
Links
- Get Postie from the Roblox website
Interface
Postie.invokeClient( // yields, server-side
player: Player,
id: string,
timeOut: number,
...data: any
) => didRespond: boolean, ...response: any
Invoke player with sent data. Invocation identified by id. Yield until timeOut (given in seconds) is reached and return false
, or a response is received back from the client and return true
plus the data returned from the client. If the invocation reaches the client, but the client doesn’t have a corresponding callback, return before timeOut regardless but return false
.
Postie.invokeServer( // yields, client-side
id: string,
timeOut: number,
...data: any
) => didRespond: boolean, ...response: any
Invoke the server with sent data. Invocation identified by id. Yield until timeOut (given in seconds) is reached and return false
, or a response is received back from the server and return true
plus the data returned from the server. If the invocation reaches the server, but the server doesn’t have a corresponding callback, return before timeOut regardless but return false
.
Postie.setCallback(
id: string,
callback?: (...data: any) -> ...response: any
)
Set the callback that is invoked when an invocation identified by id is sent. Data sent with the invocation are passed to the callback. If on the server, the player who invoked is implicitly received as the first argument. If nil
is passed instead of a function, the current callback will just be removed.
Postie.getCallback(
id: string
) => callback?: (...data: any) -> ...response: any
Return the callback corresponding with id.
Usage
Example 1 - server to client
Server
local Postie = require(the.path.to.Postie)
local function getBallsOnScreen(player)
-- We request the amount of balls on the client's screen with a time-out of 5 seconds.
local didRespond, amountOfBalls = Postie.invokeClient(player, "get-objects-on-screen", 5, "balls")
if didRespond then -- We check for the time-out (or the client has no callback registered).
-- A malicious client can always modify the returned data!
if typeof(amountOfBalls) == "number" then
return true, amountOfBalls
end
end
return false
end
Client
local Postie = require(the.path.to.Postie)
Postie.setCallback("get-objects-on-screen", function(objectType)
return amountOnScreenByObjectType[objectType]
end)
Example 2 - client to server
Server
local Postie = require(the.path.to.Postie)
Postie.setCallback("get-coins", function(player)
return coinsByPlayer[player]
end)
Client
local Postie = require(the.path.to.Postie)
local function getCoins()
-- We request how many coins we have with a time-out of 5 seconds.
return Postie.invokeServer("get-coins", 5)
end
Additional Notes
- The module is really just a wrapper for RemoteEvents and does not use RemoteFunctions under the hood.
- Unlike other approaches to the client invocation problem, the module does not cause any memory leakage; the threads and functions it creates through usage are appropriately garbage collected.