feat: add bds core, locale updates and safety fixes
This commit is contained in:
14
CHANGELOG.md
14
CHANGELOG.md
@@ -2,6 +2,19 @@
|
|||||||
|
|
||||||
All notable changes to AstraDeploy are documented here.
|
All notable changes to AstraDeploy are documented here.
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Bedrock Dedicated Server (`bds`) core support:
|
||||||
|
- `--core bds`
|
||||||
|
- version-aware install via official Bedrock download source
|
||||||
|
- `.bds-version` marker and update flow support
|
||||||
|
- Additional UI polish for beginner mode with BDS version prompt.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Improved `--force-clean` cleanup to clear hidden entries in server directories.
|
||||||
|
- Safer service unit generation for start-script paths containing special characters.
|
||||||
|
|
||||||
## [v0.1.0] - 2026-05-31
|
## [v0.1.0] - 2026-05-31
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
@@ -39,4 +52,3 @@ All notable changes to AstraDeploy are documented here.
|
|||||||
### Known limitations
|
### Known limitations
|
||||||
- Support matrix for OS/package-manager is intentionally MVP-focused (mainly Debian/Ubuntu family first, with extension points for others).
|
- Support matrix for OS/package-manager is intentionally MVP-focused (mainly Debian/Ubuntu family first, with extension points for others).
|
||||||
- `--update` path is implemented per-core where available.
|
- `--update` path is implemented per-core where available.
|
||||||
|
|
||||||
|
|||||||
22
README.md
22
README.md
@@ -46,6 +46,7 @@ Current version: `v0.1.0`.
|
|||||||
| `velocity` | `/opt/minecraft/velocity` | `25577` | Java 21 | TCP | Requires Minecraft Java EULA acceptance |
|
| `velocity` | `/opt/minecraft/velocity` | `25577` | Java 21 | TCP | Requires Minecraft Java EULA acceptance |
|
||||||
| `powernukkitx` | `/opt/minecraft/powernukkitx` | `19132` | Java 21 | UDP | Requires Minecraft Java EULA acceptance |
|
| `powernukkitx` | `/opt/minecraft/powernukkitx` | `19132` | Java 21 | UDP | Requires Minecraft Java EULA acceptance |
|
||||||
| `nethergamesmc` | `/opt/minecraft/nethergamesmc` | `19132` | PHP | UDP | Prompts for PHP build and PocketMine-MCPE version |
|
| `nethergamesmc` | `/opt/minecraft/nethergamesmc` | `19132` | PHP | UDP | Prompts for PHP build and PocketMine-MCPE version |
|
||||||
|
| `bds` | `/opt/minecraft/bds` | `19132` | native | UDP | Official Bedrock Dedicated Server zip binary |
|
||||||
|
|
||||||
> Service mode `screen` is available and handled in the service layer with the current installer version.
|
> Service mode `screen` is available and handled in the service layer with the current installer version.
|
||||||
|
|
||||||
@@ -112,9 +113,10 @@ Run output is mirrored with `tee` and saved to:
|
|||||||
|
|
||||||
### Core selection and versions
|
### Core selection and versions
|
||||||
|
|
||||||
- `--core <pmmp|paper|purpur|velocity|powernukkitx|nethergamesmc>`
|
- `--core <pmmp|paper|purpur|velocity|powernukkitx|nethergamesmc|bds>`
|
||||||
- `--mc-version <latest|version>`
|
- `--mc-version <latest|version>`
|
||||||
- `--build <latest|tag>` (for `nethergamesmc`, PHP build)
|
- `--build <latest|tag>` (for `nethergamesmc`, PHP build)
|
||||||
|
- `--mc-version` for `bds` resolves required Bedrock package version (`latest` allowed)
|
||||||
- legacy aliases: `--version`, `--core-version` for MC version
|
- legacy aliases: `--version`, `--core-version` for MC version
|
||||||
|
|
||||||
### Directory and runtime
|
### Directory and runtime
|
||||||
@@ -167,6 +169,24 @@ sudo bash install.sh \
|
|||||||
3. `PocketMine-MP` clone/build flow
|
3. `PocketMine-MP` clone/build flow
|
||||||
4. Server startup script generation
|
4. Server startup script generation
|
||||||
|
|
||||||
|
## Example for BDS
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo bash install.sh \
|
||||||
|
--easy \
|
||||||
|
--core bds \
|
||||||
|
--dir /opt/minecraft/bds \
|
||||||
|
--mc-version latest \
|
||||||
|
--service tmux-systemd
|
||||||
|
```
|
||||||
|
|
||||||
|
`bds` flow includes:
|
||||||
|
|
||||||
|
1. resolve version from official Bedrock download page or provided tag;
|
||||||
|
2. download `bedrock-server-<version>.zip`;
|
||||||
|
3. extract/update server files;
|
||||||
|
4. generate `start.sh` and configure service.
|
||||||
|
|
||||||
## Update mode
|
## Update mode
|
||||||
|
|
||||||
`--update` switches workflow to update mode. The installer validates existing directory first and replaces server artifacts via the core-specific update path where supported.
|
`--update` switches workflow to update mode. The installer validates existing directory first and replaces server artifacts via the core-specific update path where supported.
|
||||||
|
|||||||
24
README.ru.md
24
README.ru.md
@@ -46,6 +46,7 @@
|
|||||||
| `velocity` | `/opt/minecraft/velocity` | `25577` | Java 21 | TCP | Нужна явная обработка/подтверждение EULA Java |
|
| `velocity` | `/opt/minecraft/velocity` | `25577` | Java 21 | TCP | Нужна явная обработка/подтверждение EULA Java |
|
||||||
| `powernukkitx` | `/opt/minecraft/powernukkitx` | `19132` | Java 21 | UDP | Нужна явная обработка/подтверждение EULA Java |
|
| `powernukkitx` | `/opt/minecraft/powernukkitx` | `19132` | Java 21 | UDP | Нужна явная обработка/подтверждение EULA Java |
|
||||||
| `nethergamesmc` | `/opt/minecraft/nethergamesmc` | `19132` | PHP | UDP | Доп. вопросы по PHP build и версии PocketMine-MCPE |
|
| `nethergamesmc` | `/opt/minecraft/nethergamesmc` | `19132` | PHP | UDP | Доп. вопросы по PHP build и версии PocketMine-MCPE |
|
||||||
|
| `bds` | `/opt/minecraft/bds` | `19132` | native | UDP | Официальный бинарник Bedrock Dedicated Server |
|
||||||
|
|
||||||
> Режим `screen` доступен в текущей версии и настраивается через слой сервисов.
|
> Режим `screen` доступен в текущей версии и настраивается через слой сервисов.
|
||||||
|
|
||||||
@@ -112,9 +113,12 @@ sudo bash install.sh \
|
|||||||
|
|
||||||
### Ядро и версии
|
### Ядро и версии
|
||||||
|
|
||||||
- `--core <pmmp|paper|purpur|velocity|powernukkitx|nethergamesmc>`
|
- `--core <pmmp|paper|purpur|velocity|powernukkitx|nethergamesmc|bds>`
|
||||||
- `--mc-version <latest|version>`
|
- `--mc-version <latest|version>`
|
||||||
- `--build <latest|tag>` (для `nethergamesmc`, версия PHP)
|
- `--build <latest|tag>` (для `nethergamesmc`, версия PHP)
|
||||||
|
- `--mc-version` для `bds` и `nethergamesmc`:
|
||||||
|
- `nethergamesmc`: версия/тег PocketMine-MCPE
|
||||||
|
- `bds`: версия Bedrock Dedicated Server (`latest` определяется автоматически)
|
||||||
- legacy алиасы: `--version`, `--core-version`
|
- legacy алиасы: `--version`, `--core-version`
|
||||||
|
|
||||||
### Каталог и рантайм
|
### Каталог и рантайм
|
||||||
@@ -167,6 +171,24 @@ sudo bash install.sh \
|
|||||||
3. клонирование `PocketMine-MP`;
|
3. клонирование `PocketMine-MP`;
|
||||||
4. сборка/подготовка запуска.
|
4. сборка/подготовка запуска.
|
||||||
|
|
||||||
|
## Пример для BDS
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo bash install.sh \
|
||||||
|
--easy \
|
||||||
|
--core bds \
|
||||||
|
--dir /opt/minecraft/bds \
|
||||||
|
--mc-version latest \
|
||||||
|
--service tmux-systemd
|
||||||
|
```
|
||||||
|
|
||||||
|
Путь для `bds`:
|
||||||
|
|
||||||
|
1. разрешение версии Bedrock;
|
||||||
|
2. скачивание `bedrock-server-<версия>.zip`;
|
||||||
|
3. разархивация и обновление файлов сервера;
|
||||||
|
4. генерация `start.sh` и настройка сервиса.
|
||||||
|
|
||||||
## Режим обновления
|
## Режим обновления
|
||||||
|
|
||||||
`--update` переводит в режим обновления. Сначала проверяется существование и непустота директории, затем выполняется core-специфичный апдейт.
|
`--update` переводит в режим обновления. Сначала проверяется существование и непустота директории, затем выполняется core-специфичный апдейт.
|
||||||
|
|||||||
291
bot-integration-fsm-plan.md
Normal file
291
bot-integration-fsm-plan.md
Normal file
@@ -0,0 +1,291 @@
|
|||||||
|
# План интеграции AstraDeploy и бота (FSM + callback API)
|
||||||
|
|
||||||
|
## Цели
|
||||||
|
|
||||||
|
Сделать наблюдаемую, устойчивую и воспроизводимую установку через бота с поэтапными статусами.
|
||||||
|
|
||||||
|
Скрипт уже умеет отдавать:
|
||||||
|
- `started`
|
||||||
|
- `step`
|
||||||
|
- `success`
|
||||||
|
- `failed`
|
||||||
|
|
||||||
|
Нужно оборачивать это в `FSM`-движок на бэкенде бота.
|
||||||
|
|
||||||
|
## 1. Точка входа callback
|
||||||
|
|
||||||
|
### Endpoint
|
||||||
|
- `POST /astra-deploy/report`
|
||||||
|
- Заголовок: `Authorization: Bearer <report-token>`
|
||||||
|
- Content-Type: `application/json`
|
||||||
|
|
||||||
|
### Тело (из скрипта)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "step",
|
||||||
|
"core": "paper",
|
||||||
|
"dir": "/opt/minecraft/paper",
|
||||||
|
"port": 25565,
|
||||||
|
"protocol": "tcp",
|
||||||
|
"service": "tmux-systemd",
|
||||||
|
"message": "Installing base packages",
|
||||||
|
"step": "preflight_packages",
|
||||||
|
"step_index": 14,
|
||||||
|
"step_total": 80,
|
||||||
|
"step_percent": 17
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Рекомендуемый расширенный payload (бот-раннер может добавить)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"request_id": "uuid",
|
||||||
|
"job_id": "job_123",
|
||||||
|
"user_id": "123",
|
||||||
|
"locale": "ru"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ответ
|
||||||
|
- `200/204` с `{ "ok": true }` или пустым телом.
|
||||||
|
- Идемпотентность по `(request_id, status, step, step_index, step_total, ts_sec)` или аналогичной паре.
|
||||||
|
|
||||||
|
## 2. Запуск задачи из бота
|
||||||
|
|
||||||
|
Ключевые параметры запускаемого процесса:
|
||||||
|
- `--core ...`
|
||||||
|
- `--dir ...`
|
||||||
|
- `--port ...`
|
||||||
|
- `--service systemd|tmux-systemd|screen|none`
|
||||||
|
- `--user ...`
|
||||||
|
- `--firewall yes|no`
|
||||||
|
- `--yes`
|
||||||
|
- `--accept-license yes` (обязательно в автоматическом)
|
||||||
|
- `--accept-eula yes` для Java-ядер
|
||||||
|
- `--report-url ...`
|
||||||
|
- `--report-token ...`
|
||||||
|
- `--locale ru|en` (или `LANGUAGE`/`LANG`)
|
||||||
|
- `--dry-run`, `--update`, `--force-clean`, `--backup-existing`, `--allow-existing`
|
||||||
|
- `--build`, `--mc-version` для `nethergamesmc`
|
||||||
|
|
||||||
|
## 3. FSM состояния
|
||||||
|
|
||||||
|
### Состояния
|
||||||
|
- `QUEUED`
|
||||||
|
- `RUNNING`
|
||||||
|
- `VALIDATION`
|
||||||
|
- `PREPARE_ENV`
|
||||||
|
- `CORE_INSTALL`
|
||||||
|
- `POST_INSTALL`
|
||||||
|
- `WAITING_INPUT`
|
||||||
|
- `ROLLBACK`
|
||||||
|
- `DONE`
|
||||||
|
- `FAILED`
|
||||||
|
- `DRY_RUN`
|
||||||
|
- `STALLED` (таймаут между шагами)
|
||||||
|
|
||||||
|
### Переходы
|
||||||
|
- `started` -> `RUNNING`
|
||||||
|
- `step(parse_complete|prepare_defaults|preflight_*)` -> `VALIDATION`
|
||||||
|
- `step(preflight_packages|preflight_java|preflight_user|preflight_directory)` -> `PREPARE_ENV`
|
||||||
|
- `step(install_core|deploy_core|update_core|core_*)` -> `CORE_INSTALL`
|
||||||
|
- `step(core_post|core_service|service_*|finalize)` -> `POST_INSTALL`
|
||||||
|
- `status=success` -> `DONE`
|
||||||
|
- `status=failed` -> `FAILED`
|
||||||
|
- `--dry-run` (локально определено) -> `DRY_RUN`
|
||||||
|
- долгий таймаут без новых step -> `STALLED`
|
||||||
|
- interactive без автоматических флагов и требующие ответа -> `WAITING_INPUT`
|
||||||
|
|
||||||
|
### Идёмпотентная обработка
|
||||||
|
- Игнорировать дубликаты последнего шага в том же `request_id`.
|
||||||
|
- Обновлять состояние только если новый `step_index` выше предыдущего или статус изменился на более поздний.
|
||||||
|
|
||||||
|
## 4. Полная карта `step`
|
||||||
|
|
||||||
|
### Общая фаза и предзапуск
|
||||||
|
- `parse_complete`
|
||||||
|
- `prepare_defaults`
|
||||||
|
- `preflight_start`
|
||||||
|
- `install_start`
|
||||||
|
- `finalize`
|
||||||
|
- `core_runtime`
|
||||||
|
- `core_install`
|
||||||
|
- `core_update`
|
||||||
|
|
||||||
|
### Предпроверки
|
||||||
|
- `preflight_begin`
|
||||||
|
- `preflight_validate_root`
|
||||||
|
- `preflight_detect`
|
||||||
|
- `preflight_detect_os`
|
||||||
|
- `preflight_validate_support`
|
||||||
|
- `preflight_update_mode_check`
|
||||||
|
- `preflight_validate_plan`
|
||||||
|
- `preflight_validate_licenses`
|
||||||
|
- `preflight_validate_port`
|
||||||
|
- `preflight_directory`
|
||||||
|
- `preflight_packages`
|
||||||
|
- `preflight_java`
|
||||||
|
- `preflight_user`
|
||||||
|
|
||||||
|
### Установка/обновление ядер
|
||||||
|
- `core_resolve_channel`
|
||||||
|
- `core_resolve_download`
|
||||||
|
- `core_fetch`
|
||||||
|
- `core_render`
|
||||||
|
- `core_configure`
|
||||||
|
- `core_post`
|
||||||
|
- `core_service`
|
||||||
|
- `core_chown`
|
||||||
|
- `core_backup`
|
||||||
|
- `core_update_done`
|
||||||
|
- `core_phpbuild_resolve`
|
||||||
|
- `core_phpbuild_fetch`
|
||||||
|
- `core_composer`
|
||||||
|
- `core_repo_clone`
|
||||||
|
- `core_install`
|
||||||
|
- `install_core`
|
||||||
|
- `deploy_core`
|
||||||
|
- `update_core`
|
||||||
|
|
||||||
|
### Системные шаги
|
||||||
|
- `packages_start`
|
||||||
|
- `packages_dry_run`
|
||||||
|
- `packages_index`
|
||||||
|
- `download_start`
|
||||||
|
- `download_completed`
|
||||||
|
- `download_swap`
|
||||||
|
- `firewall_start`
|
||||||
|
- `firewall_done`
|
||||||
|
- `firewall_manual`
|
||||||
|
- `java_install`
|
||||||
|
- `render_start_script`
|
||||||
|
- `chmod_start_script`
|
||||||
|
- `service_stop`
|
||||||
|
- `service_create`
|
||||||
|
- `service_reload_enable`
|
||||||
|
- `service_start`
|
||||||
|
- `user_create`
|
||||||
|
- `user_exists`
|
||||||
|
- `prepare_dir`
|
||||||
|
- `permission_fix`
|
||||||
|
|
||||||
|
## 5. Маппинг на UX-сообщения (ru/en)
|
||||||
|
|
||||||
|
### RU
|
||||||
|
- `preflight_*` → «Подготовка окружения и проверок»
|
||||||
|
- `preflight_packages` / `packages_*` → «Установка базовых пакетов»
|
||||||
|
- `core_*` → «Установка и настройка ядра»
|
||||||
|
- `service_*`, `core_service` → «Настройка автозапуска»
|
||||||
|
- `core_chown` / `prepare_dir` / `permission_fix` → «Настройка доступа»
|
||||||
|
- `success` → «Установка завершена»
|
||||||
|
- `failed` → «Ошибка установки»
|
||||||
|
|
||||||
|
### EN
|
||||||
|
- `preflight_*` → `Environment preflight`
|
||||||
|
- `core_*` → `Core installation`
|
||||||
|
- `service_*` → `Service setup`
|
||||||
|
- `failed` → `Deployment failed`
|
||||||
|
|
||||||
|
## 6. Edge-cases по сценариям запуска
|
||||||
|
|
||||||
|
- **Interactive (без `--yes`)**
|
||||||
|
- может остановиться на подтверждении лицензии/ЕULA/директории
|
||||||
|
- статус `WAITING_INPUT`
|
||||||
|
- **`--easy`**
|
||||||
|
- обычный процесс, добавляется план-сводка до preflight
|
||||||
|
- **`--update`**
|
||||||
|
- `step_total` уменьшается на 6, проверяется существующая непустая директория
|
||||||
|
- **`--dry-run`**
|
||||||
|
- физически без изменений, callback может отсутствовать (как реализовано сейчас)
|
||||||
|
- **`--force-clean`**
|
||||||
|
- удаление содержимого директории перед установкой
|
||||||
|
- **`--backup-existing`**
|
||||||
|
- резервная копия `*.backup-<timestamp>`
|
||||||
|
- **`--allow-existing`**
|
||||||
|
- пропуск проверки непустой директории
|
||||||
|
- **Неправильный `core` / OS**
|
||||||
|
- жёсткий fail с объяснением
|
||||||
|
- **Порт занят**
|
||||||
|
- fail с деталями `tcp/udp` и предложением сменить порт
|
||||||
|
- **Сбой callback API**
|
||||||
|
- для скрипта не фатальный; бот не должен переводить задачу в `FAILED` из-за 5xx webhook-а
|
||||||
|
|
||||||
|
## 7. Rollback и пост-факту
|
||||||
|
|
||||||
|
На фатальной ошибке скрипт делает:
|
||||||
|
- лог ошибки
|
||||||
|
- попытку удаления созданного сервиса (если был создан)
|
||||||
|
- сообщение с путём лога (`/var/log/astra-deploy/...`)
|
||||||
|
- завершение с кодом ошибки
|
||||||
|
|
||||||
|
Для бота:
|
||||||
|
- статусы -> `ROLLBACK` (внутренний подстейт)
|
||||||
|
- показать пользователю:
|
||||||
|
- возможная причина
|
||||||
|
- путь лога
|
||||||
|
- безопасный список ручных действий
|
||||||
|
|
||||||
|
## 8. Минимальная модель хранения
|
||||||
|
|
||||||
|
### `jobs`
|
||||||
|
- `id`
|
||||||
|
- `user_id`
|
||||||
|
- `core`, `dir`, `port`, `service`, `locale`
|
||||||
|
- `fsm_state`
|
||||||
|
- `status`
|
||||||
|
- `percent`
|
||||||
|
- `step`, `step_index`, `step_total`, `message`
|
||||||
|
- `request_id`, `report_token_id`, `process_pid`
|
||||||
|
- `created_at`, `updated_at`
|
||||||
|
|
||||||
|
### `job_events`
|
||||||
|
- `job_id`
|
||||||
|
- `status`, `step`, `step_index`, `step_total`, `step_percent`
|
||||||
|
- `message`
|
||||||
|
- `raw_payload` (json)
|
||||||
|
- `created_at`
|
||||||
|
|
||||||
|
## 9. Шаблоны уведомлений
|
||||||
|
|
||||||
|
### Telegram (RU)
|
||||||
|
1) Запуск: `🚀 Установка AstraDeploy запущена`
|
||||||
|
2) Шаг: `⚙️ {percent}% | {state} | {step}`
|
||||||
|
3) Успех: `✅ Установка завершена`
|
||||||
|
4) Ошибка: `❌ Установка завершена с ошибкой`
|
||||||
|
|
||||||
|
### Telegram (EN)
|
||||||
|
1) `🚀 AstraDeploy started`
|
||||||
|
2) `⚙️ {percent}% | {state} | {step}`
|
||||||
|
3) `✅ Installation completed`
|
||||||
|
4) `❌ Installation failed`
|
||||||
|
|
||||||
|
Кнопки:
|
||||||
|
- `Получить статус`
|
||||||
|
- `Логи`
|
||||||
|
- `Запустить проверку`
|
||||||
|
- `Повторить (dry-run)`
|
||||||
|
|
||||||
|
## 10. Безопасность
|
||||||
|
|
||||||
|
- Токены webhook не логировать.
|
||||||
|
- Проверять `Authorization` строго.
|
||||||
|
- Проверка whitelist по разрешённым `step`.
|
||||||
|
- Rate limit на report endpoint.
|
||||||
|
- Защищённое хранение токенов (vault/secret/env).
|
||||||
|
|
||||||
|
## 11. Проверочный набор для интеграции
|
||||||
|
|
||||||
|
- `paper` non-interactive, валидные флаги
|
||||||
|
- `paper` без `--accept-eula` и с `--yes` -> `failed`
|
||||||
|
- `nethergamesmc --dry-run` -> без изменений, шаги в локальном логе
|
||||||
|
- занятый порт -> `failed`
|
||||||
|
- `--update` на пустой директории -> `failed`
|
||||||
|
- `--force-clean` на непустой директории -> success после очистки
|
||||||
|
- callback 5xx -> установка продолжается, без ложного FAILED в UI
|
||||||
|
|
||||||
|
## 12. Что отдаёт разработчику бота на старте
|
||||||
|
|
||||||
|
- список поддерживаемых `core`: `pmmp,paper,purpur,velocity,powernukkitx,nethergamesmc`
|
||||||
|
- поддерживаемые режимы сервиса: `systemd`, `tmux-systemd`, `screen`, `none`
|
||||||
|
- список ключевых шагов из этого плана выше
|
||||||
|
- статусные правила и fallback-поведение для отсутствующего callback
|
||||||
152
cores/bds.sh
Normal file
152
cores/bds.sh
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
core_name_bds() {
|
||||||
|
echo "Bedrock Dedicated Server"
|
||||||
|
}
|
||||||
|
|
||||||
|
core_default_port_bds() {
|
||||||
|
echo "19132"
|
||||||
|
}
|
||||||
|
|
||||||
|
core_protocol_bds() {
|
||||||
|
echo "udp"
|
||||||
|
}
|
||||||
|
|
||||||
|
core_runtime_bds() {
|
||||||
|
echo "native"
|
||||||
|
}
|
||||||
|
|
||||||
|
core_validate_bds() {
|
||||||
|
if [[ -z "${MC_VERSION:-}" ]]; then
|
||||||
|
MC_VERSION="latest"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
bds_resolve_latest_download_url() {
|
||||||
|
local page
|
||||||
|
local url
|
||||||
|
|
||||||
|
page="$(http_get "https://www.minecraft.net/en-us/download/server/bedrock")"
|
||||||
|
if [[ -z "$page" ]]; then
|
||||||
|
fail "$(t no_stable_build "Bedrock Dedicated Server" "latest")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
url="$(printf '%s\n' "$page" | grep -oE "https://minecraft\\.azureedge\\.net/bin-linux/bedrock-server-[^\" ]+\\.zip" | head -n 1)"
|
||||||
|
if [[ -z "$url" ]]; then
|
||||||
|
fail "$(t no_stable_build "Bedrock Dedicated Server" "latest")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$url"
|
||||||
|
}
|
||||||
|
|
||||||
|
bds_resolve_download_url() {
|
||||||
|
local requested="${1:-latest}"
|
||||||
|
|
||||||
|
if [[ "$requested" == "latest" ]]; then
|
||||||
|
bds_resolve_latest_download_url
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$requested" == http* ]]; then
|
||||||
|
echo "$requested"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "https://minecraft.azureedge.net/bin-linux/bedrock-server-${requested}.zip"
|
||||||
|
}
|
||||||
|
|
||||||
|
bds_resolve_version_from_url() {
|
||||||
|
local url="$1"
|
||||||
|
local requested="${2:-latest}"
|
||||||
|
local file
|
||||||
|
|
||||||
|
file="${url##*/}"
|
||||||
|
file="${file#bedrock-server-}"
|
||||||
|
file="${file%.zip*}"
|
||||||
|
file="${file%%\?*}"
|
||||||
|
|
||||||
|
if [[ -z "$file" || "$file" == "$requested" ]]; then
|
||||||
|
echo "$requested"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$file"
|
||||||
|
}
|
||||||
|
|
||||||
|
bds_render_start_script() {
|
||||||
|
local dir="$SERVER_DIR"
|
||||||
|
emit_step "core_render" "bds start script"
|
||||||
|
run_cmd bash -c "cat > '$dir/start.sh' <<EOF
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
cd \"\$(dirname \"\$0\")\"
|
||||||
|
export LD_LIBRARY_PATH=.:\$LD_LIBRARY_PATH
|
||||||
|
exec ./bedrock_server
|
||||||
|
EOF"
|
||||||
|
run_cmd chmod +x "$dir/start.sh"
|
||||||
|
emit_step "chmod_start_script" "$dir/start.sh"
|
||||||
|
}
|
||||||
|
|
||||||
|
core_install_bds() {
|
||||||
|
info "$(t core_install "$(core_name_bds)")"
|
||||||
|
emit_step "core_install" "$(core_name_bds)"
|
||||||
|
|
||||||
|
local requested_version="${MC_VERSION:-latest}"
|
||||||
|
local download_url
|
||||||
|
local version
|
||||||
|
local archive="$SERVER_DIR/.bds-package.zip"
|
||||||
|
|
||||||
|
download_url="$(bds_resolve_download_url "$requested_version")"
|
||||||
|
version="$(bds_resolve_version_from_url "$download_url" "$requested_version")"
|
||||||
|
|
||||||
|
emit_step "core_resolve_download" "$version"
|
||||||
|
emit_step "core_fetch" "$download_url"
|
||||||
|
|
||||||
|
if [[ -f "$SERVER_DIR/.bds-version" ]]; then
|
||||||
|
local existing_version
|
||||||
|
existing_version="$(cat "$SERVER_DIR/.bds-version" 2>/dev/null || true)"
|
||||||
|
if [[ "$UPDATE_MODE" == "no" && -n "$existing_version" && "$existing_version" == "$version" && -f "$SERVER_DIR/bedrock_server" ]]; then
|
||||||
|
info "BDS already at requested version ${version}. Skipping binary download."
|
||||||
|
run_cmd chmod +x "$SERVER_DIR/bedrock_server"
|
||||||
|
bds_render_start_script
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_cmd rm -f "$archive"
|
||||||
|
download_as_user "$download_url" "$archive"
|
||||||
|
emit_step "core_fetch" "Bedrock server archive"
|
||||||
|
run_cmd unzip -o -q "$archive" -d "$SERVER_DIR"
|
||||||
|
run_cmd rm -f "$archive"
|
||||||
|
|
||||||
|
if [[ ! -f "$SERVER_DIR/bedrock_server" ]]; then
|
||||||
|
fail "bedrock_server not found after extraction"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_cmd chmod +x "$SERVER_DIR/bedrock_server"
|
||||||
|
bds_render_start_script
|
||||||
|
run_cmd bash -lc "printf '%s\n' '$version' > '$SERVER_DIR/.bds-version'"
|
||||||
|
emit_step "core_configure" "BDS version ${version}"
|
||||||
|
}
|
||||||
|
|
||||||
|
core_post_install_bds() {
|
||||||
|
emit_step "core_post" "BDS"
|
||||||
|
run_cmd bash -lc "chown -R ${SERVER_USER}:${SERVER_USER} '$SERVER_DIR'"
|
||||||
|
emit_step "core_chown" "BDS"
|
||||||
|
open_firewall_port "$SERVER_PORT" "udp"
|
||||||
|
emit_step "core_service" "BDS"
|
||||||
|
create_service "$CORE" "$SERVER_DIR" "$SERVICE_MODE" "$SERVER_DIR/start.sh"
|
||||||
|
start_service
|
||||||
|
}
|
||||||
|
|
||||||
|
core_update_bds() {
|
||||||
|
info "$(t core_update "$(core_name_bds)")"
|
||||||
|
emit_step "core_update" "$(core_name_bds)"
|
||||||
|
|
||||||
|
backup_file "$SERVER_DIR/bedrock_server"
|
||||||
|
backup_file "$SERVER_DIR/start.sh"
|
||||||
|
backup_file "$SERVER_DIR/.bds-version"
|
||||||
|
|
||||||
|
core_install_bds
|
||||||
|
emit_step "core_update_done" "BDS"
|
||||||
|
}
|
||||||
59
cores/paper.sh
Normal file
59
cores/paper.sh
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
core_name_paper() {
|
||||||
|
echo "Paper"
|
||||||
|
}
|
||||||
|
|
||||||
|
core_default_port_paper() {
|
||||||
|
echo "25565"
|
||||||
|
}
|
||||||
|
|
||||||
|
core_protocol_paper() {
|
||||||
|
echo "tcp"
|
||||||
|
}
|
||||||
|
|
||||||
|
core_runtime_paper() {
|
||||||
|
echo "java"
|
||||||
|
}
|
||||||
|
|
||||||
|
core_validate_paper() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
core_install_paper() {
|
||||||
|
info "$(t core_install "$(core_name_paper)")"
|
||||||
|
emit_step "core_install" "$(core_name_paper)"
|
||||||
|
|
||||||
|
local url
|
||||||
|
emit_step "core_resolve_download" "paper"
|
||||||
|
url="$(get_papermc_download_url "paper" "${MC_VERSION:-latest}")"
|
||||||
|
download_as_user "$url" "$SERVER_DIR/server.jar"
|
||||||
|
emit_step "core_configure" "eula"
|
||||||
|
run_cmd bash -lc "printf 'eula=true\n' > '$SERVER_DIR/eula.txt'"
|
||||||
|
create_java_start_script "$SERVER_DIR" "server.jar" "$RAM"
|
||||||
|
}
|
||||||
|
|
||||||
|
core_post_install_paper() {
|
||||||
|
emit_step "core_post" "Paper"
|
||||||
|
run_cmd bash -lc "chown -R ${SERVER_USER}:${SERVER_USER} '$SERVER_DIR'"
|
||||||
|
emit_step "core_chown" "Paper"
|
||||||
|
open_firewall_port "$SERVER_PORT" "tcp"
|
||||||
|
emit_step "core_service" "Paper"
|
||||||
|
create_service "$CORE" "$SERVER_DIR" "$SERVICE_MODE" "$SERVER_DIR/start.sh"
|
||||||
|
start_service
|
||||||
|
}
|
||||||
|
|
||||||
|
core_update_paper() {
|
||||||
|
info "$(t core_update "$(core_name_paper)")"
|
||||||
|
emit_step "core_update" "$(core_name_paper)"
|
||||||
|
|
||||||
|
local target="$SERVER_DIR/server.jar"
|
||||||
|
backup_file "$target"
|
||||||
|
emit_step "core_backup" "$target"
|
||||||
|
|
||||||
|
local url
|
||||||
|
emit_step "core_resolve_download" "paper"
|
||||||
|
url="$(get_papermc_download_url "paper" "${MC_VERSION:-latest}")"
|
||||||
|
download_as_user_atomic "$url" "$target"
|
||||||
|
emit_step "core_update_done" "Paper"
|
||||||
|
}
|
||||||
62
cores/pmmp.sh
Normal file
62
cores/pmmp.sh
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
core_name_pmmp() {
|
||||||
|
echo "PocketMine-MP"
|
||||||
|
}
|
||||||
|
|
||||||
|
core_default_port_pmmp() {
|
||||||
|
echo "19132"
|
||||||
|
}
|
||||||
|
|
||||||
|
core_protocol_pmmp() {
|
||||||
|
echo "udp"
|
||||||
|
}
|
||||||
|
|
||||||
|
core_runtime_pmmp() {
|
||||||
|
echo "php"
|
||||||
|
}
|
||||||
|
|
||||||
|
core_validate_pmmp() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
core_install_pmmp() {
|
||||||
|
info "$(t core_install "$(core_name_pmmp)")"
|
||||||
|
emit_step "core_install" "$(core_name_pmmp)"
|
||||||
|
emit_step "core_resolve_channel" "${BUILD:-latest}"
|
||||||
|
|
||||||
|
local channel="${BUILD}"
|
||||||
|
if [[ "$channel" == "latest" || -z "$channel" ]]; then
|
||||||
|
channel="stable"
|
||||||
|
fi
|
||||||
|
download_url="https://get.pmmp.io"
|
||||||
|
emit_step "core_fetch" "pmmp"
|
||||||
|
run_cmd sudo -u "$SERVER_USER" bash -lc "cd '$SERVER_DIR' && curl -sL '$download_url' | bash -s - -d '$SERVER_DIR' -v '$channel'"
|
||||||
|
emit_step "core_configure" "pmmp"
|
||||||
|
}
|
||||||
|
|
||||||
|
core_post_install_pmmp() {
|
||||||
|
emit_step "core_post" "pmmp"
|
||||||
|
open_firewall_port "$SERVER_PORT" "udp"
|
||||||
|
emit_step "core_service" "pmmp"
|
||||||
|
create_service "$CORE" "$SERVER_DIR" "$SERVICE_MODE" "$SERVER_DIR/start.sh"
|
||||||
|
start_service
|
||||||
|
}
|
||||||
|
|
||||||
|
core_update_pmmp() {
|
||||||
|
info "$(t core_update "$(core_name_pmmp)")"
|
||||||
|
emit_step "core_update" "$(core_name_pmmp)"
|
||||||
|
|
||||||
|
local download_url="https://get.pmmp.io"
|
||||||
|
local channel="${BUILD}"
|
||||||
|
if [[ "$channel" == "latest" || -z "$channel" ]]; then
|
||||||
|
channel="stable"
|
||||||
|
fi
|
||||||
|
|
||||||
|
backup_file "$SERVER_DIR/server.phar"
|
||||||
|
backup_file "$SERVER_DIR/start.sh"
|
||||||
|
|
||||||
|
emit_step "core_fetch" "pmmp"
|
||||||
|
run_cmd sudo -u "$SERVER_USER" bash -lc "cd '$SERVER_DIR' && curl -sL '$download_url' | bash -s - -d '$SERVER_DIR' -v '$channel'"
|
||||||
|
emit_step "core_update_done" "pmmp"
|
||||||
|
}
|
||||||
65
cores/powernukkitx.sh
Normal file
65
cores/powernukkitx.sh
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
core_name_powernukkitx() {
|
||||||
|
echo "PowerNukkitX"
|
||||||
|
}
|
||||||
|
|
||||||
|
core_default_port_powernukkitx() {
|
||||||
|
echo "19132"
|
||||||
|
}
|
||||||
|
|
||||||
|
core_protocol_powernukkitx() {
|
||||||
|
echo "udp"
|
||||||
|
}
|
||||||
|
|
||||||
|
core_runtime_powernukkitx() {
|
||||||
|
echo "java"
|
||||||
|
}
|
||||||
|
|
||||||
|
core_validate_powernukkitx() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
core_install_powernukkitx() {
|
||||||
|
info "$(t core_install "$(core_name_powernukkitx)")"
|
||||||
|
emit_step "core_install" "$(core_name_powernukkitx)"
|
||||||
|
|
||||||
|
local url
|
||||||
|
emit_step "core_resolve_download" "powernukkitx"
|
||||||
|
url="$(get_github_release_asset_url PowerNukkitX/PowerNukkitX 'powernukkitx.*\\.jar')"
|
||||||
|
if [[ -z "$url" || "$url" == "null" ]]; then
|
||||||
|
fail "$(t no_stable_build "PowerNukkitX" "$BUILD")"
|
||||||
|
fi
|
||||||
|
download_as_user "$url" "$SERVER_DIR/powernukkitx.jar"
|
||||||
|
create_powernukkit_start_script "$SERVER_DIR" "$RAM"
|
||||||
|
emit_step "core_render" "powernukkitx"
|
||||||
|
}
|
||||||
|
|
||||||
|
core_post_install_powernukkitx() {
|
||||||
|
emit_step "core_configure" "eula"
|
||||||
|
run_cmd bash -lc "printf 'eula=true\n' > '$SERVER_DIR/eula.txt'"
|
||||||
|
emit_step "core_chown" "PowerNukkitX"
|
||||||
|
run_cmd bash -lc "chown -R ${SERVER_USER}:${SERVER_USER} '$SERVER_DIR'"
|
||||||
|
open_firewall_port "$SERVER_PORT" "udp"
|
||||||
|
emit_step "core_service" "PowerNukkitX"
|
||||||
|
create_service "$CORE" "$SERVER_DIR" "$SERVICE_MODE" "$SERVER_DIR/start.sh"
|
||||||
|
start_service
|
||||||
|
}
|
||||||
|
|
||||||
|
core_update_powernukkitx() {
|
||||||
|
info "$(t core_update "$(core_name_powernukkitx)")"
|
||||||
|
emit_step "core_update" "$(core_name_powernukkitx)"
|
||||||
|
|
||||||
|
local target="$SERVER_DIR/powernukkitx.jar"
|
||||||
|
backup_file "$target"
|
||||||
|
emit_step "core_backup" "$target"
|
||||||
|
|
||||||
|
local url
|
||||||
|
emit_step "core_resolve_download" "powernukkitx"
|
||||||
|
url="$(get_github_release_asset_url PowerNukkitX/PowerNukkitX 'powernukkitx.*\\.jar')"
|
||||||
|
if [[ -z "$url" || "$url" == "null" ]]; then
|
||||||
|
fail "$(t no_stable_build "PowerNukkitX" "$BUILD")"
|
||||||
|
fi
|
||||||
|
download_as_user_atomic "$url" "$target"
|
||||||
|
emit_step "core_update_done" "PowerNukkitX"
|
||||||
|
}
|
||||||
59
cores/purpur.sh
Normal file
59
cores/purpur.sh
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
core_name_purpur() {
|
||||||
|
echo "Purpur"
|
||||||
|
}
|
||||||
|
|
||||||
|
core_default_port_purpur() {
|
||||||
|
echo "25565"
|
||||||
|
}
|
||||||
|
|
||||||
|
core_protocol_purpur() {
|
||||||
|
echo "tcp"
|
||||||
|
}
|
||||||
|
|
||||||
|
core_runtime_purpur() {
|
||||||
|
echo "java"
|
||||||
|
}
|
||||||
|
|
||||||
|
core_validate_purpur() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
core_install_purpur() {
|
||||||
|
info "$(t core_install "$(core_name_purpur)")"
|
||||||
|
emit_step "core_install" "$(core_name_purpur)"
|
||||||
|
|
||||||
|
local url
|
||||||
|
emit_step "core_resolve_download" "purpur"
|
||||||
|
url="$(get_purpur_download_url "${MC_VERSION:-latest}" "${BUILD:-latest}")"
|
||||||
|
download_as_user "$url" "$SERVER_DIR/server.jar"
|
||||||
|
emit_step "core_configure" "eula"
|
||||||
|
run_cmd bash -lc "printf 'eula=true\n' > '$SERVER_DIR/eula.txt'"
|
||||||
|
create_java_start_script "$SERVER_DIR" "server.jar" "$RAM"
|
||||||
|
}
|
||||||
|
|
||||||
|
core_post_install_purpur() {
|
||||||
|
emit_step "core_post" "Purpur"
|
||||||
|
run_cmd bash -lc "chown -R ${SERVER_USER}:${SERVER_USER} '$SERVER_DIR'"
|
||||||
|
emit_step "core_chown" "Purpur"
|
||||||
|
open_firewall_port "$SERVER_PORT" "tcp"
|
||||||
|
emit_step "core_service" "Purpur"
|
||||||
|
create_service "$CORE" "$SERVER_DIR" "$SERVICE_MODE" "$SERVER_DIR/start.sh"
|
||||||
|
start_service
|
||||||
|
}
|
||||||
|
|
||||||
|
core_update_purpur() {
|
||||||
|
info "$(t core_update "$(core_name_purpur)")"
|
||||||
|
emit_step "core_update" "$(core_name_purpur)"
|
||||||
|
|
||||||
|
local target="$SERVER_DIR/server.jar"
|
||||||
|
backup_file "$target"
|
||||||
|
emit_step "core_backup" "$target"
|
||||||
|
|
||||||
|
local url
|
||||||
|
emit_step "core_resolve_download" "purpur"
|
||||||
|
url="$(get_purpur_download_url "${MC_VERSION:-latest}" "${BUILD:-latest}")"
|
||||||
|
download_as_user_atomic "$url" "$target"
|
||||||
|
emit_step "core_update_done" "Purpur"
|
||||||
|
}
|
||||||
65
cores/velocity.sh
Normal file
65
cores/velocity.sh
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
core_name_velocity() {
|
||||||
|
echo "Velocity"
|
||||||
|
}
|
||||||
|
|
||||||
|
core_default_port_velocity() {
|
||||||
|
echo "25577"
|
||||||
|
}
|
||||||
|
|
||||||
|
core_protocol_velocity() {
|
||||||
|
echo "tcp"
|
||||||
|
}
|
||||||
|
|
||||||
|
core_runtime_velocity() {
|
||||||
|
echo "java"
|
||||||
|
}
|
||||||
|
|
||||||
|
core_validate_velocity() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
core_install_velocity() {
|
||||||
|
info "$(t core_install "$(core_name_velocity)")"
|
||||||
|
emit_step "core_install" "$(core_name_velocity)"
|
||||||
|
|
||||||
|
local url
|
||||||
|
emit_step "core_resolve_download" "velocity"
|
||||||
|
url="$(get_papermc_download_url "velocity" "${MC_VERSION:-latest}")"
|
||||||
|
download_as_user "$url" "$SERVER_DIR/velocity.jar"
|
||||||
|
run_cmd bash -c "cat > '$SERVER_DIR/start.sh' <<EOF
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
cd \"\$(dirname \"\$0\")\"
|
||||||
|
exec java -Xms${RAM} -Xmx${RAM} -jar velocity.jar
|
||||||
|
EOF"
|
||||||
|
run_cmd chmod +x "$SERVER_DIR/start.sh"
|
||||||
|
emit_step "chmod_start_script" "$SERVER_DIR/start.sh"
|
||||||
|
emit_step "core_render" "velocity"
|
||||||
|
}
|
||||||
|
|
||||||
|
core_post_install_velocity() {
|
||||||
|
emit_step "core_post" "Velocity"
|
||||||
|
run_cmd bash -lc "chown -R ${SERVER_USER}:${SERVER_USER} '$SERVER_DIR'"
|
||||||
|
emit_step "core_chown" "Velocity"
|
||||||
|
open_firewall_port "$SERVER_PORT" "tcp"
|
||||||
|
emit_step "core_service" "Velocity"
|
||||||
|
create_service "$CORE" "$SERVER_DIR" "$SERVICE_MODE" "$SERVER_DIR/start.sh"
|
||||||
|
start_service
|
||||||
|
}
|
||||||
|
|
||||||
|
core_update_velocity() {
|
||||||
|
info "$(t core_update "$(core_name_velocity)")"
|
||||||
|
emit_step "core_update" "$(core_name_velocity)"
|
||||||
|
|
||||||
|
local target="$SERVER_DIR/velocity.jar"
|
||||||
|
backup_file "$target"
|
||||||
|
emit_step "core_backup" "$target"
|
||||||
|
|
||||||
|
local url
|
||||||
|
emit_step "core_resolve_download" "velocity"
|
||||||
|
url="$(get_papermc_download_url "velocity" "${MC_VERSION:-latest}")"
|
||||||
|
download_as_user_atomic "$url" "$target"
|
||||||
|
emit_step "core_update_done" "Velocity"
|
||||||
|
}
|
||||||
@@ -196,6 +196,32 @@ You can use script output or report callback events:
|
|||||||
- `success`
|
- `success`
|
||||||
- `failed`
|
- `failed`
|
||||||
|
|
||||||
|
### BDS (`--core bds`)
|
||||||
|
|
||||||
|
- Beginner:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo bash install.sh --easy --core bds --service tmux-systemd
|
||||||
|
```
|
||||||
|
|
||||||
|
- Non-interactive:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo bash install.sh \
|
||||||
|
--yes \
|
||||||
|
--core bds \
|
||||||
|
--dir /opt/minecraft/bds \
|
||||||
|
--service systemd \
|
||||||
|
--mc-version latest \
|
||||||
|
--accept-license yes \
|
||||||
|
--firewall yes \
|
||||||
|
--locale en
|
||||||
|
```
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
- `--mc-version` for BDS means Bedrock server version (example: `1.21.3.01`) or `latest`.
|
||||||
|
|
||||||
## Update mode
|
## Update mode
|
||||||
|
|
||||||
Use for replacing files on an existing server directory:
|
Use for replacing files on an existing server directory:
|
||||||
@@ -210,4 +236,3 @@ sudo bash install.sh \
|
|||||||
```
|
```
|
||||||
|
|
||||||
For Java cores add `--accept-eula yes`.
|
For Java cores add `--accept-eula yes`.
|
||||||
|
|
||||||
|
|||||||
@@ -196,6 +196,32 @@
|
|||||||
- `success`
|
- `success`
|
||||||
- `failed`
|
- `failed`
|
||||||
|
|
||||||
|
### BDS (`--core bds`)
|
||||||
|
|
||||||
|
- Easy:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo bash install.sh --easy --core bds --service tmux-systemd
|
||||||
|
```
|
||||||
|
|
||||||
|
- Non-interactive:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo bash install.sh \
|
||||||
|
--yes \
|
||||||
|
--core bds \
|
||||||
|
--dir /opt/minecraft/bds \
|
||||||
|
--service systemd \
|
||||||
|
--mc-version latest \
|
||||||
|
--accept-license yes \
|
||||||
|
--firewall yes \
|
||||||
|
--locale ru
|
||||||
|
```
|
||||||
|
|
||||||
|
Комментарии:
|
||||||
|
|
||||||
|
- `--mc-version` для `bds` — это версия Bedrock сервера (`latest` или конкретный номер версии, например `1.21.3.01`).
|
||||||
|
|
||||||
## Режим обновления
|
## Режим обновления
|
||||||
|
|
||||||
Для обновления уже установленного каталога:
|
Для обновления уже установленного каталога:
|
||||||
@@ -210,4 +236,3 @@ sudo bash install.sh \
|
|||||||
```
|
```
|
||||||
|
|
||||||
Для Java-ядер добавляйте `--accept-eula yes`.
|
Для Java-ядер добавляйте `--accept-eula yes`.
|
||||||
|
|
||||||
|
|||||||
10
install.sh
10
install.sh
@@ -69,7 +69,7 @@ Usage:
|
|||||||
curl -fsSL https://deploy.example.com/install.sh | sudo bash -s -- [options]
|
curl -fsSL https://deploy.example.com/install.sh | sudo bash -s -- [options]
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
--core <pmmp|paper|purpur|velocity|powernukkitx|nethergamesmc>
|
--core <pmmp|paper|purpur|velocity|powernukkitx|nethergamesmc|bds>
|
||||||
--core-version <latest|version> (legacy alias for mc-version)
|
--core-version <latest|version> (legacy alias for mc-version)
|
||||||
--dir <path>
|
--dir <path>
|
||||||
--port <number>
|
--port <number>
|
||||||
@@ -313,7 +313,7 @@ resolve_core_defaults() {
|
|||||||
|
|
||||||
local check_fn="core_default_port_${CORE}"
|
local check_fn="core_default_port_${CORE}"
|
||||||
if [[ "$(type -t "$check_fn" 2>/dev/null)" != "function" ]]; then
|
if [[ "$(type -t "$check_fn" 2>/dev/null)" != "function" ]]; then
|
||||||
fail "$(t unsupported_core "$CORE" "pmmp,paper,purpur,velocity,powernukkitx,nethergamesmc" "${SCRIPT_DIR}/manifests/cores.json")"
|
fail "$(t unsupported_core "$CORE" "pmmp,paper,purpur,velocity,powernukkitx,nethergamesmc,bds" "${SCRIPT_DIR}/manifests/cores.json")"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
CORE_NAME="$(core_name_${CORE})"
|
CORE_NAME="$(core_name_${CORE})"
|
||||||
@@ -423,6 +423,8 @@ show_noob_plan() {
|
|||||||
if [[ "$CORE" == "nethergamesmc" ]]; then
|
if [[ "$CORE" == "nethergamesmc" ]]; then
|
||||||
info "$(t easy_plan_build "$BUILD")"
|
info "$(t easy_plan_build "$BUILD")"
|
||||||
info "$(t easy_plan_mc_version "$MC_VERSION")"
|
info "$(t easy_plan_mc_version "$MC_VERSION")"
|
||||||
|
elif [[ "$CORE" == "bds" ]]; then
|
||||||
|
info "$(t easy_plan_bds_version "$MC_VERSION")"
|
||||||
fi
|
fi
|
||||||
if is_java_core "$CORE"; then
|
if is_java_core "$CORE"; then
|
||||||
info "$(t easy_plan_eula "$ACCEPT_EULA")"
|
info "$(t easy_plan_eula "$ACCEPT_EULA")"
|
||||||
@@ -452,6 +454,8 @@ interact() {
|
|||||||
if [[ "$CORE" == "nethergamesmc" ]]; then
|
if [[ "$CORE" == "nethergamesmc" ]]; then
|
||||||
ask_value "$(t prompt_php_build)" "${BUILD:-latest}" BUILD
|
ask_value "$(t prompt_php_build)" "${BUILD:-latest}" BUILD
|
||||||
ask_value "$(t prompt_pocketmine_version)" "${MC_VERSION:-latest}" MC_VERSION
|
ask_value "$(t prompt_pocketmine_version)" "${MC_VERSION:-latest}" MC_VERSION
|
||||||
|
elif [[ "$CORE" == "bds" ]]; then
|
||||||
|
ask_value "$(t prompt_bds_version)" "${MC_VERSION:-latest}" MC_VERSION
|
||||||
fi
|
fi
|
||||||
ask_value "$(t prompt_service_mode)" "$SERVICE_MODE" SERVICE_MODE
|
ask_value "$(t prompt_service_mode)" "$SERVICE_MODE" SERVICE_MODE
|
||||||
ask_value "$(t prompt_open_firewall)" "$FIREWALL" FIREWALL
|
ask_value "$(t prompt_open_firewall)" "$FIREWALL" FIREWALL
|
||||||
@@ -525,7 +529,7 @@ check_existing_directory() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$FORCE_CLEAN" == "yes" ]]; then
|
if [[ "$FORCE_CLEAN" == "yes" ]]; then
|
||||||
run_cmd find "$SERVER_DIR" -mindepth 1 -maxdepth 1 -exec rm -rf -- {} +
|
run_cmd bash -c "find \"$SERVER_DIR\" -mindepth 1 -delete"
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
33
lib/detect.sh
Normal file
33
lib/detect.sh
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
detect_system() {
|
||||||
|
if [[ -f /etc/os-release ]]; then
|
||||||
|
# shellcheck disable=SC1091
|
||||||
|
. /etc/os-release
|
||||||
|
OS_ID="${ID:-unknown}"
|
||||||
|
OS_VERSION="${VERSION_ID:-unknown}"
|
||||||
|
OS_NAME="${PRETTY_NAME:-unknown}"
|
||||||
|
else
|
||||||
|
fail "$(t detect_os_failed)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
ARCHITECTURE="$(uname -m)"
|
||||||
|
|
||||||
|
if command -v apt-get >/dev/null 2>&1; then
|
||||||
|
PKG_MANAGER="apt"
|
||||||
|
elif command -v dnf >/dev/null 2>&1; then
|
||||||
|
PKG_MANAGER="dnf"
|
||||||
|
elif command -v yum >/dev/null 2>&1; then
|
||||||
|
PKG_MANAGER="yum"
|
||||||
|
elif command -v pacman >/dev/null 2>&1; then
|
||||||
|
PKG_MANAGER="pacman"
|
||||||
|
else
|
||||||
|
fail "$(t no_package_manager)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if command -v systemctl >/dev/null 2>&1; then
|
||||||
|
INIT_SYSTEM="systemd"
|
||||||
|
else
|
||||||
|
INIT_SYSTEM="none"
|
||||||
|
fi
|
||||||
|
}
|
||||||
65
lib/download.sh
Normal file
65
lib/download.sh
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
http_get() {
|
||||||
|
local url="$1"
|
||||||
|
curl -fsSL -H "User-Agent: AstraDeploy/${DEPLOY_VERSION}" "$url"
|
||||||
|
}
|
||||||
|
|
||||||
|
get_papermc_download_url() {
|
||||||
|
local project="$1"
|
||||||
|
local version="$2"
|
||||||
|
|
||||||
|
if [[ "$version" == "latest" ]]; then
|
||||||
|
version="$(http_get "https://fill.papermc.io/v3/projects/${project}" | jq -r '.versions | if type=="array" then .[-1] else (to_entries | last | .value[0]) end')"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local builds_json
|
||||||
|
builds_json="$(http_get "https://fill.papermc.io/v3/projects/${project}/versions/${version}/builds")"
|
||||||
|
local url
|
||||||
|
url="$(printf '%s\n' "$builds_json" | jq -r '.builds | map(select(.channel=="STABLE")) | .[0].downloads."server:default".url // empty')"
|
||||||
|
if [[ -z "$url" || "$url" == "null" ]]; then
|
||||||
|
fail "$(t no_stable_build "$project" "$version")"
|
||||||
|
fi
|
||||||
|
echo "$url"
|
||||||
|
}
|
||||||
|
|
||||||
|
get_purpur_download_url() {
|
||||||
|
local version="$1"
|
||||||
|
local build="${2:-latest}"
|
||||||
|
|
||||||
|
if [[ "$version" == "latest" ]]; then
|
||||||
|
version="$(http_get "https://api.purpurmc.org/v2/purpur/" | jq -r '.versions | .[-1]')"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$build" == "latest" ]]; then
|
||||||
|
echo "https://api.purpurmc.org/v2/purpur/${version}/latest/download"
|
||||||
|
else
|
||||||
|
echo "https://api.purpurmc.org/v2/purpur/${version}/${build}/download"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
get_github_release_asset_url() {
|
||||||
|
local repo="$1"
|
||||||
|
local pattern="$2"
|
||||||
|
http_get "https://api.github.com/repos/${repo}/releases/latest" \
|
||||||
|
| jq -r --arg pattern "$pattern" '.assets[] | select(.name | test($pattern)) | .browser_download_url' | head -n 1
|
||||||
|
}
|
||||||
|
|
||||||
|
download_as_user() {
|
||||||
|
local url="$1"
|
||||||
|
local target="$2"
|
||||||
|
|
||||||
|
emit_step "download_start" "$url"
|
||||||
|
run_cmd sudo -u "$SERVER_USER" bash -lc "curl -fL \"$url\" -o \"${target}\""
|
||||||
|
emit_step "download_completed" "$target"
|
||||||
|
}
|
||||||
|
|
||||||
|
download_as_user_atomic() {
|
||||||
|
local url="$1"
|
||||||
|
local target="$2"
|
||||||
|
local tmp="${target}.tmp.${RANDOM}.$(date +%s)"
|
||||||
|
|
||||||
|
download_as_user "$url" "$tmp"
|
||||||
|
run_cmd mv "$tmp" "$target"
|
||||||
|
emit_step "download_swap" "$target"
|
||||||
|
}
|
||||||
30
lib/firewall.sh
Normal file
30
lib/firewall.sh
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
open_firewall_port() {
|
||||||
|
local port="$1"
|
||||||
|
local proto="$2"
|
||||||
|
|
||||||
|
if [[ "$FIREWALL" != "yes" ]]; then
|
||||||
|
info "$(t firewall_skip)"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
emit_step "firewall_start" "$port/$proto"
|
||||||
|
|
||||||
|
if command -v ufw >/dev/null 2>&1; then
|
||||||
|
run_cmd ufw allow "$port/$proto" || true
|
||||||
|
run_cmd ufw --force enable || true
|
||||||
|
emit_step "firewall_done" "ufw"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if command -v firewall-cmd >/dev/null 2>&1; then
|
||||||
|
run_cmd firewall-cmd --permanent --add-port="$port/$proto"
|
||||||
|
run_cmd firewall-cmd --reload
|
||||||
|
emit_step "firewall_done" "firewalld"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
warn "$(t firewall_manual_open "$port" "$proto")"
|
||||||
|
emit_step "firewall_manual" "$port/$proto"
|
||||||
|
}
|
||||||
50
lib/i18n.sh
Normal file
50
lib/i18n.sh
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
LOCALE_DIRECTORY="${SCRIPT_DIR}/locales"
|
||||||
|
|
||||||
|
load_locale() {
|
||||||
|
case "${LOCALE,,}" in
|
||||||
|
ru*|ru_*)
|
||||||
|
LOCALE="ru"
|
||||||
|
;;
|
||||||
|
en*|en_*)
|
||||||
|
LOCALE="en"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
if [[ "${LANGUAGE:-${LANG:-}}" == ru* ]]; then
|
||||||
|
LOCALE="ru"
|
||||||
|
else
|
||||||
|
LOCALE="en"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
case "$LOCALE" in
|
||||||
|
ru)
|
||||||
|
LOCALE_FILE="$LOCALE_DIRECTORY/ru.sh"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
LOCALE_FILE="$LOCALE_DIRECTORY/en.sh"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [[ ! -f "$LOCALE_FILE" ]]; then
|
||||||
|
LOCALE="en"
|
||||||
|
LOCALE_FILE="$LOCALE_DIRECTORY/en.sh"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
. "$LOCALE_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
t() {
|
||||||
|
local key="$1"
|
||||||
|
shift || true
|
||||||
|
|
||||||
|
local fn="locale_msg_${key}"
|
||||||
|
if declare -F "$fn" >/dev/null 2>&1; then
|
||||||
|
"$fn" "$@"
|
||||||
|
else
|
||||||
|
echo "$key"
|
||||||
|
fi
|
||||||
|
}
|
||||||
77
lib/java.sh
Normal file
77
lib/java.sh
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
is_java_core() {
|
||||||
|
local core="$1"
|
||||||
|
case "$core" in
|
||||||
|
paper|purpur|velocity|powernukkitx) return 0 ;;
|
||||||
|
*) return 1 ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
install_java_21() {
|
||||||
|
if command -v java >/dev/null 2>&1; then
|
||||||
|
local version
|
||||||
|
version="$(java -version 2>&1 | sed -n 's/.*version "\(.*\)".*/\1/p' | head -n1 | sed 's/"//g' | sed 's/^\([0-9][0-9]*\).*/\1/')"
|
||||||
|
if [[ "$version" =~ ^[0-9]+$ ]] && (( version >= 21 )); then
|
||||||
|
info "$(t java_already_available "$version")"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "$(t installing_java_21)"
|
||||||
|
case "$PKG_MANAGER" in
|
||||||
|
apt)
|
||||||
|
emit_step "java_install" "apt"
|
||||||
|
run_cmd apt-get install -y openjdk-21-jre-headless
|
||||||
|
;;
|
||||||
|
dnf|yum)
|
||||||
|
emit_step "java_install" "$PKG_MANAGER"
|
||||||
|
run_cmd "$PKG_MANAGER" install -y java-21-openjdk-headless
|
||||||
|
;;
|
||||||
|
pacman)
|
||||||
|
emit_step "java_install" "pacman"
|
||||||
|
run_cmd pacman -Sy --noconfirm jre-openjdk-headless
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
create_java_start_script() {
|
||||||
|
local dir="$1"
|
||||||
|
local jar_file="$2"
|
||||||
|
local ram="$3"
|
||||||
|
local extra_opts="${4:-}"
|
||||||
|
|
||||||
|
local script="$dir/start.sh"
|
||||||
|
emit_step "render_start_script" "$jar_file"
|
||||||
|
run_cmd bash -c "cat > '$script' <<EOF
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
cd \"\$(dirname \"\$0\")\"
|
||||||
|
exec java -Xms${ram} -Xmx${ram} ${extra_opts} -jar ${jar_file} nogui
|
||||||
|
EOF"
|
||||||
|
run_cmd chmod +x "$script"
|
||||||
|
emit_step "chmod_start_script" "$script"
|
||||||
|
}
|
||||||
|
|
||||||
|
create_powernukkit_start_script() {
|
||||||
|
local dir="$1"
|
||||||
|
local ram="$2"
|
||||||
|
|
||||||
|
local script="$dir/start.sh"
|
||||||
|
emit_step "render_start_script" "powernukkitx.jar"
|
||||||
|
run_cmd bash -c "cat > '$script' <<EOF
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
cd \"\$(dirname \"\$0\")\"
|
||||||
|
exec java -Xms${ram} -Xmx${ram} \\
|
||||||
|
-Dfile.encoding=UTF-8 \\
|
||||||
|
-Djansi.passthrough=true \\
|
||||||
|
-Dterminal.ansi=true \\
|
||||||
|
--add-opens java.base/java.lang=ALL-UNNAMED \\
|
||||||
|
--add-opens java.base/java.io=ALL-UNNAMED \\
|
||||||
|
--add-opens java.base/java.net=ALL-UNNAMED \\
|
||||||
|
-jar powernukkitx.jar
|
||||||
|
EOF"
|
||||||
|
run_cmd chmod +x "$script"
|
||||||
|
emit_step "chmod_start_script" "$script"
|
||||||
|
}
|
||||||
38
lib/logger.sh
Normal file
38
lib/logger.sh
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
log() {
|
||||||
|
local level="$1"
|
||||||
|
shift
|
||||||
|
printf '[%s] [%s] %s\n' "$(date +'%Y-%m-%d %H:%M:%S')" "$level" "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
info() {
|
||||||
|
log "INFO" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
warn() {
|
||||||
|
log "WARN" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
error() {
|
||||||
|
log "ERROR" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
debug() {
|
||||||
|
if [[ "${DEBUG:-no}" == "yes" ]]; then
|
||||||
|
log "DEBUG" "$@"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
fail() {
|
||||||
|
error "$@"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
run_cmd() {
|
||||||
|
if [[ "${DRY_RUN:-no}" == "yes" ]]; then
|
||||||
|
info "[dry-run] $*"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
"$@"
|
||||||
|
}
|
||||||
34
lib/packages.sh
Normal file
34
lib/packages.sh
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
install_base_packages() {
|
||||||
|
emit_step "packages_start" "$(t installing_base_packages)"
|
||||||
|
|
||||||
|
local packages=("curl" "wget" "ca-certificates" "jq" "tar" "unzip" "tmux" "screen" "lsof")
|
||||||
|
local pkg_string
|
||||||
|
pkg_string="$(printf '%s ' "${packages[@]}")"
|
||||||
|
|
||||||
|
if [[ "$DRY_RUN" == "yes" ]]; then
|
||||||
|
emit_step "packages_dry_run" "$pkg_string"
|
||||||
|
info "$(t dry_run_install "$pkg_string")"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$PKG_MANAGER" in
|
||||||
|
apt)
|
||||||
|
run_cmd apt-get update
|
||||||
|
emit_step "packages_index" "apt"
|
||||||
|
run_cmd DEBIAN_FRONTEND=noninteractive apt-get install -y "${packages[@]}"
|
||||||
|
;;
|
||||||
|
dnf|yum)
|
||||||
|
emit_step "packages_index" "$PKG_MANAGER"
|
||||||
|
run_cmd "$PKG_MANAGER" install -y "${packages[@]}"
|
||||||
|
;;
|
||||||
|
pacman)
|
||||||
|
emit_step "packages_index" "pacman"
|
||||||
|
run_cmd pacman -Sy --noconfirm "${packages[@]}"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
fail "$(t package_manager_unsupported "$PKG_MANAGER")"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
72
lib/report.sh
Normal file
72
lib/report.sh
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
json_escape() {
|
||||||
|
local raw="${1//\\/\\\\}"
|
||||||
|
raw="${raw//\"/\\\"}"
|
||||||
|
raw="${raw//$'\n'/\\n}"
|
||||||
|
echo "$raw"
|
||||||
|
}
|
||||||
|
|
||||||
|
send_report() {
|
||||||
|
local status="$1"
|
||||||
|
local message="${2:-}"
|
||||||
|
local step="${3:-}"
|
||||||
|
local step_index="${4:-}"
|
||||||
|
local step_total="${5:-}"
|
||||||
|
local step_percent="null"
|
||||||
|
local step_index_json="null"
|
||||||
|
local step_total_json="null"
|
||||||
|
local step_percent_json="null"
|
||||||
|
|
||||||
|
if [[ "$step_index" =~ ^[0-9]+$ ]]; then
|
||||||
|
step_index_json="$step_index"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$step_total" =~ ^[0-9]+$ && "$step_total" -gt 0 ]]; then
|
||||||
|
step_total_json="$step_total"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$step_index_json" != "null" && "$step_total_json" != "null" ]]; then
|
||||||
|
if (( step_index > step_total )); then
|
||||||
|
step_percent_json=100
|
||||||
|
else
|
||||||
|
step_percent=$(( step_index * 100 / step_total ))
|
||||||
|
step_percent_json="$step_percent"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "${REPORT_URL:-}" || -z "${REPORT_TOKEN:-}" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local payload
|
||||||
|
payload="$(printf '{ "status":"%s", "core":"%s", "dir":"%s", "port":%s, "protocol":"%s", "service":"%s", "message":"%s", "step":"%s", "step_index":%s, "step_total":%s, "step_percent":%s }' \
|
||||||
|
"$(json_escape "$status")" \
|
||||||
|
"$(json_escape "${CORE}")" \
|
||||||
|
"$(json_escape "${SERVER_DIR}")" \
|
||||||
|
"${SERVER_PORT}" \
|
||||||
|
"$(json_escape "${CORE_PROTOCOL}")" \
|
||||||
|
"$(json_escape "${SERVICE_MODE_ACTIVE}")" \
|
||||||
|
"$(json_escape "$message")" \
|
||||||
|
"$(json_escape "$step")" \
|
||||||
|
"$step_index_json" \
|
||||||
|
"$step_total_json" \
|
||||||
|
"$step_percent_json")"
|
||||||
|
|
||||||
|
local attempt
|
||||||
|
for attempt in 1 2; do
|
||||||
|
if [[ "${DRY_RUN}" == "yes" ]]; then
|
||||||
|
info "$(t report_dry_run "$payload")"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if curl -fsSL -X POST "$REPORT_URL" \
|
||||||
|
-H "Authorization: Bearer ${REPORT_TOKEN}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "$payload" >/dev/null 2>&1; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
warn "$(t report_failed)"
|
||||||
|
}
|
||||||
30
lib/rollback.sh
Normal file
30
lib/rollback.sh
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
ROLLBACK_IN_PROGRESS="no"
|
||||||
|
|
||||||
|
rollback_on_error() {
|
||||||
|
local exit_code="${1:-1}"
|
||||||
|
local command_failed="${2:-unknown}"
|
||||||
|
|
||||||
|
if [[ "$ROLLBACK_IN_PROGRESS" == "yes" ]]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
ROLLBACK_IN_PROGRESS="yes"
|
||||||
|
|
||||||
|
local fail_message
|
||||||
|
fail_message="$(t deployment_failed "$exit_code" "$command_failed")"
|
||||||
|
error "$fail_message"
|
||||||
|
send_report "failed" "$fail_message"
|
||||||
|
|
||||||
|
warn "$(t rollback_starts "$SERVER_DIR")"
|
||||||
|
|
||||||
|
if [[ "${SERVICE_CREATED:-no}" == "yes" ]]; then
|
||||||
|
remove_service_if_exists
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "${LOG_FILE:-}" ]]; then
|
||||||
|
info "$(t see_log "$LOG_FILE")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit "$exit_code"
|
||||||
|
}
|
||||||
132
lib/services.sh
Normal file
132
lib/services.sh
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
SERVICE_NAME=""
|
||||||
|
SERVICE_FILE=""
|
||||||
|
SERVICE_CREATED="no"
|
||||||
|
SERVICE_MODE_ACTIVE="none"
|
||||||
|
|
||||||
|
stop_service_if_running() {
|
||||||
|
local service_name="${1:-${SERVICE_NAME:-${CORE}_minecraft}}"
|
||||||
|
|
||||||
|
if [[ "$INIT_SYSTEM" != "systemd" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${DRY_RUN:-no}" == "yes" ]]; then
|
||||||
|
info "[dry-run] systemctl stop ${service_name}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
emit_step "service_stop" "$service_name"
|
||||||
|
systemctl stop "$service_name" >/dev/null 2>&1 || true
|
||||||
|
}
|
||||||
|
|
||||||
|
create_service() {
|
||||||
|
local core="$1"
|
||||||
|
local dir="$2"
|
||||||
|
local mode="$3"
|
||||||
|
local start_script="$4"
|
||||||
|
local start_script_escaped="$4"
|
||||||
|
|
||||||
|
start_script_escaped="${start_script_escaped//\\/\\\\}"
|
||||||
|
start_script_escaped="${start_script_escaped//\"/\\\"}"
|
||||||
|
start_script_escaped="${start_script_escaped//%/%%}"
|
||||||
|
|
||||||
|
SERVICE_NAME="${core}_minecraft"
|
||||||
|
SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service"
|
||||||
|
|
||||||
|
if [[ "$mode" == "none" || "$INIT_SYSTEM" != "systemd" ]]; then
|
||||||
|
warn "$(t service_create_skipped "$mode" "$INIT_SYSTEM")"
|
||||||
|
SERVICE_MODE_ACTIVE="none"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
emit_step "service_create" "$core:$mode"
|
||||||
|
if [[ "$mode" == "tmux-systemd" ]]; then
|
||||||
|
run_cmd bash -c "cat > '$SERVICE_FILE' <<EOF
|
||||||
|
[Unit]
|
||||||
|
Description=Minecraft ${core} server (tmux)
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=forking
|
||||||
|
User=${SERVER_USER}
|
||||||
|
Group=${SERVER_USER}
|
||||||
|
WorkingDirectory=${dir}
|
||||||
|
ExecStart=/usr/bin/tmux new-session -d -s ${SERVICE_NAME} \"${start_script_escaped}\"
|
||||||
|
ExecStop=/usr/bin/tmux send-keys -t ${SERVICE_NAME} 'stop' C-m
|
||||||
|
ExecStopPost=/bin/sleep 8
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF"
|
||||||
|
elif [[ "$mode" == "screen" ]]; then
|
||||||
|
run_cmd bash -c "cat > '$SERVICE_FILE' <<EOF
|
||||||
|
[Unit]
|
||||||
|
Description=Minecraft ${core} server (screen)
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=forking
|
||||||
|
User=${SERVER_USER}
|
||||||
|
Group=${SERVER_USER}
|
||||||
|
WorkingDirectory=${dir}
|
||||||
|
ExecStart=/usr/bin/screen -dmS ${SERVICE_NAME} \"${start_script_escaped}\"
|
||||||
|
ExecStop=/usr/bin/screen -S ${SERVICE_NAME} -X quit
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF"
|
||||||
|
else
|
||||||
|
run_cmd bash -c "cat > '$SERVICE_FILE' <<EOF
|
||||||
|
[Unit]
|
||||||
|
Description=Minecraft ${core} server
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=${SERVER_USER}
|
||||||
|
Group=${SERVER_USER}
|
||||||
|
WorkingDirectory=${dir}
|
||||||
|
ExecStart="${start_script_escaped}"
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF"
|
||||||
|
fi
|
||||||
|
|
||||||
|
SERVICE_CREATED="yes"
|
||||||
|
SERVICE_MODE_ACTIVE="$mode"
|
||||||
|
emit_step "service_reload_enable" "$SERVICE_NAME"
|
||||||
|
run_cmd systemctl daemon-reload
|
||||||
|
run_cmd systemctl enable "$SERVICE_NAME"
|
||||||
|
}
|
||||||
|
|
||||||
|
start_service() {
|
||||||
|
if [[ "$SERVICE_MODE_ACTIVE" == "none" ]]; then
|
||||||
|
info "$(t service_start_skipped "$SERVICE_MODE_ACTIVE")"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
emit_step "service_start" "$SERVICE_NAME"
|
||||||
|
run_cmd systemctl restart "$SERVICE_NAME"
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_service_if_exists() {
|
||||||
|
if [[ "$SERVICE_CREATED" != "yes" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if [[ -n "$SERVICE_NAME" ]]; then
|
||||||
|
run_cmd systemctl stop "$SERVICE_NAME" || true
|
||||||
|
run_cmd systemctl disable "$SERVICE_NAME" || true
|
||||||
|
fi
|
||||||
|
if [[ -n "${SERVICE_FILE}" && -f "$SERVICE_FILE" ]]; then
|
||||||
|
run_cmd rm -f "$SERVICE_FILE"
|
||||||
|
fi
|
||||||
|
run_cmd systemctl daemon-reload || true
|
||||||
|
}
|
||||||
31
lib/users.sh
Normal file
31
lib/users.sh
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
create_minecraft_user() {
|
||||||
|
local user="$1"
|
||||||
|
|
||||||
|
if [[ "$user" == "root" ]]; then
|
||||||
|
fail "$(t refuse_root_user)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! id "$user" >/dev/null 2>&1; then
|
||||||
|
emit_step "user_create" "$user"
|
||||||
|
info "$(t creating_user "$user")"
|
||||||
|
if [[ "$DRY_RUN" == "no" ]]; then
|
||||||
|
useradd -r -m -s /bin/bash "$user"
|
||||||
|
else
|
||||||
|
info "$(t dry_run_useradd "$user")"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
emit_step "user_exists" "$user"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
prepare_server_dir() {
|
||||||
|
local dir="$1"
|
||||||
|
local user="$2"
|
||||||
|
|
||||||
|
emit_step "prepare_dir" "$dir"
|
||||||
|
run_cmd mkdir -p "$dir"
|
||||||
|
run_cmd chown -R "${user}:${user}" "$dir"
|
||||||
|
emit_step "permission_fix" "$dir"
|
||||||
|
}
|
||||||
@@ -7,12 +7,12 @@ validate_root_usage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
validate_core_support() {
|
validate_core_support() {
|
||||||
local supported_cores="pmmp,paper,purpur,velocity,powernukkitx,nethergamesmc"
|
local supported_cores="pmmp,paper,purpur,velocity,powernukkitx,nethergamesmc,bds"
|
||||||
local supported_os="ubuntu,debian"
|
local supported_os="ubuntu,debian"
|
||||||
local planned_os="arch,fedora,rocky,almalinux"
|
local planned_os="arch,fedora,rocky,almalinux"
|
||||||
|
|
||||||
case "$CORE" in
|
case "$CORE" in
|
||||||
pmmp|paper|purpur|velocity|powernukkitx|nethergamesmc)
|
pmmp|paper|purpur|velocity|powernukkitx|nethergamesmc|bds)
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
fail "$(t unsupported_core "$CORE" "$supported_cores" "${SCRIPT_DIR}/manifests/cores.json")"
|
fail "$(t unsupported_core "$CORE" "$supported_cores" "${SCRIPT_DIR}/manifests/cores.json")"
|
||||||
|
|||||||
@@ -85,12 +85,13 @@ locale_msg_port_out_of_range() { echo "Port out of range: $1"; }
|
|||||||
locale_msg_invalid_service_mode() { echo "Invalid service mode: $1"; }
|
locale_msg_invalid_service_mode() { echo "Invalid service mode: $1"; }
|
||||||
locale_msg_invalid_firewall_value() { echo "Invalid --firewall value: $1"; }
|
locale_msg_invalid_firewall_value() { echo "Invalid --firewall value: $1"; }
|
||||||
|
|
||||||
locale_msg_prompt_core() { echo "Core (pmmp|paper|purpur|velocity|powernukkitx|nethergamesmc)"; }
|
locale_msg_prompt_core() { echo "Core (pmmp|paper|purpur|velocity|powernukkitx|nethergamesmc|bds)"; }
|
||||||
locale_msg_prompt_server_directory() { echo "Server directory"; }
|
locale_msg_prompt_server_directory() { echo "Server directory"; }
|
||||||
locale_msg_prompt_server_port() { echo "Server port"; }
|
locale_msg_prompt_server_port() { echo "Server port"; }
|
||||||
locale_msg_prompt_ram() { echo "RAM"; }
|
locale_msg_prompt_ram() { echo "RAM"; }
|
||||||
locale_msg_prompt_php_build() { echo "PHP build tag (latest|tag)"; }
|
locale_msg_prompt_php_build() { echo "PHP build tag (latest|tag)"; }
|
||||||
locale_msg_prompt_pocketmine_version() { echo "PocketMine-MCPE version/tag (latest|tag)"; }
|
locale_msg_prompt_pocketmine_version() { echo "PocketMine-MCPE version/tag (latest|tag)"; }
|
||||||
|
locale_msg_prompt_bds_version() { echo "Bedrock Dedicated Server version (latest|version)"; }
|
||||||
locale_msg_prompt_service_mode() { echo "Service mode"; }
|
locale_msg_prompt_service_mode() { echo "Service mode"; }
|
||||||
locale_msg_prompt_open_firewall() { echo "Open firewall (yes/no)"; }
|
locale_msg_prompt_open_firewall() { echo "Open firewall (yes/no)"; }
|
||||||
locale_msg_prompt_install_user() { echo "Install user"; }
|
locale_msg_prompt_install_user() { echo "Install user"; }
|
||||||
@@ -105,6 +106,7 @@ locale_msg_easy_plan_user() { echo "Service user: $1"; }
|
|||||||
locale_msg_easy_plan_firewall() { echo "Firewall: $1"; }
|
locale_msg_easy_plan_firewall() { echo "Firewall: $1"; }
|
||||||
locale_msg_easy_plan_build() { echo "PHP build: $1"; }
|
locale_msg_easy_plan_build() { echo "PHP build: $1"; }
|
||||||
locale_msg_easy_plan_mc_version() { echo "PocketMine-MCPE tag: $1"; }
|
locale_msg_easy_plan_mc_version() { echo "PocketMine-MCPE tag: $1"; }
|
||||||
|
locale_msg_easy_plan_bds_version() { echo "BDS version: $1"; }
|
||||||
locale_msg_easy_plan_eula() { echo "EULA: $1"; }
|
locale_msg_easy_plan_eula() { echo "EULA: $1"; }
|
||||||
|
|
||||||
locale_msg_dir_not_empty_auto() { echo "Directory '$1' is not empty. Add --allow-existing, --backup-existing or --force-clean."; }
|
locale_msg_dir_not_empty_auto() { echo "Directory '$1' is not empty. Add --allow-existing, --backup-existing or --force-clean."; }
|
||||||
|
|||||||
@@ -85,12 +85,13 @@ locale_msg_port_out_of_range() { echo "Порт вне диапазона: $1";
|
|||||||
locale_msg_invalid_service_mode() { echo "Неподдерживаемый режим сервиса: $1"; }
|
locale_msg_invalid_service_mode() { echo "Неподдерживаемый режим сервиса: $1"; }
|
||||||
locale_msg_invalid_firewall_value() { echo "Недопустимое значение --firewall: $1"; }
|
locale_msg_invalid_firewall_value() { echo "Недопустимое значение --firewall: $1"; }
|
||||||
|
|
||||||
locale_msg_prompt_core() { echo "Ядро (pmmp|paper|purpur|velocity|powernukkitx|nethergamesmc)"; }
|
locale_msg_prompt_core() { echo "Ядро (pmmp|paper|purpur|velocity|powernukkitx|nethergamesmc|bds)"; }
|
||||||
locale_msg_prompt_server_directory() { echo "Каталог сервера"; }
|
locale_msg_prompt_server_directory() { echo "Каталог сервера"; }
|
||||||
locale_msg_prompt_server_port() { echo "Порт сервера"; }
|
locale_msg_prompt_server_port() { echo "Порт сервера"; }
|
||||||
locale_msg_prompt_ram() { echo "RAM"; }
|
locale_msg_prompt_ram() { echo "RAM"; }
|
||||||
locale_msg_prompt_php_build() { echo "Тег PHP-билда (latest|tag)"; }
|
locale_msg_prompt_php_build() { echo "Тег PHP-билда (latest|tag)"; }
|
||||||
locale_msg_prompt_pocketmine_version() { echo "Версия/тег PocketMine-MCPE (latest|tag)"; }
|
locale_msg_prompt_pocketmine_version() { echo "Версия/тег PocketMine-MCPE (latest|tag)"; }
|
||||||
|
locale_msg_prompt_bds_version() { echo "Версия Bedrock Dedicated Server (latest|версия)"; }
|
||||||
locale_msg_prompt_service_mode() { echo "Режим сервиса"; }
|
locale_msg_prompt_service_mode() { echo "Режим сервиса"; }
|
||||||
locale_msg_prompt_open_firewall() { echo "Открыть firewall (yes/no)"; }
|
locale_msg_prompt_open_firewall() { echo "Открыть firewall (yes/no)"; }
|
||||||
locale_msg_prompt_install_user() { echo "Пользователь для установки"; }
|
locale_msg_prompt_install_user() { echo "Пользователь для установки"; }
|
||||||
@@ -105,6 +106,7 @@ locale_msg_easy_plan_user() { echo "Пользователь сервиса: $1"
|
|||||||
locale_msg_easy_plan_firewall() { echo "Firewall: $1"; }
|
locale_msg_easy_plan_firewall() { echo "Firewall: $1"; }
|
||||||
locale_msg_easy_plan_build() { echo "PHP-билд: $1"; }
|
locale_msg_easy_plan_build() { echo "PHP-билд: $1"; }
|
||||||
locale_msg_easy_plan_mc_version() { echo "Версия/тег PocketMine-MCPE: $1"; }
|
locale_msg_easy_plan_mc_version() { echo "Версия/тег PocketMine-MCPE: $1"; }
|
||||||
|
locale_msg_easy_plan_bds_version() { echo "Версия BDS: $1"; }
|
||||||
locale_msg_easy_plan_eula() { echo "EULA: $1"; }
|
locale_msg_easy_plan_eula() { echo "EULA: $1"; }
|
||||||
|
|
||||||
locale_msg_dir_not_empty_auto() { echo "Каталог '$1' не пустой. Укажите --allow-existing, --backup-existing или --force-clean."; }
|
locale_msg_dir_not_empty_auto() { echo "Каталог '$1' не пустой. Укажите --allow-existing, --backup-existing или --force-clean."; }
|
||||||
|
|||||||
@@ -62,5 +62,15 @@
|
|||||||
"default_dir": "/opt/minecraft/nethergamesmc",
|
"default_dir": "/opt/minecraft/nethergamesmc",
|
||||||
"supports_protocols": ["udp"],
|
"supports_protocols": ["udp"],
|
||||||
"supported_services": ["systemd", "tmux-systemd", "screen", "none"]
|
"supported_services": ["systemd", "tmux-systemd", "screen", "none"]
|
||||||
|
},
|
||||||
|
"bds": {
|
||||||
|
"name": "Minecraft Bedrock Dedicated Server",
|
||||||
|
"edition": "bedrock",
|
||||||
|
"runtime": "native",
|
||||||
|
"default_port": 19132,
|
||||||
|
"protocol": "udp",
|
||||||
|
"default_dir": "/opt/minecraft/bds",
|
||||||
|
"supports_protocols": ["udp"],
|
||||||
|
"supported_services": ["systemd", "tmux-systemd", "screen", "none"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
15
manifests/os-support.json
Normal file
15
manifests/os-support.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"supports": {
|
||||||
|
"mvp": ["ubuntu", "debian"],
|
||||||
|
"planned": ["arch", "fedora", "rocky", "almalinux"]
|
||||||
|
},
|
||||||
|
"service": {
|
||||||
|
"systemd": ["ubuntu", "debian", "fedora", "rocky", "almalinux"]
|
||||||
|
},
|
||||||
|
"java_21": {
|
||||||
|
"apt": "openjdk-21-jre-headless",
|
||||||
|
"dnf": "java-21-openjdk-headless",
|
||||||
|
"yum": "java-21-openjdk-headless",
|
||||||
|
"pacman": "jre-openjdk-headless"
|
||||||
|
}
|
||||||
|
}
|
||||||
15
templates/service-systemd.tpl
Normal file
15
templates/service-systemd.tpl
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Minecraft {{CORE_NAME}} server
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User={{SERVER_USER}}
|
||||||
|
Group={{SERVER_USER}}
|
||||||
|
WorkingDirectory={{SERVER_DIR}}
|
||||||
|
ExecStart={{START_SCRIPT}}
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
17
templates/service-tmux.tpl
Normal file
17
templates/service-tmux.tpl
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Minecraft {{CORE_NAME}} server (tmux)
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=forking
|
||||||
|
User={{SERVER_USER}}
|
||||||
|
Group={{SERVER_USER}}
|
||||||
|
WorkingDirectory={{SERVER_DIR}}
|
||||||
|
ExecStart=/usr/bin/tmux new-session -d -s {{SERVICE_NAME}} '{{START_SCRIPT}}'
|
||||||
|
ExecStop=/usr/bin/tmux send-keys -t {{SERVICE_NAME}} 'stop' C-m
|
||||||
|
ExecStopPost=/bin/sleep 8
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
4
templates/start-java.tpl
Normal file
4
templates/start-java.tpl
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
exec java -Xms{{RAM}} -Xmx{{RAM}} {{JAVA_OPTS}} -jar {{JAR_FILE}}
|
||||||
4
templates/start-pmmp.tpl
Normal file
4
templates/start-pmmp.tpl
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
./start.sh "$@"
|
||||||
Reference in New Issue
Block a user