Skip to content

Generated API

A schema compiles to a server module, a client module and, with TypesOutput, a shared types module. This page lists everything they expose. Names are shown in the default Pascal casing. Every module declares --!strict and passes it, so the types below are what a strict script sees.

In the signatures below, ...Data stands for an event’s or function’s Data as parameters: one parameter named Value for a single type, or one per element of a type pack, named as the schema names them or Value1, Value2… when it does not. ...Return is a function’s Return the same way. An event with no Data takes a single nil parameter.

Server.server.luau
local Net = require(ServerScriptService.Network.Server)
Client.client.luau
local Net = require(ReplicatedStorage.Network.Client)
Module Requirable from When it loads
Server the server only; errors elsewhere Creates the two remotes if they are missing, publishes the schema signature, and checks both remotes’ classes. Under AutoStart = false, none of this happens until Start().
Client a client only; errors elsewhere Waits for the two remotes, checks their classes, then checks the schema signature and errors on a mismatch. May yield – for ever, unless ClientConnectTimeout bounds the wait.
Types anywhere Nothing: it holds types and exported Read/Write pairs.

Only the server module opens with --!native. Every Roblox server compiles native code, while a client device that cannot logs Native code generation is not supported on this device for each module that asks it to, so the client and types modules run interpreted, as most players’ clients run code anyway. All three keep --!strict and --!optimize 2.

Requiring a second server or client module with the same RemoteScope in the same Luau environment errors: An instance of BlinkBlox is already running with the remote scope "BLINK".

Member Server Client
StepReplication() Sends every player’s queued reliable traffic. Sends the client’s queued reliable traffic.
SetRateLimitHandler(Handler?) Handler: (Player: Player, Event: string?, Refused: number) -> () Accepted and ignored: the client does not rate-limit.
SetDecodeErrorHandler(Handler?) Handler: (Player: Player, Event: string?, Failure: string) -> () Handler: (Event: string?, Failure: string) -> ()
Limits With ExportLimits: a frozen table of the schema’s bounds. The same table.
Rates With ExportRates: a frozen table of the rate limits the server enforces. The same table.
SetPacketDropHandler(Handler?) Handler: (Player: Player, Reason: "Malformed" | "Oversized" | "Budget" | "Instances" | "Events", Refused: number, Detail: number) -> () Accepted and ignored: the client does not refuse packets.
SetListenerErrorHandler(Handler?) Handler: (Player: Player, Event: string, Failure: string, Count: number) -> () Accepted and ignored: a client listener’s error surfaces in the output.
Remotes { Reliable: string, Unreliable: string }, the remotes’ names in ReplicatedStorage The same.
Start() Only under AutoStart = false: creates and connects the remotes and the Heartbeat. A second call does nothing. –
Connected – Only under ClientConnectTimeout: true when the remotes were found in time, false when the module returned stubs.
  • StepReplication is connected to RunService.Heartbeat unless ManualReplication is set, in which case you call it.

  • The rate-limit handler is called at most once a second per player and event, with Refused the number of refusals since the last call. It hears three kinds of refusal: an event or call over its Rate, a call over its function’s Concurrency (with the function’s name as Event), and whole packets over the inbound byte budget, with Event nil – the last only while no packet-drop handler is installed.

  • The packet-drop handler hears every packet the server refuses before decoding it, and every packet cut short at the event cap. It is called at most once a second per player and reason, with Refused the packets refused since the last call, and Detail:

    Reason Refused because Detail
    Malformed the remote’s arguments were not a buffer and a table 0
    Oversized the packet was over MaxPacketSize its length in bytes
    Budget the player’s inbound byte budget could not pay for it its length in bytes
    Instances it carried more than MaxInstancesPerPacket Instances its instance count
    Events MaxEventsPerPacket events were decoded and the rest dropped the events decoded

    With one installed, the server prints none of these warnings, and Budget no longer reaches the rate-limit handler. Without one, both behave as before 0.37.0. Nothing but a modified client sends a malformed, oversized or instance-stuffed packet; an honest one can overrun its budget after a network hitch.

  • The decode-error handler receives the event the packet claimed to carry, or nil if its index named none. On the server it is called at most once a second per player, channel and event, and without one the server is silent. On the client, without one, each failure is a warning.

  • The listener-error handler hears every error a server listener throws: an event’s, Sync or Async, Single or Many, reliable or unreliable, whether it arrived live, through Predict, or from the queue replayed when .On connects; and a function’s, including a return value its serialiser refuses – that call is still answered with a failure. It is called at most once a second per player and event, with Failure the error that call is about and Count the errors since the last call, that one included. A player who has already left is reported at once with Count 1. A polled event has no listener: the code iterating it is the game’s own. With one installed, the server prints none of those errors. Without one, they surface as they always have: a Sync listener’s raised on a thread of its own, an Async one’s on the thread it ran on, a function’s as the warning "Name" encountered an error, ....

  • Every handler runs on a thread of its own, so one that yields or throws does not disturb decoding. Pass nil to remove one.

With option TrafficStats, and only with it, both modules have two more members:

Member Server and client
GetTrafficStats() Returns { Events: { [string]: Counts }, Reliable: Counts, Unreliable: Counts }, where Counts is { Sent: number, SentBytes: number, Received: number, ReceivedBytes: number }.
ResetTrafficStats() Sets every count back to zero.
  • The totals run from the module’s start or the last ResetTrafficStats(). Each call returns a fresh table, so keep one and subtract it from the next to get a rate.
  • Events has one entry per event this module sends or receives, keyed by its path as the module exposes it – "Chat", or "Inner.Poll" inside a scope – present from the start with zeros. An event counts events in Sent/Received, and its bytes in the buffer in SentBytes/ReceivedBytes: its one-byte index, the two-byte sequence number of an OrderedUnreliable event, and its payload. The Instances that ride beside the buffer and what Roblox adds around a remote call are not counted. Functions are not listed; their calls and replies are counted in Reliable.
  • Reliable and Unreliable count packets in Sent/Received and whole buffers in the byte fields. A reliable packet is counted when StepReplication sends the batch, and holds everything batched that frame, functions included. An unreliable event is a packet of its own.
  • A reliable event is counted as sent when it is fired, as it joins the batch, so before the flush its event counts can run ahead of Reliable.
  • The server counts once per recipient, since each one is sent the bytes. Fire counts one; FireList one per entry of the list, a player listed twice counting twice; FireExcept one per player but the one left out; FireAll one per player in Players:GetPlayers() when it is called, also for an unreliable event, which goes out through FireAllClients once. A server with no players counts nothing. The server’s Reliable counts one packet per player flushed: a player’s own batch is one, and the broadcast batch is one for every player it is sent to, each counted with the batch’s bytes, though it goes out in a single FireAllClients when it can. A client’s batch cut to the server’s packet limits counts each packet it is cut into. So a channel’s SentBytes is, after the flush, never less than the SentBytes of the events it carried.
  • A stream is counted when the scheduler sends it, as a FireList to every player is: once per player, under the stream’s name in Events and in Unreliable. Its bytes include the two-byte stamp and, for a state, the optional’s flag byte. A step that sends nothing counts nothing.
  • Received is counted once an event is decoded, before its rate limit and before a stale ordered event is discarded – those did arrive. A packet refused before decoding (malformed, oversized, over the inbound budget or its Instance limit) is not counted at all. A packet that fails part way counts as a packet with its whole length, and its events up to the failure.
  • A fire that throws, or an unreliable one refused as too large, is not counted.

The counts are Luau numbers, exact up to 2^53. In edit mode both members exist and report zeros.

Which members an event has depends on the side and on its From:

Event Server module Client module
From: Server Fire, FireAll, FireList, FireExcept On and Predict, or Iter when polled
From: Client On and Predict, or Iter when polled Fire
Stream Set, Clear, Urgent, SetFast, each taking the player first under Per: Player On, never polled, with the state and the server time
Stamped From: Client On, never polled, with SentAt after the values Fire, taking the values only

Predict exists only with option Predict. An event is polled when it is declared Call: Polling or UsePolling is set.

Member Side Signature Sends to
Fire server (Player: Player, ...Data) -> () one player
FireAll server (...Data) -> () every player
FireList server (List: {Player}, ...Data) -> () each player in List
FireExcept server (Except: Player, ...Data) -> () every player but Except
Fire client (...Data) -> () the server
  • A reliable send is written into the recipient’s batch and sent on the next StepReplication.
  • An unreliable send is sent at once, as its own packet. One larger than MaxUnreliableSize, counting 6 bytes for each Instance it carries, is dropped with a warning.
  • A send throws if a value fails validation: always for a string, buffer or array length outside its bounds or a map with more than 65535 entries, and for types, ranges and classes with WriteValidations. Whatever the throw had already written is taken back out, so the rest of the batch is unaffected.
Member Side Signature
On server (Listener: (Player: Player, ...Data) -> ()) -> (() -> ())
On client (Listener: (...Data) -> ()) -> (() -> ())
Predict server (Player: Player, ...Data) -> ()
Predict client (...Data) -> ()
Iter server () -> iterator of (Index: number, Player: Player, ...Data)
Iter client () -> iterator of (Index: number, ...Data)
Next both Deprecated alias of Iter, the same function.
  • On returns a function that disconnects the listener.

  • A Single event keeps one listener. A second On replaces the first and warns, and the first listener’s disconnect function stops doing anything. A Many event keeps them all.

  • A Sync listener is called on the decode thread and must not yield; an Async one runs on a thread of its own. See Call.

  • A reliable event that arrives with no listener is queued and replayed to the first listener that connects. The server keeps at most 256 per event and drops the rest silently; the client warns past 256. An unreliable event with no listener is dropped.

  • Predict delivers to this side’s own listeners, exactly as an arriving event would be delivered, queue included. It sends nothing.

  • Iter takes rows out of the event’s queue as it goes, so each row is seen once:

    for Index, Player, Value in Net.Jump.Iter() do
    -- ...
    end

    On the server a polled queue holds at most 256 x Players.MaxPlayers rows; the rest are dropped.

Function Server module Client module
From: Client (the default) On Invoke
From: Server Invoke On
Member Side Signature
Invoke client (...Data) -> (...Return), or a Future or Promise
Invoke server (Player: Player, ...Data) -> (...Return), or a Future or Promise
On server (Listener: (Player: Player, ...Data) -> (...Return)) -> ()
On client (Listener: (...Data) -> (...Return)) -> ()

What Invoke returns depends on the function’s Yield:

Yield Invoke returns On failure
Coroutine the return values, after yielding throws There was an exception while processing "Name".
Future Future.Try(...): a future of (Success, ...Return) the future’s success is false
Promise Promise.new(...) resolving with the return values the promise rejects; cancelling it releases the call
  • A call fails when the listener errors, when its return value fails to serialise, when a rate limit refuses it, when the player already has the function’s Concurrency calls running, when the server’s queue for a function with no listener is full, when the call is not answered within InvocationTimeout (10 seconds), and, on the server, when the player leaves or has already left.
  • At most 32 calls may be outstanding at once: per client, and per player on the server. The 33rd Invoke errors at once.
  • On keeps one listener and returns nothing; a second On replaces the first. Calls that arrive before it connects are queued and run when it does – on the server up to 256, past which each is answered with a failure.
  • The listener’s return values are the reply. An error in the listener is warned on the listening side and fails the caller.

A type declared with export gets a Read/Write pair in all three modules, under its name:

Member Signature
Name.Read (Buffer: buffer) -> Name
Name.Write (Value: Name) -> buffer

They serialise one value into a buffer of exactly its size and back, with the same encoding and the same receive-side checks the events use. They work in edit mode too. A type containing an Instance or unknown cannot be exported (E3013). See Exports.

Every named type in the schema is declared in each module as a Luau export type, so Net.Name works in a type annotation. A scope becomes a nested table: Net.Combat.Hit.Fire(...). A type inside a scope is exported as Scope_Name, for example Net.Combat_Damage.

option Casing renames the generated members. The names of your own declarations are never changed.

Pascal (default) Camel Snake
Fire fire fire
FireAll fireAll fire_all
FireList fireList fire_list
FireExcept fireExcept fire_except
On on on
Iter iter iter
Next next next
Predict predict predict
Invoke invoke invoke
Read read read
Write write write
Set set set
Clear clear clear
Urgent urgent urgent
SetFast setFast set_fast
StepReplication stepReplication step_replication
SetRateLimitHandler setRateLimitHandler set_rate_limit_handler
SetDecodeErrorHandler setDecodeErrorHandler set_decode_error_handler
SetPacketDropHandler setPacketDropHandler set_packet_drop_handler
SetListenerErrorHandler setListenerErrorHandler set_listener_error_handler
Remotes remotes remotes
Start start start
Connected connected connected
GetTrafficStats getTrafficStats get_traffic_stats
ResetTrafficStats resetTrafficStats reset_traffic_stats

The fields of GetTrafficStats‘s result follow the casing too: Events, Reliable, Unreliable, Sent, SentBytes, Received and ReceivedBytes under Pascal; events, sentBytes, receivedBytes and the rest under Camel; sent_bytes and received_bytes under Snake. The keys of Events are your events’ names, unchanged.

When RunService:IsRunning() is false – in edit mode, and in Roblox stories (Hoarcekat, UI Labs, Flipbook) – no remotes exist, and both modules return stubs that keep the real API’s shape:

Member Stub
Fire, FireAll, FireList, FireExcept does nothing
On (events and functions) does nothing and returns a disconnect function that does nothing
Iter, Next returns an iterator that ends at once, so a for loop runs zero times
Predict does nothing
A stream’s Set, Clear, Urgent, SetFast do nothing
Invoke does nothing and returns nil
StepReplication, SetRateLimitHandler, SetDecodeErrorHandler, SetPacketDropHandler, SetListenerErrorHandler do nothing
Remotes the real names
Start does nothing
Connected false
GetTrafficStats, ResetTrafficStats the real functions; nothing is sent in edit mode, so every count is zero
exported Read, Write work normally

A client under ClientConnectTimeout that did not find its remotes in time returns the same stubs, with one difference: Invoke fails at once with The client is not connected to a server, as a real call fails on its timeout, instead of returning nil.

The stub is returned before the module checks which side it is on, so a story can require either module – but not both. The RemoteScope guard above runs first, in edit mode too, and the server and client modules of one schema share a scope, so the second one required errors.

Name Why
StepReplication, SetRateLimitHandler, SetDecodeErrorHandler, SetPacketDropHandler, SetListenerErrorHandler, GetTrafficStats, ResetTrafficStats, Limits under ExportLimits, and Rates under ExportRates Module members; GetTrafficStats and ResetTrafficStats are reserved without TrafficStats too, so turning it on never starts refusing a schema. A top-level event, function, scope or type may not take these names, nor, under Camel or Snake casing, the spelling the module gives them there (stepReplication, step_replication, …) (E3005).
Remotes; Start under AutoStart = false; Connected under ClientConnectTimeout Module members added after 0.37.0, refused in the spelling the casing gives them for a top-level event, function, scope or exported type (E3005). A plain type may keep the name: it is a Luau type, not a key of the module.
BLINK_ENTRY, BLINK_QUEUE, BLINK_BUFFER_SAVE, BLINK_INVOCATIONS, BLINK_DROP_REASON, BLINK_TRAFFIC_COUNTS, BLINK_TRAFFIC_STATS, BLINK_EVENTS_SYMBOL, BLINK_STREAM Types the module declares for itself. A top-level type of that name would redefine one (E3005).
Built-in Luau types: any, never, number, thread A declared type is exported under its own name, and Luau refuses export type number = number (E3005, since 0.32.0).
Roblox types the module uses: Player, RemoteEvent, UnreliableRemoteEvent, Vector3, and any class the schema names in Instance(...) A top-level type of that name would refer to itself and shadow every use of the Roblox type in the module (E3005, since 0.32.0). A type inside a scope is exported with the scope’s name in front, so it may take these names.
BlinkBlox’s own types: boolean, buffer, string, unknown, Instance, CFrame, Color3, DateTime, BrickColor and the rest The parser reads them as primitives, so a declaration cannot be named after one (E2002).
Luau keywords (end, local, …) A named type-pack element becomes a parameter, so it must be a Luau identifier (E3005). Struct fields, flags and tags may be keywords; they are quoted.
Player, Buffer, Load, Size, Success and other names the generated functions use Refused as type-pack element names, which would shadow them (E3005); the diagnostic names the clash.
A tagged enum’s tag May not also be a field of one of its variants (E3005).
Limit Value
Outstanding invocations 32 per client; 32 per player on the server
Invocation timeout InvocationTimeout, 10 seconds by default
Queued reliable events with no listener 256 per event (server drops, client warns)
Queued calls with no listener 256 per function on the server, then answered with a failure
Calls running at once Concurrency per player per function on the server, when declared; the rest are answered with a failure
Polled queue on the server 256 x Players.MaxPlayers rows per event
Enum values or tagged-enum variants 256
Declarations per channel 256 (reliable events and functions share one channel, unreliable events the other)
Unbounded string, buffer or array 65535 bytes or elements