Files
CS2-SimpleAdmin/CS2-SimpleAdmin-docs/docs/developer/api/penalties.md
Dawid Bepierszcz b0d8696756 Add CS2-SimpleAdmin documentation site
Introduces a new documentation site for CS2-SimpleAdmin using Docusaurus, including developer API references, tutorials, user guides, and module documentation. Removes the CleanModule example and updates FunCommands and ExampleModule. Also updates main plugin and API files to support new documentation and module structure.
2025-10-20 01:27:01 +02:00

13 KiB

sidebar_position
sidebar_position
4

Penalties API

Complete reference for issuing and managing player penalties.

Penalty Types

public enum PenaltyType
{
    Ban,      // Ban player from server
    Kick,     // Kick player from server
    Gag,      // Block text chat
    Mute,     // Block voice chat
    Silence,  // Block both text and voice
    Warn      // Issue warning
}

Issue Penalties

IssuePenalty (Online Player)

Issue a penalty to a currently connected player.

void IssuePenalty(
    CCSPlayerController player,
    CCSPlayerController? admin,
    PenaltyType penaltyType,
    string reason,
    int duration = -1
)

Parameters:

  • player - Target player controller
  • admin - Admin issuing penalty (null for console)
  • penaltyType - Type of penalty
  • reason - Reason for penalty
  • duration - Duration in minutes (0 = permanent, -1 = default)

Example:

// Ban player for 1 day
_api!.IssuePenalty(
    player,
    admin,
    PenaltyType.Ban,
    "Cheating",
    1440  // 24 hours in minutes
);

// Permanent ban
_api.IssuePenalty(
    player,
    admin,
    PenaltyType.Ban,
    "Severe rule violation",
    0
);

// Kick player
_api.IssuePenalty(
    player,
    admin,
    PenaltyType.Kick,
    "AFK"
);

// Gag for 30 minutes
_api.IssuePenalty(
    player,
    admin,
    PenaltyType.Gag,
    "Chat spam",
    30
);

IssuePenalty (Offline Player)

Issue a penalty to a player by SteamID (even if offline).

void IssuePenalty(
    SteamID steamid,
    CCSPlayerController? admin,
    PenaltyType penaltyType,
    string reason,
    int duration = -1
)

Parameters:

  • steamid - Target player's SteamID
  • admin - Admin issuing penalty (null for console)
  • penaltyType - Type of penalty
  • reason - Reason for penalty
  • duration - Duration in minutes (0 = permanent, -1 = default)

Example:

// Ban offline player
var steamId = new SteamID(76561198012345678);

_api!.IssuePenalty(
    steamId,
    admin,
    PenaltyType.Ban,
    "Ban evasion",
    10080  // 7 days
);

// Mute offline player
_api.IssuePenalty(
    steamId,
    admin,
    PenaltyType.Mute,
    "Voice abuse",
    1440
);

Supported SteamID Formats:

// SteamID64
new SteamID(76561198012345678)

// Also works with SteamID string parsing
SteamID.FromString("STEAM_1:0:12345678")
SteamID.FromString("[U:1:12345678]")

Get Player Information

GetPlayerInfo

Get detailed player information including penalty counts.

PlayerInfo GetPlayerInfo(CCSPlayerController player)

Returns: PlayerInfo object containing:

  • PlayerName - Player's name
  • SteamId - Steam ID (ulong)
  • IpAddress - Player's IP address
  • Warnings - Warning count
  • Bans - Ban count
  • Mutes - Mute count
  • Gags - Gag count

Example:

var playerInfo = _api!.GetPlayerInfo(player);

Console.WriteLine($"Player: {playerInfo.PlayerName}");
Console.WriteLine($"SteamID: {playerInfo.SteamId}");
Console.WriteLine($"Warnings: {playerInfo.Warnings}");
Console.WriteLine($"Total Bans: {playerInfo.Bans}");

// Check if player has penalties
if (playerInfo.Warnings >= 3)
{
    _api.IssuePenalty(player, null, PenaltyType.Ban, "Too many warnings", 1440);
}

Throws:

  • KeyNotFoundException - If player doesn't have a valid UserId

GetPlayerMuteStatus

Get current mute/gag/silence status for a player.

Dictionary<PenaltyType, List<(DateTime EndDateTime, int Duration, bool Passed)>> GetPlayerMuteStatus(
    CCSPlayerController player
)

Returns: Dictionary mapping penalty types to lists of active penalties

Example:

var muteStatus = _api!.GetPlayerMuteStatus(player);

// Check if player is gagged
if (muteStatus.ContainsKey(PenaltyType.Gag))
{
    var gagPenalties = muteStatus[PenaltyType.Gag];

    foreach (var (endTime, duration, passed) in gagPenalties)
    {
        if (!passed)
        {
            var remaining = endTime - DateTime.UtcNow;
            Console.WriteLine($"Gagged for {remaining.TotalMinutes:F0} more minutes");
        }
    }
}

// Check if player is muted
if (muteStatus.ContainsKey(PenaltyType.Mute))
{
    Console.WriteLine("Player is currently muted");
}

// Check if player is silenced
if (muteStatus.ContainsKey(PenaltyType.Silence))
{
    Console.WriteLine("Player is silenced (gag + mute)");
}

Server Information

GetConnectionString

Get the database connection string.

string GetConnectionString()

Example:

var connectionString = _api!.GetConnectionString();
// Use for custom database operations

GetServerAddress

Get the server's IP address and port.

string GetServerAddress()

Example:

var serverAddress = _api!.GetServerAddress();
Console.WriteLine($"Server: {serverAddress}");
// Example output: "192.168.1.100:27015"

GetServerId

Get the server's unique ID in the database.

int? GetServerId()

Returns:

  • int - Server ID if multi-server mode enabled
  • null - If single-server mode

Example:

var serverId = _api!.GetServerId();

if (serverId.HasValue)
{
    Console.WriteLine($"Server ID: {serverId.Value}");
}
else
{
    Console.WriteLine("Single-server mode");
}

Complete Examples

Ban with Validation

private void BanPlayer(CCSPlayerController? admin, CCSPlayerController target, int duration, string reason)
{
    // Validate player
    if (!target.IsValid)
    {
        admin?.PrintToChat("Invalid player!");
        return;
    }

    // Check immunity
    if (admin != null && !admin.CanTarget(target))
    {
        admin.PrintToChat($"You cannot ban {target.PlayerName} (higher immunity)!");
        return;
    }

    // Get player info to check history
    var playerInfo = _api!.GetPlayerInfo(target);

    Logger.LogInformation(
        $"{admin?.PlayerName ?? "Console"} banning {playerInfo.PlayerName} " +
        $"(SteamID: {playerInfo.SteamId}, Previous bans: {playerInfo.Bans})"
    );

    // Issue ban
    _api.IssuePenalty(target, admin, PenaltyType.Ban, reason, duration);

    // Show activity
    if (admin == null || !_api.IsAdminSilent(admin))
    {
        var durationText = duration == 0 ? "permanently" : $"for {duration} minutes";
        Server.PrintToChatAll($"{admin?.PlayerName ?? "Console"} banned {target.PlayerName} {durationText}: {reason}");
    }
}

Progressive Punishment System

private void HandlePlayerOffense(CCSPlayerController? admin, CCSPlayerController target, string reason)
{
    var playerInfo = _api!.GetPlayerInfo(target);

    // Progressive punishment based on warning count
    if (playerInfo.Warnings == 0)
    {
        // First offense - warning
        _api.IssuePenalty(target, admin, PenaltyType.Warn, reason);
        target.PrintToChat("This is your first warning!");
    }
    else if (playerInfo.Warnings == 1)
    {
        // Second offense - gag for 30 minutes
        _api.IssuePenalty(target, admin, PenaltyType.Gag, $"Second offense: {reason}", 30);
        target.PrintToChat("Second warning! You are gagged for 30 minutes.");
    }
    else if (playerInfo.Warnings == 2)
    {
        // Third offense - 1 day ban
        _api.IssuePenalty(target, admin, PenaltyType.Ban, $"Third offense: {reason}", 1440);
        target.PrintToChat("Third offense! You are banned for 1 day.");
    }
    else
    {
        // More than 3 warnings - permanent ban
        _api.IssuePenalty(target, admin, PenaltyType.Ban, $"Multiple offenses: {reason}", 0);
    }
}

Check Active Penalties Before Action

private void AllowPlayerToChat(CCSPlayerController player)
{
    var muteStatus = _api!.GetPlayerMuteStatus(player);

    // Check if player is gagged
    if (muteStatus.ContainsKey(PenaltyType.Gag))
    {
        player.PrintToChat("You are currently gagged and cannot use chat!");
        return;
    }

    // Check if player is silenced (includes gag)
    if (muteStatus.ContainsKey(PenaltyType.Silence))
    {
        player.PrintToChat("You are silenced and cannot communicate!");
        return;
    }

    // Player can chat
    ProcessChatMessage(player);
}

Offline Player Ban

private void BanOfflinePlayer(CCSPlayerController? admin, string steamIdString, int duration, string reason)
{
    // Parse SteamID
    if (!ulong.TryParse(steamIdString, out ulong steamId64))
    {
        admin?.PrintToChat("Invalid SteamID format!");
        return;
    }

    var steamId = new SteamID(steamId64);

    // Issue offline ban
    _api!.IssuePenalty(steamId, admin, PenaltyType.Ban, reason, duration);

    Logger.LogInformation(
        $"{admin?.PlayerName ?? "Console"} banned offline player " +
        $"(SteamID: {steamId64}) for {duration} minutes: {reason}"
    );

    admin?.PrintToChat($"Offline ban issued to SteamID {steamId64}");
}

Multi-Account Detection

[GameEventHandler]
public HookResult OnPlayerConnect(EventPlayerConnectFull @event, GameEventInfo info)
{
    var player = @event.Userid;
    if (player == null || !player.IsValid) return HookResult.Continue;

    var playerInfo = _api!.GetPlayerInfo(player);

    // Check if player has multiple accounts
    if (playerInfo.Bans > 0)
    {
        // Notify admins
        var admins = Utilities.GetPlayers()
            .Where(p => AdminManager.PlayerHasPermissions(p, "@css/ban"));

        foreach (var admin in admins)
        {
            admin.PrintToChat(
                $"⚠ {player.PlayerName} has {playerInfo.Bans} previous ban(s)!"
            );
        }
    }

    return HookResult.Continue;
}

Best Practices

1. Always Validate Players

if (!target.IsValid || !target.PawnIsAlive)
{
    return;
}

// Check immunity
if (admin != null && !admin.CanTarget(target))
{
    admin.PrintToChat("Cannot target this player!");
    return;
}

2. Provide Clear Reasons

// ✅ Good - Specific reason
_api.IssuePenalty(player, admin, PenaltyType.Ban, "Aimbot detected in Round 12", 10080);

// ❌ Bad - Vague reason
_api.IssuePenalty(player, admin, PenaltyType.Ban, "cheating", 10080);

3. Log Penalty Actions

_api.IssuePenalty(player, admin, PenaltyType.Ban, reason, duration);

Logger.LogInformation(
    $"Penalty issued: {admin?.PlayerName ?? "Console"} -> {player.PlayerName} " +
    $"| Type: {PenaltyType.Ban} | Duration: {duration}m | Reason: {reason}"
);

4. Handle Kick Separately

// Kick doesn't need duration
_api.IssuePenalty(player, admin, PenaltyType.Kick, reason);

// NOT:
_api.IssuePenalty(player, admin, PenaltyType.Kick, reason, 0);

5. Check Active Penalties

// Before issuing new penalty, check existing ones
var muteStatus = _api.GetPlayerMuteStatus(player);

if (muteStatus.ContainsKey(PenaltyType.Gag))
{
    admin?.PrintToChat($"{player.PlayerName} is already gagged!");
    return;
}

Common Patterns

Duration Helpers

public static class PenaltyDurations
{
    public const int OneHour = 60;
    public const int OneDay = 1440;
    public const int OneWeek = 10080;
    public const int TwoWeeks = 20160;
    public const int OneMonth = 43200;
    public const int Permanent = 0;
}

// Usage
_api.IssuePenalty(player, admin, PenaltyType.Ban, reason, PenaltyDurations.OneWeek);

Penalty History Display

private void ShowPlayerHistory(CCSPlayerController admin, CCSPlayerController target)
{
    var info = _api!.GetPlayerInfo(target);

    admin.PrintToChat($"=== {info.PlayerName} History ===");
    admin.PrintToChat($"Warnings: {info.Warnings}");
    admin.PrintToChat($"Bans: {info.Bans}");
    admin.PrintToChat($"Mutes: {info.Mutes}");
    admin.PrintToChat($"Gags: {info.Gags}");

    var muteStatus = _api.GetPlayerMuteStatus(target);

    if (muteStatus.ContainsKey(PenaltyType.Gag))
        admin.PrintToChat("Currently: GAGGED");
    if (muteStatus.ContainsKey(PenaltyType.Mute))
        admin.PrintToChat("Currently: MUTED");
    if (muteStatus.ContainsKey(PenaltyType.Silence))
        admin.PrintToChat("Currently: SILENCED");
}

Error Handling

Handle Invalid Players

try
{
    var playerInfo = _api!.GetPlayerInfo(player);
    // Use playerInfo...
}
catch (KeyNotFoundException)
{
    Logger.LogError($"Player info not found for {player?.PlayerName}");
    return;
}

Validate SteamID

private bool TryParseSteamId(string input, out SteamID steamId)
{
    steamId = default;

    if (ulong.TryParse(input, out ulong steamId64))
    {
        steamId = new SteamID(steamId64);
        return true;
    }

    return false;
}