Skip to main content
The Rpc static class is the entry point for all remote procedure call functionality. Decorate methods with the RPC attributes and the engine code-generates the network plumbing. Use [Sync] and [HostSync] on properties to replicate state automatically.

RPC attributes

Apply one of the following attributes to a Component or GameObjectSystem method to make it an RPC.
The method runs on every connected client, including the host.
[Rpc.Broadcast]
public void OnExplosion( Vector3 position )
{
    // Executes on all clients
    Sound.Play( "explosion", position );
    SpawnParticles( position );
}
Call the method normally — the engine intercepts the call and sends it over the network before executing it locally.

NetFlags

Pass NetFlags to the attribute constructor to control reliability:
// Unreliable — faster but may drop packets
[Rpc.Broadcast( NetFlags.Unreliable )]
public void OnPositionUpdate( Vector3 pos ) { ... }
FlagDescription
NetFlags.ReliableGuaranteed delivery with ordering. Default for all RPC types.
NetFlags.UnreliableNo delivery guarantee. Lower overhead for high-frequency updates.
NetFlags.HostOnlyOnly the host is permitted to call this RPC.
NetFlags.OwnerOnlyOnly the owner of the GameObject is permitted to call this RPC.

Rpc static properties

Rpc.Caller

Rpc.Caller
Connection
The Connection that initiated the current RPC call. Only valid inside an executing RPC method. Use this to identify who sent the message.
[Rpc.Host]
public void TakeDamage( float amount )
{
    Log.Info( $"Damage request from {Rpc.Caller.DisplayName}" );
    ApplyDamage( amount );
}

Rpc.Calling

Rpc.Calling
bool
Returns true when the current method is being executed as a result of an incoming remote call. Returns false when you are the local initiator.
[Rpc.Broadcast]
public void DoSomething()
{
    if ( Rpc.Calling )
        Log.Info( "Received from remote" );
    else
        Log.Info( "Initiated locally" );
}

Filter methods

Filter methods restrict which connections receive an RPC. They return an IDisposable scope — use using to ensure the filter is removed after the call.
You cannot nest filter scopes. A second FilterInclude or FilterExclude call while a filter is active throws InvalidOperationException.

FilterInclude

Send the RPC only to connections that match the criteria.
connections
IEnumerable<Connection>
An explicit set of connections to include.
connection
Connection
A single connection to include.
predicate
Predicate<Connection>
A delegate that returns true for connections that should receive the RPC.
// Single connection
using ( Rpc.FilterInclude( targetConnection ) )
{
    SendPrivateMessage( "Hello!" );
}

// Collection
using ( Rpc.FilterInclude( teamConnections ) )
{
    BroadcastTeamUpdate( teamScore );
}

// Predicate
using ( Rpc.FilterInclude( c => c.Ping < 150 ) )
{
    SendHighFrequencyData( data );
}

FilterExclude

Send the RPC to all connections except those that match the criteria.
connections
IEnumerable<Connection>
An explicit set of connections to exclude.
connection
Connection
A single connection to exclude.
predicate
Predicate<Connection>
A delegate that returns true for connections that should not receive the RPC.
// Exclude a single connection
using ( Rpc.FilterExclude( localConnection ) )
{
    BroadcastToOthers( "Player joined!" );
}

// Exclude spectators
using ( Rpc.FilterExclude( spectatorConnections ) )
{
    BroadcastGameplayEvent();
}

Sync attributes

Mark a Component property with [Sync] to have the engine automatically replicate it from the owner to all other clients every network tick.
public sealed class PlayerStats : Component
{
    [Sync] public float Health { get; set; } = 100f;
    [Sync] public int Score { get; set; }
    [Sync] public bool IsAlive { get; set; } = true;
}

SyncFlags

SyncFlags.Query
SyncFlags
Poll the property getter for changes every tick instead of relying on the setter being called. Use this when the returned value can change without going through the setter — for example, a value driven by a physics simulation.
SyncFlags.Interpolate
SyncFlags
Interpolate the value between network ticks for smoother rendering. Supported types: float, double, Angles, Rotation, Transform, Vector3.
SyncFlags.FromHost
SyncFlags
The host is authoritative for this value. Equivalent to [HostSync].
// Poll every tick — for externally-driven values
[Sync( SyncFlags.Query )] public Vector3 PhysicsPosition { get; set; }

// Interpolate between ticks for smooth visuals
[Sync( SyncFlags.Interpolate )] public Vector3 Position { get; set; }

// Host-owned
[Sync( SyncFlags.FromHost )] public int ServerScore { get; set; }

HostSync

[HostSync] is shorthand for [Sync( SyncFlags.FromHost )]. The host writes the value; all clients (including the owning client) receive it read-only.
[HostSync] public int RoundNumber { get; set; }

NetworkObject properties

Every networked GameObject exposes network state through GameObject.Network. The table below documents the most commonly used properties:
PropertyTypeDescription
IsOwnerbooltrue if the local connection owns this object.
IsProxybooltrue if another connection owns this object.
IsUnownedbooltrue if no connection currently owns the object.
OwnerIdGuidThe Guid of the owning connection, or Guid.Empty if unowned.
CreatorGuidThe Guid of the connection that originally spawned this object.
protected override void OnUpdate()
{
    // Only run owner-side logic
    if ( IsProxy ) return;

    HandleMovement();
}

void OnSomethingHappened()
{
    // Transfer ownership to another player
    GameObject.Network.AssignOwnership( newConnection );
}
IsProxy is the recommended guard for owner-vs-client branching. It returns true on every peer that does not own the object, including the host when another client owns it.

Complete networked component example

using Sandbox;

public sealed class NetworkedWeapon : Component
{
    // Replicated from owner to all clients
    [Sync] public int Ammo { get; set; } = 30;
    [Sync] public bool IsReloading { get; set; }

    // Host-controlled
    [HostSync] public int TotalKills { get; set; }

    protected override void OnUpdate()
    {
        // Only the owner should read input and fire
        if ( IsProxy ) return;

        if ( Input.Pressed( "Attack1" ) && Ammo > 0 && !IsReloading )
        {
            Ammo--;
            FireWeapon();
        }

        if ( Input.Pressed( "Reload" ) && Ammo < 30 )
        {
            IsReloading = true;
            RequestReload();
        }
    }

    // Broadcast a visual/audio event to all clients
    [Rpc.Broadcast]
    private void FireWeapon()
    {
        Sound.Play( "weapon_fire", WorldPosition );
        SpawnMuzzleFlash();
    }

    // Only the host processes reload completion
    [Rpc.Host]
    private void RequestReload()
    {
        // Validate on host side
        _ = ReloadAsync();
    }

    private async Task ReloadAsync()
    {
        await Task.DelaySeconds( 2f );
        Ammo = 30;
        FinishReload();
    }

    // Tell the owner reload is done
    [Rpc.Owner]
    private void FinishReload()
    {
        IsReloading = false;
    }

    // Notify all clients about a kill (called by host)
    [Rpc.Broadcast]
    public static void AnnounceKill( string killerName, string victimName )
    {
        Log.Info( $"{killerName} eliminated {victimName}" );
    }
}
1

Set NetworkMode

Set the GameObject’s NetworkMode to Object in the Inspector or in code.
2

Mark synced properties

Add [Sync] to properties that should replicate from owner to clients. Use [HostSync] for host-authoritative values.
3

Guard owner-only logic

Check IsProxy before running logic that only the owner should execute.
4

Use RPCs for events

Use [Rpc.Broadcast] for events all clients must react to, [Rpc.Host] for authoritative server-side requests, and [Rpc.Owner] to push results back to the owning client.

Build docs developers (and LLMs) love