menu_bookDocumentation

Sandbox: BanSystem — persistent ban list with INetworkListener.AcceptConnection

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

Sandbox BanSystem — Persistent Ban List with INetworkListener

BanSystem is a GameObjectSystem that implements Component.INetworkListener to intercept incoming connections and reject banned players before they join.

Implementation

CSHARP
public sealed class BanSystem : GameObjectSystem<BanSystem>, Component.INetworkListener
{
    public record struct BanEntry( string DisplayName, string Reason );

    private Dictionary<long, BanEntry> _bans = new();

    public BanSystem( Scene scene ) : base( scene )
    {
        // Load bans from local persistent storage on startup
        _bans = LocalData.Get<Dictionary<long, BanEntry>>( "bans", new() ) ?? new();
    }

    // Called before a connection is accepted — return false to reject
    bool Component.INetworkListener.AcceptConnection( Connection connection, ref string reason )
    {
        if ( !_bans.TryGetValue( connection.SteamId, out var entry ) )
            return true;

        reason = $"You're banned from this server: {entry.Reason}";
        return false;
    }

    public void Ban( Connection connection, string reason )
    {
        Assert.True( Networking.IsHost );
        _bans[connection.SteamId] = new BanEntry( connection.DisplayName, reason );
        Save();
        connection.Kick( $"Banned: {reason}" );
    }

    public void Unban( long steamId )
    {
        Assert.True( Networking.IsHost );
        _bans.Remove( steamId );
        Save();
    }

    public bool IsBanned( long steamId ) => _bans.ContainsKey( steamId );

    public IReadOnlyDictionary<long, BanEntry> GetBannedList() => _bans;

    private void Save() => LocalData.Set( "bans", _bans );
}

RPC ban command

CSHARP
// Admin-only RPC — can be called by any client with "admin" permission
[Rpc.Host]
public static void RpcBanPlayer( Connection target, string reason = "Banned" )
{
    if ( !Rpc.Caller.HasPermission( "admin" ) ) return;
    Current.Ban( target, reason );
}

Console commands

CSHARP
// sb_ban <name> <reason> — partial name match
[ConCmd( "sb_ban" )]
public static void BanCmd( string target, string reason = "Banned" )
{
    var conn = GameManager.FindPlayerWithName( target );
    if ( conn is not null )
        Current.Ban( conn, reason );
}

// sb_unban <steamid>
[ConCmd( "sb_unban" )]
public static void UnbanCmd( long steamId )
{
    Current.Unban( steamId );
}

ServerSettings

CSHARP
public static class ServerSettings
{
    // sb.developer_entities — show dev entities in spawn menu for all players
    [ConVar( "sb.developer_entities", ConVarFlags.Replicated | ConVarFlags.GameSetting | ConVarFlags.Server )]
    public static bool DeveloperEntities { get; set; } = false;

    // Always true in editor regardless of ConVar
    public static bool ShowDeveloperEntities => Game.IsEditor || DeveloperEntities;
}

Key points

Was this helpful?