Modules

Docs / API / Modules / plugin

Plugin

Platform

@s2script/sdk/plugin

the typed plugin artifact: plugin(factory), the load-scoped PluginContext, and Scope.

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
ctx.events.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 the factory: 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 + damage 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.
onDamage(handler: (info: DamageInfo) => HookResultValue | void): void

Damage pre-hook (SDKHooks-equivalent): read/modify DamageInfo; return Handled semantics via info.

// plugins/basecommands/src/plugin.ts:81 — halve incoming damage
ctx.entities.onDamage((info) => { info.damage = info.damage / 2; });
interface
CtxServer

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

onGameFrame(fn: () => void, opts?: { priority?: "high" | "normal" | "low" | "monitor" }): void

Run fn every game frame.

opts
priority orders it within the frame (monitor runs last, read-only).
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: (cmd: CommandInvocation) => void): void
Register a public command (any client may run it).
registerServer(name: string, handler: (cmd: CommandInvocation) => void): void
Register a server-only command (console/rcon, not client-runnable).
registerAdmin(name: string, flags: number, handler: (cmd: CommandInvocation) => void): void
Register an admin command gated by flags (an ADMFLAG bitmask; fail-safe default-deny).
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.
addItem(category: string, item: TopMenuItem): void
Add an item under an existing category.
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/damage 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 passed to a plugin's PluginFactory. Its sub-objects register every subscription; all registration is load-window only (buffered, armed at Active).

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/damage 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 topmenu: CtxTopMenu
TopMenu (adminmenu) contribution (CtxTopMenu).
publish(name: string, impl: T): PublishHandle
Publish this plugin's manifest-declared interface. Buffered; goes live at Active.
use(name: string): InterfaceHandle<T>
Resolve a HARD dep (must be in pluginDependencies). Immediate — the proxy is callable inside the factory.
tryUse(name: string): InterfaceHandle<T> | null
Resolve an OPTIONAL dep (must be in optionalPluginDependencies); null while unpublished.
createScope(): Scope
Allocate a disposable subscription scope (load-window only — the capability originates at load).
interface
PluginHooks

Optional lifecycle hooks a PluginFactory may return to participate in unload + hot-reload.

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 ctx.previous.
type
PluginFactory

A plugin's body: called once at load with the PluginContext, optionally returning PluginHooks. May be async (the load waits for it to settle).

interface
PluginDefinition

The opaque artifact plugin returns; a module must export default it to be a valid plugin.

readonly __s2plugin: 1
Brand tag proving this object came from plugin (host-checked at load).
fn
plugin(factory: PluginFactory): PluginDefinition

Define a plugin from its factory. export default the result — the host calls the factory once at load.

import { plugin } from "@s2script/sdk/plugin";
// examples/greeter-plugin/src/plugin.ts:8
export default plugin((ctx) => {
  const handle = ctx.publish("@demo/greeter", impl);
  ctx.server.onGameFrame(() => handle.emit("greeted", { slot: 0 }));
});
Back to modules All packages

s2script — Source 2 plugin framework

GitHub