Skip to main content
An invocation is Flux’s replacement for RemoteFunction. The client calls Invoke, the calling thread yields, the server’s OnInvoke handler runs, and its return values come back to the caller.

Direction

Invocations only run from client to server.The Client type has Invoke but no OnInvoke. The Server type has OnInvoke but no Invoke. There is no server-to-client invocation in Flux, because the client’s dispatcher only handles responses and the server’s only handles requests.
If you need data that only a client has, model it as two events instead of a round trip:
This is usually the better design anyway. A server thread yielding on a client’s response is a thread an exploiter can park indefinitely, whereas two events let you choose your own timeout and default.

Handling invocations on the server

Assign a function to OnInvoke. It receives the sending player first, then the client’s arguments, and may return any number of values.
OnInvoke is a property with a setter, not a method. Flux intercepts the assignment through __newindex and stores the handler in a table keyed by the event name. Assigning it twice replaces the previous handler, so there is only ever one handler per name, and it is shared by every Server object built with that name.
If no OnInvoke handler is registered for a name, incoming requests are silently dropped. The client will not get an error; it will yield until the 60-second timeout expires. Register handlers during startup, before clients can reach them.

Invoking from the client

Invoke yields the calling thread, so it has to be called from a thread that can yield.

Error propagation

The server handler runs under pcall. If it errors, the failure is sent back and Invoke re-raises it on the client.
The error is transmitted as a value and re-raised with error(tostring(...)), so the client sees the message text only. Stack traces, error objects and custom metatables do not survive the trip. Return a structured result if the caller needs to branch on the failure:

Timeouts

Every request starts a 60-second timer. If no response arrives before it expires, the pending request is discarded and Invoke errors with Request timed out.
The timeout is a fixed internal constant, REQUEST_TIMEOUT = 60, and is not configurable through the public API. If you need a shorter deadline, race the invoke against your own task.delay rather than editing the module.

Concurrency

Requests are tracked by an incrementing per-name counter, so many invocations can be in flight at once and each resumes its own thread with its own response.
The server spawns each handler with task.spawn, so a slow handler never blocks other requests. It also means responses can come back out of order. Never assume the first invoke you sent is the first to return.

Invocations ignore schemas

Schemas apply to events only. A schema encodes the payloads of Fire, FireUnreliable, FireAll and FireAllUnreliable. Invoke requests and their responses always fall back to the dynamic WriteAny/ReadAny encoder, whatever schema you passed to Flux.Server or Flux.Client.
In practice this means invoke arguments and return values always carry one type tag per value. That is slightly larger on the wire, and it is also why OnInvoke can return a variable number of values without you declaring anything.

Reliability

Requests and responses are always sent over the reliable channel. There is no unreliable invocation, because a dropped request would simply hang until the timeout.

Next steps

Schemas

Shrink your event payloads with fixed layouts.

Client API

The full Invoke signature and semantics.