The amxts command
amxts is the command a project runs: it creates projects and modules, adds
modules, builds, deploys, type-checks and tests. It is the package
@amxts/cli, which @amxts/core depends on, so every project has it and runs
it through its package manager:
npx amxts <command>
pnpm amxts <command>
yarn amxts <command>
bunx amxts <command>
The command works with the project's own core, the @amxts/core in its
node_modules: a core older or newer than the command knows is an error that
says which of the two to update. Installed globally
(npm install -g @amxts/cli), amxts works the same way in any project, and
init anywhere.
amxts --help lists the commands, amxts <command> --help shows a
command's options with examples. A mistyped command gets a suggestion:
$ npx amxts buidl
✖ Unknown command buidl
Did you mean amxts build?
An error is one line and a hint under it. --debug adds where it happened,
for a bug report.
Commands
| command | what it does |
|---|---|
init | creates a project, or a module package with --module |
dev | builds, deploys to the server and does it again on every save |
build | builds the plugins and the modules into dist/ |
typecheck | checks the plugins and amxts.config.ts as the editor does |
test | runs the project's tests on a fake server |
module | module add installs a module and lists it in the config; module list shows them |
check | in a module package: checks it is ready to publish |
prepare | gets the server's includes and writes .amxts/ for the editor |
upgrade | moves the project to another amxts release: the packages, the code, the build and the server |
info | versions and settings, for a bug report |
init
Creates a project. It asks, in this order: the package manager (the one that
runs the command is picked), the folder, the modules (the
modules catalog's), oxlint and oxfmt, git,
the server's folder for dev and, when that server's includes do not say,
which server the project is for. Then it writes the files, installs the
dependencies, gets the server's includes and says
what to run next:
npm create amxts@latest
pnpm create amxts
yarn create amxts
bun create amxts
npm create amxts and npx amxts init are the same command. What it asks
looks like this:
┌ amxts 0.2.0 · a new project
│
◇ Which package manager?
│ npm
│
◇ Where should the project go?
│ my-server
│
◇ Which modules? (space to pick, enter to go on)
│ menu-core
│
◇ Add oxlint and oxfmt? (lint and format)
│ Yes
│
◇ Initialize a git repository?
│ Yes
│
◇ Where is the server? its addons/amxts folder, for npm run dev - empty to skip
│ D:/hlds/cstrike/addons/amxts
│
◆ Created my-server in my-server
│
● config-core is listed too: menu-core needs it
│
◇ Installed dependencies with npm 11.16.0
│
◆ Includes: the server's own (D:/hlds/cstrike/addons/amxmodx/scripting/include)
│
◆ Initialized a git repository
│
◆ Prepared the editor config (.amxts/tsconfig.json)
│
◇ Next steps ──────────────────────────────────────╮
│ │
│ cd my-server │
│ npm run dev # build, deploy, rebuild on save │
│ │
├───────────────────────────────────────────────────╯
│
└ Docs: https://amxts.github.io/docs/getting-started/quick-start
Every question is also a flag, and --yes takes the defaults for the rest -
for a script or CI:
npx amxts init my-server --modules menu-core,config-core --no-git --server D:/hlds/cstrike/addons/amxts --yes
pnpm dlx amxts init my-server --modules menu-core,config-core --no-git --server D:/hlds/cstrike/addons/amxts --yes
yarn dlx amxts init my-server --modules menu-core,config-core --no-git --server D:/hlds/cstrike/addons/amxts --yes
bunx amxts init my-server --modules menu-core,config-core --no-git --server D:/hlds/cstrike/addons/amxts --yes
| option | what it does |
|---|---|
[folder] | where the project goes; my-server by default |
--pm <npm|pnpm|yarn|bun> | the package manager; the one running the command by default |
--modules <list> | the modules, comma-separated, by their names in the catalog or as npm packages: menu-core,config-core; "" for none |
--no-lint | no oxlint and oxfmt (they are added by default) |
--no-git | no git init |
--server <path> | the server: its folder, its cstrike or its addons/amxts; the addons/amxts is written to .env as AMXTS_SERVER, and the build takes the includes installed there |
--target <rehlds|hlds> | the server the project is for, when no server's includes say it: rehlds - ReHLDS, ReGameDLL and ReAPI (by default) - or hlds, plain HLDS |
--os <windows|linux> | the server's system, when its folder does not show it: written to .env as AMXTS_SERVER_OS (the server's system) |
--no-install | write the files, do not install |
--yes, -y | ask nothing |
--force | write into a folder that is not empty |
--local | link the core and the official modules from their folders on this machine instead of installing them from npm, for working on amxts itself; AMXTS_CORE names the core's folder |
A module that needs another brings it: with menu-core, config-core is
installed too, and modules lists it right after menu-core, with a comment
that says which module needs it. The build loads it first:
// amxts.config.ts
export default defineConfig({
modules: [
"@amxts/menu-core",
"@amxts/config-core", // needed by menu-core
],
target: "rehlds",
});
The project it writes:
my-server/
├── package.json scripts: dev, build, typecheck, test, lint
├── amxts.config.ts the modules you picked, and the ones they need
├── tsconfig.json { "extends": "./.amxts/tsconfig.json" }
├── .oxlintrc.json the lint rules: @antfu/eslint-config's, run by oxlint
├── .oxfmtrc.json oxfmt, for the JSON and YAML files
├── knip.json entry points for knip: plugins, the config, the tests
├── .env AMXTS_SERVER, when you gave the folder; AMXTS_SERVER_OS
├── pnpm-workspace.yaml with pnpm: bun's install script is not needed
├── .gitignore
├── .gitattributes LF for the files oxfmt formats
├── .vscode/ settings and the recommended extensions
├── README.md
├── plugins/
│ └── hello.ts a plugin that uses the modules you picked
└── test/
├── hello.test.ts its tests, on the fake server
└── tsconfig.json the tests' own: Bun's types, not the plugins'
The server's includes
Where a plugin needs a Pawn include - an include it implements,
a Pawn plugin's natives or forwards, a Pawn test - the build looks in the
project's includes/ first, then in the server's includes, then in the ones
amxts ships (AMX Mod X's own among them). The server's includes are:
- with
AMXTS_SERVER, the server's ownaddons/amxmodx/scripting/include, exactly what is installed there; - without a server, the includes of the server
targetinamxts.config.tsnames: for"rehlds"- ReHLDS, ReGameDLL and ReAPI, the default - amxts downloads ReAPI's includes into.amxts/include, the release its API is made from;"hlds"- plain HLDS - needs nothing more than AMX Mod X's own.
// amxts.config.ts
export default defineConfig({
modules: [],
target: "hlds",
});
init asks the target when there is no server, or the server's folder has no
includes; --target answers it. prepare - after every install - and every
build fetch the includes when they are not there yet.
npx amxts prepare, or point AMXTS_SERVER at a
server with its includes.lint checks the code with oxlint, by @antfu/eslint-config's rules, and
the JSON and YAML files with oxfmt; lint:fix fixes what they can - those
rules format TypeScript too. In VS Code the oxc extension, which
.vscode/extensions.json recommends, does the same as you type and save.
A module package
--module creates a module package instead - a working module with its
playground project and a test - see creating a module:
npx amxts init --module @you/greeter # ./greeter
npx amxts init --module @you/greeter --natives # with natives for Pawn plugins
npx amxts init --module greeter --dir my-greeter # into ./my-greeter
pnpm dlx amxts init --module @you/greeter # ./greeter
pnpm dlx amxts init --module @you/greeter --natives # with natives for Pawn plugins
pnpm dlx amxts init --module greeter --dir my-greeter # into ./my-greeter
yarn dlx amxts init --module @you/greeter # ./greeter
yarn dlx amxts init --module @you/greeter --natives # with natives for Pawn plugins
yarn dlx amxts init --module greeter --dir my-greeter # into ./my-greeter
bunx amxts init --module @you/greeter # ./greeter
bunx amxts init --module @you/greeter --natives # with natives for Pawn plugins
bunx amxts init --module greeter --dir my-greeter # into ./my-greeter
dev
npx amxts dev
pnpm amxts dev
yarn amxts dev
bunx amxts dev
Builds every plugin and module, copies them to the server in AMXTS_SERVER,
reloads it, then watches the plugins folder and the project's modules: a save
rebuilds the plugins that import the saved file.
amxts 0.2.0 · dev
Core @amxts/core 0.2.0
Modules menu-core 0.2.0 · config-core 0.1.1
Server D:/hlds/cstrike/addons/amxts · Windows · ReHLDS · running
Plugins 1 in plugins/
✔ hello · 6.2s
✔ 14:02:11 deployed, the server reloaded config-core, menu-core, hello (7.1s)
◇ watching plugins - save a plugin and it goes to the server (Ctrl+C stops)
✔ hello · 4.1s
✔ 14:03:40 plugins/hello.ts changed: deployed, the server reloaded hello (4.2s)
It opens with what it works with: the core and the modules with their
versions - one no
plugin uses says so (no plugin uses it), one from a folder on this machine
rather than from npm is local - then the server - its folder, its system
(below), ReHLDS or plain HLDS, and whether it is
running - and how many plugins there are.
Each plugin and module is shown as it compiles: ◇ compiling hello 1/1
while it does, ✔ hello · 6.2s when it is done - in a terminal the one line
turns into the other, in a log (CI) both stay. A plugin goes through two
compilers, to WebAssembly and then to machine code, and that is what a build
spends its time on. So:
- the official modules come compiled for Windows and Linux in their npm
packages, and a build takes them as they came, with nothing to compile. When the project
would compile one differently - its options set in
amxts.config.ts, a core of another version - the build says why (menu-core 0.2.0 was built for @amxts/core 0.2.0, the project has 0.2.1 - compiling it here) and compiles it, once. A plugin that uses a module compiles against what the module came with, too, rather than reading the module through first; - a plugin nothing has changed for since the last build - not its file, not
what it imports - is not compiled again: it is kept from
before in
node_modules/.cache/amxts. Adevstarted again goes to the server in seconds; - several plugins compile at once, each in a process of its own: as many as
fit in 3 GB of memory - a compile takes about one - and no more than the
machine has processor cores. In a terminal the line names them all:
◇ compiling hello, shop 2/3.AMXTS_BUILD_MEMORYin.envor the environment sets the memory in megabytes (AMXTS_BUILD_MEMORY=6144),AMXTS_BUILD_JOBSthe number of compiles outright (1: one at a time). A plugin comes out the same either way.
A save rebuilds only when the file holds something new - an editor that saves a file as it was, or a package manager that writes a module again, is not a change - and the line names the file.
dev compiles for speed: a small plugin in a few seconds, where build
takes about ten. Its plugins do the same, a little slower - the server
people play on gets npx amxts build --deploy.
AMXTS_SERVER is the addons/amxts folder of a server with amxts
(installing on a server), set in the project's .env:
AMXTS_SERVER=D:/hlds/cstrike/addons/amxts
It may also name the server's own folder (D:/hlds), its cstrike or
cstrike/addons: the build takes cstrike/addons/amxts under it, and the
Server line says which folder that is. A folder that is none of them stops
the build with one line naming where it looked.
Without it dev and build --deploy ask for the folder, as init does,
and write it to .env. In CI, or with the output in a pipe, nothing is
asked: the build stops and says AMXTS_SERVER is not set.
When the server's module is of another release than the project's core,
dev and build say so first - the plugins a project builds load only on
the module of its own release:
▲ the server runs amxts 0.1.0, this project 0.2.0 - run amxts upgrade
upgrade updates the server too. The release is read from the
module file, not from the running server.
The reload goes over rcon to 127.0.0.1:27015 (AMXTS_PORT sets another
port), with rcon_password from the server's cstrike/server.cfg. See
hot reload. --os is as for build.
npx amxts dev --docker
pnpm amxts dev --docker
yarn amxts dev --docker
bunx amxts dev --docker
The same for the server in Docker that mounts the project: it
builds for Linux into dist/ and again on every save, and deploys nothing -
the server reads dist/ and reloads a plugin whose file changed - and it
shows the amxts lines of that server's console under the build.
AMXTS_SERVER is not used.
build
npx amxts build # dist/: the .aot files and plugins.ini
npx amxts build --deploy # and copies them to AMXTS_SERVER, reloading the server
npx amxts build --os linux # for a Linux server
npx amxts build --watch # and again on every save, without deploying
pnpm amxts build # dist/: the .aot files and plugins.ini
pnpm amxts build --deploy # and copies them to AMXTS_SERVER, reloading the server
pnpm amxts build --os linux # for a Linux server
pnpm amxts build --watch # and again on every save, without deploying
yarn amxts build # dist/: the .aot files and plugins.ini
yarn amxts build --deploy # and copies them to AMXTS_SERVER, reloading the server
yarn amxts build --os linux # for a Linux server
yarn amxts build --watch # and again on every save, without deploying
bunx amxts build # dist/: the .aot files and plugins.ini
bunx amxts build --deploy # and copies them to AMXTS_SERVER, reloading the server
bunx amxts build --os linux # for a Linux server
bunx amxts build --watch # and again on every save, without deploying
plugins.ini lists the modules first, each after what it requires, then the
project's plugins. A module no plugin uses is left out, unless pawn keeps it
(only what is used is built).
outDir in amxts.config.ts changes where it writes. A plugin nothing has
changed for since the last build is taken as it was built, an official module as it came
(dev); node_modules/.cache/amxts removed, every one compiles again.
The server's system
A plugin is compiled to machine code for its server's system, Windows or
Linux, and a .aot built for one does not load on the other. The build
takes the system from, first to last:
--os windowsor--os linuxonbuildordev;AMXTS_SERVER_OSin the environment or.env;- the
AMXTS_SERVERfolder:hlds_linuxorhlds.exebeside the game folder, else the files in itsaddons/amxmodx/modules; - the system of the machine the build runs on.
The Server line of dev and build says which: Linux. So a project
built on Windows deploys to a Linux server as it is, once
AMXTS_SERVER points at it - a mounted share, say - or .env says
AMXTS_SERVER_OS=linux. init asks when the server's folder does not show
it, and info shows what the build would pick.
A
.aot built for the other system does not load, and the console says so:
was compiled for a Linux server, and this one runs Windows (or the other
way). A .ts the server compiles itself is always built for it.typecheck
npx amxts typecheck
pnpm amxts typecheck
yarn amxts typecheck
bunx amxts typecheck
Checks the plugins and amxts.config.ts with TypeScript, as the editor
does: a misspelled option of a module, a wrong event name, a field that is
not there.
test
npx amxts test # every test in the project
npx amxts test hello # the files whose name has "hello"
npx amxts test --watch # again on every save
pnpm amxts test # every test in the project
pnpm amxts test hello # the files whose name has "hello"
pnpm amxts test --watch # again on every save
yarn amxts test # every test in the project
yarn amxts test hello # the files whose name has "hello"
yarn amxts test --watch # again on every save
bunx amxts test # every test in the project
bunx amxts test hello # the files whose name has "hello"
bunx amxts test --watch # again on every save
Runs bun test: its options go to it as they are. A test puts the project on
a fake server with setup() - see testing.
module
npx amxts module add menu-core # @amxts/menu-core
npx amxts module add @you/greeter # any module package from npm
npx amxts module add ../greeter # a module in a folder
npx amxts module list
pnpm amxts module add menu-core # @amxts/menu-core
pnpm amxts module add @you/greeter # any module package from npm
pnpm amxts module add ../greeter # a module in a folder
pnpm amxts module list
yarn amxts module add menu-core # @amxts/menu-core
yarn amxts module add @you/greeter # any module package from npm
yarn amxts module add ../greeter # a module in a folder
yarn amxts module list
bunx amxts module add menu-core # @amxts/menu-core
bunx amxts module add @you/greeter # any module package from npm
bunx amxts module add ../greeter # a module in a folder
bunx amxts module list
module add installs the package with the project's package manager (found
by its lockfile) and adds it to modules in amxts.config.ts, followed by
the modules it needs that the list does not have yet, each with a
// needed by menu-core comment - only the list changes, the rest of the
file stays as you wrote it. A short name is a
module's name in the modules catalog: menu-core is
@amxts/menu-core. A package the catalog does not list is installed from npm
all the same, with a warning; a package that is not an amxts module is
installed but not added to the config. A module from npm comes at its newest
version that works with the project's core. The command reads the catalog
from the registry on GitHub, and offline takes the copy it was published
with.
| option | what it does |
|---|---|
--skip-install | only add it to amxts.config.ts |
--skip-config | only install it |
--local | an official module from its folder on this machine, linked, for working on amxts itself |
module list shows the modules in the config and whether each is installed,
the installed ones the config does not list, and the catalog's modules the
project does not have yet - a community module marked (community):
In amxts.config.ts
✔ @amxts/menu-core 0.2.0 An opinionated way to create menus
✔ @amxts/config-core 0.1.1 Configs in INI, YAML or JSON, read into typed objects and written back
In the amxts catalog
○ resemiclip Semiclip over the ReSemiclip module - who walks through whom, as a rule over two players - npx amxts module add resemiclip
○ ftp FTP, FTPS and SFTP - upload, download and list files on another server, every call a promise - npx amxts module add ftp
check
npx amxts check
pnpm amxts check
yarn amxts check
bunx amxts check
In a module's folder, before publishing it: its amxts field points at
files that are there, the module file has defineModule with meta.name,
README.md and LICENSE are there, and include/<name>.inc is what the
build writes from the natives. It writes nothing. See
creating a module.
prepare
npx amxts prepare
pnpm amxts prepare
yarn amxts prepare
bunx amxts prepare
Gets the server's includes when they are not there
yet, and writes what the editor reads into .amxts/:
.amxts/tsconfig.json, which the project'stsconfig.jsonextends:~/is the plugins folder,@amxts/coreand its entries are the core's API, the modules are found by their names, andamxts.config.tsis typed from them;.amxts/imports.d.ts: what plugins use without an import, as globals for the editor (auto-imports);.amxts/api/, only whenAMXTS_DOCS_LANGpicks a language other than English: a copy of the core's API and the modules' with the tooltips in that language, which the editor reads instead of the installed packages. The packages innode_modulesare never changed, and the build compiles them, so the plugins come out the same in every language.
build, dev and typecheck do it first; init does it after the install.
.amxts/ is the command's own: it is in .gitignore, and written again
whenever it is out of date.
upgrade since v0.2
npx amxts upgrade # to the latest release
npx amxts upgrade --to 0.2.0 # to a release of your choice
npx amxts upgrade --dry-run # what it would change, changing nothing
pnpm amxts upgrade # to the latest release
pnpm amxts upgrade --to 0.2.0 # to a release of your choice
pnpm amxts upgrade --dry-run # what it would change, changing nothing
yarn amxts upgrade # to the latest release
yarn amxts upgrade --to 0.2.0 # to a release of your choice
yarn amxts upgrade --dry-run # what it would change, changing nothing
bunx amxts upgrade # to the latest release
bunx amxts upgrade --to 0.2.0 # to a release of your choice
bunx amxts upgrade --dry-run # what it would change, changing nothing
::: warning The command first
upgrade is a command of @amxts/cli, and a project runs the version of it its core came with, or the one its own package.json names. A project whose command has no upgrade yet adds the newest command first, with its package manager:
npm i @amxts/cli@latest # or: bun add @amxts/cli@latest, pnpm add @amxts/cli@latest, yarn add @amxts/cli@latest
npx amxts upgrade
pnpm add @amxts/cli@latest # or: bun add @amxts/cli@latest, pnpm add @amxts/cli@latest, yarn add @amxts/cli@latest
pnpm amxts upgrade
yarn add @amxts/cli@latest # or: bun add @amxts/cli@latest, pnpm add @amxts/cli@latest, yarn add @amxts/cli@latest
yarn amxts upgrade
bun add @amxts/cli@latest # or: bun add @amxts/cli@latest, pnpm add @amxts/cli@latest, yarn add @amxts/cli@latest
bunx amxts upgrade
:::
Moves the project to another amxts release, in one go:
- The packages.
@amxts/coregoes to the latest version on the registry, or to--to. Every other@amxts/package inpackage.json- the official modules, the command - has a version of its own, and goes to its newest version that works with that core: a module names the cores it works with in itspeerDependencies. Each is written the way the project writes it:^0.1.0becomes^0.2.0,0.1.0becomes0.2.0. A package from a folder (file:) stays as it is; a module with no version for that core yet stops the upgrade before anything changes. The project's package manager - the one its lockfile names - installs them from the registry npm is set to (.npmrc,NPM_CONFIG_REGISTRY). - The code, rewritten to the new API by the new core, every change listed: the code, below.
- The build, as
amxts builddoes it. - The server in
AMXTS_SERVER: its module - and the compiler for.tswritten on the server, where it has one - from the GitHub Release of the new version, each file checked against the sha256 the release lists. The file it replaces stays beside it with its version:amxts_amxx.dll.0.1.0. A lineamxts_host.amxxin AMX Mod X'splugins.inigoes: the module loads its host plugin itself. With noAMXTS_SERVER- a server in Docker - it says which image to pull. - A summary: the versions, the files rewritten, what to check by hand and what happened to the server.
amxts 0.2.0 · upgrade
◇ Packages: amxts 0.2.0 (latest)
i @amxts/core ^0.1.0 → ^0.2.0
i @amxts/cli ^0.1.0 → ^0.2.0
i @amxts/menu-core ^0.1.0 → ^0.2.0
i @amxts/config-core ^0.1.0 → ^0.1.1
◇ Installing with npm
✔ Installed with npm
◇ Code
plugins/hello.ts:1 ~/natives → @amxts/core/natives
plugins/hello.ts:9 player.account → player.money
◇ Build
...
✔ built hello into dist (11.4s)
◇ Server: D:/hlds/cstrike (windows)
✔ amxts_amxx.dll 0.1.0 → 0.2.0 (the old one: amxts_amxx.dll.0.1.0)
✔ Took amxts_host.amxx out of D:/hlds/cstrike/addons/amxmodx/configs/plugins.ini: the module loads it itself
Upgraded to amxts 0.2.0
@amxts/core 0.1.0 → 0.2.0
@amxts/cli 0.1.0 → 0.2.0
@amxts/menu-core 0.1.0 → 0.2.0
@amxts/config-core 0.1.0 → 0.1.1
✔ rewritten: plugins/hello.ts
▲ to check by hand:
plugins/shop.ts:14 it reads the words after the name: write them in the usage, "/give <amount>", and take them by name, ({ player, amount })
✔ built
✔ server: amxts_amxx.dll 0.1.0 → 0.2.0 - restart the server to load it
| option | what it does |
|---|---|
--to <version> | the release to move to, instead of the latest |
--no-server | leave the server as it is |
--server-only | only the server's step - for a server that was running |
--dry-run | print what it would do: nothing is installed or written, and of the release only its list of files is read |
A dry run lists the rewrites only when the project has that version of the core already: before the install, the new core is not there to ask.
The command a project runs is the one its core came with. An amxts of an
older release installs the new packages, then hands the rest - the code, the
build, the server - to the project's new one.
A Windows server holds its module while it runs, and the file cannot be replaced then.
upgrade changes nothing on the server, says so, and does
the rest: stop the server, then run npx amxts upgrade --server-only. Either
system loads the new module on its next start.The code
The new core brings the project's code to its API, and lists every line it
changed. An import of the core's API by ~/ becomes one by the
package's name, and one of a module package by its place in the build
(~/modules/menu-core) one by the module's name:
import { user_slap } from "~/natives"; // before
import { user_slap } from "@amxts/core/natives"; // after
It reads the code with the TypeScript parser: imports, exports, import()
and declare module change; a string or a comment that only looks like an
import does not. Your own files under ~/ stay as they are. A second run
changes nothing. It goes through every .ts file of the project - the
plugins, the tests, a module's sources - but not node_modules and dist/.
A command's handler gets one object
(commands): (player) => becomes
({ player }) =>, and a function passed by its name and taking the player
is called from ({ player }) => name(player); (player, args) => that
never reads args becomes ({ player }) => too. A handler that reads the
words after the command's name is listed, with its line, to be written by
hand: the words go into the usage, "/give <amount>", and the handler takes
them by name.
menu-core's code is brought to the menu's context
(menus): addItem("text", options) becomes
addItem({ title: "text", ... }), and so does addFixedItem; (player) =>
and (player, target) => become ({ player }) => and
({ player, target }) => - in an item, a menu's options, a filter, a list
source and what is registered by name. A function passed by its name and
declared in the file is called from an arrow that hands it what it took.
Listed to change by hand: a function declared in another file, a menu's name
read as text, a target given as a number, and menus.runActions, which is a
menu's method (menu.runActions).
The names follow the API: Player.all() becomes
server.players, and its options
a filter - Player.all({ alive: true, team: "CT" }) is
server.players.filter(player => player.isAlive && player.team === "CT");
where the code has a player already, as a command's handler does, the
filter names its player other (or p), so it hides none. A
field, a method or a game event named in the engine's words gets the
player's: player.account becomes player.money, player.authidplayer.steamId, game.numCtWins game.ctWins, weapon.inReloadweapon.isReloading, the event restartRound newRound, and an event's
class follows its name. A value is
taken for a player, a weapon or the game where the file says it is one -
event.player, a Player, Client or Weapon annotation, { player } from an
event or a command, the first parameter of a command's handler, an element of server.players, player.activeItem,
game. Listed to change by hand: an old name on any other value, options that
are not written out, and a field or an event the API does not have, which the
natives in @amxts/core/natives still reach (get_member,
RegisterHookChain). A test's player joins with steamId too:
server.join("Alice", { authid }) becomes
server.join("Alice", { steamId: authid }).
A server event is named in the author's words
(server events):
server.addEventListener("putinserver", ...) becomes
server.addEventListener("putInServer", ...), "cfg" "pluginsLoaded",
"kill" "suicide", and a forward's own name ("client_putinserver") the
event's. One that is a game event is heard through game:
server.addEventListener("PreThink", ...) becomes
game.addEventListener("preThink", ...), and so do "PostThink",
"pfnTouch" ("touch") and "infochanged" ("userInfoChange"). A field
of an event named after its Pawn parameter gets the author's word where the
file says which event it is - in a listener, or a parameter with the event's
class: event.weapon_entity becomes event.weapon, event.authidevent.steamId, event.tracehandle event.trace, event.infobufferevent.info, and ({ dir }) becomes ({ direction: dir }). Listed to change by hand: cstrike's "CS_OnBuy"
and "CS_OnBuyAttempt", which are the game events buyWeapon, buyItem,
buyAmmo and itemRestricted.
A game message is heard through its own method, by its name in the player's
words (messages):
server.addEventListener("message:DeathMsg", ...) becomes
server.addMessageListener("death", ...), and removeEventListenerremoveMessageListener. A message name the game does not have is listed.
A flag's name is lowerCamelCase (flags):
player.buttons.includes("Jump") becomes player.buttons.includes("jump"),
{ access: "Kick" } { access: "kick" }. A string is taken for a flag
where the file says it is one: assigned to a flag property or the buttons
and access options, given to includes, push or concat of one or to
player.screen.hideHud, compared with an element of one - in its filter,
a for of, a switch - or held by a name of a flag type (Button,
HideHud[]). A name given where flags go is followed to the list it was
declared with; one the file does not say - a parameter without a type, an
import, a function's result - is listed.
info
npx amxts info
pnpm amxts info
yarn amxts info
bunx amxts info
What a bug report needs: the operating system, Node, Bun, the package
manager, the command, the core and its compilers, the project's modules,
AMXTS_SERVER and the system the plugins are compiled for.
amxts info - paste this into a bug report
Operating system Windows 10.0.19045 (x64)
Node.js 24.18.0
Bun 1.4.2
Package manager npm 11.16.0
@amxts/cli 0.2.0
@amxts/core 0.2.0
AssemblyScript 0.28.20 + amxts patch
WAMR 2.4.5 + amxts patch, wamrc found
Project my-server
Modules @amxts/menu-core 0.2.0
AMXTS_SERVER D:/hlds/cstrike/addons/amxts
Server system Windows (hlds.exe)
AMXTS_DOCS_LANG en
Quick start
amxts runs Counter-Strike 1.6 plugins written in TypeScript inside AMX Mod X, next to your Pawn plugins. A plugin is an ordinary .ts file: players are objects, events have types, and the editor knows every field the game has. This page shows a project, a first plugin, how to reload it while you work, and what is different from the TypeScript you already know.
Installing on a server
A game server runs amxts plugins with two things: the amxts_amxx module and the addons/amxts folder with the plugins. This page puts them on a server of your own. The other way is the server in Docker, which has all of it inside.