SwiftlyS2
Development

Swiftly Core

ISwiftlyCore is the central entry point for all plugin services in SwiftlyS2.

In development docs, Core refers to the protected ISwiftlyCore instance exposed by BasePlugin.

How Core Is Provided

BasePlugin receives ISwiftlyCore in its constructor and exposes it through Core.

public sealed class MyPlugin : BasePlugin
{
    public MyPlugin(ISwiftlyCore core) : base(core)
    {
    }

    public override void Load(bool hotReload)
    {
        Core.Logger.LogInformation("Plugin loaded. Hot reload: {HotReload}", hotReload);
    }

    public override void Unload()
    {
        Core.Logger.LogInformation("Plugin unloaded.");
    }
}

Prefer passing ISwiftlyCore (or specific services from it) into helper classes instead of using global/static access.

Core Services at a Glance

PropertyPurpose
Core.EventCore event subscription (IEventSubscriber)
Core.GameEventGenerated game-event fire/hook APIs
Core.NetMessageTyped protobuf net-message send/hook APIs
Core.CommandCommand registration and chat/command hooks
Core.EntitySystemEntity creation, query, input/output hooks
Core.ConVarConVar registration and value access
Core.SchedulerMain-thread dispatch, timers, and delayed work
Core.PlayerManagerPlayer lookup and player manager features
Core.PermissionPermission checks and assignment
Core.Translation, Core.LocalizerPlayer and server localization
Core.DatabaseDatabase service
Core.ProfilerProfiling context and timers
Core.Datamap, Core.StringTableDatamap and string-table services
Core.PluginManagerPlugin discovery and management
Core.RegistratorAttribute-based handler registration for instances
Core.Logger, Core.LoggerFactoryLogging APIs

Runtime and File-System Paths

Use these properties when resolving plugin/game directories:

PropertyMeaning
Core.PluginPathAbsolute path to current plugin directory
Core.PluginDataDirectoryPlugin data path (ensured to exist)
Core.CSGODirectoryAbsolute path to game/csgo
Core.GameDirectoryAbsolute path to game root

Thread Awareness

Core.IsGameThread tells you whether execution is currently on the game thread.

public void EnsureMainThreadAction(Action action)
{
    if (Core.IsGameThread)
    {
        action();
        return;
    }

    Core.Scheduler.NextTick(action);
}

Many game-facing APIs are thread-unsafe. If you are not on the game thread, hand work back using Core.Scheduler before touching entities, players, net messages, or engine state.

Working with Additional Handler Classes

When attributes are used outside your main plugin class, register that instance through Core.Registrator.

public sealed class ChatHandlers
{
    [ClientChatHookHandler]
    public HookResult OnClientChat(int playerId, string text, bool teamonly)
    {
        return HookResult.Continue;
    }
}

public override void Load(bool hotReload)
{
    Core.Registrator.Register(new ChatHandlers());
}

Complete Example

private EventDelegates.OnClientConnected? _onClientConnected;

public override void Load(bool hotReload)
{
    Core.Logger.LogInformation("Plugin path: {Path}", Core.PluginPath);
    Core.Logger.LogInformation("Data path: {Path}", Core.PluginDataDirectory);

    _onClientConnected = OnClientConnected;
    Core.Event.OnClientConnected += _onClientConnected;
}

public override void Unload()
{
    if (_onClientConnected != null)
    {
        Core.Event.OnClientConnected -= _onClientConnected;
    }
}

private void OnClientConnected(IOnClientConnectedEvent @event)
{
    Core.Scheduler.NextTick(() =>
    {
        Core.Logger.LogInformation("Client connected: {PlayerId}", @event.PlayerId);
    });
}

Reference

See ISwiftlyCore for complete property definitions.

See BasePlugin for plugin lifecycle and Core access.

See IRegistratorService for instance registration used by attribute handlers.

On this page