CounterStrikeSharp Documentation

repository·main·Indexed 20 days ago

https://github.com/roflmuffin/counterstrikesharp

A .NET 8 scripting layer for Counter-Strike 2 that runs on top of a Metamod Source Plugin, enabling developers to create high-performance server-side plugins using C#. The framework provides APIs for console and chat commands, game event handlers, dependency injection via IPluginServiceCollection, semi-automatic configuration parsing with IPluginConfig, and localization using IStringLocalizer. It supports shared APIs, entity output hooks, and integration with Dapper and SQLite for data persistence.

Tokens
19.3K
Snippets
46
Records
106
Agent score
79%

What's inside CounterStrikeSharp

  1. Use Shared Types (Capabilities) to communicate between plugins

    main
    You can use the Shared Types (Capabilities) pattern to allow one plugin to expose a library or contract (e.g., a balance contract) that other plugins can consume. This enables a plugin to expose a specific capability for a player or another plugin, which can then be accessed via an exposed API by any other plugin in the environment.
  2. Access and use GameEvent parameters

    main

    Subclasses of GameEvent provide strongly typed properties corresponding to the event definition (e.g., long for time limits, CCSPlayerController for user IDs).

    Important Lifecycle Warning: GameEvent instances and their properties are transient. They cease to exist once the event listener function returns. Do not attempt to access event properties inside asynchronous tasks, timers, or functions like Server.NextFrame(). Instead, capture the required values into local variables before the listener function completes.

  3. How to implement inter-plugin communication using Capabilities

    main

    Inter-plugin communication in CounterStrikeSharp is achieved through a Shared Plugin API (Capabilities). This pattern allows one plugin to expose functionality (an API) that other plugins can consume without direct project references.

    To implement this, you follow a three-part pattern:

    1. Contract Library: A shared assembly containing only interfaces (no business logic).
    2. Capability Declaration: A static variable in your plugin class that identifies the API via a unique string name.
    3. Registration/Usage: The provider plugin registers an implementation, and the consumer plugin retrieves it using .Get().

    There are two types of capabilities:

    • PlayerCapability<T>: Provides functionality scoped to a specific player (e.g., a player's balance).
    • PluginCapability<T>: Provides generic functionality scoped to the plugin instance (e.g., a global service).
    // 1. The Contract (in a shared library)
    public interface IBalanceHandler
    {
        decimal Balance { get; }
        public decimal Add(decimal amount);
        public decimal Subtract(decimal amount);
    }
    
    // 2. The Declaration (in your plugin class)
    public static PlayerCapability<IBalanceHandler> BalanceCapability { get; } = new("myplugin:balance");
    
    // 3. The Registration (by the provider plugin)
    Capabilities.RegisterPlayerCapability(BalanceCapability, player => new BalanceHandler(player));
    
    // 4. The Usage (by the consumer plugin)
    var balance = BalanceCapability.Get(player);
    if (balance != null) 
    {
        balance.Add(500);
    }
  4. Implement Immunity Checks in Plugins

    main

    CounterStrikeSharp does not automatically enforce immunity checks for all actions. Plugin developers must manually implement immunity logic when a player targets another player.

    Use the following AdminManager methods to handle immunity:

    • AdminManager.CanPlayerTarget: Check if a player is allowed to target another player based on their immunity levels.
    • AdminManager.SetPlayerImmunity: Programmatically set a player's immunity value.
  5. How dependency injection works in CounterStrikeSharp

    main

    CounterStrikeSharp utilizes a standard IServiceCollection to support dependency injection (DI) within plugins.

    To register your own services (scoped or singleton), you must implement the IPluginServiceCollection<T> interface, where T is your plugin class.

    Lifecycle and Discovery:

    1. CounterStrikeSharp scans your assembly for implementations of IPlugin and IPluginServiceCollection<T>.
    2. It configures the service provider using your ConfigureServices implementation.
    3. It requests a singleton instance of your plugin class.
    4. Any dependencies declared in your plugin's constructor are automatically injected at instantiation time, which occurs before the Load method is called.
    public class TestPluginServiceCollection : IPluginServiceCollection<TestPlugin>
    {
        public void ConfigureServices(IServiceCollection serviceCollection)
        {
            serviceCollection.AddScoped<ExampleInjectedClass>();
        }
    }
  6. Understand the difference between Controllers and Pawns

    main

    In CS2, player data is split into two distinct entities:

    1. Player Controller (CCSPlayerController): Represents the player on the server. Use this to access persistent data like SteamIDs.
    2. Player Pawn (CCSPlayerPawn): Represents the player's physical character in the game world. Use this to modify gameplay attributes like health.

    Every controller has a PlayerPawn property (which is a CHandle), and every pawn has a Controller property (also a CHandle). To access the underlying object from a handle, you must use the .Value property.

  7. How hot reloading works in CounterStrikeSharp

    main

    CounterStrikeSharp supports automatic hot reloading. If you replace the .dll file in your plugin folder while the server is running, the framework will automatically trigger a reload.

    During a hot reload:

    1. The framework calls the Unload function (if implemented).
    2. The framework calls the Load function with the hotReload parameter set to true.

    Note on Event Handlers: The framework automatically deregisters event handlers and listeners during unload. You can safely re-register them in your Load method without manually checking the hotReload flag, though you may use the flag to perform specific logic unique to a reload scenario.

  8. Register chat and console commands

    main

    You can register commands that function both as console commands and chat commands.

    To ensure a command is automatically registered as a chat command (accessible via ! or / prefixes), prefix the command name with css_. For example, a command named css_ping will be automatically available in chat as !ping or /ping.

    This example demonstrates the pattern for registering these types of commands within CounterStrikeSharp.

  9. Implement localization with IStringLocalizer and JSON files

    main

    To provide localized strings in your Counter-Strike Sharp plugin, follow these steps:

    1. Setup Language Files: Create a lang folder within your plugin directory. Add a .json file for every language you wish to support, naming the files after their locale codes (e.g., en.json, fr.json).
    2. Configure Project Output: Ensure the lang folder is included in your plugin's output directory. You can automate this in your .csproj file.
    3. Access the Localizer: You can obtain localization capabilities in two ways:
      • Use Dependency Injection to add IStringLocalizer to your services.
      • Use the Localizer property provided directly on the plugin instance.
    4. Localize Strings: Use the IStringLocalizer instance to retrieve the appropriate string based on the current locale.
  10. Upgrade CounterStrikeSharp

    main

    To upgrade CounterStrikeSharp, download the latest release and copy the files to your server using the same method as the original installation.

    CounterStrikeSharp is designed to prevent your configuration files from being overwritten during an upgrade.

    Note on builds: If CounterStrikeSharp is already installed, you may use the non with-runtime build, but you are responsible for ensuring your server's .NET runtime is up-to-date.