Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/facepunch/sbox-public/llms.txt

Use this file to discover all available pages before exploring further.

Remote Procedure Calls (RPCs) let you invoke a method across the network so it runs on one or more connected clients. s&box provides three RPC targeting modes — Rpc.Broadcast, Rpc.Host, and Rpc.Owner — and a set of NetFlags to control delivery and permissions. This page explains all three modes, how to restrict who may invoke an RPC, and how to filter recipients at call time.

The three RPC attributes

Decorate any instance or static method with one of the three nested attributes inside Rpc:
The method runs on every connected client including the host.
[Rpc.Broadcast]
public void OnPlayerDied()
{
    // Called for all clients when any client invokes this
    Log.Info( "A player died!" );
    Sound.Play( "death", Transform.Position );
}
Call it normally from the owner or from the host:
OnPlayerDied(); // sends to all clients
Before the RPC method body runs on remote clients, any [Sync] properties and the GameTransform of the target object are brought up-to-date. The only exception is properties marked with SyncFlags.Query, which are updated on the regular network tick cadence.

NetFlags: delivery and permission flags

Every RPC attribute accepts an optional NetFlags argument. Flags can be combined with |.

Delivery flags

FlagDescription
NetFlags.ReliableDelivery is guaranteed; retransmitted until acknowledged. Default for all RPCs.
NetFlags.UnreliableBest-effort delivery. May arrive out of order or not at all.
NetFlags.SendImmediateDo not batch with other outgoing messages; send right away.
NetFlags.UnreliableNoDelay`UnreliableSendImmediateDiscardOnDelay`. Use for streaming data such as voice.

Permission flags

FlagDescription
NetFlags.HostOnlyOnly the host may invoke this RPC. Clients that call it are ignored.
NetFlags.OwnerOnlyOnly the owner of the object may invoke this RPC (or the host for static RPCs).
// Host-only broadcast — no client can trigger this
[Rpc.Broadcast( NetFlags.HostOnly )]
public void ForceRespawn()
{
    GameObject.WorldPosition = GetSpawnPoint();
}

// Only the owner can send voice data; unreliable + no-delay for real-time audio
[Rpc.Broadcast( NetFlags.OwnerOnly | NetFlags.UnreliableNoDelay )]
public void SendVoiceData( byte[] data )
{
    VoicePlayer.Feed( data );
}
Permission flags are enforced on the receiving side. The host validates HostOnly and OwnerOnly before forwarding or executing the RPC. Never rely solely on client-side guards for security-sensitive actions.

Reading Rpc.Caller

Inside any RPC method body you can find out which connection triggered the call:
[Rpc.Host]
public void RequestItem( string itemId )
{
    var requester = Rpc.Caller;       // the Connection that sent this RPC
    var requesterId = Rpc.CallerId;   // its Guid

    Log.Info( $"{requester.DisplayName} asked for {itemId}" );
}
Rpc.Calling is true only while the method body is running as a result of a remote call. When running locally (the same client that invoked it), Rpc.Calling is false.

Filtering recipients at call time

Rpc.Broadcast sends to every client by default. You can narrow the recipient list at call time using Rpc.FilterInclude or Rpc.FilterExclude. Both return an IDisposable — place them in a using statement so the filter is removed after the call:
// Send only to one specific connection
using ( Rpc.FilterInclude( targetConnection ) )
{
    OnPlayerDied();
}

// Send to everyone except the caller
using ( Rpc.FilterExclude( Rpc.Caller ) )
{
    BroadcastChatMessage( text );
}

// Send to a set of connections
using ( Rpc.FilterInclude( Connection.All.Where( c => c.IsHost ) ) )
{
    SendHostReport();
}

// Include connections that match a predicate
using ( Rpc.FilterInclude( c => c.SteamId != excludedId ) )
{
    PlayEffect();
}
Only one filter can be active at a time. Nesting FilterInclude or FilterExclude inside an already-active filter throws an InvalidOperationException.

Static RPCs

RPCs can be declared on static methods. A static Rpc.Broadcast sends a message with no object context; the engine routes it using just the class and method name:
public static class GameEvents
{
    [Rpc.Broadcast]
    public static void OnRoundStart( int roundNumber )
    {
        Log.Info( $"Round {roundNumber} started" );
    }
}

// Invoke from the host:
GameEvents.OnRoundStart( currentRound );
For static RPCs, NetFlags.OwnerOnly behaves like NetFlags.HostOnly because there is no object with an owner to check against.

How RPCs interact with [Sync] properties

RPCs and synced properties are designed to work together. The engine guarantees that by the time an RPC method body runs on a remote client, all [Sync] properties on the same object reflect the state the sender had at the moment the RPC was called:
[Sync] public string PlayerName { get; set; }

[Rpc.Broadcast]
public void AnnounceJoin()
{
    // PlayerName is up-to-date here on every client
    Chat.AddMessage( $"{PlayerName} joined the game" );
}
Properties marked with SyncFlags.Query are the exception — they are updated on the normal unreliable snapshot cadence, not flushed ahead of RPCs.

Practical patterns

// Client calls this, host executes it
[Rpc.Host]
public void RequestPickup( GameObject item )
{
    if ( !Rpc.Caller.IsValid() ) return;

    // Only the host can reassign ownership
    item.Network.AssignOwnership( Rpc.Caller );
}

Next steps

Sync vars

Keep component properties in sync automatically with [Sync] and SyncFlags.

Networking overview

Revisit the host/client model, NetworkObject, and ownership.

Build docs developers (and LLMs) love