@s2script/sdk 0.0.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/admin.d.ts ADDED
@@ -0,0 +1,63 @@
1
+ /**
2
+ * @s2script/admin — engine-generic admin flag model + cache API.
3
+ *
4
+ * Resolved at runtime via `globalThis.__s2pkg_admin`; no game-specific symbols.
5
+ * Import: `import { ADMFLAG, Admin } from "./admin";`
6
+ */
7
+
8
+ /** SourceMod-parity admin flag bitmask constants (SM bit values). */
9
+ export declare const ADMFLAG: {
10
+ readonly RESERVATION: number;
11
+ readonly GENERIC: number;
12
+ readonly KICK: number;
13
+ readonly BAN: number;
14
+ readonly UNBAN: number;
15
+ readonly SLAY: number;
16
+ readonly CHANGEMAP: number;
17
+ readonly CONVARS: number;
18
+ readonly CONFIG: number;
19
+ readonly CHAT: number;
20
+ readonly VOTE: number;
21
+ readonly PASSWORD: number;
22
+ readonly RCON: number;
23
+ readonly CHEATS: number;
24
+ readonly ROOT: number;
25
+ };
26
+
27
+ /** An admin entry: their SteamID64, their combined flag mask, and a helper to check flags. */
28
+ export interface AdminInfo {
29
+ readonly steamId: string;
30
+ readonly flags: number;
31
+ /** Immunity level (0 = none). A lower-immunity admin cannot target a higher one. */
32
+ readonly immunity: number;
33
+ /** Names of the groups this admin belongs to. */
34
+ readonly groups: readonly string[];
35
+ /** True if `required` flags are all set, OR if this admin has ROOT (root ⇒ all). */
36
+ hasFlags(required: number): boolean;
37
+ }
38
+
39
+ /** A resolved admin group (from admin_groups.json): its flags, immunity, and per-command overrides. */
40
+ export interface AdminGroup {
41
+ readonly name: string;
42
+ readonly flags: number;
43
+ readonly immunity: number;
44
+ readonly overrides: Readonly<Record<string, { public: boolean; mask: number }>>;
45
+ }
46
+
47
+ /** The admin API: add/remove runtime admins, look up by SteamID or slot, reload from file. */
48
+ export declare const Admin: {
49
+ /** Add (or overwrite) a runtime admin. Not persisted to admins.json. */
50
+ add(steamId: string, flags: number, immunity?: number): void;
51
+ /** Remove a runtime admin. */
52
+ remove(steamId: string): void;
53
+ /** Get an admin by SteamID64 (union of file + runtime tiers), or null if not an admin. */
54
+ get(steamId: string): AdminInfo | null;
55
+ /** Get an admin by player slot (resolves SteamID via engine), or null. */
56
+ forSlot(slot: number): AdminInfo | null;
57
+ /** True if `callerSlot` may act on `targetSlot` (console = infinite; blocked iff target immunity > caller). */
58
+ canTarget(callerSlot: number, targetSlot: number): boolean;
59
+ /** A resolved group by name (from admin_groups.json), or null. */
60
+ getGroup(name: string): AdminGroup | null;
61
+ /** Re-read admins.json into the file tier (clears old file-tier entries first). */
62
+ reload(): void;
63
+ };
package/bans.d.ts ADDED
@@ -0,0 +1,44 @@
1
+ /**
2
+ * @s2script/bans — engine-generic SteamID64 ban store + bans.json persistence.
3
+ *
4
+ * Resolved at runtime via `globalThis.__s2pkg_bans`; no game-specific symbols.
5
+ * Import: `import { Bans } from "./bans";`
6
+ *
7
+ * The store is host-global in core, populated from `addons/s2script/configs/bans.json` via the config
8
+ * bridge. Enforcement is JS-driven: a ban plugin (e.g. @s2script/basebans) registers a
9
+ * `Clients.onConnect` handler that looks a connecting SteamID64 up here and, if banned, shows the
10
+ * reason then kicks (`Client.kickWithReason`) — a banned player is admitted, shown their reason, and
11
+ * kicked, not instant-rejected. A 3rd-party ban system registers its own onConnect against its own store.
12
+ */
13
+
14
+ /** A ban entry's value: expiry + reason. `until === 0` = permanent, else unix-second expiry. */
15
+ export interface BanInfo {
16
+ /** Unix-second expiry, or 0 for a permanent ban. */
17
+ until: number;
18
+ /** The ban reason (may be empty). */
19
+ reason: string;
20
+ }
21
+
22
+ /** A ban entry as listed by `Bans.list()`: the SteamID64 plus its value. */
23
+ export interface BanEntry {
24
+ /** The banned SteamID64 (decimal string). */
25
+ steamid: string;
26
+ /** Unix-second expiry, or 0 for a permanent ban. */
27
+ until: number;
28
+ /** The ban reason (may be empty). */
29
+ reason: string;
30
+ }
31
+
32
+ /** The ban API: add/remove bans, look up by SteamID64, list, and reload from bans.json. */
33
+ export declare const Bans: {
34
+ /** Add (or overwrite) a ban and persist it to bans.json. `minutes <= 0` = permanent. */
35
+ add(steamId: string, minutes: number, reason?: string): void;
36
+ /** Remove a ban and persist. Returns whether the SteamID64 was banned. */
37
+ remove(steamId: string): boolean;
38
+ /** Get a ban by SteamID64, or null if not banned. */
39
+ get(steamId: string): BanInfo | null;
40
+ /** List every ban currently in the cache. */
41
+ list(): BanEntry[];
42
+ /** Re-read bans.json into the cache (clears the old cache first). */
43
+ reload(): void;
44
+ };
package/chat.d.ts ADDED
@@ -0,0 +1,22 @@
1
+ /** @s2script/chat — print messages to player chat. NO runtime code (injected at load). */
2
+ import type { HookResultValue } from "./events";
3
+
4
+ export declare const Chat: {
5
+ /**
6
+ * An opaque prefix prepended to every chat message (NOT rcon/console replies). Color is content the
7
+ * caller owns (SourceMod-parity), so the engine-generic layer never picks one. A game/plugin sets this
8
+ * to a color control byte — e.g. `Chat.color = ChatColors.Green` from `@s2script/cs2`. `""` = send raw
9
+ * (CS2 needs a leading control byte for chat to render). A message may also embed its own colors.
10
+ */
11
+ color: string;
12
+ /** Print to the chat of the client in `slot` (0-based). */
13
+ toSlot(slot: number, message: string): void;
14
+ /** Print to every live player's chat. */
15
+ toAll(message: string): void;
16
+ /**
17
+ * Subscribe to raw player chat. The handler receives the speaker's `slot`, the raw `text`, and
18
+ * `teamonly`. Returning `>= HookResult.Handled` (2) suppresses the broadcast (SM-parity). Non-command
19
+ * chat lines are delivered here; the `@`-trigger layer subscribes through this.
20
+ */
21
+ onMessage(handler: (slot: number, text: string, teamonly: boolean) => HookResultValue | void): void;
22
+ };
package/clients.d.ts ADDED
@@ -0,0 +1,61 @@
1
+ /**
2
+ * @s2script/clients — engine-generic client handle + lifecycle events.
3
+ * Resolved at runtime via globalThis.__s2pkg_clients. Import: import { Client, Clients } from "./clients";
4
+ */
5
+ /** A connected client, identified by its 0-based slot (CPlayerSlot). Slot-backed; getters read live. */
6
+ export declare class Client {
7
+ readonly slot: number;
8
+ /** True while a client occupies this slot. */
9
+ isValid(): boolean;
10
+ /** Decimal SteamID64; "0" for a bot or an unauthenticated client. */
11
+ readonly steamId: string;
12
+ /** Display name; "" if unavailable. */
13
+ readonly name: string;
14
+ /** Engine user-id; -1 if none. */
15
+ readonly userId: number;
16
+ /** Tracked signon state: 0 = none/disconnected, 2 = connected, 5 = spawned, 6 = full (in-game); -1 if the slot is out of range. */
17
+ readonly signonState: number;
18
+ /** True for a fake client (bot) — derived from steamId === "0". */
19
+ readonly isBot: boolean;
20
+ /** Disconnect this client. */
21
+ kick(reason?: string): void;
22
+ /** Send a chat (SayText2) line to this client. */
23
+ chat(message: string): void;
24
+ /** Print one line to this client's developer console (skipped for bots). */
25
+ print(message: string): void;
26
+ /** This client's IP address (":port" stripped); "" for a bot. */
27
+ readonly ip: string;
28
+ /** Show `reason` (chat + console) once the client is in-game, then kick after `delaySeconds` (default 5). Intended to be called from a Clients.onConnect handler. */
29
+ kickWithReason(reason: string, delaySeconds?: number): void;
30
+ /**
31
+ * Server-side voice mute: while true, this client's OUTGOING voice is silenced for every receiver.
32
+ * Framework state (not an engine field): cleared automatically on disconnect, persists across map
33
+ * changes while connected. If the voice descriptor is degraded (hook/validation failure — named
34
+ * reason in the server log), setting is an inert no-op and reads stay false.
35
+ */
36
+ voiceMuted: boolean;
37
+ }
38
+ export declare const Clients: {
39
+ /** Fires when a client connects (all clients incl. bots; carries name/xuid). May be async. */
40
+ onConnect(handler: (client: Client) => void | Promise<void>): void;
41
+ /** Fires when a client is put in the server (controller/pawn context now exists). May be async. */
42
+ onPutInServer(handler: (client: Client) => void | Promise<void>): void;
43
+ /** Fires when a client goes active (spawned / in-game). May be async. */
44
+ onActive(handler: (client: Client) => void | Promise<void>): void;
45
+ /** Fires when a client is fully connected. May be async. */
46
+ onFullyConnect(handler: (client: Client) => void | Promise<void>): void;
47
+ /** Fires when a client disconnects. Only `.slot` is guaranteed live here — capture identity earlier if needed. */
48
+ onDisconnect(handler: (client: Client) => void): void;
49
+ /** Fires when a client's settings (name/cvars) change. */
50
+ onSettingsChanged(handler: (client: Client) => void): void;
51
+ /**
52
+ * Fires while a client transmits voice. Throttled to at most one dispatch per client per second;
53
+ * the FIRST packet of a transmission always fires (a lazy mute-on-talk lands immediately).
54
+ * Handlers should be idempotent. Never fires for bots.
55
+ */
56
+ onVoice(handler: (client: Client) => void): void;
57
+ /** The client in `slot`, or null if the slot is empty. */
58
+ fromSlot(slot: number): Client | null;
59
+ /** Every currently-connected client. */
60
+ all(): Client[];
61
+ };
package/commands.d.ts ADDED
@@ -0,0 +1,51 @@
1
+ /** @s2script/commands — register server commands. NO runtime code (injected at load). */
2
+ export interface CommandContext {
3
+ /** 0-based caller slot, or -1 for the server console. */
4
+ readonly callerSlot: number;
5
+ /** argString split on whitespace (0-based; the command name is NOT included). Kept for compat. */
6
+ readonly args: string[];
7
+ /** everything after the command name (raw) — SM `GetCmdArgString`. */
8
+ readonly argString: string;
9
+ /** number of whitespace-split arguments — SM `GetCmdArgs`. */
10
+ readonly argCount: number;
11
+ /** the nth argument (0-based), or `""` if absent — SM `GetCmdArg`. */
12
+ arg(n: number): string;
13
+ /** the nth argument parsed as an integer, or `fallback` (default 0) if absent/non-numeric. */
14
+ argInt(n: number, fallback?: number): number;
15
+ /** the nth argument parsed as a float, or `fallback` (default 0) if absent/non-numeric. */
16
+ argFloat(n: number, fallback?: number): number;
17
+ /** every argument from index `n` onward, re-joined with a single space (a reason/message/value that spans spaces). */
18
+ argsFrom(n: number): string;
19
+ /** reply to the caller: server console → server print; a player → their chat. */
20
+ reply(message: string): void;
21
+ /** reply to the caller, translated for THEIR language (SM's `%t` on the reply path). Soft-deps
22
+ * `@s2script/translations` — degrades to the raw `key` if it isn't loaded. */
23
+ replyT(key: string, ...args: (string | number)[]): void;
24
+ }
25
+ /** A parsed chat trigger: which command + args, and whether it was the silent (`/`) trigger. */
26
+ export interface ChatTrigger {
27
+ /** `true` = the silent trigger (`/`, hidden); `false` = the public trigger (`!`). */
28
+ readonly silent: boolean;
29
+ /** the command name (the first token after the trigger char; NOT `sm_`-prefixed). */
30
+ readonly name: string;
31
+ /** everything after the command name. */
32
+ readonly argString: string;
33
+ }
34
+
35
+ export declare const Commands: {
36
+ register(name: string, handler: (ctx: CommandContext) => void): void;
37
+ registerServer(name: string, handler: (ctx: CommandContext) => void): void;
38
+ registerAdmin(name: string, flags: number, handler: (ctx: CommandContext) => void): void;
39
+ /** Invoke a registered command by name in THIS plugin (applying its gating). Returns true if it exists. */
40
+ dispatch(name: string, slot: number, argString: string): boolean;
41
+ /** Parse a chat message for a trigger (`!`/`/`). Returns the parsed trigger, or null if it's ordinary chat. */
42
+ parseChatTrigger(message: string): ChatTrigger | null;
43
+ /** If `message` is a trigger, dispatch the command (tries `name` then `sm_<name>`) as `slot`; returns
44
+ * `{ silent, ran }` (the caller should suppress the chat message), or null if it was ordinary chat. */
45
+ handleChatTrigger(slot: number, message: string): { silent: boolean; ran: boolean } | null;
46
+ /** The trigger characters — SM PublicChatTrigger (`"!"`) / SilentChatTrigger (`"/"`). Mutate to reconfigure. */
47
+ readonly triggers: { public: string; silent: string };
48
+ /** Every globally-registered command with its required admin `flags`: `0` = anyone, `-1` = console/server-only,
49
+ * else the `ADMFLAG` bit mask (map bits→names in your plugin). The `sm_help` backend. */
50
+ list(): { name: string; flags: number }[];
51
+ };
package/config.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ /** @s2script/config — typed access to the plugin's materialized config. NO runtime code. */
2
+ export type Config = Record<string, string | number | boolean>;
3
+ export declare const config: {
4
+ getString(key: string): string;
5
+ getInt(key: string): number;
6
+ getFloat(key: string): number;
7
+ getBool(key: string): boolean;
8
+ /** Opt into live-reload: the handler fires with the re-materialized config when the file changes. */
9
+ onChange(handler: (cfg: Config) => void): void;
10
+ /** Read a raw file from the configs dir (name includes its extension, e.g. "maplist.txt"). null if absent. */
11
+ readFile(name: string): string | null;
12
+ /** Write a raw file to the configs dir (creates/overwrites). */
13
+ writeFile(name: string, content: string): void;
14
+ };
package/console.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @s2script/console — author-time type stubs for the engine console.
3
+ * NO runtime code: the engine injects the implementation at load time.
4
+ */
5
+
6
+ /** Engine-provided console (log/error/warn/info). Also available as the global `console`. */
7
+ export declare const console: {
8
+ log(...data: any[]): void;
9
+ error(...data: any[]): void;
10
+ warn(...data: any[]): void;
11
+ info(...data: any[]): void;
12
+ };
package/cookies.d.ts ADDED
@@ -0,0 +1,25 @@
1
+ /** @s2script/cookies — SM-parity client preference cookies. NO runtime code (injected as __s2pkg_cookies). */
2
+ import type { Client } from "./clients";
3
+ export enum CookieAccess { Public, Protected, Private }
4
+ export interface Cookie { readonly name: string; readonly access: CookieAccess; readonly default: string; }
5
+ export interface CookieOptions { description?: string; access?: CookieAccess; default?: string; }
6
+ export declare const Cookies: {
7
+ /** Register (or return an existing) cookie definition. */
8
+ register(name: string, opts?: CookieOptions): Cookie;
9
+ /** Cache value for this client (a stored "" is a real value), else the cookie's default. Default for bots. */
10
+ get(client: Client, cookie: Cookie): string;
11
+ /** Write the cache + mark dirty (flushed to the DB on disconnect). No-op for bots. */
12
+ set(client: Client, cookie: Cookie, value: string): void;
13
+ /** Has this client's cookies finished loading from the DB? */
14
+ areCached(client: Client): boolean;
15
+ /** Unix timestamp of the cookie's last write (set or DB load), or 0 if never set. 0 for bots. */
16
+ getTime(client: Client, cookie: Cookie): number;
17
+ /** Write a cookie for a SteamID64 that may not be connected right now (`SetAuthIdCookie` parity):
18
+ * updates the cache immediately (an online client sees it right away) and queues the write for the
19
+ * clientprefs plugin to persist directly next frame — an offline SteamID never fires the disconnect
20
+ * flush. No-op for "0" (bot/unset). */
21
+ setAuthId(steamId: string, cookie: Cookie, value: string): void;
22
+ /** Fires once per client, after that client's cookies finish loading (`OnClientCookiesCached`
23
+ * parity) — post-frame, after the load's async DB read completes. */
24
+ onCached(handler: (client: Client) => void): void;
25
+ };
package/damage.d.ts ADDED
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @s2script/damage — damage pre-hooks (SDKHooks-equivalent). NO runtime code (injected at load).
3
+ * CTakeDamageInfo is a Source 2 engine type, so this module is engine-generic (lives in core).
4
+ */
5
+ import type { EntityRef } from "./entity";
6
+
7
+ /** A block-scoped view of the current damage event (valid only inside a Damage.onPre handler). */
8
+ export interface DamageInfo {
9
+ /** The damage amount (m_flDamage). Assigning MODIFIES the live damage; set to 0 to block. */
10
+ damage: number;
11
+ /** The damage-type bit flags (m_bitsDamageType). */
12
+ readonly damageType: number;
13
+ /** The attacking entity (m_hAttacker), or null if none/stale. */
14
+ readonly attacker: EntityRef | null;
15
+ /** The inflicting entity (m_hInflictor), or null if none/stale. */
16
+ readonly inflictor: EntityRef | null;
17
+ /** The victim — the entity taking damage (the hooked `this`), or null if stale. */
18
+ readonly victim: EntityRef | null;
19
+ }
20
+
21
+ export declare const Damage: {
22
+ /** Register a pre-damage hook. The handler reads/modifies the DamageInfo in place (damage=0 blocks). */
23
+ onPre(handler: (info: DamageInfo) => void): void;
24
+ };
package/db.d.ts ADDED
@@ -0,0 +1,27 @@
1
+ /** @s2script/db — engine-generic async SQLite database. NO runtime code (injected as __s2pkg_db). */
2
+ export type SqlValue = string | number | boolean | null;
3
+ export type Row = Record<string, SqlValue>;
4
+ export interface ExecuteResult { changes: number; lastInsertId: number; }
5
+
6
+ export interface DriverConnection {
7
+ query(sql: string, params?: SqlValue[]): Promise<Row[]>;
8
+ execute(sql: string, params?: SqlValue[]): Promise<ExecuteResult>;
9
+ close(): Promise<void>;
10
+ }
11
+ export interface ConnectionConfig { driver: string; name: string; [k: string]: unknown; }
12
+ export interface Driver {
13
+ readonly name: string;
14
+ connect(config: ConnectionConfig): Promise<DriverConnection>;
15
+ }
16
+ /** A live database connection (delegates to its driver). */
17
+ export interface Database {
18
+ query(sql: string, params?: SqlValue[]): Promise<Row[]>;
19
+ execute(sql: string, params?: SqlValue[]): Promise<ExecuteResult>;
20
+ close(): Promise<void>;
21
+ }
22
+ export declare const Database: {
23
+ /** Open a connection by name (default "default"). Resolves the driver + config, then connects. */
24
+ open(name?: string): Promise<Database>;
25
+ /** Register a custom driver (per-plugin context). SQLite is built in. */
26
+ registerDriver(driver: Driver): void;
27
+ };