Skip to main content
Created only by Flux.Client. Client-only, so constructing one on the server errors.

Type definition

Client callbacks receive only the event’s arguments, with no Player parameter, since the server is the only possible sender. This is the most common source of off-by-one argument bugs when moving code between contexts.

Fire()

Sends to the server over the reliable channel. There is no target argument, because the server is the only recipient.
Returns immediately. The payload is appended to the client’s reliable buffer and flushed on the next Heartbeat.

FireUnreliable()

Sends to the server over the unreliable channel. May be dropped or reordered.
Never send anything the server must not miss over this channel, such as inputs that grant rewards, purchases, or state transitions. Use Fire for those.

Invoke()

Sends a request to the server, yields the calling thread, and returns the values from the server’s OnInvoke handler.
Args...
Arguments forwarded to the server handler.
Out...
Whatever the server handler returned.

Errors

Invoke raises rather than returning a failure flag, so wrap it when failure is expected:
error(message)
The server handler’s error message is transmitted and re-raised with error(tostring(...)). Only the message text survives: no stack trace, no error object, no metatable.
error("Request timed out")
Raised if no response arrives within 60 seconds. The pending request is discarded at that point, so a late response is ignored.
REQUEST_TIMEOUT = 60 is an internal constant and is not configurable through the public API. For a shorter deadline, race the invoke against your own task.delay rather than editing the module.

Concurrency

Requests are tracked by an incrementing per-channel counter, so many can be in flight at once and each resumes its own thread.
Responses can arrive out of order, because the server spawns each handler in its own thread. Never assume the first request you sent is the first to return.
Requests always travel over the reliable channel, and they never use the event’s schema. See Invocations.
There is no Client.OnInvoke. The server cannot invoke a client.

Connect()

Registers a persistent listener for server sends.
(Args...) -> ()
required
Invoked once per received message, with the event’s arguments only.
{ Disconnect: () -> () }
A plain table with a single Disconnect method, not an RBXScriptConnection.
Listeners are never removed automatically. A LocalScript under StarterCharacterScripts re-runs on every respawn, so without a disconnect you accumulate one listener per life and your callback fires once per life, cumulatively. See Connections.
Callbacks are dispatched with task.spawn and the listener list is walked backwards, so listeners run concurrently and the most recently connected runs first.

Once()

Fires at most once, then removes itself.
Returns nothing, so there is no handle and it cannot be cancelled before it fires.

Wait()

Yields until the next message on this channel and returns its arguments.
Args...
The payload, with no Player, unlike Server:Wait.
Has to be called from a yieldable thread, and has no timeout, so a message that never arrives parks the thread for the session.

Shared state

As on the server, the object is a thin typed handle over module-level tables keyed by channel name.
string
The channel name. Private.
{ Types.Type }?
The schema passed at construction. Private.
Construction also initialises this channel’s pending-request table and request-id counter, which is why Invoke works without any further setup.

Server and client differences

Next steps

Server

The other half of the pair.

Invocations

Round trips end to end.