Files
CS2-SimpleAdmin/CS2-SimpleAdmin-docs/docs/developer/architecture.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

16 KiB

sidebar_position
sidebar_position
8

Plugin Architecture

Deep dive into CS2-SimpleAdmin's architecture and design patterns.

Overview

CS2-SimpleAdmin follows a layered architecture with clear separation of concerns and well-defined responsibilities for each component.


Architecture Layers

┌─────────────────────────────────────────┐
│  CounterStrikeSharp Integration Layer   │ ← CS2_SimpleAdmin.cs
├─────────────────────────────────────────┤
│         Manager Layer                   │ ← /Managers/
│  • PermissionManager                    │
│  • BanManager                           │
│  • MuteManager                          │
│  • WarnManager                          │
│  • CacheManager                         │
│  • PlayerManager                        │
│  • ServerManager                        │
│  • DiscordManager                       │
├─────────────────────────────────────────┤
│         Database Layer                  │ ← /Database/
│  • IDatabaseProvider (Interface)        │
│  • MySqlDatabaseProvider                │
│  • SqliteDatabaseProvider               │
│  • Migration System                     │
├─────────────────────────────────────────┤
│         Menu System                     │ ← /Menus/
│  • MenuManager (Singleton)              │
│  • MenuBuilder (Factory)                │
│  • Specific Menu Classes                │
├─────────────────────────────────────────┤
│         Command System                  │ ← /Commands/
│  • RegisterCommands                     │
│  • Command Handlers (basebans, etc.)    │
├─────────────────────────────────────────┤
│         Public API                      │ ← /Api/
│  • ICS2_SimpleAdminApi (Interface)      │
│  • CS2_SimpleAdminApi (Implementation)  │
└─────────────────────────────────────────┘

Core Components

1. CounterStrikeSharp Integration Layer

File: CS2_SimpleAdmin.cs

Responsibilities:

  • Plugin lifecycle management (Load/Unload)
  • Event registration (player_connect, player_disconnect, etc.)
  • Command routing
  • Low-level game operations using MemoryFunctionVoid
  • Timer management

Key Methods:

public override void Load(bool hotReload)
public override void Unload(bool hotReload)
private HookResult OnPlayerConnect(EventPlayerConnectFull @event, GameEventInfo info)
private HookResult OnPlayerDisconnect(EventPlayerDisconnect @event, GameEventInfo info)

2. Manager Layer

Each manager encapsulates specific domain logic:

PermissionManager

File: /Managers/PermissionManager.cs

Responsibilities:

  • Load admin flags and groups from database
  • Maintain in-memory AdminCache with lazy-loading
  • Time-based cache expiry
  • Immunity level management

Key Patterns:

  • Caching for performance
  • Lazy loading of admin data
  • Periodic refresh

BanManager

File: /Managers/BanManager.cs

Responsibilities:

  • Issue bans (SteamID, IP, or hybrid)
  • Remove bans (unban)
  • Handle ban expiration cleanup
  • Multi-server ban synchronization

Key Operations:

Task BanPlayer(...)
Task AddBanBySteamId(...)
Task RemoveBan(...)

MuteManager

File: /Managers/MuteManager.cs

Responsibilities:

  • Three mute types: GAG (text), MUTE (voice), SILENCE (both)
  • Duration-based mutes
  • Expiration tracking

WarnManager

File: /Managers/WarnManager.cs

Responsibilities:

  • Progressive warning system
  • Auto-escalation to bans based on WarnThreshold config
  • Warning history tracking

CacheManager

File: /Managers/CacheManager.cs

Purpose: Performance optimization layer

Features:

  • In-memory ban cache with O(1) lookups by SteamID and IP
  • Player IP history tracking for multi-account detection
  • Reduces database queries on player join

Data Structures:

Dictionary<ulong, BanInfo> _banCacheBySteamId
Dictionary<string, List<BanInfo>> _banCacheByIp
Dictionary<ulong, List<string>> _playerIpHistory

PlayerManager

File: /Managers/PlayerManager.cs

Responsibilities:

  • Load player data on connect
  • Check bans against cache
  • Update IP history
  • Semaphore limiting (max 5 concurrent loads)

Key Pattern:

private readonly SemaphoreSlim _semaphore = new(5, 5);

public async Task LoadPlayerData(CCSPlayerController player)
{
    await _semaphore.WaitAsync();
    try
    {
        // Load player data
    }
    finally
    {
        _semaphore.Release();
    }
}

ServerManager

File: /Managers/ServerManager.cs

Responsibilities:

  • Load/register server metadata (IP, port, hostname, RCON)
  • Multi-server mode support
  • Server ID management

DiscordManager

File: /Managers/DiscordManager.cs

Responsibilities:

  • Send webhook notifications for admin actions
  • Configurable webhooks per penalty type
  • Embed formatting with placeholders

3. Database Layer

Files: /Database/

Provider Pattern for database abstraction:

public interface IDatabaseProvider
{
    Task ExecuteAsync(string query, object? parameters = null);
    Task<T> QueryFirstOrDefaultAsync<T>(string query, object? parameters = null);
    Task<List<T>> QueryAsync<T>(string query, object? parameters = null);

    // Query generation methods
    string GetBanQuery(bool multiServer);
    string GetMuteQuery(bool multiServer);
    // ... more query methods
}

Implementations:

  • MySqlDatabaseProvider - MySQL-specific SQL syntax
  • SqliteDatabaseProvider - SQLite-specific SQL syntax

Benefits:

  • Single codebase supports both MySQL and SQLite
  • Easy to add new database providers
  • Query methods accept multiServer boolean for scoping

Migration System:

File: Database/Migration.cs

  • File-based migrations in /Database/Migrations/{mysql,sqlite}/
  • Numbered files: 001_CreateTables.sql, 002_AddColumn.sql
  • Tracking table: sa_migrations
  • Auto-applies on plugin load
  • Safe for multi-server environments

4. Menu System

Files: /Menus/

MenuManager (Singleton Pattern):

public class MenuManager
{
    public static MenuManager Instance { get; private set; }

    private readonly Dictionary<string, MenuCategory> _categories = new();
    private readonly Dictionary<string, Dictionary<string, MenuInfo>> _menus = new();

    public void RegisterCategory(string id, string name, string permission);
    public void RegisterMenu(string categoryId, string menuId, string name, ...);
    public MenuBuilder CreateCategoryMenuPublic(MenuCategory category, CCSPlayerController player);
}

MenuBuilder (Factory Pattern):

public class MenuBuilder
{
    public MenuBuilder(string title);
    public MenuBuilder AddOption(string name, Action<CCSPlayerController> action, ...);
    public MenuBuilder AddSubMenu(string name, Func<CCSPlayerController, MenuBuilder> factory, ...);
    public MenuBuilder WithBackAction(Action<CCSPlayerController> backAction);
    public void OpenMenu(CCSPlayerController player);
}

Specific Menu Classes:

  • AdminMenu - Main admin menu with categories
  • ManagePlayersMenu - Player management menus
  • ManageServerMenu - Server settings
  • DurationMenu - Duration selection
  • ReasonMenu - Reason selection

Benefits:

  • Centralized menu management
  • Permission-aware rendering
  • Automatic back button handling
  • Reusable menu components

5. Command System

Files: /Commands/

Central Registration:

File: RegisterCommands.cs

public static class RegisterCommands
{
    public static Dictionary<string, List<CommandDefinition>> _commandDefinitions = new();

    public static void RegisterCommands(CS2_SimpleAdmin plugin)
    {
        // Load Commands.json
        // Map commands to handler methods
        // Register with CounterStrikeSharp
    }
}

Command Handlers:

Organized by category:

  • basebans.cs - Ban, unban, warn commands
  • basecomms.cs - Gag, mute, silence commands
  • basecommands.cs - Admin management, server commands
  • basechat.cs - Chat commands (asay, csay, etc.)
  • playercommands.cs - Player manipulation (slay, hp, etc.)
  • funcommands.cs - Fun commands (god, noclip, etc.)
  • basevotes.cs - Voting system

Two-Tier Pattern:

// Entry command - parses arguments
[CommandHelper(2, "<#userid> <duration> [reason]")]
[RequiresPermissions("@css/ban")]
public void OnBanCommand(CCSPlayerController? caller, CommandInfo command)
{
    var targets = GetTarget(command);
    int duration = ParseDuration(command.GetArg(2));
    string reason = ParseReason(command);

    foreach (var target in targets)
    {
        Ban(caller, target, duration, reason);  // Core method
    }
}

// Core method - database writes, events
private void Ban(CCSPlayerController? admin, CCSPlayerController target, int duration, string reason)
{
    // Write to database
    BanManager.BanPlayer(target, admin, duration, reason);

    // Update cache
    CacheManager.AddBan(target);

    // Trigger events
    ApiInstance.OnPlayerPenaltiedEvent(target, admin, PenaltyType.Ban, reason, duration);

    // Kick player
    Server.ExecuteCommand($"kick {target.UserId}");

    // Send Discord notification
    DiscordManager.SendBanNotification(target, admin, duration, reason);

    // Broadcast action
    ShowAdminActivity("ban_message", admin?.PlayerName, target.PlayerName, duration, reason);
}

Benefits:

  • Separation of parsing and execution
  • Reusable core methods
  • Consistent event triggering
  • Easy to test

6. Public API

Files: /Api/

Interface: ICS2_SimpleAdminApi.cs (in CS2-SimpleAdminApi project)

Implementation: CS2_SimpleAdminApi.cs

Capability System:

// In API interface
public static readonly PluginCapability<ICS2_SimpleAdminApi> PluginCapability = new("simpleadmin:api");

// In module
private readonly PluginCapability<ICS2_SimpleAdminApi> _pluginCapability = new("simpleadmin:api");

public override void OnAllPluginsLoaded(bool hotReload)
{
    _api = _pluginCapability.Get();
}

Event Publishing:

// API exposes events
public event Action<PlayerInfo, PlayerInfo?, PenaltyType, ...>? OnPlayerPenaltied;

// Core plugin triggers events
ApiInstance.OnPlayerPenaltiedEvent(player, admin, type, reason, duration, id);

// Modules subscribe
_api.OnPlayerPenaltied += (player, admin, type, reason, duration, id, sid) =>
{
    // React to penalty
};

Data Flow Patterns

Player Join Flow

1. player_connect event
   ↓
2. PlayerManager.LoadPlayerData()
   ↓
3. Semaphore.WaitAsync() ← Max 5 concurrent
   ↓
4. CacheManager.CheckBan(steamId, ip)
   ↓
5a. BANNED → Kick player immediately
5b. CLEAN → Continue
   ↓
6. Load active penalties from DB
   ↓
7. Store in PlayersInfo dictionary
   ↓
8. Update player IP history

Ban Command Flow

1. OnBanCommand() ← Parse arguments
   ↓
2. Ban() ← Core method
   ↓
3. BanManager.BanPlayer() ← Write to DB
   ↓
4. CacheManager.AddBan() ← Update cache
   ↓
5. ApiInstance.OnPlayerPenaltiedEvent() ← Trigger event
   ↓
6. Server.ExecuteCommand("kick") ← Kick player
   ↓
7. DiscordManager.SendNotification() ← Discord webhook
   ↓
8. ShowAdminActivity() ← Broadcast action

Admin Permission Check Flow

1. Plugin Load
   ↓
2. PermissionManager.LoadAdmins()
   ↓
3. Build AdminCache ← SteamID → Flags/Immunity
   ↓
4. Command Execution
   ↓
5. RequiresPermissions attribute check
   ↓
6. AdminManager.PlayerHasPermissions() ← Check cache
   ↓
7a. HAS PERMISSION → Execute
7b. NO PERMISSION → Deny

Design Patterns Used

Singleton Pattern

public class MenuManager
{
    public static MenuManager Instance { get; private set; }

    public static void Initialize(CS2_SimpleAdmin plugin)
    {
        Instance = new MenuManager(plugin);
    }
}

Used for:

  • MenuManager - Single menu registry
  • Cache management - Single source of truth

Factory Pattern

public class MenuBuilder
{
    public static MenuBuilder Create(string title) => new MenuBuilder(title);

    public MenuBuilder AddOption(...) { /* ... */ return this; }
    public MenuBuilder AddSubMenu(...) { /* ... */ return this; }
}

Used for:

  • Menu creation
  • Database provider creation

Strategy Pattern

public interface IDatabaseProvider
{
    Task<List<BanInfo>> GetBans(bool multiServer);
}

public class MySqlDatabaseProvider : IDatabaseProvider { /* ... */ }
public class SqliteDatabaseProvider : IDatabaseProvider { /* ... */ }

Used for:

  • Database abstraction
  • Query generation per DB type

Observer Pattern

// Publisher
public event Action<PlayerInfo, ...>? OnPlayerPenaltied;

// Trigger
OnPlayerPenaltied?.Invoke(player, admin, type, reason, duration, id, sid);

// Subscribers
_api.OnPlayerPenaltied += (player, admin, type, reason, duration, id, sid) =>
{
    // React
};

Used for:

  • Event system
  • Module communication

Concurrency & Thread Safety

Async/Await Patterns

All database operations use async/await:

public async Task BanPlayer(...)
{
    await _database.ExecuteAsync(query, parameters);
}

Semaphore for Rate Limiting

private readonly SemaphoreSlim _semaphore = new(5, 5);

public async Task LoadPlayerData(CCSPlayerController player)
{
    await _semaphore.WaitAsync();
    try
    {
        // Load data
    }
    finally
    {
        _semaphore.Release();
    }
}

Thread-Safe Collections

private readonly ConcurrentDictionary<ulong, PlayerInfo> PlayersInfo = new();

Memory Management

In-Memory Caches

AdminCache:

Dictionary<ulong, (List<string> Flags, int Immunity, DateTime Expiry)> AdminCache

BanCache:

Dictionary<ulong, BanInfo> _banCacheBySteamId
Dictionary<string, List<BanInfo>> _banCacheByIp

Benefits:

  • Reduces database load
  • O(1) lookups
  • TTL-based expiry

Cleanup

// On player disconnect
PlayersInfo.TryRemove(player.SteamID, out _);

// Periodic cache cleanup
AddTimer(3600f, CleanupExpiredCache, TimerFlags.REPEAT);

Configuration System

Multi-Level Configuration

  1. Main Config: CS2-SimpleAdmin.json
  2. Commands Config: Commands.json
  3. Module Configs: Per-module JSON files

Hot Reload Support

public void OnConfigParsed(Config config)
{
    Config = config;
    // Reconfigure without restart
}

Performance Optimizations

  1. Caching - Minimize database queries
  2. Lazy Loading - Load admin data on-demand
  3. Semaphore - Limit concurrent operations
  4. Connection Pooling - Reuse DB connections
  5. Indexed Queries - Fast database lookups
  6. Memory Cleanup - Remove disconnected player data

Future Extensibility

Plugin Capabilities

New modules can extend functionality:

// New capability
var customCapability = new PluginCapability<ICustomFeature>("custom:feature");
Capabilities.RegisterPluginCapability(customCapability, () => _customFeature);

// Other plugins can use it
var feature = _customCapability.Get();

Event-Driven Architecture

New events can be added without breaking changes:

public event Action<NewEventArgs>? OnNewEvent;

Testing Considerations

Unit Testing

  • Managers can be tested independently
  • Mock IDatabaseProvider for testing
  • Test command handlers with mock players

Integration Testing

  • Test on actual CS2 server
  • Multi-server scenarios
  • Database migration testing