Search docs
Search guides, modules and symbols
Getting started

Getting started

s2script is a TypeScript plugin runtime for Source 2 games (Counter-Strike 2 first), loaded via Metamod:Source. The live runtime is CS2 on linuxsteamrt64 only.

Prerequisites#

  1. A Counter-Strike 2 dedicated server (Linux x86-64 / linuxsteamrt64).
  2. Stock Metamod:Source with plugin API (PLAPI) 18 support, installed under game/csgo/addons/metamod/. No patched or custom Metamod host is required; a compatible official upstream release is fine if you confirm PLAPI for that release and every other native plugin on the server.
  3. Custom Panorama HUD also needs MultiAddonManager v1.6.1 or later. v1.6 is stale for CS2 builds 25515854+: the addon-list offset does not match, so clients never mount the HUD addon. See HUD.

Install the runtime#

  1. Download the latest s2script-cs2-linux-*.zip from the Download page (or GitHub Releases).
  2. Extract into game/csgo/ so addons/ overlays in place. That creates a SHA-256–verified CS2 game package under addons/s2script/:
addons/s2script/
  bin/linuxsteamrt64/s2script.so
  bin/linuxsteamrt64/libs2script_core.so
  game-packages.json
  game-packages/cs2/index.js
  game-packages/cs2/gamedata.json
  game-packages/cs2/trusted-functions.json
  gamedata/core/
  gamedata/sdkhooks/
  plugins/

game-packages.json is selected-package metadata plus artifact hashes. The native shim (s2script.so) and core (libs2script_core.so) must ship as a pair — replace the whole zip rather than mixing versions.

Operator overrides still live in addons/s2script/gamedata/cs2/custom/ and apply after the verified package data (acquire, HUD-click, and damage signature repairs included). Every repair is reported at boot. Shipped game files are no longer loaded from gamedata/cs2/ or js/pawn.js; there is no prelude-by-path fallback.

  1. Patch gameinfo.gi so Metamod is the first SearchPath:
Game    csgo/addons/metamod
  1. Ensure addons/s2script/configs and addons/s2script/data are writable.
  2. Restart the server and confirm with meta list.

The native s2script shim stays resident during gameplay. .s2sp plugins still hot-reload inside s2script; stop and restart the server for native shim or Metamod updates.

Operators can bound native async admission with S2SCRIPT_ASYNC_LIMITS_JSON before starting the process — Async & networking.

Base plugins#

The release zip includes the SourceMod-parity suite (basecommands, basechat, playercommands, antiflood, adminhelp, basecomm, basebans, reservedslots, basetriggers, funcommands, clientprefs, adminmenu, basevotes, zones).

First adoption of the versioned cookie schema needs matched core and clientprefs artifacts installed with the server stopped, then a fresh process. Do not hot-load a newer clientprefs over an older generation with unversioned cookie writes still in flight. clientprefs is the sole cookie-table writer — see Cookies.

Opt-in plugins (nominations, rockthevote, nextmap, funvotes) live under plugins/disabled/ — the loader’s non-recursive scan skips that subdirectory. To enable one, move its .s2sp up one level into addons/s2script/plugins/. Those shipped files are not in plugins.json; a registry plugin is (see below), so do not move a managed file by hand.

Install a plugin without loading it#

s2s install records registry plugins in addons/s2script/plugins.json. To stage one so it stays off across restarts:

s2s install @edge/rtv --disabled

The .s2sp lands in plugins/disabled/ (the loader never scans that folder) and the name is listed under "disabled":

{
	"dependencies": { "@edge/rtv": "^1.2.0" },
	"disabled": ["@edge/rtv"]
}

"disabled" must name a dependency — the CLI and the runtime reject anything else. A dependency is disabled only when every plugin that pulls it in is disabled; one shared with an enabled plugin stays enabled. The startup update still keeps a disabled plugin current there, so it is ready the moment you turn it on.

Enable and disable move the already-installed file without downloading. The loader’s file watch then loads or unloads it:

s2s enable @edge/rtv
s2s disable @edge/rtv

The same feature is on the plugin API (Plugins.install(names, { disabled: true }), Plugins.enable / Plugins.disable from @s2script/sdk/plugins) and on the server console / RCON (sm plugins install <name> --disabled, sm plugins enable / sm plugins disable). Those console commands are not available to players, admins included.

enable / disable are permanent: they move the file and edit plugins.json, so they survive restarts. sm plugins load / unload only change what runs right now and touch nothing on disk.

Load and unload for this run#

sm plugins load <folder>/<plugin id> (and Plugins.load with the same spec) runs a plugin kept in any folder under plugins/ for this run only — disabled/@edgegamers/jailbreak, fun/@edge/rtv, even nested folders like events/halloween/@edge/rtv. Previously load could only bring back a plugin unloaded earlier in the same run.

sm plugins load disabled/@edge/rtv
sm plugins load fun/@edge/rtv
sm plugins load events/halloween/@edge/rtv
sm plugins unload @edge/rtv

unload takes the same <folder>/<id> form, or just the id.

  • Folder vs. id. The plugin id is the last segment, or the last two when they form @scope/name. Everything before is the folder.
  • No accidental loads. Only a folder prefix reaches into a folder. A bare sm plugins load <id> never does.
  • You type the id. s2script maps it to its file by the registry naming rule (@ and / become _, e.g. _edgegamers_jailbreak.s2sp). If no file has that name (dropped in or renamed by hand), the loader falls back to the archive whose manifest carries that id.
  • Confined to plugins/. The folder must resolve inside it. .., absolute paths, symlinks leading out, and plugins/ itself are refused.

The file stays where it is, so the next restart — or sm plugins unload — turns it off again. sm plugins list marks it (running from plugins/<folder>/, this run only). Plugins.list() entries (PluginInfo) include folder: string | null (the folder under plugins/ it was loaded from for this run; null for plugins/ itself). If nothing matches, the console says there is nothing to load as that spec and shows the <folder>/<plugin id> form with an example.

Use this for servers that keep plugins around in folders and only switch them on for an event. See the plugins module.

Next#