TypeScript for Counter-Strike 2

Your CS2 server,
now in TypeScript.

Edit the plugin below, then type the command in game chat. That's the whole loop: write, save, it's live.

src/plugin.ts Click the highlighted code to edit
import { command, HookResult } from '@s2script/sdk';
import { hudkit } from '@s2script/cs2';

export function OnPluginStart(): void {
  command('', (cmd) => {
    cmd.reply('');
    hudkit.forSlot(cmd.callerSlot)
      .toast({ title: '' });
    return HookResult.Handled;
  });
}
plugin.s2sp loaded TypeScript · 0 errors

ana: gl hf

kev: rush b?

restarts to reload a plugin
0 restarts to reload a plugin
first-party plugins ready to install
18 first-party plugins ready to install
command to publish to the registry
1 command to publish to the registry
migration guides, SourceMod to Swiftly
4 migration guides, SourceMod to Swiftly
01

Write

Every feature is a few lines away.

Commands, events, real Panorama HUD, timers, typed config, plugin-to-plugin APIs.

src/plugin.ts
import { command, HookResult, Chat } from '@s2script/sdk';
import { Player } from '@s2script/cs2';
​
export function OnPluginStart(): void {
  command('roll', (cmd) => {
    const name = Player.fromSlot(cmd.callerSlot)?.playerName;
    const roll = 1 + Math.floor(Math.random() * 100);
    Chat.toAll(`${name} rolled ${roll}`);
    return HookResult.Handled;
  });
}

Chat and console commands in one call. !roll and /roll just work. Docs →

02

Reload

Change code mid-match. Keep the score.

Replace the .s2sp and the plugin reloads in place. Hand your state to the next version with previous(). Try it.

src/plugin.ts v1
import { hook, previous, Chat } from '@s2script/sdk';
​
let kills = 0;
​
export function OnPluginStart(): void {
  kills = (previous() as { kills: number } | undefined)?.kills ?? 0;
  hook.on('player_death', () => {
    kills++;
    Chat.toAll(`Kills: ${kills}`);
  });
}
​
export function OnPluginState(): unknown {
  return { kills };
}
kills = 3
03

Ship

A plugin registry, built in.

Publish with s2s deploy. Install on any server with s2s install. Every package gets a page, versions, a README, browsable source and a config table.

  • One command to ship

    Build, types, README and source in one upload.

  • Orgs and scopes

    Claim @your-team and publish together.

  • Browsable source

    A Code tab with linkable lines, like npm.

  • Config tab for free

    package.json config becomes a settings table.

  • Bundles

    Release a set of plugins as one install.

  • Restricted packages

    Internal plugins, visible to your org only.

  • CI-ready

    Deploy tokens and --ci for GitHub Actions.

  • Dependencies resolved

    s2s install pulls in what a plugin needs.

Install Rock The Vote Open its page →
04

Run

Everything a server needs.

Coming from SourceMod?

The APIs you know, in a language you'll like.

hello.sp · SourcePawn
public void OnPluginStart() {
  RegConsoleCmd("sm_hello", Cmd_Hello);
}
​
public Action Cmd_Hello(int client, int args) {
  ReplyToCommand(client, "hi from slot %d", client);
  return Plugin_Handled;
}
plugin.ts · s2script
export function OnPluginStart(): void {
  command('hello', (cmd) => {
    cmd.reply(`hi from slot ${cmd.callerSlot}`);
    return HookResult.Handled;
  });
}

Ship your first plugin before warmup ends.

Install the runtime, scaffold a plugin, drop it in. Three commands, no restart.

~/my-plugin
$ npx @s2script/sdk create my-plugin --game cs2
✔ package.json  tsconfig.json  src/plugin.ts
$ npm run build
✔ my-plugin.s2sp
$ cp my-plugin.s2sp server/addons/s2script/plugins/
[s2script] loaded my-plugin