terminalCode Example
BanSystem Pattern for Player Management
BanSystem - Player Ban Management
A GameObjectSystem that manages player bans with persistent storage. Demonstrates Component.INetworkListener, LocalData persistence, and RPC commands.
Implementation
CSHARP
using Sandbox.UI;
/// <summary>
/// Holds a banlist, can ban users
/// </summary>
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
_bans = LocalData.Get<Dictionary<long, BanEntry>>("bans", new()) ?? new();
}
/// <summary>
/// Called when a player tries to connect. Return false to reject.
/// </summary>
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;
}
/// <summary>
/// Bans a connected player and kicks them immediately
/// </summary>
public void Ban(Connection connection, string reason)
{
Assert.True(Networking.IsHost, "Only the host may ban players.");
_bans[connection.SteamId] = new BanEntry(connection.DisplayName, reason);
Save();
Scene.Get<Chat>()?.AddSystemText($"{connection.DisplayName} was banned: {reason}", "🔨");
connection.Kick(reason);
}
/// <summary>
/// Bans a Steam ID by value. Use for pre-banning or banning offline players.
/// </summary>
public void Ban(SteamId steamId, string reason)
{
Assert.True(Networking.IsHost, "Only the host may ban players.");
_bans[steamId] = new BanEntry(steamId.ToString(), reason);
Save();
}
/// <summary>
/// Removes the ban for the given Steam ID.
/// </summary>
public void Unban(SteamId steamId)
{
Assert.True(Networking.IsHost, "Only the host may unban players.");
if (_bans.Remove(steamId))
Save();
}
/// <summary>
/// Returns true if the given Steam ID is currently banned
/// </summary>
public bool IsBanned(SteamId steamId) => _bans.ContainsKey(steamId);
/// <summary>
/// Returns a read-only view of all active bans
/// </summary>
public IReadOnlyDictionary<SteamId, BanEntry> GetBannedList() =>
_bans.ToDictionary(x => (SteamId)x.Key, x => x.Value);
private void Save() => LocalData.Set("bans", _bans);
/// <summary>
/// RPC to ban a connected player. Caller must be host or have admin permission.
/// </summary>
[Rpc.Host]
public static void RpcBanPlayer(Connection target, string reason = "Banned")
{
if (!Rpc.Caller.HasPermission("admin")) return;
Current.Ban(target, reason);
}
/// <summary>
/// Bans a player by name or Steam ID. Usage: ban [name|steamid] [reason]
/// </summary>
[ConCmd("ban")]
public static void BanCommand(string target, string reason = "Banned")
{
if (!Networking.IsHost) return;
// Try parsing as a Steam ID (64-bit integer) first
if (ulong.TryParse(target, out var steamIdValue))
{
var steamId = steamIdValue;
var connection = Connection.All.FirstOrDefault(c => c.SteamId == steamId);
if (connection is not null)
Current.Ban(connection, reason);
else
Current.Ban(steamId, reason);
Log.Info($"Banned {steamId}: {reason}");
return;
}
// Fall back to partial name match
var conn = GameManager.FindPlayerWithName(target);
if (conn is not null)
{
Current.Ban(conn, reason);
Log.Info($"Banned {conn.DisplayName}: {reason}");
}
else
{
Log.Warning($"Could not find player '{target}'");
}
}
}Key Patterns
LocalData Persistence
CSHARP
// Load on initialization
_bans = LocalData.Get<Dictionary<long, BanEntry>>("bans", new()) ?? new();
// Save after modifications
private void Save() => LocalData.Set("bans", _bans);INetworkListener for Connection Control
CSHARP
public sealed class BanSystem : GameObjectSystem<BanSystem>, Component.INetworkListener
{
bool INetworkListener.AcceptConnection(Connection connection, ref string reason)
{
if (IsBanned(connection.SteamId))
{
reason = "You're banned from this server";
return false;
}
return true;
}
}Permission Checking
CSHARP
[Rpc.Host]
public static void RpcBanPlayer(Connection target, string reason)
{
// Rpc.Caller is the connection that made the RPC call
if (!Rpc.Caller.HasPermission("admin"))
return;
Current.Ban(target, reason);
}Connection Management
CSHARP
// Kick a player
connection.Kick(reason);
// Find player by Steam ID
var connection = Connection.All.FirstOrDefault(c => c.SteamId == steamId);
// Find player by name
var conn = GameManager.FindPlayerWithName(name);Security Considerations
- Always use [Rpc.Host] for administrative actions
- Verify Networking.IsHost for console commands
- Check Rpc.Caller.HasPermission() for elevated operations
- Use Assert.True(Networking.IsHost, "...") for host-only public APIs
Was this helpful?