swiftlys2/docs/api/ai/

Core Events

Core events are SwiftlyS2's own engine/framework callbacks - client lifecycle, entity lifecycle, convar changes, server startup, tick/world update - separate from the CS2 game events system. They're exposed through Core.Event (APIIEventSubscriber), a set of plain C# events.

Subscribing

Subscribe with a plain +=, or with [EventListener<T>] where T is the matching delegate from EventDelegates.

public override void Load(bool hotReload)
{
    Core.Event.OnClientConnected += OnClientConnected;
}
 
private void OnClientConnected(IOnClientConnectedEvent @event)
{
    Console.WriteLine($"Client connected: {@event.PlayerId}");
}

Match the delegate signature exactly - EventDelegates.OnClientConnected is void(IOnClientConnectedEvent @event); OnTick/OnWorldUpdate take no parameters.

Blocking a Client from Joining

Some client-lifecycle events expose a settable Result to reject the action, e.g. APIIOnClientConnectedEvent:

[EventListener<EventDelegates.OnClientConnected>]
public void OnClientConnected(IOnClientConnectedEvent @event)
{
    if (IsBanned(@event.PlayerId))
    {
        @event.Result = HookResult.Stop;
    }
}

Unsubscribing

-= only removes a subscription added with += - keep a reference to the exact delegate instance:

private EventDelegates.OnClientConnected? _onClientConnected;
 
public override void Load(bool hotReload)
{
    _onClientConnected = OnClientConnected;
    Core.Event.OnClientConnected += _onClientConnected;
}
 
public override void Unload()
{
    if (_onClientConnected != null)
        Core.Event.OnClientConnected -= _onClientConnected;
}

The framework tears down every listener a plugin registered on hot reload or unload automatically - unsubscribe manually only to stop listening earlier.

Hot Path Events

OnTick, OnWorldUpdate, and the obsolete OnClientProcessUsercmds fire every tick/frame - keep handlers allocation-free and cheap; push anything heavier to a scheduled task or a flag.

Deprecated Hook-Style Events

Several Core.Event members that used to be the only way to intercept a native call are [Obsolete] in favor of the more structured Core.GameHooks categories - separate pre/post delegates and a richer context object instead of one generic event:

Obsolete eventUse instead
OnClientProcessUsercmdsCore.GameHooks.Controller/Movement
OnEntityTakeDamageCore.GameHooks.Entities.TakeDamage
OnItemServicesCanAcquireHookCore.GameHooks.Items.CanAcquire
OnWeaponServicesCanUseHook / OnWeaponServicesDropWeaponHookCore.GameHooks.Weapons.*
OnEntityStartTouch / OnEntityTouch / OnEntityEndTouchCore.GameHooks.Entities.{StartTouch,Touch,EndTouch}
OnMovementServicesRunCommandHookCore.GameHooks.Movement.RunCommand
OnPlayerPawnPostThinkCore.GameHooks.Pawn.PostThink
OnEntityIdentityAcceptInputHookCore.GameHooks.Entities.AcceptInput
OnEntityFireOutputHookCore.GameHooks.Entities.FireOutput

Event Reference

CategoryEvents
Client lifecycleOnClientConnected, OnClientDisconnected, OnClientPutInServer, OnClientKeyStateChanged, OnClientSteamAuthorize, OnClientSteamAuthorizeFail, OnClientVoice
Entity lifecycleOnEntityCreated, OnEntitySpawned, OnEntityDeleted, OnEntityParentChanged
ConVar / commandsOnConVarCreated, OnConVarValueChanged, OnConCommandCreated, OnCommandExecuteHook, OnConsoleOutput
Server / worldOnStartupServer, OnMapLoad, OnMapUnload, OnTick, OnWorldUpdate, OnSteamAPIActivated, OnPrecacheResource
UIOnCustomHudClicked

Each parameter interface lives under SwiftlyS2.Shared.Events (e.g. APIIOnClientConnectedEvent, APIIOnMapLoadEvent) and exposes only the fields relevant to that callback - open the type's page for the full field list instead of guessing from the event name.