Skip to main content
s&box networking is built around a host-authoritative model using Steam’s networking stack. Objects are networked per-GameObject, and synchronization happens automatically for properties you mark with [Sync]. For one-off events you call Remote Procedure Calls (RPCs).

NetworkMode

Every GameObject has a NetworkMode that controls whether and how it is networked.
ValueDescription
NeverThe object is never networked. This is the default.
ObjectThe object is networked as a single network object. It can have an owner and [Sync] properties.
SnapshotThe object is networked as part of the scene snapshot (position/rotation only).
Set it from the Inspector or in code:
GameObject.NetworkMode = NetworkMode.Object;

Spawning a networked object

To spawn a networked GameObject that is owned by a specific connection, call NetworkSpawn on the GameObject after enabling it:
// Host-side: spawn a prefab and assign ownership to a specific connection
var go = SceneUtility.GetPrefabScene( prefab ).Clone();
go.NetworkSpawn( connection );
Only the host can spawn networked objects. Clients must request the host to spawn objects on their behalf.

Sync properties

Mark any property on a Component with [Sync] and the engine will automatically replicate it from the owner to all other clients every network tick.
public sealed class HealthComponent : Component
{
    [Sync] public float Health { get; set; } = 100f;
    [Sync] public bool IsAlive { get; set; } = true;
}
[Sync] hooks into the property setter via code generation. The value is sent reliably when it changes, so you can set it as often as you like without flooding the network.

HostSync

Use [HostSync] when only the host should be able to write the value and all clients (including the owning client) should receive it read-only:
[HostSync] public int Score { get; set; }

SyncFlags

Pass SyncFlags to control sync behaviour:
// Poll the getter every tick instead of relying on set being called
[Sync( SyncFlags.Query )] public Vector3 ExternalPosition { get; set; }

RPCs

RPCs let you call a method across the network. Decorate a method with the appropriate attribute and the engine code-generates the networking plumbing.

Rpc.Broadcast

The method is called on every connected client, including the host.
[Rpc.Broadcast]
public void OnPlayerDied( string killerName )
{
    // Runs on all clients
    Sound.Play( "player_death" );
    Log.Info( $"{killerName} killed someone" );
}
Call it normally on the owner — the engine intercepts the call and sends it over the network:
OnPlayerDied( "Alice" );

Rpc.Owner

The method is called only on the owner of the GameObject (or the host if the object is unowned).
[Rpc.Owner]
public void ReceiveAmmo( int amount )
{
    // Only runs on the owning client
    CurrentAmmo += amount;
}

Rpc.Host

The method is called only on the host.
[Rpc.Host]
public void RequestSpawnItem( string itemId )
{
    // Only runs on the host — safe to trust for game logic
    SpawnItemForPlayer( Rpc.Caller, itemId );
}

Rpc.Caller

Inside an RPC method you can check who invoked it:
[Rpc.Host]
public void TakeDamage( float amount )
{
    Log.Info( $"Damage from {Rpc.Caller.DisplayName}" );
}

Filtering RPC recipients

By default Rpc.Broadcast sends to every connection. Wrap a call in a filter scope to restrict delivery.

FilterInclude

// Send only to a single connection
using ( Rpc.FilterInclude( targetConnection ) )
{
    OnPlayerDied( "Alice" );
}

// Send to a set of connections
using ( Rpc.FilterInclude( teamConnections ) )
{
    SendTeamMessage( "Enemy spotted!" );
}

// Send to connections that match a predicate
using ( Rpc.FilterInclude( c => c.Ping < 200 ) )
{
    SendHighPriorityEvent();
}

FilterExclude

// Send to everyone except one connection
using ( Rpc.FilterExclude( localConnection ) )
{
    BroadcastEvent( "something happened" );
}

// Exclude multiple connections
using ( Rpc.FilterExclude( spectatorConnections ) )
{
    BroadcastGameplayEvent();
}
You cannot nest filter scopes. Attempting to set a second filter while one is already active throws an InvalidOperationException.

Ownership

A networked object can be owned by at most one Connection. The engine tracks ownership so that:
  • [Sync] values flow from the owner to everyone else.
  • Rpc.Owner is routed to the right client.
  • IsProxy returns true on every client that does not own the object.
// Check ownership from within a component
if ( IsProxy )
{
    // We don't own this object — read only
    return;
}

// Transfer ownership to another player
GameObject.Network.AssignOwnership( newOwnerConnection );

NetworkOrphaned

When the owning connection disconnects, the engine executes the object’s NetworkOrphaned policy:
PolicyBehaviour
DestroyThe object is destroyed.
ClearOwnerOwnership is cleared (host takes control).
HostOwnership is transferred to the host connection.
RandomOwnership is given to a random remaining connection.

Networking state helpers

// Is this peer the host?
bool amHost = Networking.IsHost;

// Is this peer a connected client (not the host)?
bool amClient = Networking.IsClient;

// Is there an active network session?
bool sessionActive = Networking.IsActive;

Minimal networked component example

public sealed class NetworkedCounter : Component
{
    // Replicated from owner to all clients
    [Sync] public int Count { get; set; }

    protected override void OnUpdate()
    {
        // Only the owner increments
        if ( IsProxy ) return;

        if ( Input.Pressed( "Attack1" ) )
        {
            Count++;
            NotifyAll( Count );
        }
    }

    [Rpc.Broadcast]
    private void NotifyAll( int newCount )
    {
        Log.Info( $"Counter is now {newCount} (told by {Rpc.Caller.DisplayName})" );
    }
}
1

Add NetworkObject mode

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

Mark synced properties

Add [Sync] to any property that should replicate from owner to clients.
3

Guard owner-only logic

Check IsProxy before writing state or calling host-side RPCs.
4

Use RPCs for events

Use [Rpc.Broadcast] for events all clients need to react to, and [Rpc.Host] for authoritative requests.

Build docs developers (and LLMs) love