@s2script/sdk 0.5.0 → 0.6.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/LICENSE-APACHE +201 -0
- package/LICENSE-MIT +21 -0
- package/README.md +40 -6
- package/admin.d.ts +28 -1
- package/bans.d.ts +8 -1
- package/chat.d.ts +7 -0
- package/clients.d.ts +9 -1
- package/commands.d.ts +65 -4
- package/config.d.ts +12 -0
- package/console.d.ts +9 -0
- package/cookies.d.ts +37 -4
- package/damage.d.ts +7 -1
- package/db.d.ts +41 -3
- package/dist/cli.js +460 -25
- package/entity.d.ts +27 -1
- package/events.d.ts +27 -1
- package/globals.d.ts +6 -1
- package/http.d.ts +35 -4
- package/interfaces.d.ts +8 -1
- package/math.d.ts +15 -1
- package/menu.d.ts +92 -7
- package/net.d.ts +26 -2
- package/package.json +6 -3
- package/plugin.d.ts +85 -2
- package/plugins.d.ts +9 -0
- package/server.d.ts +8 -0
- package/sound.d.ts +9 -0
- package/timers.d.ts +7 -1
- package/topmenu.d.ts +15 -0
- package/trace.d.ts +8 -0
- package/translations.d.ts +10 -0
- package/transmit.d.ts +8 -0
- package/usercmd.d.ts +6 -0
- package/usermessages.d.ts +18 -0
- package/votes.d.ts +46 -3
- package/ws.d.ts +18 -1
package/entity.d.ts
CHANGED
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
* time" footgun). Obtain refs from the engine (events, `findByClass`, `readHandle`, …).
|
|
16
16
|
*/
|
|
17
17
|
export declare class EntityRef {
|
|
18
|
+
/** This entity's slot index in the game entity system. Paired with the host-minted
|
|
19
|
+
* {@link EntityRef.id} for slot validation (books first, then identity-slot match). */
|
|
18
20
|
readonly index: number;
|
|
19
21
|
/** The host-minted liveness id for this ref (books key). Not the raw engine serial. */
|
|
20
22
|
readonly id: number;
|
|
@@ -80,21 +82,33 @@ export declare class EntityRef {
|
|
|
80
82
|
/** Write an int32 at the end of a pointer chain (each hop deref'd, liveness-gated at the root). Returns
|
|
81
83
|
* false on a stale ref or a null hop. Used to clear a flag on a pointer-referenced sub-object. */
|
|
82
84
|
writeInt32Via(pathOffs: number[], finalOff: number, value: number): boolean;
|
|
85
|
+
/** Read an i8 (sign-extended) at the end of a pointer chain (each hop deref'd, liveness-gated at the root); null if the root is stale or any hop is null. */
|
|
83
86
|
readInt8Via(pathOffs: number[], finalOff: number): number | null;
|
|
87
|
+
/** Read an i16 (sign-extended) through a pointer chain; null if the root is stale or any hop is null. */
|
|
84
88
|
readInt16Via(pathOffs: number[], finalOff: number): number | null;
|
|
89
|
+
/** Read a u8 through a pointer chain; null if the root is stale or any hop is null. */
|
|
85
90
|
readUInt8Via(pathOffs: number[], finalOff: number): number | null;
|
|
91
|
+
/** Read a u16 through a pointer chain; null if the root is stale or any hop is null. */
|
|
86
92
|
readUInt16Via(pathOffs: number[], finalOff: number): number | null;
|
|
93
|
+
/** Read a u32 through a pointer chain; null if the root is stale or any hop is null. */
|
|
87
94
|
readUInt32Via(pathOffs: number[], finalOff: number): number | null;
|
|
95
|
+
/** Read an f32 through a pointer chain; null if the root is stale or any hop is null. */
|
|
88
96
|
readFloat32Via(pathOffs: number[], finalOff: number): number | null;
|
|
97
|
+
/** Read a bool through a pointer chain; null if the root is stale or any hop is null. */
|
|
89
98
|
readBoolVia(pathOffs: number[], finalOff: number): boolean | null;
|
|
99
|
+
/** Read a u64 as a BigInt through a pointer chain; null if the root is stale or any hop is null. */
|
|
90
100
|
readUInt64Via(pathOffs: number[], finalOff: number): bigint | null;
|
|
101
|
+
/** Read an i64 as a BigInt through a pointer chain; null if the root is stale or any hop is null. */
|
|
91
102
|
readInt64Via(pathOffs: number[], finalOff: number): bigint | null;
|
|
103
|
+
/** Decode a `CEntityHandle` at the end of a pointer chain into a liveness-gated {@link EntityRef}; null if the root is stale, any hop is null, or the handle is dead. */
|
|
92
104
|
readHandleVia(pathOffs: number[], finalOff: number): EntityRef | null;
|
|
93
105
|
/** Write a scalar through a pointer chain (write mirror of `read*Via`). Liveness-gated at the root;
|
|
94
106
|
* returns false on a stale ref, an unresolved hop, or a bad offset/kind. Does NOT notifyStateChanged —
|
|
95
107
|
* the caller decides (many sub-object fields, e.g. the fire gate, are server-authoritative).
|
|
96
108
|
* (`writeInt32Via` is declared above alongside `readInt32Via`.) */
|
|
97
109
|
writeFloat32Via(pathOffs: number[], finalOff: number, value: number): boolean;
|
|
110
|
+
/** Write a bool through a pointer chain (write mirror of {@link EntityRef.readBoolVia}; shares the
|
|
111
|
+
* no-notify semantics on {@link EntityRef.writeFloat32Via}). False on a stale ref, an unresolved hop, or a bad offset/kind. */
|
|
98
112
|
writeBoolVia(pathOffs: number[], finalOff: number, value: boolean): boolean;
|
|
99
113
|
/** Read a `CEntityHandle` at `offset`, decode it, and return a live `EntityRef` — or null if stale/invalid. */
|
|
100
114
|
readHandle(offset: number): EntityRef | null;
|
|
@@ -138,7 +152,12 @@ export type EntityKeyValueMap = { [key: string]: string | number | boolean };
|
|
|
138
152
|
/** Create a new entity by class name. WITHOUT keyvalues: create only — set fields, then call
|
|
139
153
|
* `.spawn()`. WITH keyvalues: create + DispatchSpawn(keyvalues) in one call — a non-null result is
|
|
140
154
|
* a LIVE, SPAWNED entity (on spawn failure the entity is removed and null returned). The created
|
|
141
|
-
* entity is game-world-owned (NOT auto-removed on plugin unload) — the plugin owns cleanup.
|
|
155
|
+
* entity is game-world-owned (NOT auto-removed on plugin unload) — the plugin owns cleanup.
|
|
156
|
+
* @example
|
|
157
|
+
* import { createEntity } from "@s2script/sdk/entity";
|
|
158
|
+
* const wt = createEntity("point_worldtext", { message: "s2-ekv-proof", fullbright: true });
|
|
159
|
+
* if (wt) wt.remove();
|
|
160
|
+
*/
|
|
142
161
|
export declare function createEntity(className: string, keyvalues?: EntityKeyValueMap): EntityRef | null;
|
|
143
162
|
|
|
144
163
|
/** The payload delivered to an `Entity.onOutput` handler. */
|
|
@@ -155,6 +174,13 @@ export interface OutputEvent {
|
|
|
155
174
|
delay: number;
|
|
156
175
|
}
|
|
157
176
|
|
|
177
|
+
/**
|
|
178
|
+
* Entry point for entity lookup by class name.
|
|
179
|
+
* @example
|
|
180
|
+
* import { Entity } from "@s2script/sdk/entity";
|
|
181
|
+
* const triggers = Entity.findByClass("trigger_multiple");
|
|
182
|
+
* for (const t of triggers) console.log(t.index, t.name);
|
|
183
|
+
*/
|
|
158
184
|
export declare const Entity: {
|
|
159
185
|
/** Find every entity whose designer-name (class) exactly matches `className`. Returns liveness-gated refs. */
|
|
160
186
|
findByClass(className: string): EntityRef[];
|
package/events.d.ts
CHANGED
|
@@ -3,18 +3,27 @@
|
|
|
3
3
|
/** A live game-event accessor. Valid ONLY during the synchronous handler — read fields before any `await`;
|
|
4
4
|
* a stashed GameEvent used later reads defaults. The raw engine event never crosses to JS. */
|
|
5
5
|
export declare class GameEvent {
|
|
6
|
+
/** The event's name (e.g. `"player_death"`). */
|
|
6
7
|
readonly name: string;
|
|
8
|
+
/** Read an integer field. `0` if absent or non-integer. */
|
|
7
9
|
getInt(key: string): number;
|
|
10
|
+
/** Read a float field. `0` if absent or non-float. */
|
|
8
11
|
getFloat(key: string): number;
|
|
12
|
+
/** Read a boolean field. `false` if absent. */
|
|
9
13
|
getBool(key: string): boolean;
|
|
14
|
+
/** Read a string field. `""` if absent. */
|
|
10
15
|
getString(key: string): string;
|
|
11
16
|
/** A 64-bit field as a decimal string (SM-parity, wire-safe). */
|
|
12
17
|
getUint64(key: string): string;
|
|
13
18
|
/** A player field (e.g. "userid"/"attacker") as a 0-based slot, or -1 if absent. Resolve with Player.fromSlot. */
|
|
14
19
|
getPlayerSlot(key: string): number;
|
|
20
|
+
/** Overwrite an integer field on the live event (valid only in the synchronous handler). */
|
|
15
21
|
setInt(key: string, value: number): void;
|
|
22
|
+
/** Overwrite a float field on the live event (valid only in the synchronous handler). */
|
|
16
23
|
setFloat(key: string, value: number): void;
|
|
24
|
+
/** Overwrite a boolean field on the live event (valid only in the synchronous handler). */
|
|
17
25
|
setBool(key: string, value: boolean): void;
|
|
26
|
+
/** Overwrite a string field on the live event (valid only in the synchronous handler). */
|
|
18
27
|
setString(key: string, value: string): void;
|
|
19
28
|
/** Set a 64-bit field from a decimal string (SM-parity, wire-safe). */
|
|
20
29
|
setUint64(key: string, value: string): void;
|
|
@@ -22,9 +31,26 @@ export declare class GameEvent {
|
|
|
22
31
|
|
|
23
32
|
/** Collapsed pre-hook result. Return from an `onPre` handler; `Handled`/`Stop` suppress the client
|
|
24
33
|
* broadcast (server still processes). Returning nothing = `Continue`. */
|
|
25
|
-
export declare const HookResult: {
|
|
34
|
+
export declare const HookResult: {
|
|
35
|
+
/** No change — proceed normally. Equivalent to returning nothing. */
|
|
36
|
+
readonly Continue: 0;
|
|
37
|
+
/** Fields were modified in place; proceed with the changes (the client broadcast still happens). */
|
|
38
|
+
readonly Changed: 1;
|
|
39
|
+
/** Suppress the client broadcast (the server still processes the event). */
|
|
40
|
+
readonly Handled: 2;
|
|
41
|
+
/** Suppress the client broadcast AND stop running lower-priority handlers. The collapse takes the MAX. */
|
|
42
|
+
readonly Stop: 3;
|
|
43
|
+
};
|
|
44
|
+
/** The numeric union an `onPre` handler may return — one of the {@link HookResult} values. */
|
|
26
45
|
export type HookResultValue = 0 | 1 | 2 | 3;
|
|
27
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Fire and target game events from JS.
|
|
49
|
+
* @example
|
|
50
|
+
* import { Events } from "@s2script/sdk/events";
|
|
51
|
+
* // plugins/playercommands/src/plugin.ts:78 — announce a name change
|
|
52
|
+
* Events.fire("player_changename", { userid: p.userId, oldname, newname });
|
|
53
|
+
*/
|
|
28
54
|
export declare const Events: {
|
|
29
55
|
/** Fire a game event. Returns the engine FireEvent result. */
|
|
30
56
|
fire(name: string, fields?: Record<string, number | string | boolean | bigint>, dontBroadcast?: boolean): boolean;
|
package/globals.d.ts
CHANGED
|
@@ -3,7 +3,12 @@
|
|
|
3
3
|
// typecheck gate as a root file so plugins that use these globals WITHOUT importing type-check against
|
|
4
4
|
// the real sandbox — NOT lib.dom (the sandbox has no window/document/etc.).
|
|
5
5
|
|
|
6
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* The engine-injected console (a subset of the browser/node console). Global — no import needed.
|
|
8
|
+
* @example
|
|
9
|
+
* // plugins/antiflood/src/plugin.ts:48 — write a line to the server console/log
|
|
10
|
+
* console.log("[antiflood] onLoad — chat flood protection active");
|
|
11
|
+
*/
|
|
7
12
|
declare const console: {
|
|
8
13
|
log(...data: any[]): void;
|
|
9
14
|
error(...data: any[]): void;
|
package/http.d.ts
CHANGED
|
@@ -1,11 +1,42 @@
|
|
|
1
1
|
/** @s2script/http — async HTTP. NO runtime code (injected as __s2pkg_http). */
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
/** Options for {@link fetch}. */
|
|
4
|
+
export interface FetchOptions {
|
|
5
|
+
/** HTTP method (e.g. `"GET"`, `"POST"`). @defaultValue `"GET"` */
|
|
6
|
+
method?: string;
|
|
7
|
+
/** Request headers as a name→value map. */
|
|
8
|
+
headers?: Record<string, string>;
|
|
9
|
+
/** Request body (already-serialized string; set your own `content-type`). */
|
|
10
|
+
body?: string;
|
|
11
|
+
/** Abort the request after this many milliseconds. @defaultValue no timeout */
|
|
12
|
+
timeoutMs?: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** The response from a {@link fetch} call — a copied snapshot (no live socket). */
|
|
3
16
|
export interface Response {
|
|
4
|
-
|
|
17
|
+
/** HTTP status code (e.g. `200`, `404`). */
|
|
18
|
+
readonly status: number;
|
|
19
|
+
/** True iff {@link Response.status} is in the 2xx range. */
|
|
20
|
+
readonly ok: boolean;
|
|
21
|
+
/** HTTP status reason phrase (e.g. `"Not Found"`). */
|
|
22
|
+
readonly statusText: string;
|
|
23
|
+
/** Response headers as a lower-cased name→value map. */
|
|
5
24
|
readonly headers: Record<string, string>;
|
|
25
|
+
/** The response body decoded as text. */
|
|
6
26
|
text(): string;
|
|
27
|
+
/** Parse the response body as JSON into `T` (throws on invalid JSON). */
|
|
7
28
|
json<T = unknown>(): T;
|
|
8
29
|
}
|
|
9
|
-
|
|
10
|
-
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Perform an HTTP request off the game thread.
|
|
33
|
+
* @param url - Absolute `http(s)://` URL.
|
|
34
|
+
* @param options - Method, headers, body, and timeout (see {@link FetchOptions}).
|
|
35
|
+
* @returns Resolves for ANY HTTP response — a 4xx/5xx resolves with `ok=false`, it does not reject.
|
|
36
|
+
* @throws Rejects only on a network error or timeout.
|
|
37
|
+
* @example
|
|
38
|
+
* import { fetch } from "@s2script/sdk/http";
|
|
39
|
+
* const r = await fetch("https://httpbin.org/get", { timeoutMs: 15000 });
|
|
40
|
+
* if (r.ok) console.log(r.json<{ url: string }>().url);
|
|
41
|
+
*/
|
|
11
42
|
export declare function fetch(url: string, options?: FetchOptions): Promise<Response>;
|
package/interfaces.d.ts
CHANGED
|
@@ -8,6 +8,13 @@
|
|
|
8
8
|
* events to every plugin subscribed to this interface via its `on(event, …)`.
|
|
9
9
|
*/
|
|
10
10
|
export interface PublishHandle {
|
|
11
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* Emit a forwarded event to all consumers subscribed via `interface.on(event, …)`. The payload is
|
|
13
|
+
* structured-copied across the context boundary (JSON, EntityRef-aware — never a live reference).
|
|
14
|
+
* @example
|
|
15
|
+
* import type { PublishHandle } from "@s2script/sdk/interfaces";
|
|
16
|
+
* // plugins/zones/src/plugin.ts:30 — notify consumers a zone was created
|
|
17
|
+
* iface.emit("created", { zone: z.name, min: z.min, max: z.max, tags: z.tags });
|
|
18
|
+
*/
|
|
12
19
|
emit(event: string, payload: unknown): void;
|
|
13
20
|
}
|
package/math.d.ts
CHANGED
|
@@ -3,23 +3,37 @@
|
|
|
3
3
|
* NO runtime code: the engine injects the implementation (core prelude) at load time.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* A 3-component vector value (a copied snapshot; never a live pointer).
|
|
8
|
+
* @example
|
|
9
|
+
* import { Vector } from "@s2script/sdk/math";
|
|
10
|
+
* const start = new Vector(0, 0, 100);
|
|
11
|
+
* console.log(start.length());
|
|
12
|
+
*/
|
|
7
13
|
export declare class Vector {
|
|
14
|
+
/** The X component. */
|
|
8
15
|
x: number;
|
|
16
|
+
/** The Y component. */
|
|
9
17
|
y: number;
|
|
18
|
+
/** The Z component. */
|
|
10
19
|
z: number;
|
|
11
20
|
constructor(x: number, y: number, z: number);
|
|
12
21
|
/** Euclidean magnitude — e.g. speed from a velocity vector. */
|
|
13
22
|
length(): number;
|
|
23
|
+
/** Format as `"Vector(x, y, z)"` (for logging). */
|
|
14
24
|
toString(): string;
|
|
15
25
|
}
|
|
16
26
|
|
|
17
27
|
/** A Source 2 Euler angle value (x=pitch, y=yaw, z=roll), a copied snapshot. */
|
|
18
28
|
export declare class QAngle {
|
|
29
|
+
/** Pitch, in degrees. */
|
|
19
30
|
x: number;
|
|
31
|
+
/** Yaw, in degrees. */
|
|
20
32
|
y: number;
|
|
33
|
+
/** Roll, in degrees. */
|
|
21
34
|
z: number;
|
|
22
35
|
constructor(x: number, y: number, z: number);
|
|
36
|
+
/** Format as `"QAngle(x, y, z)"` (for logging). */
|
|
23
37
|
toString(): string;
|
|
24
38
|
}
|
|
25
39
|
|
package/menu.d.ts
CHANGED
|
@@ -1,12 +1,62 @@
|
|
|
1
1
|
/** @s2script/menu — interactive player menus (chat + center backends). NO runtime code (injected at load). */
|
|
2
|
-
export declare const enum MenuStyle { Chat = "chat", Center = "center" }
|
|
3
|
-
export declare const enum MenuCancelReason { Exit = 0, Timeout = 1, Disconnect = 2, NewMenu = 3 }
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
export
|
|
3
|
+
/** The display backend a {@link Menu} renders through — the value of {@link Menu.style}. */
|
|
4
|
+
export declare const enum MenuStyle {
|
|
5
|
+
/** Chat-log backend: numbered lines printed to chat, picked by typing the number. */
|
|
6
|
+
Chat = "chat",
|
|
7
|
+
/** CS2 center-screen HTML backend: W/S move the cursor, E selects; re-painted each tick. */
|
|
8
|
+
Center = "center",
|
|
9
|
+
}
|
|
10
|
+
/** Why a menu closed without a selection — the {@link MenuCancelEvent.reason}. */
|
|
11
|
+
export declare const enum MenuCancelReason {
|
|
12
|
+
/** The player chose the Exit control (or `close`/`cancel` was called). */
|
|
13
|
+
Exit = 0,
|
|
14
|
+
/** The `display` seconds elapsed with no selection. */
|
|
15
|
+
Timeout = 1,
|
|
16
|
+
/** The player left the server. */
|
|
17
|
+
Disconnect = 2,
|
|
18
|
+
/** A newer menu was shown to the same slot, superseding this one. */
|
|
19
|
+
NewMenu = 3,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Payload for {@link Menu.onSelect} — the item a player picked. */
|
|
23
|
+
export interface MenuSelectEvent {
|
|
24
|
+
/** 0-based player slot that made the selection. */
|
|
25
|
+
readonly slot: number;
|
|
26
|
+
/** 0-based index of the chosen item within the menu's item list. */
|
|
27
|
+
readonly item: number;
|
|
28
|
+
/** The `info` string the item was added with — the machine key, returned to your handler. */
|
|
29
|
+
readonly info: string;
|
|
30
|
+
/** The visible label the item was added with. */
|
|
31
|
+
readonly display: string;
|
|
32
|
+
}
|
|
33
|
+
/** Payload for {@link Menu.onCancel} — a menu that closed without a selection. */
|
|
34
|
+
export interface MenuCancelEvent {
|
|
35
|
+
/** 0-based player slot whose menu closed. */
|
|
36
|
+
readonly slot: number;
|
|
37
|
+
/** Why it closed (see {@link MenuCancelReason}). */
|
|
38
|
+
readonly reason: MenuCancelReason;
|
|
39
|
+
}
|
|
7
40
|
|
|
41
|
+
/**
|
|
42
|
+
* A slot-based interactive menu. Add items, wire {@link Menu.onSelect}/{@link Menu.onCancel},
|
|
43
|
+
* then {@link Menu.display} to a player slot. The model paginates automatically (7 items per chat
|
|
44
|
+
* page) and picks a renderer by {@link Menu.style}, falling back to Chat when the style's renderer
|
|
45
|
+
* is unregistered. Only one menu is active per slot; displaying a new one supersedes the old
|
|
46
|
+
* ({@link MenuCancelReason.NewMenu}).
|
|
47
|
+
* @example
|
|
48
|
+
* import { Menu, MenuStyle } from "@s2script/sdk/menu";
|
|
49
|
+
* const m = new Menu("s2script Menu Demo");
|
|
50
|
+
* m.style = MenuStyle.Center;
|
|
51
|
+
* m.freezePlayer = true; // freeze movement while the WASD menu is open (nav still works)
|
|
52
|
+
* m.addItem("hp", "Heal to 100");
|
|
53
|
+
* m.addItem("noclip", "Toggle Noclip");
|
|
54
|
+
* m.onSelect(e => { console.log(`picked ${e.info} (slot ${e.slot})`); });
|
|
55
|
+
* m.display(slot, 30);
|
|
56
|
+
*/
|
|
8
57
|
export declare class Menu {
|
|
9
58
|
constructor(title?: string);
|
|
59
|
+
/** Heading shown above the item list. */
|
|
10
60
|
title: string;
|
|
11
61
|
/** Which registered renderer to use. Falls back to Chat if the style's renderer is unregistered. */
|
|
12
62
|
style: MenuStyle;
|
|
@@ -18,7 +68,9 @@ export declare class Menu {
|
|
|
18
68
|
freezePlayer: boolean;
|
|
19
69
|
/** (info, display) — `info` is returned to onSelect; `display` is shown. */
|
|
20
70
|
addItem(info: string, display: string, opts?: { disabled?: boolean }): void;
|
|
71
|
+
/** Register the callback fired when a player picks an item. Replaces any previous handler. */
|
|
21
72
|
onSelect(handler: (e: MenuSelectEvent) => void): void;
|
|
73
|
+
/** Register the callback fired when the menu closes without a selection (see {@link MenuCancelReason}). Replaces any previous handler. */
|
|
22
74
|
onCancel(handler: (e: MenuCancelEvent) => void): void;
|
|
23
75
|
/** Show to a 0-based slot for `seconds` (0 = until selection/cancel/disconnect). */
|
|
24
76
|
display(slot: number, seconds?: number): void;
|
|
@@ -28,11 +80,44 @@ export declare class Menu {
|
|
|
28
80
|
static registerRenderer(name: string, renderer: MenuRenderer): void;
|
|
29
81
|
}
|
|
30
82
|
|
|
83
|
+
/** A live display of one menu to one slot, passed to a {@link MenuRenderer}. Owns the page/cursor state and exposes the input verbs a renderer drives. */
|
|
31
84
|
export interface MenuSession {
|
|
85
|
+
/** 0-based player slot this session is displayed to. */
|
|
32
86
|
readonly slot: number;
|
|
87
|
+
/** The resolved snapshot the renderer paints: heading, per-line state, and 0-based page position. */
|
|
33
88
|
view(): { title: string; lines: MenuLine[]; page: number; pageCount: number; exit: boolean };
|
|
89
|
+
/** Chat idiom: apply a number-key press against the current view (1..7 select, 8=Back, 9=Next, 0=Exit). */
|
|
34
90
|
pickNumber(n: number): void;
|
|
35
|
-
|
|
91
|
+
/** Center idiom: move the cursor up over the current page's nav targets (items + Back/Next/Exit), wrapping. */
|
|
92
|
+
moveUp(): void;
|
|
93
|
+
/** Center idiom: move the cursor down over the current page's nav targets, wrapping. */
|
|
94
|
+
moveDown(): void;
|
|
95
|
+
/** Center idiom: activate the current cursor target (select an item, paginate, or exit). */
|
|
96
|
+
confirm(): void;
|
|
97
|
+
/** Close this session with an {@link MenuCancelReason.Exit}. */
|
|
98
|
+
cancel(): void;
|
|
99
|
+
}
|
|
100
|
+
/** One rendered line of a {@link MenuSession} view. */
|
|
101
|
+
export interface MenuLine {
|
|
102
|
+
/** The line's visible text. */
|
|
103
|
+
text: string;
|
|
104
|
+
/** The chat number key ("1".."0") that picks this line, or null for an unselectable/disabled line. */
|
|
105
|
+
key: string | null;
|
|
106
|
+
/** True if this line is a selectable item. */
|
|
107
|
+
selectable: boolean;
|
|
108
|
+
/** True if the center cursor is currently on this line (the renderer highlights it). */
|
|
109
|
+
cursor?: boolean;
|
|
110
|
+
/** For a control line, which control it is: `"back"`, `"next"`, or `"exit"` (absent on item lines). */
|
|
111
|
+
control?: string;
|
|
112
|
+
/** For an item line, its 0-based index into the menu's item list (absent on control lines). */
|
|
113
|
+
index?: number;
|
|
114
|
+
}
|
|
115
|
+
/** A display backend for {@link Menu} — registered via {@link Menu.registerRenderer}, driven with a {@link MenuSession}. */
|
|
116
|
+
export interface MenuRenderer {
|
|
117
|
+
/** Show the session for the first time (the menu just opened). */
|
|
118
|
+
open(session: MenuSession): void;
|
|
119
|
+
/** Re-paint the session after a page/cursor change. */
|
|
120
|
+
update(session: MenuSession): void;
|
|
121
|
+
/** Tear down the display for `slot` (the menu closed). */
|
|
122
|
+
close(slot: number): void;
|
|
36
123
|
}
|
|
37
|
-
export interface MenuLine { text: string; key: string | null; selectable: boolean; cursor?: boolean; control?: string; index?: number; }
|
|
38
|
-
export interface MenuRenderer { open(session: MenuSession): void; update(session: MenuSession): void; close(slot: number): void; }
|
package/net.d.ts
CHANGED
|
@@ -1,19 +1,43 @@
|
|
|
1
1
|
/** @s2script/net — engine-generic raw TCP + UDP sockets (binary), off the game thread. */
|
|
2
|
+
|
|
3
|
+
/** A connected TCP client socket — a per-plugin handle over a copied byte stream (no live socket crosses to JS). */
|
|
2
4
|
export interface TcpSocket {
|
|
5
|
+
/** Send bytes; a `string` is sent as its UTF-8 encoding. */
|
|
3
6
|
send(data: Uint8Array | string): void;
|
|
7
|
+
/** Register a handler invoked for each inbound chunk of bytes. */
|
|
4
8
|
onData(handler: (bytes: Uint8Array) => void): void;
|
|
9
|
+
/** Register a handler for the peer closing the connection. */
|
|
5
10
|
onClose(handler: () => void): void;
|
|
11
|
+
/** Register a handler for a transport error; `err` is the error text. */
|
|
6
12
|
onError(handler: (err: string) => void): void;
|
|
13
|
+
/** Close the connection. */
|
|
7
14
|
close(): void;
|
|
8
15
|
}
|
|
16
|
+
/** A bound UDP socket — a per-plugin handle for connectionless datagrams. */
|
|
9
17
|
export interface UdpSocket {
|
|
18
|
+
/** Send a datagram to `host:port`; a `string` is sent as its UTF-8 encoding. */
|
|
10
19
|
sendTo(host: string, port: number, data: Uint8Array | string): void;
|
|
20
|
+
/** Register a handler invoked for each inbound datagram; `from` is the sender's address. */
|
|
11
21
|
onMessage(handler: (from: { host: string; port: number }, bytes: Uint8Array) => void): void;
|
|
22
|
+
/** Close the socket. */
|
|
12
23
|
close(): void;
|
|
13
24
|
}
|
|
25
|
+
/** Entry point for opening raw TCP + UDP sockets. */
|
|
14
26
|
export declare const Net: {
|
|
15
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* Connect a TCP client to `host:port` off the game thread.
|
|
29
|
+
* @returns Resolves on connect with the live {@link TcpSocket} handle.
|
|
30
|
+
* @throws Rejects on connect failure (refused, unreachable, DNS error).
|
|
31
|
+
* @example
|
|
32
|
+
* import { Net } from "@s2script/sdk/net";
|
|
33
|
+
* const sock = await Net.connectTcp("127.0.0.1", 9000);
|
|
34
|
+
* sock.onData((bytes) => console.log("got " + bytes.length + " bytes"));
|
|
35
|
+
* sock.send("hello");
|
|
36
|
+
*/
|
|
16
37
|
connectTcp(host: string, port: number): Promise<TcpSocket>;
|
|
17
|
-
/**
|
|
38
|
+
/**
|
|
39
|
+
* Bind a UDP socket on an ephemeral local port.
|
|
40
|
+
* @returns Resolves with the bound {@link UdpSocket} handle.
|
|
41
|
+
*/
|
|
18
42
|
udp(): Promise<UdpSocket>;
|
|
19
43
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@s2script/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"license": "MIT OR Apache-2.0",
|
|
4
5
|
"description": "s2script SDK — the builtin capability types (imported as @s2script/sdk/entity, …) plus the `s2s` CLI for building Source 2 / CS2 plugins.",
|
|
5
6
|
"type": "module",
|
|
6
7
|
"bin": {
|
|
@@ -27,7 +28,9 @@
|
|
|
27
28
|
"files": [
|
|
28
29
|
"dist",
|
|
29
30
|
"*.d.ts",
|
|
30
|
-
"README.md"
|
|
31
|
+
"README.md",
|
|
32
|
+
"LICENSE-MIT",
|
|
33
|
+
"LICENSE-APACHE"
|
|
31
34
|
],
|
|
32
35
|
"exports": {
|
|
33
36
|
"./admin": {
|
|
@@ -124,6 +127,6 @@
|
|
|
124
127
|
},
|
|
125
128
|
"repository": {
|
|
126
129
|
"type": "git",
|
|
127
|
-
"url": "https://github.com/
|
|
130
|
+
"url": "https://github.com/s2script/s2script.git"
|
|
128
131
|
}
|
|
129
132
|
}
|
package/plugin.d.ts
CHANGED
|
@@ -16,8 +16,16 @@ import type { TopMenuItem } from "./topmenu";
|
|
|
16
16
|
import type { PrecacheContext } from "./sound";
|
|
17
17
|
import type { UserCmdView } from "./usercmd";
|
|
18
18
|
|
|
19
|
+
/** Game-event subscriptions on this plugin's load-scope ({@link PluginContext.events}). */
|
|
19
20
|
export interface CtxEvents {
|
|
21
|
+
/**
|
|
22
|
+
* Subscribe to a fired game event (post-phase). The {@link GameEvent} is valid only synchronously.
|
|
23
|
+
* @example
|
|
24
|
+
* // plugins/disabled/nextmap/src/plugin.ts:133
|
|
25
|
+
* ctx.events.on("round_end", () => console.log("round ended"));
|
|
26
|
+
*/
|
|
20
27
|
on(name: string, handler: (ev: GameEvent) => void): void;
|
|
28
|
+
/** Pre-hook a game event: return a {@link HookResultValue} (`Handled`/`Stop` suppress the client broadcast). */
|
|
21
29
|
onPre(name: string, handler: (ev: GameEvent) => HookResultValue | void): void;
|
|
22
30
|
}
|
|
23
31
|
/**
|
|
@@ -27,37 +35,73 @@ export interface CtxEvents {
|
|
|
27
35
|
* out of its real order).
|
|
28
36
|
*/
|
|
29
37
|
export interface CtxClients {
|
|
38
|
+
/** A client began connecting (pre-auth). */
|
|
30
39
|
onConnect(handler: (client: Client) => void | Promise<void>): void;
|
|
40
|
+
/** A client's entity was put in the server (`ClientPutInServer`). */
|
|
31
41
|
onPutInServer(handler: (client: Client) => void | Promise<void>): void;
|
|
42
|
+
/** A client became fully active (in-game, receiving snapshots). */
|
|
32
43
|
onActive(handler: (client: Client) => void | Promise<void>): void;
|
|
44
|
+
/** A client finished authenticating (Steam ticket validated). */
|
|
33
45
|
onFullyConnect(handler: (client: Client) => void | Promise<void>): void;
|
|
46
|
+
/** A client disconnected. */
|
|
34
47
|
onDisconnect(handler: (client: Client) => void): void;
|
|
48
|
+
/** A client's convars/settings changed (`ClientSettingsChanged`). */
|
|
35
49
|
onSettingsChanged(handler: (client: Client) => void): void;
|
|
50
|
+
/** A client sent a voice packet (per-frame while speaking). */
|
|
36
51
|
onVoice(handler: (client: Client) => void): void;
|
|
52
|
+
/** A client's persisted cookies finished loading and are now readable. */
|
|
37
53
|
onCookiesCached(handler: (client: Client) => void): void;
|
|
54
|
+
/** A client sent chat: return a {@link HookResultValue} to suppress it. @param teamonly - team-channel say. */
|
|
38
55
|
onSay(handler: (slot: number, text: string, teamonly: boolean) => HookResultValue | void): void;
|
|
56
|
+
/** Per-tick usercmd hook (SM `OnPlayerRunCmd`): read/modify {@link UserCmdView}; return `Handled` to block the tick. */
|
|
39
57
|
onRunCmd(handler: (cmd: UserCmdView, info: { slot: number }) => HookResultValue | void): void;
|
|
40
58
|
}
|
|
59
|
+
/** Entity lifecycle + damage subscriptions on this plugin's load-scope ({@link PluginContext.entities}). */
|
|
41
60
|
export interface CtxEntities {
|
|
61
|
+
/** An entity of `className` was created (not yet spawned). @param className - match, or `"*"` for all. */
|
|
42
62
|
onCreate(className: string, handler: (entity: EntityRef | null, className: string) => void): void;
|
|
63
|
+
/** An entity of `className` spawned (post-`DispatchSpawn`). */
|
|
43
64
|
onSpawn(className: string, handler: (entity: EntityRef | null, className: string) => void): void;
|
|
65
|
+
/** An entity of `className` is being deleted; the ref goes stale right after. */
|
|
44
66
|
onDelete(className: string, handler: (entity: EntityRef | null, className: string) => void): void;
|
|
67
|
+
/** Hook a named entity output (`FireOutputInternal`); return a {@link HookResultValue} to suppress it. */
|
|
45
68
|
onOutput(classname: string, output: string, handler: (ev: OutputEvent) => HookResultValue | void): void;
|
|
69
|
+
/**
|
|
70
|
+
* Damage pre-hook (SDKHooks-equivalent): read/modify {@link DamageInfo}; return `Handled` semantics via `info`.
|
|
71
|
+
* @example
|
|
72
|
+
* // plugins/basecommands/src/plugin.ts:81 — halve incoming damage
|
|
73
|
+
* ctx.entities.onDamage((info) => { info.damage = info.damage / 2; });
|
|
74
|
+
*/
|
|
46
75
|
onDamage(handler: (info: DamageInfo) => HookResultValue | void): void;
|
|
47
76
|
}
|
|
77
|
+
/** Per-frame + map/precache hooks on this plugin's load-scope ({@link PluginContext.server}). */
|
|
48
78
|
export interface CtxServer {
|
|
79
|
+
/** Run `fn` every game frame. @param opts - `priority` orders it within the frame (`monitor` runs last, read-only). */
|
|
49
80
|
onGameFrame(fn: () => void, opts?: { priority?: "high" | "normal" | "low" | "monitor" }): void;
|
|
81
|
+
/** A new map became live; `mapName` is the BSP name. */
|
|
50
82
|
onMapStart(handler: (mapName: string) => void): void;
|
|
83
|
+
/** Precache window — register models/sounds to precache for the current map. */
|
|
51
84
|
onPrecache(handler: (pc: PrecacheContext) => void): void;
|
|
52
85
|
}
|
|
86
|
+
/** Console/chat command registration on this plugin's load-scope ({@link PluginContext.commands}). */
|
|
53
87
|
export interface CtxCommands {
|
|
88
|
+
/** Register a public command (any client may run it). */
|
|
54
89
|
register(name: string, handler: (cmd: CommandInvocation) => void): void;
|
|
90
|
+
/** Register a server-only command (console/rcon, not client-runnable). */
|
|
55
91
|
registerServer(name: string, handler: (cmd: CommandInvocation) => void): void;
|
|
92
|
+
/** Register an admin command gated by `flags` (an `ADMFLAG` bitmask; fail-safe default-deny). */
|
|
56
93
|
registerAdmin(name: string, flags: number, handler: (cmd: CommandInvocation) => void): void;
|
|
57
94
|
}
|
|
58
|
-
|
|
95
|
+
/** Config live-reload subscription on this plugin's load-scope ({@link PluginContext.config}). */
|
|
96
|
+
export interface CtxConfig {
|
|
97
|
+
/** Fires when the plugin's config file is re-materialized on disk; re-read values inside. */
|
|
98
|
+
onChange(handler: (cfg: Config) => void): void;
|
|
99
|
+
}
|
|
100
|
+
/** TopMenu (adminmenu) contribution on this plugin's load-scope ({@link PluginContext.topmenu}). */
|
|
59
101
|
export interface CtxTopMenu {
|
|
102
|
+
/** Add (or reuse) a top-level menu category. */
|
|
60
103
|
addCategory(name: string): void;
|
|
104
|
+
/** Add an item under an existing category. */
|
|
61
105
|
addItem(category: string, item: TopMenuItem): void;
|
|
62
106
|
}
|
|
63
107
|
|
|
@@ -67,29 +111,49 @@ export type InterfaceHandle<T extends object> = T & {
|
|
|
67
111
|
on(event: string, handler: (payload: any) => void): void;
|
|
68
112
|
};
|
|
69
113
|
|
|
114
|
+
/**
|
|
115
|
+
* A disposable bundle of subscriptions ({@link PluginContext.createScope}). Registering through a scope
|
|
116
|
+
* lets you drop the whole group at once with {@link Scope.clear} without unloading the plugin.
|
|
117
|
+
*/
|
|
70
118
|
export interface Scope {
|
|
119
|
+
/** Game-event subscriptions bound to this scope. */
|
|
71
120
|
readonly events: CtxEvents;
|
|
121
|
+
/** Client-lifecycle subscriptions bound to this scope. */
|
|
72
122
|
readonly clients: CtxClients;
|
|
123
|
+
/** Entity/damage subscriptions bound to this scope. */
|
|
73
124
|
readonly entities: CtxEntities;
|
|
125
|
+
/** Per-frame/map subscriptions bound to this scope. */
|
|
74
126
|
readonly server: CtxServer;
|
|
75
127
|
/** Remove every subscription this scope holds; the scope stays usable (re-register on next open). */
|
|
76
128
|
clear(): void;
|
|
77
129
|
/** clear() + permanently retire the scope. Idempotent. */
|
|
78
130
|
dispose(): void;
|
|
131
|
+
/** True once {@link Scope.dispose} has run. */
|
|
79
132
|
readonly disposed: boolean;
|
|
80
133
|
}
|
|
81
134
|
|
|
135
|
+
/**
|
|
136
|
+
* The load-scoped context passed to a plugin's {@link PluginFactory}. Its sub-objects register every
|
|
137
|
+
* subscription; all registration is load-window only (buffered, armed at Active).
|
|
138
|
+
*/
|
|
82
139
|
export interface PluginContext {
|
|
83
140
|
/** This plugin's id (manifest `id`). */
|
|
84
141
|
readonly id: string;
|
|
85
142
|
/** The revived hot-reload handoff (the previous instance's `state()` return), or undefined. */
|
|
86
143
|
readonly previous: unknown;
|
|
144
|
+
/** Game-event subscriptions ({@link CtxEvents}). */
|
|
87
145
|
readonly events: CtxEvents;
|
|
146
|
+
/** Client-lifecycle subscriptions ({@link CtxClients}). */
|
|
88
147
|
readonly clients: CtxClients;
|
|
148
|
+
/** Entity/damage subscriptions ({@link CtxEntities}). */
|
|
89
149
|
readonly entities: CtxEntities;
|
|
150
|
+
/** Per-frame/map/precache subscriptions ({@link CtxServer}). */
|
|
90
151
|
readonly server: CtxServer;
|
|
152
|
+
/** Console/chat command registration ({@link CtxCommands}). */
|
|
91
153
|
readonly commands: CtxCommands;
|
|
154
|
+
/** Config live-reload subscription ({@link CtxConfig}). */
|
|
92
155
|
readonly config: CtxConfig;
|
|
156
|
+
/** TopMenu (adminmenu) contribution ({@link CtxTopMenu}). */
|
|
93
157
|
readonly topmenu: CtxTopMenu;
|
|
94
158
|
/** Publish this plugin's manifest-declared interface. Buffered; goes live at Active. */
|
|
95
159
|
publish<T extends object>(name: string, impl: T): PublishHandle;
|
|
@@ -101,12 +165,31 @@ export interface PluginContext {
|
|
|
101
165
|
createScope(): Scope;
|
|
102
166
|
}
|
|
103
167
|
|
|
168
|
+
/** Optional lifecycle hooks a {@link PluginFactory} may return to participate in unload + hot-reload. */
|
|
104
169
|
export interface PluginHooks {
|
|
105
170
|
/** Best-effort cleanup at unload (the ledger remains the teardown authority). */
|
|
106
171
|
onUnload?(): void;
|
|
107
172
|
/** Hot-reload handoff capture; JSON-serialized (EntityRef-aware) and revived as the next instance's ctx.previous. */
|
|
108
173
|
state?(): unknown;
|
|
109
174
|
}
|
|
175
|
+
/**
|
|
176
|
+
* A plugin's body: called once at load with the {@link PluginContext}, optionally returning
|
|
177
|
+
* {@link PluginHooks}. May be async (the load waits for it to settle).
|
|
178
|
+
*/
|
|
110
179
|
export type PluginFactory = (ctx: PluginContext) => void | PluginHooks | Promise<void | PluginHooks>;
|
|
111
|
-
|
|
180
|
+
/** The opaque artifact {@link plugin} returns; a module must `export default` it to be a valid plugin. */
|
|
181
|
+
export interface PluginDefinition {
|
|
182
|
+
/** Brand tag proving this object came from {@link plugin} (host-checked at load). */
|
|
183
|
+
readonly __s2plugin: 1;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Define a plugin from its factory. `export default` the result — the host calls the factory once at load.
|
|
187
|
+
* @example
|
|
188
|
+
* import { plugin } from "@s2script/sdk/plugin";
|
|
189
|
+
* // examples/greeter-plugin/src/plugin.ts:8
|
|
190
|
+
* export default plugin((ctx) => {
|
|
191
|
+
* const handle = ctx.publish("@demo/greeter", impl);
|
|
192
|
+
* ctx.server.onGameFrame(() => handle.emit("greeted", { slot: 0 }));
|
|
193
|
+
* });
|
|
194
|
+
*/
|
|
112
195
|
export declare function plugin(factory: PluginFactory): PluginDefinition;
|
package/plugins.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
/** @s2script/plugins — runtime plugin management (the SM `sm plugins` backend). NO runtime code (injected). */
|
|
2
|
+
|
|
3
|
+
/** A snapshot of one plugin's identity and lifecycle state, as returned by {@link Plugins.list}. */
|
|
2
4
|
export interface PluginInfo {
|
|
3
5
|
/** the plugin id (from its manifest). */
|
|
4
6
|
readonly id: string;
|
|
@@ -15,6 +17,13 @@ export interface PluginInfo {
|
|
|
15
17
|
*/
|
|
16
18
|
readonly state: "running" | "loading" | "waiting" | "failed" | "unloaded";
|
|
17
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Enumerate and drive the plugin set at runtime (list / load / unload / reload).
|
|
22
|
+
* @example
|
|
23
|
+
* import { Plugins } from "@s2script/sdk/plugins";
|
|
24
|
+
* // plugins/basecommands/src/plugin.ts:126 — sm plugins list
|
|
25
|
+
* for (const p of Plugins.list()) cmd.reply(p.id + " — " + p.state);
|
|
26
|
+
*/
|
|
18
27
|
export declare const Plugins: {
|
|
19
28
|
/** Every loaded/unloaded plugin. */
|
|
20
29
|
list(): PluginInfo[];
|
package/server.d.ts
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
/** @s2script/server — server control (run console commands, query map validity). NO runtime code (injected at load). */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Server console + globals: run commands, register/read/set cvars, and query map/time state.
|
|
5
|
+
* @example
|
|
6
|
+
* import { Server } from "@s2script/sdk/server";
|
|
7
|
+
* // plugins/basecommands/src/plugin.ts:46 — sm_map
|
|
8
|
+
* Server.command("changelevel " + map);
|
|
9
|
+
*/
|
|
2
10
|
export declare const Server: {
|
|
3
11
|
/** Run `cmd` at the server console (queued; executes next frame). */
|
|
4
12
|
command(cmd: string): void;
|