groupsCommunity

Multiplayer Authority Patterns

calendar_today May 3, 2026 schedule ~1 min read person patrickjr verified 50

Multiplayer Authority Patterns

Community guide for structuring multiplayer code based on official networking documentation and proven patterns.

Host-Authoritative Pattern

The host/server has final say on game state:

CSHARP
public class GameManager : Component, INetworkListener
{
    // Only host manages game state
    [Sync] public GameState CurrentState { get; set; }
    
    [Rpc.Host]
    public void RequestGameStart()
    {
        // Host validates and decides
        if (CanStartGame())
        {
            StartGameRpc();
        }
    }
    
    [Rpc.Broadcast]
    void StartGameRpc()
    {
        // All clients receive this
        CurrentState = GameState.Playing;
    }
}

Owner-Auth with Host Validation

Clients simulate their owned objects, host validates:

CSHARP
public class PlayerController : Component
{
    [Sync(SyncFlags.Interpolate)] 
    public Vector3 Position { get; set; }
    
    protected override void OnUpdate()
    {
        // Client predicts movement
        if (Network.IsOwner)
        {
            var input = Input.AnalogMove;
            var newPos = Position + input * Speed * Time.Delta;
            
            // Request host validation
            MoveRequestRpc(newPos);
        }
    }
    
    [Rpc.Host]
    void MoveRequestRpc(Vector3 requestedPosition)
    {
        // Host validates (anti-cheat)
        if (IsValidMove(requestedPosition))
        {
            Position = requestedPosition;
        }
    }
}

Client-Side Prediction Pattern

Smooth gameplay despite latency:

CSHARP
public class Weapon : Component
{
    void Fire()
    {
        // 1. Client shows effect immediately (prediction)
        PlayMuzzleFlash();
        PlayFireSound();
        
        // 2. Server validates and confirms
        FireRequestRpc();
    }
    
    [Rpc.Host]
    void FireRequestRpc()
    {
        // Server checks: ammo, cooldown, line of sight
        if (!CanFire()) return;
        
        // Apply damage
        var tr = Scene.Trace.Ray(Owner.AimRay, 10000).Run();
        if (tr.Hit && tr.GameObject.IsValid())
        {
            ApplyDamageRpc(tr.GameObject.Id, Damage);
        }
    }
    
    [Rpc.Broadcast]
    void ApplyDamageRpc(int targetId, float damage)
    {
        // All clients apply damage (if they have the object)
        var target = Scene.Directory.FindByIndex(targetId);
        if (target.IsValid() && target.Components.TryGet<Health>(out var health))
        {
            health.Damage(damage);
        }
    }
}

Spawn Ownership Transfer

Proper object lifecycle in multiplayer:

CSHARP
public class ItemSpawner : Component
{
    void SpawnItem(Vector3 position)
    {
        // Spawn networked object
        var item = new GameObject();
        item.WorldPosition = position;
        
        // Host owns spawned items by default
        item.NetworkSpawn();
        
        // Allow players to take ownership when picked up
        item.Network.SetOwnerTransfer(OwnerTransfer.Takeover);
    }
}

public class PickupItem : Component
{
    void OnInteract(Player player)
    {
        // Player takes ownership
        if (player.Network.IsOwner)
        {
            GameObject.Network.TakeOwnership();
            
            // Parent to player
            GameObject.SetParent(player.GameObject);
        }
    }
}

Network Visibility (PVS)

Control what each player receives:

CSHARP
public class StealthPlayer : Component, INetworkVisible
{
    public bool IsInvisible { get; set; }
    
    public bool IsNetworkVisible(Connection connection)
    {
        // Only visible to self and host when invisible
        if (!IsInvisible) return true;
        
        return connection == Network.OwnerConnection || 
               connection == Connection.Host;
    }
}

Common Multiplayer Structure

CSHARP
public class MultiplayerGame : Component, INetworkListener
{
    // Host-only game state
    [Sync(SyncFlags.FromHost)]
    public float GameTime { get; set; }
    
    // Spawned on player connect
    [Property] public GameObject PlayerPrefab { get; set; }
    
    public void OnActive(Connection connection)
    {
        // Host spawns player for new connection
        if (Network.IsHost)
        {
            var player = PlayerPrefab.Clone();
            player.NetworkSpawn(connection);
        }
    }
    
    public void OnDisconnected(Connection connection)
    {
        // Cleanup player's objects
        foreach (var obj in Scene.GetAllObjects(true))
        {
            if (obj.Network.OwnerId == connection.Id)
            {
                obj.Destroy();
            }
        }
    }
}

Source

  • Official networking docs: https://sbox.game/dev/doc/networking
  • Facepunch multiplayer examples
  • Community-tested patterns from s&box Discord
Was this helpful?