Skip to main content
Any GameObject in s&box can become a networked object. Once networked, it is replicated to all connected clients and can have synchronized properties, RPCs, and ownership.

Spawning a networked object

Call NetworkSpawn() on any GameObject to make it networked. From that point on, the object is sent to all clients.
var go = PlayerPrefab.Clone( SpawnPoint.Transform.World );
go.NetworkSpawn();
To assign a specific connection as the owner at spawn time, pass the connection:
var player = PlayerPrefab.Clone( SpawnPoint.Transform.World );
player.NetworkSpawn( connection );

Network mode

Every GameObject has a Network Mode that controls how (or whether) it is networked.
ModeBehaviour
NetworkMode.NeverThe object is never networked to other clients.
NetworkMode.ObjectThe object is sent to other clients as its own networked object with synchronized properties and RPCs.
NetworkMode.Snapshot (default)The host sends the object as part of the initial scene snapshot when a client joins.
You can change the Network Mode on a GameObject in the Inspector, or set it in code before calling NetworkSpawn().

Destroying a networked object

Destroy a networked object the same way you would any other GameObject:
go.Destroy();
The destruction is automatically replicated to all clients.

Sync properties

Add the [Sync] attribute to a property on a Component to have its value automatically sent to all clients whenever it changes.
public class MyComponent : Component
{
    [Sync] public int Kills { get; set; }
}
Only the owner of the networked object can change synced properties.

Supported types

[Sync] supports unmanaged value types and string. This includes int, bool, float, Vector3, structs, and specific classes such as GameObject, Component, and GameResource.

Detecting changes

Apply [Change] alongside [Sync] to register a callback that fires when the property’s value changes:
public class MyComponent : Component
{
    [Sync, Change( "OnIsRunningChanged" )] public bool IsRunning { get; set; }

    private void OnIsRunningChanged( bool oldValue, bool newValue )
    {
        // IsRunning has changed
    }
}
The [Change] callback is not invoked when a collection changes — only when the property itself is assigned a different value.

Sync flags

Customize synchronized property behaviour with SyncFlags:
FlagDescription
SyncFlags.QueryChecks the value for changes every network update instead of relying on the setter. Use this when the backing field can be modified without going through the property setter.
SyncFlags.FromHostThe host owns the value regardless of who owns the object. Only the host can change it.
SyncFlags.InterpolateInterpolates the value over a few ticks for other clients.

Networked collections

Use NetList<T> and NetDictionary<K,V> to synchronize collections:
public enum AmmoCount { Pistol, Rifle }

public class MyComponent : Component
{
    [Sync] public NetList<int> List { get; set; } = new();
    [Sync] public NetDictionary<AmmoCount, int> Dictionary { get; set; } = new();
}
NetList and NetDictionary do not currently support the [Property] attribute.

Interpolation

By default, the transform of all networked objects is smoothly interpolated for other clients.

Disabling interpolation

// Disable interpolation for this networked object
Network.DisableInterpolation();
You can also disable interpolation from the Inspector on the networked object.

Clearing interpolation

Use Network.ClearInterpolation() to instantly snap an object to its current position for everyone — useful for teleportation:
Transform.Position = Vector3.Zero;
Network.ClearInterpolation();

Refreshing a networked object

Once you call NetworkSpawn(), any further changes to the object’s components or hierarchy are not automatically networked. Only the state at spawn time is sent to other clients.
If you add new components, change component enabled states, or restructure a networked object’s hierarchy after spawning, call Network.Refresh() to push the updated structure to all clients:
Network.Refresh();
By default, only the host can send refresh updates. You can allow the owner to send them too via connection permissions.

Ownership

Every networked object can have an owner — the connection responsible for simulating it (position, rotation, scale, and synced properties). If no owner is assigned, the host simulates the object.

Checking ownership

Use IsProxy to determine whether the local client should be simulating the object:
public override void Update()
{
    // Controlled by another client — skip local input
    if ( IsProxy ) return;

    if ( Input.Pressed( "use" ) )
    {
        TryPickup();
    }
}

Taking ownership

go.Network.TakeOwnership();

Dropping ownership

Carrying.Network.DropOwnership();

Transferring ownership

By default, only the host can change a networked object’s owner. Change this with SetOwnerTransfer:
// Allow anyone to take ownership
go.Network.SetOwnerTransfer( OwnerTransfer.Takeover );
TypeBehaviour
OwnerTransfer.Fixed (default)Only the host can change the owner.
OwnerTransfer.TakeoverAny client can take ownership.
OwnerTransfer.RequestA client must request ownership from the host.

Orphaned objects on disconnect

When an owner disconnects, all objects they own are destroyed by default. Change this with SetOrphanedMode:
go.Network.SetOrphanedMode( NetworkOrphaned.Host );
TypeBehaviour
NetworkOrphaned.Destroy (default)Destroy the object when the owner disconnects.
NetworkOrphaned.HostAssign ownership to the host when the owner disconnects.
NetworkOrphaned.RandomAssign ownership to a random client when the owner disconnects.
NetworkOrphaned.ClearOwnerKeep the object but clear ownership (host simulates it).

Network visibility

By default, all networked objects transmit to all connected clients. For larger games, you can cull objects to reduce bandwidth.

Always Transmit

Every networked object has an Always Transmit flag (default: true). When true, the object is never culled and is visible to every player.

Custom visibility

To control visibility per connection, attach a component that implements INetworkVisible to the root GameObject of the networked object, then disable Always Transmit:
public class MyVisibilityComponent : Component, INetworkVisible
{
    public bool IsVisibleToConnection( Connection connection, in BBox worldBounds )
    {
        // Return true to send updates; false to cull
        return true;
    }
}
ParameterDescription
Connection connectionThe target client being tested.
BBox worldBoundsThe object’s world-space bounding box. Useful for distance or frustum checks.
Only the owner of a networked object decides its visibility for each connection.

Hammer PVS

If no INetworkVisible component is present and the map is a Hammer map with VIS compiled, the engine falls back to PVS (Potentially Visible Set) automatically.

What happens when an object is culled

When an object is culled for a connection:
  • Sync var updates and transform updates stop being sent to that client.
  • The object remains spawned on the client but is disabled locally.
  • RPCs are still delivered to that client.

Custom snapshot data

When a client joins, they receive a snapshot of the current scene state. You can write additional data into this snapshot by implementing Component.INetworkSnapshot on a component.

Writing snapshot data

private byte[] MyVoxelData { get; set; }

void INetworkSnapshot.WriteSnapshot( ref ByteStream writer )
{
    writer.Write( MyVoxelData.Length );
    writer.WriteArray( MyVoxelData );
}

Reading snapshot data

void INetworkSnapshot.ReadSnapshot( ref ByteStream reader )
{
    var length = reader.Read<int>();
    MyVoxelData = reader.ReadArray<byte>( length ).ToArray();
}

protected override async Task OnLoad()
{
    await LoadVoxelWorld( MyVoxelData );
}

private async Task LoadVoxelWorld( byte[] data )
{
    // parse and build the voxel world from data
}
Using async Task OnLoad() causes the loading screen to wait until the task completes before letting the player in.