Search docs
Search guides, modules and symbols
Modules

Docs / API / Modules / plugin

Plugin

Platform

@s2script/sdk/plugin

Load-window authoring: hook (game-event catalog: hook.on / hook.onPre), previous(), pluginId(), publish / use / tryUse / watchOptional / bindForwards, onOutput, createScope, Scope. The artifact is export function OnPluginStart (plus optional named publics). CJS evaluation is before the load window.

Import

import { CtxEvents } from "@s2script/sdk/plugin";

Author-time types ship in the npm package; the engine injects the runtime at plugin load. Add @s2script/sdk to your plugin's dependencies.

API reference

Generated from the shipped type definitions.

interface
CtxEvents

Game-event subscriptions on this plugin's load-scope (PluginContext.events).

on(name: string, handler: (ev: GameEvent) => void): void

Subscribe to a fired game event (post-phase). The GameEvent is valid only synchronously.

// plugins/disabled/nextmap/src/plugin.ts:133
hook.on("round_end", () => console.log("round ended"));
onPre(name: string, handler: (ev: GameEvent) => HookResultValue | void): void
— Pre-hook a game event: return a HookResultValue (Handled/Stop suppress the client broadcast).
interface
CtxClients

Handlers fire for clients that connect AFTER Active. To cover already-connected clients, seed explicitly in OnPluginStart: for (const c of Clients.all()) { … } — there is no framework replay (replaying onConnect for pre-existing clients would fire auth/ban/reservation logic out of its real order).

onConnect(handler: (client: Client) => void | Promise<void>): void
— A client began connecting (pre-auth).
onPutInServer(handler: (client: Client) => void | Promise<void>): void
— A client's entity was put in the server (ClientPutInServer).
onActive(handler: (client: Client) => void | Promise<void>): void
— A client became fully active (in-game, receiving snapshots).
onFullyConnect(handler: (client: Client) => void | Promise<void>): void
— A client finished authenticating (Steam ticket validated).
onDisconnect(handler: (client: Client) => void): void
— A client disconnected.
onSettingsChanged(handler: (client: Client) => void): void
— A client's convars/settings changed (ClientSettingsChanged).
onVoice(handler: (client: Client) => void): void
— A client sent a voice packet (per-frame while speaking).
onCookiesCached(handler: (client: Client) => void): void
— A client's persisted cookies finished loading and are now readable.
onSay(handler: (
      slot: number,
      text: string,
      teamonly: boolean
    ) => HookResultValue | void): void

A client sent chat: return a HookResultValue to suppress it.

teamonly
team-channel say.
onRunCmd(handler: (
      cmd: UserCmdView,
      info: { slot: number }
    ) => HookResultValue | void): void
— Per-tick usercmd hook (SM OnPlayerRunCmd): read/modify UserCmdView; return Handled to block the tick.
interface
CtxEntities

Entity lifecycle / I/O subscriptions on this plugin's load-scope (PluginContext.entities).

onCreate(className: string, handler: (entity: EntityRef | null, className: string) => void): void

An entity of className was created (not yet spawned).

className
match, or "*" for all.
onSpawn(className: string, handler: (entity: EntityRef | null, className: string) => void): void
— An entity of className spawned (post-DispatchSpawn).
onDelete(className: string, handler: (entity: EntityRef | null, className: string) => void): void
— An entity of className is being deleted; the ref goes stale right after.
onOutput(classname: string, output: string, handler: (ev: OutputEvent) => HookResultValue | void): void
— Hook a named entity output (FireOutputInternal); return a HookResultValue to suppress it.
interface
CtxServer

Per-frame + map/precache hooks on this plugin's load-scope (PluginContext.server).

onGameFrame(fn: () => void, opts?: {
      priority?: "high" | "normal" | "low" | "monitor";
      phase?: "pre" | "post";
    }): void
— Run fn every game frame. phase picks WHERE in the frame it runs and defaults to "pre" (before simulation). Use "post" when the work must land after the engine's own per-frame writes — re-asserting a netvar the engine re-derives during simulation is overwritten if written in "pre", because the derivation happens after and the outgoing snapshot carries the engine's value.
onMapStart(handler: (mapName: string) => void): void
— A new map became live; mapName is the BSP name.
onPrecache(handler: (pc: PrecacheContext) => void): void
— Precache window — register models/sounds to precache for the current map.
interface
CtxCommands

Console/chat command registration on this plugin's load-scope (PluginContext.commands).

register(name: string, handler: CommandHandler): void
— Register a public command (any client may run it).
registerServer(name: string, handler: CommandHandler): void
— Register a server-only command (console/rcon, not client-runnable).
registerAdmin(name: string, flags: number, handler: CommandHandler): void
— Register an admin command gated by flags (an ADMFLAG bitmask; fail-safe default-deny).
onClientCommand(name: string, handler: (slot: number, argString: string) => HookResultValue | void): void
— Observe an existing CLIENT command by name — SourceMod's AddCommandListener. For engine-owned commands (player_ping, jointeam, drop), which register cannot claim. Observe-by-default: the engine still handles it unless the handler returns >= HookResult.Handled. Unsubscribed automatically when the plugin unloads.
interface
CtxTranslations

The phrase files this plugin uses (PluginContext.translations). Nothing is loaded automatically — a plugin declares what it needs, the same rule SourceMod's LoadTranslations enforces. The build reads this call to work out which keys cmd.replyT and Translations.translate will accept, so a key from a file you did not load is a compile error.

load(...names: string[]): void

Load translations/<name>.phrases.json for each name, in the order given. Order is significant: translate takes the first hit within each of its two passes (the client's language, then English), so list your own set before any shared one if you want to be able to override a shared phrase.

translations.load("basecomm", "common");
interface
CtxConfig

Config live-reload subscription on this plugin's load-scope (PluginContext.config).

onChange(handler: (cfg: Config) => void): void
— Fires when the plugin's config file is re-materialized on disk; re-read values inside.
interface
CtxTopMenu

TopMenu (adminmenu) contribution on this plugin's load-scope (PluginContext.topmenu).

addCategory(name: string): void
— Add (or reuse) a top-level menu category. Same as CtxTopMenu.addTab with id === title.
addTab(tab: TopMenuTab): void
— Declare this plugin's dashboard tab. The hub (sm_admin / sm_menu) paints one tab per distinct id that still has a visible item. Call before CtxTopMenu.addItem.
addItem(category: string, item: TopMenuItem): void
— Add an item under a tab id (or a legacy shared category name).
type
InterfaceHandle

A producer-backed inter-plugin interface: its methods, plus forward subscriptions.

interface
Scope

A disposable bundle of subscriptions (PluginContext.createScope). Registering through a scope lets you drop the whole group at once with Scope.clear without unloading the plugin.

readonly events: CtxEvents
— Game-event subscriptions bound to this scope.
readonly clients: CtxClients
— Client-lifecycle subscriptions bound to this scope.
readonly entities: CtxEntities
— Entity lifecycle / I/O subscriptions bound to this scope.
readonly server: CtxServer
— Per-frame/map subscriptions bound to this scope.
clear(): void
— Remove every subscription this scope holds; the scope stays usable (re-register on next open).
dispose(): void
— clear() + permanently retire the scope. Idempotent.
readonly disposed: boolean
— True once Scope.dispose has run.
interface
PluginContext

The load-scoped context the host builds for one plugin load. Public authoring uses hook / command / named publics instead of receiving this object. Scope reuses the same subscription namespaces.

readonly id: string
— This plugin's id (manifest id).
readonly previous: unknown
— The revived hot-reload handoff (the previous instance's state() return), or undefined.
readonly events: CtxEvents
— Game-event subscriptions (CtxEvents).
readonly clients: CtxClients
— Client-lifecycle subscriptions (CtxClients).
readonly entities: CtxEntities
— Entity lifecycle / I/O subscriptions (CtxEntities).
readonly server: CtxServer
— Per-frame/map/precache subscriptions (CtxServer).
readonly commands: CtxCommands
— Console/chat command registration (CtxCommands).
readonly config: CtxConfig
— Config live-reload subscription (CtxConfig).
readonly translations: CtxTranslations
— Phrase files this plugin uses (CtxTranslations).
readonly topmenu: CtxTopMenu
— TopMenu (adminmenu) contribution (CtxTopMenu).
publish<N extends keyof InterfaceContracts>(name: N, impl: ContractMethods<InterfaceContracts[N]>): TypedPublishHandle<InterfaceContracts[N]>
— Publish this plugin's manifest-declared interface. Buffered; goes live at Active.
publish<T extends object>(name: keyof InterfaceContracts extends never ? string : never, impl: T): PublishHandle
use<N extends keyof InterfaceContracts>(name: N): TypedInterfaceHandle<InterfaceContracts[N]>
— Resolve a HARD dep (must be in pluginDependencies). Immediate — the proxy is callable during OnPluginStart.
use<T extends object>(name: keyof InterfaceContracts extends never ? string : never): InterfaceHandle<T>
tryUse<N extends keyof InterfaceContracts>(name: N): TypedInterfaceHandle<InterfaceContracts[N]> | null
— Resolve an OPTIONAL dep (must be in optionalPluginDependencies); null while unpublished. One-shot lookup — does not track later availability. Prefer watchOptional to follow a provider across load and reload.
tryUse<T extends object>(name: keyof InterfaceContracts extends never ? string : never): InterfaceHandle<T> | null
watchOptional<N extends keyof InterfaceContracts>(name: N, attach: (service: TypedInterfaceHandle<InterfaceContracts[N]>, scope: AttachmentScope) => void): Subscription
— Observe a declared optional provider, after both plugins are Active.
bindForwards<N extends keyof InterfaceContracts, H extends ForwardHandlers<InterfaceContracts[N]>>(name: N, handlers: ExactForwardHandlers<InterfaceContracts[N], H>): Subscription
— Bind a provider-qualified map of forward names to local handlers.
createScope(): Scope
— Allocate a disposable subscription scope (load-window only — the capability originates at load).
interface
PluginHooks

Optional lifecycle hooks a plugin may return to participate in unload + hot-reload. Public authoring uses export function OnPluginEnd / OnPluginState instead.

onUnload(): void
— Best-effort cleanup at unload (the ledger remains the teardown authority).
state(): unknown
— Hot-reload handoff capture; JSON-serialized (EntityRef-aware) and revived as the next instance's previous.
const
hook

Game-event catalog. Load-window only — throws after settle (same window as command). hook.on is post-fire; hook.onPre is pre (Handled/Stop suppress the client broadcast). The GameEvent is valid only synchronously.

on(name: string, handler: (ev: GameEvent) => void): void
— Post-phase game-event subscribe. The GameEvent is valid only synchronously.
onPre(name: string, handler: (ev: GameEvent) => HookResultValue | void): void
— Pre-phase game-event subscribe. Return HookResultValue Handled/Stop to suppress the client broadcast.
fn
onOutput(classname: string, output: string, handler: (ev: OutputEvent) => HookResultValue | void): void

Load-window entity I/O subscribe (SourceMod HookEntityOutput). Keyed by (classname, output) at the native mux — use "*" for either side. Throws after settle.

fn
previous(): unknown

The revived hot-reload handoff (the previous instance's OnPluginState return), or undefined. Load-window only.

fn
pluginId(): string

This plugin's id (manifest id). Load-window only.

fn
createScope(): Scope

Allocate a disposable subscription scope. Load-window only — same contract as PluginContext.createScope.

fn
publish<N extends keyof InterfaceContracts>(name: N, impl: ContractMethods<InterfaceContracts[N]>): TypedPublishHandle<InterfaceContracts[N]>

Publish this plugin's manifest-declared interface. Load-window only (buffered, armed at Active). Same contract as PluginContext.publish.

fn
publish<T extends object>(name: keyof InterfaceContracts extends never ? string : never, impl: T): PublishHandle
fn
use<N extends keyof InterfaceContracts>(name: N): TypedInterfaceHandle<InterfaceContracts[N]>

Resolve a HARD dep (must be in pluginDependencies). Load-window only. Same contract as PluginContext.use. Prefer import { greet } from "@demo/greeter" for the producer-as-import form; use() remains the explicit load-window form and the optional-dep path.

fn
use<T extends object>(name: keyof InterfaceContracts extends never ? string : never): InterfaceHandle<T>
fn
tryUse<N extends keyof InterfaceContracts>(name: N): TypedInterfaceHandle<InterfaceContracts[N]> | null

Resolve an OPTIONAL dep (must be in optionalPluginDependencies); null while unpublished. Load-window only. One-shot lookup — does not track later availability. Same contract as PluginContext.tryUse.

fn
tryUse<T extends object>(name: keyof InterfaceContracts extends never ? string : never): InterfaceHandle<T> | null
const
topmenu

Load-window TopMenu contribution. Same contract as PluginContext.topmenu. Throws after settle.

const
translations

Load-window phrase-file declaration. Same contract as PluginContext.translations. Throws after settle. s2s build / sync-phrase-types.mjs collect translations.load(...) the same way they collect translations.load(...).

fn
watchOptional<N extends keyof InterfaceContracts>(name: N, attach: (service: TypedInterfaceHandle<InterfaceContracts[N]>, scope: AttachmentScope) => void): Subscription

Declare an optional-provider watch during load. Requires optionalPluginDependencies. Attachment runs synchronously after both plugins are Active, once per compatible provider generation. Failed attachments roll back; reappearance creates a fresh scope. Only the supplied service permits forward registration during the attachment callback.

fn
bindForwards<N extends keyof InterfaceContracts, H extends ForwardHandlers<InterfaceContracts[N]>>(name: N, handlers: ExactForwardHandlers<InterfaceContracts[N], H>): Subscription

Bind provider forward names to local functions during the normal load window. The returned subscription disposes every registration created by this call.

Back to modules All packages