menu_bookDocumentation
Sandbox: BanSystem — persistent ban list with INetworkListener.AcceptConnection
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
- Bans are stored in LocalData (persistent local storage) — they survive server restarts
- AcceptConnection is called before the player joins — banned players never enter the scene
- Rpc.Caller.HasPermission("admin") is the standard admin check throughout Sandbox
- GameManager.FindPlayerWithName(target) does a partial case-insensitive name match
- sb.ownership_checks (on Ownable) and sb.developer_entities follow the same ConVarFlags.Replicated | ConVarFlags.Server | ConVarFlags.GameSetting pattern
Was this helpful?