Начало работы
amxts запускает плагины для Counter-Strike 1.6, написанные на TypeScript,
внутри AMX Mod X, рядом с вашими плагинами на Pawn. Плагин — обычный файл
.ts: игроки — объекты, у событий есть типы, а редактор знает каждое поле
игры. На этой странице — проект, первый плагин, как перезагружать его во
время работы и чем язык отличается от привычного TypeScript.
Проект
Новый проект начинается с одной команды:
npm create amxts@latest
pnpm create amxts
yarn create amxts
bun create amxts
Она спрашивает менеджер пакетов, папку, модули, oxlint и oxfmt, git и папку
сервера, пишет проект с первым плагином и его тестом, всё ставит и говорит,
что запускать дальше. npx amxts init в папке — та же команда; у каждого вопроса
есть и флаг (команда amxts).
Проект — папка с плагинами и одним файлом настроек, amxts.config.ts, где
перечислены модули проекта и их настройки:
my-server/
├── package.json
├── amxts.config.ts
├── tsconfig.json { "extends": "./.amxts/tsconfig.json" }
├── .env AMXTS_SERVER: сервер, на который выкладывает dev
├── plugins/
│ └── hello.ts
└── test/
└── hello.test.ts
// amxts.config.ts
export default defineConfig({
modules: [
"@amxts/menu-core",
"@amxts/config-core", // needed by menu-core
],
menus: { file: "my-server/menu" },
});
defineConfig глобальная — импорт не нужен. modules перечисляет
пакеты-модули проекта; настройки каждого модуля пишутся под его ключом
(menus — у menu-core), и редактор проверяет их по модулю. pluginsDir
("plugins") и outDir ("dist") говорят, где плагины и куда пишет сборка;
target ("rehlds") — для какого сервера проект: его include сборка читает,
когда сервера нет (include сервера); imports и
pawn — чем плагин пользуется без строки импорта и какие модули собираются
(автоимпорты).
Другая сторона — свой модуль.
npx amxts module add menu-core # ставит модуль и вписывает его в amxts.config.ts вместе с теми, что ему нужны
npx amxts build # плагины и плагины модулей в dist/, с plugins.ini
npx amxts build --deploy # и копирует их на сервер
npx amxts dev # собирает, выкладывает и снова — на каждое сохранение
npx amxts typecheck # проверяет проект так же, как редактор
npx amxts test # тесты проекта, на поддельном сервере
pnpm amxts module add menu-core # ставит модуль и вписывает его в amxts.config.ts вместе с теми, что ему нужны
pnpm amxts build # плагины и плагины модулей в dist/, с plugins.ini
pnpm amxts build --deploy # и копирует их на сервер
pnpm amxts dev # собирает, выкладывает и снова — на каждое сохранение
pnpm amxts typecheck # проверяет проект так же, как редактор
pnpm amxts test # тесты проекта, на поддельном сервере
yarn amxts module add menu-core # ставит модуль и вписывает его в amxts.config.ts вместе с теми, что ему нужны
yarn amxts build # плагины и плагины модулей в dist/, с plugins.ini
yarn amxts build --deploy # и копирует их на сервер
yarn amxts dev # собирает, выкладывает и снова — на каждое сохранение
yarn amxts typecheck # проверяет проект так же, как редактор
yarn amxts test # тесты проекта, на поддельном сервере
bunx amxts module add menu-core # ставит модуль и вписывает его в amxts.config.ts вместе с теми, что ему нужны
bunx amxts build # плагины и плагины модулей в dist/, с plugins.ini
bunx amxts build --deploy # и копирует их на сервер
bunx amxts dev # собирает, выкладывает и снова — на каждое сохранение
bunx amxts typecheck # проверяет проект так же, как редактор
bunx amxts test # тесты проекта, на поддельном сервере
В package.json проекта они есть и скриптами: npm run dev,
npm run build, npm test. Все команды и их параметры —
команда amxts.
plugins.ini сборка пишет сама: сначала модули, которыми пользуются плагины,
каждый после тех, что ему нужны, потом плагины проекта. Ещё она пишет
.amxts/tsconfig.json, который расширяет tsconfig.json проекта, и
.amxts/imports.d.ts: так редактор знает API ядра (@amxts/core), модули по
именам, то,
чем плагин пользуется без импорта (автоимпорты), и
настройки в amxts.config.ts (npx amxts prepare пишет только их).
Подсказки редактора — у print, Player, событий, полей, функций модулей —
на английском, а с AMXTS_DOCS_LANG=ru в .env проекта — на русском;
переключает их npx amxts prepare (и каждая сборка). Русские слова — копия
API в .amxts/api/, которую читает только редактор: пакеты в node_modules
остаются такими, какими их поставили, а плагины собираются одинаково на любом
языке.
Первый плагин
plugin({ name: "Hello", version: "1.0.0", author: "you", description: "An example" });
server.addCommand("/hp", ({ player }) => sayHp(player));
server.addEventListener("putInServer", (event) => {
print(0, `${event.player.name} joined`);
});
function sayHp(player: Player) {
print(player, `${player.name}, your HP: ${player.health}`);
if (player.health < 50) player.health = 100;
}
Файл кладётся в plugins/ проекта, затем — npx amxts build --deploy.
Плагин работает и без проекта: файл .ts в addons/amxts/plugins, с именем в
plugins.ini, сервер компилирует сам — и снова при каждом сохранении, без
смены карты. Что нужно серверу и куда ложится каждый файл —
установка на сервер.
Горячая перезагрузка
npx amxts dev
pnpm amxts dev
yarn amxts dev
bunx amxts dev
dev собирает и выкладывает все плагины, затем следит за папкой ваших
плагинов и модулями проекта. Когда вы сохраняете файл, она пересобирает плагины, которые его
импортируют, копирует их на сервер и перезагружает запущенный сервер через
rcon:
✔ my-plugin · 4.1s
✔ 14:03:40 plugins/my-plugin.ts changed: deployed, the server reloaded my-plugin (4.2s)
Изменение общего файла пересобирает все плагины, которые его импортируют.
Ошибка компиляции выводится как file.ts:строка:столбец - сообщение, над
строкой, на которую она указывает. На сервере остаётся последняя удачная
сборка.
AMXTS_SERVERв.envуказывает, где сервер, как и для--deploy. Без негоdevспрашивает папку и записывает её туда.- Плагины компилируются под систему сервера, Windows или Linux, — сборка
видит её в этой папке (
hlds.exeилиhlds_linux). Для сервера, которого она не видит, это говоритAMXTS_SERVER_OS=linuxв.envили--os linux. - Перезагрузка идёт через rcon на
127.0.0.1:27015(другой порт задаётся черезAMXTS_PORT). Пароль берётся изrcon_passwordвcstrike/server.cfgсервера. Без пароля модуль всё равно сам перезагружает изменённый.aot, но его ответа вы не увидите. - Новый плагин добавляется в
plugins.iniсервера и загружается вместе с перезагрузкой. Без пароля rcon он загрузится со следующей перезагрузкой или сменой карты. - Если сервер не запущен, команда только собирает и выкладывает плагины. Она никогда не запускает и не останавливает hlds.
- Для сервера в Docker
npx amxts dev --dockerсобирает вdist/, который читает сервер, и сервер перезагружает плагины сам.
Разделы
| страница | что внутри |
|---|---|
| Плагин | plugin, команды, события, таймеры, чат |
| Меню: menu-core | меню для сервера: из файлов, с условиями, плейсхолдерами и списками — npx amxts module add menu-core |
| Плагин: меню | быстрое меню в коде: new Menu("!yМагазин"), addItem({ title, onSelect }), show(player); нативы меню AMX Mod X |
| Игроки и сущности | Player, Entity: свойства с типами |
| Игроки и сущности | Vector, Entity.findAll / create / remove |
| Игроки: действия | server.players, hasModule, команда, действия (give, respawn, …) |
| Флаги | флаги как массивы имён: player.hideHud = ["money"] |
| Эффекты | временные эффекты — лучи, взрывы, искры: effects.beamCylinder({ ... }) |
| Квары | настройки сервера: new Cvar("mp_freezetime"), .number, событие "change" |
| События игры | события игры (хукчейны ReAPI и Ham Sandwich): game.addEventListener("takeDamage", ...) |
| Форварды | свои форварды для других плагинов: new Forward<number>("name").emit(1), .subscribe(...) |
| Async | async/await, Promise, sleep, отмена через AbortSignal |
| Хранилище | хранилище между картами как Map: new Storage("name").get(key) |
| HTTP | веб-запросы: useFetch<T>(url) читает JSON в интерфейс; fetch, URL — как в браузере |
| Файлы | файлы как в Node: fs.readFileSync, fs.writeFile, fs.readdir |
| Нативы | свои нативы для Pawn-плагинов: export function |
| Нативы | нативы AMXX/ReAPI напрямую, когда в @amxts/core этого нет |
| Общие модули | модули: один экземпляр на сервере для всех плагинов, которые им пользуются |
| Тесты | тесты плагина на поддельном сервере, через bun test |
| Команда amxts | команда amxts: init, dev, build, module add, test, info |
| Установка на сервер | amxts на игровом сервере: модуль, addons/amxts, список плагинов |
| Сервер в Docker | сервер на Linux в Docker со смонтированным проектом: amxts dev --docker |
| Ограничения | чего amxts пока не умеет и что писать вместо этого |
Главное о языке
Плагин — это TypeScript, заранее скомпилированный в машинный код. Почти всё работает так, как вы привыкли; вот отличия, которые встречаются первыми:
number— число JavaScript:7 / 2— это3.5,`${hp}`— это"100", побитовые операции берут его как 32-битное целое. Для нативов числа переводить не нужно: целочисленное поле или аргумент просто отбрасывает дробную часть.- Юнион — из строковых литералов или с
null:"CT" | "TERRORIST",Player | null,number | undefined.number | stringне собирается. - Замыкания работают как в JavaScript: обработчик или таймер
пользуется переменными вокруг, цикл
for (let ...)даёт каждому замыканию своёi, аthisв стрелке — этоthisметода. - Настройки — интерфейс с необязательными полями: незаданное поле — это
undefined,??даёт значение по умолчанию,?.читает сквозь отсутствующий объект, а деструктуризация принимает значения по умолчанию:interface GreetOptions {times?: number;loud?: boolean;onDone?: (player: Player) => void; } function greet(player: Player, {times= 1,loud= false }: GreetOptions = {}) { for (leti= 0;i<times;i++)print(player,loud? "ПРИВЕТ!" : "Привет"); } function farewell(player: Player,options: GreetOptions = {}) { if (options.times!==undefined)print(player, `${options.times} раз`);options.onDone?.(player); } catchполучаетError:throwпринимаетErrorили строку, аcatch (error)читаетerror.messageбез приведения. Ошибка, которую не поймал ни одинtry, завершает текущий вызов с сообщением в консоли сервера, а плагин работает дальше.- В
Recordможет не быть нужного ключа: вRecord<string, number>ключ, который не задавали, читается какundefined, поэтому значения там —number | undefined; значение по умолчанию даёт??. УRecord, чьи ключи все выписаны, —Record<"red" | "blue", number>, — есть все они.
Чего ещё язык в плагине пока не умеет и что писать вместо этого — в Ограничениях.
Введение
amxts позволяет писать плагины AMX Mod X для Counter-Strike 1.6 на TypeScript. Плагин — обычный файл .ts: привычные типы, классы, замыкания, async/await, события, на которые подписываетесь. Он работает на сервере рядом с вашими плагинами на Pawn, и они вызывают друг друга.
CLI
amxts — команда, которой пользуется проект: создаёт проекты и модули, добавляет модули, собирает, выкладывает, проверяет типы и запускает тесты. Это пакет @amxts/cli, от которого зависит @amxts/core, поэтому она есть в каждом проекте и проект запускает её через свой менеджер пакетов: