Files
pzmanager/README.md
T
mrleo1nidandClaude Opus 5 1e659678b9
CI / test (push) Successful in 23s
Release / release (push) Successful in 30s
«Работает» — только после строки о готовности сервера
Регистрацию в Steam сервер печатает задолго до того, как мир загружен, и по
ней панель показывала «Работает» на сервере, который ещё никого не пустит.
Теперь готовность распознаётся только по «*** SERVER STARTED ****», а пока
идёт загрузка, состояние остаётся «Запускается» с пояснением под ним и
счётчиком времени загрузки.

Если строки о готовности так и не будет (своя версия, свои моды), через
пятнадцать минут менеджер сочтёт сервер запущенным и скажет об этом в
консоли: иначе панель навсегда осталась бы без опроса игроков.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-06 14:15:54 +03:00

474 lines
29 KiB
Markdown

# PZ Manager
[![release](https://img.shields.io/gitea/v/release/mrleo1nid/pzmanager?gitea_url=https%3A%2F%2Fgitea.hsrv.site&logo=gitea&logoColor=white&label=релиз)](https://gitea.hsrv.site/mrleo1nid/pzmanager/releases/latest)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
[![Go](https://img.shields.io/badge/Go-1.24%2B-00ADD8?logo=go&logoColor=white)](https://go.dev/)
[![Platform](https://img.shields.io/badge/platform-Linux%20%2F%20Ubuntu-EEEEEE?logo=ubuntu&logoColor=white&labelColor=555555)](https://ubuntu.com/)
[![last commit](https://img.shields.io/gitea/last-commit/mrleo1nid/pzmanager?gitea_url=https%3A%2F%2Fgitea.hsrv.site&logo=gitea&logoColor=white)](https://gitea.hsrv.site/mrleo1nid/pzmanager/commits/branch/main)
Веб-панель для управления выделенным сервером Project Zomboid на Ubuntu.
Один бинарник без зависимостей: веб-интерфейс вшит внутрь, Node.js и Python на
сервере не нужны.
## Что умеет
- **Профили серверов** — несколько независимых сборок (свой мир, конфиг,
песочница, моды и лимит памяти) с переключением одной кнопкой.
- **Управление сервером** — запуск, остановка (через команду `quit`, мир
сохраняется), перезапуск, автоподъём после падения, автозапуск вместе с
менеджером. «Работает» панель показывает не раньше, чем сервер напечатает
`*** SERVER STARTED ****`: пока идёт загрузка мира, состояние остаётся
«Запускается».
- **Живая консоль** — вывод сервера в реальном времени и отправка любых команд
(`players`, `save`, `servermsg`, `kickuser`, …).
- **Мониторинг** — состояние, аптайм, CPU и память процесса сервера, свободные
ОЗУ и диск на хосте, список игроков онлайн.
- **Редактор конфигов** — `servertest.ini` и `SandboxVars.lua` прямо в
браузере: параметры разложены по разделам, у каждого видно описание из
самого конфига, поиск идёт и по названию, и по описанию.
- **Мод-менеджер** — моды списком с галочками и перетаскиванием порядка
загрузки, добавление по ссылке и разворачивание коллекций Steam целиком.
- **Бэкапы** — ручные и по расписанию, отдельно по каждому профилю, с
ротацией, скачиванием и восстановлением (перед восстановлением
автоматически снимается страховочная копия).
- **Два языка** — русский и английский, с выбором в шапке и автоопределением
по языку браузера.
- **Установка и обновление** сервера через SteamCMD прямо из панели.
- **Обновление самой панели** — кнопкой из интерфейса или одной командой в
терминале.
## Как это устроено
Менеджер запускает `start-server.sh` как дочерний процесс и держит его stdin,
stdout и stderr. Отсюда следует всё остальное: команды консоли пишутся прямо в
stdin (RCON не нужен), логи стримятся в браузер через SSE, а остановка идёт по
цепочке `quit``SIGTERM``SIGKILL` для всей группы процессов.
Сам менеджер живёт под systemd, который его и перезапускает.
```
systemd ──> pzmanager ──> start-server.sh ──> java (GameServer)
├── HTTP + SSE ──> браузер
└── SteamCMD, бэкапы, конфиги
```
## Быстрый старт
На чистом Ubuntu/Debian всё ставится одной командой — она возьмёт последний
релиз, проверит контрольные суммы и настроит systemd:
```bash
curl -fsSL https://gitea.hsrv.site/mrleo1nid/pzmanager/raw/branch/main/deploy/get.sh | sudo bash
```
По умолчанию панель слушает `127.0.0.1:8080` — только с самой машины. Чтобы
она была доступна в локальной сети, укажите адрес сервера:
```bash
curl -fsSL https://gitea.hsrv.site/mrleo1nid/pzmanager/raw/branch/main/deploy/get.sh \
| sudo PZ_LISTEN=10.10.1.142:8080 bash
```
Переменные: `PZ_LISTEN` (адрес панели), `PZ_HOME` (каталог, по умолчанию
`/opt/pzmanager`), `PZ_USER` (системный пользователь), `PZ_VERSION`
(конкретный тег вместо последнего).
Скрипт запускается от root, так что перед первым запуском его разумно
прочитать: [deploy/get.sh](deploy/get.sh).
Установщик поставит зависимости (SteamCMD, JRE, SDL2), заведёт пользователя
`pzserver` без shell, положит бинарник в `/usr/local/bin` и создаст структуру:
```
/opt/pzmanager/
├── config.yaml настройки менеджера
├── data/ учётки панели и скачанный SteamCMD
├── server/ серверные файлы Project Zomboid
├── Zomboid/ миры, конфиги и логи игры
└── backups/ архивы
```
**Обновление** — та же команда или кнопка в панели (вкладка «Настройки» →
«Версия панели»). Конфиг, миры и учётные записи не трогаются. Повторный запуск
с той же версией службу не трогает вовсе: если бинарник не изменился,
установщик не станет ради него ронять игровой сервер.
Дальше:
1. Откройте панель в браузере и создайте администратора, введя код первичной
настройки — установщик печатает его последним сообщением. Если панель
слушает localhost, пробросьте порт:
```bash
ssh -L 8080:127.0.0.1:8080 пользователь@сервер
```
Код одноразовый и перестаёт работать сразу после создания администратора.
Если вывод установщика потерялся, код есть в журнале:
```bash
journalctl -u pzmanager -n 30 --no-pager
```
2. Во вкладке «Обзор» нажмите **Установить / обновить через SteamCMD** и
дождитесь окончания — прогресс виден во вкладке «Консоль».
3. Запустите сервер. При первом запуске PZ создаст `Zomboid/Server/*.ini` —
после этого станут доступны вкладки «Конфиг сервера», «Песочница» и «Моды».
Порты, которые нужно открыть:
```bash
sudo ufw allow 8080/tcp # веб-панель, если она слушает не localhost
sudo ufw allow 16261/udp # игровой порт
sudo ufw allow 16262/udp # второй игровой порт
```
### Установка из исходников
Если релизам предпочитаете свою сборку (нужен Go 1.24+, собирать можно на
любой ОС, включая Windows):
```bash
make build-linux
```
Скопируйте `pzmanager` и папку `deploy/` на сервер и запустите установщик:
```bash
sudo PZ_LISTEN=10.10.1.142:8080 ./deploy/install.sh
```
### Удаление
Убрать панель, оставив миры и настройки:
```bash
sudo /opt/pzmanager/install.sh --uninstall
```
Удалить всё вместе с мирами, бэкапами и учётными записями:
```bash
sudo /opt/pzmanager/install.sh --uninstall --purge
```
`--purge` спрашивает подтверждение; в неинтерактивном запуске нужен ещё и
`--yes`. Каталог сносится, только если похож на установку панели — иначе
опечатка в `PZ_HOME` стоила бы чужих файлов.
Установщик кладёт свою копию в `/opt/pzmanager/install.sh`, так что для
удаления ничего скачивать не нужно. Если копии нет, те же аргументы принимает
и установочная команда:
```bash
curl -fsSL https://gitea.hsrv.site/mrleo1nid/pzmanager/raw/branch/main/deploy/get.sh | sudo bash -s -- --uninstall --purge
```
## Язык интерфейса
Панель говорит по-русски и по-английски. Язык выбирается списком в шапке (и на
экране входа, до того как вошли), а до первого выбора берётся из настроек
браузера: русский — русским, всем остальным — английский. Выбор запоминается в
браузере, поэтому у каждого, кто заходит в панель, он свой.
Переводится всё, что показывает панель: интерфейс, подписи и описания всех
параметров `servertest.ini` и `SandboxVars.lua`, ошибки и служебные сообщения
менеджера в консоли. Вывод самого игрового сервера остаётся как есть — он
приходит от Project Zomboid.
## Моды
Project Zomboid хранит моды в двух строках конфига: `WorkshopItems` — что
скачать из мастерской, `Mods` — что включить и в каком порядке. Панель
показывает их одним списком, где каждая строка — мод:
- **галочка** включает мод (добавляет его Mod ID в `Mods`);
- **перетаскивание** (или стрелки) задаёт порядок загрузки — для модов,
которые переопределяют одни и те же файлы, он решает, чья версия победит;
- **«Убрать»** выбрасывает пакет из `WorkshopItems` вместе со всеми его модами.
Добавить мод можно ссылкой на страницу мастерской или голым ID. Кнопка
**«Добавить коллекцию»** разворачивает коллекцию в список входящих в неё модов
— порядок берётся тот же, что на странице коллекции.
Mod ID, который нужен серверу, лежит внутри самого мода (`mod.info`), поэтому
панель читает уже скачанные моды с диска:
```
<server_dir>/steamapps/workshop/content/108600/<workshop id>/mods/<мод>/mod.info
~/Zomboid/mods/<мод>/mod.info # моды, положенные вручную
```
Пока сервер не скачал мод, его строка помечена «ещё не скачан» и галочка
недоступна: Mod ID неизвестен. Файлы загружает сам сервер при запуске по
списку `WorkshopItems` — то есть достаточно добавить моды, сохранить и
запустить сервер, после чего они станут доступны для включения.
Один пакет мастерской может содержать несколько модов — в списке они идут
отдельными строками, и включать их можно по отдельности.
## Редактор конфигов
Вкладки «Конфиг сервера» и «Песочница» показывают параметры не сплошным
списком, а разделами: «Основное», «PVP и урон», «Убежища», «Античит» и так
далее. Разделы сворачиваются, а поиск сверху раскрывает те, где нашлось
совпадение. Ключи, которых панель не знает (в новой версии игры их всегда
добавляют), не теряются — они собираются в раздел «Прочее».
Подписи и описания у панели свои, на языке интерфейса: игра пишет свои
комментарии на языке сервера, а на Linux он почти всегда английский. Расшифровку
конкретных значений (что означает 1, 2, 3) панель добавляет из самого конфига.
Полный текст виден при наведении, начало — прямо под полем.
Правка меняет ровно ту строку, которую поправили: комментарии, порядок и
переводы строк файла сохраняются, а рядом остаётся копия прежней версии с
расширением `.bak`.
## Профили серверов
Профиль — это отдельный сервер: свой мир, свой `*.ini`, свои настройки
песочницы, свой список модов и свой лимит памяти. Project Zomboid различает их
по параметру `-servername`, поэтому профили не мешают друг другу:
```
~/Zomboid/Server/vanilla.ini ~/Zomboid/Saves/Multiplayer/vanilla/
~/Zomboid/Server/vanilla_SandboxVars.lua
~/Zomboid/Server/modded.ini ~/Zomboid/Saves/Multiplayer/modded/
~/Zomboid/Server/modded_SandboxVars.lua
```
Работает всегда один профиль — активный: серверные файлы, игровые порты и файл
запуска JVM общие. Переключить профиль можно на остановленном сервере, выбрав
его в шапке панели или на вкладке «Профили»; после этого все вкладки —
конфиг, песочница, моды, бэкапы — относятся уже к нему.
При создании профиля можно скопировать `.ini` и настройки песочницы из
существующего: удобно, когда нужен тот же набор правил, но чистый мир.
### Память JVM
`java_memory` — это `-Xmx`, верхняя граница кучи. `java_memory_min` (`-Xms`)
по умолчанию пуст, и это осознанно: равные значения избавляют от роста кучи на
ходу, но заставляют JVM занять весь объём сразу при старте. На машине, где
столько свободной памяти не набирается, сервер тогда не поднимется вовсе —
`Failed to allocate initial Java heap`. Задавайте минимум, только если памяти
заведомо хватает.
Сервер, упавший сразу после запуска три раза подряд, панель перестаёт
поднимать автоматически: обычно так проявляется нехватка памяти или битый
конфиг, и перезапуск делу не помогает. Пока идёт отсчёт до автоподъёма, его
можно отменить кнопкой «Отменить перезапуск».
Удаление профиля убирает его только из панели — мир и конфиги остаются на
диске. Чтобы вернуть профиль, создайте его с тем же идентификатором.
Обновление с версии, где сервер был один, проходит само: старые поля
`server_name`, `admin_password` и `java_memory` превращаются в первый профиль
при первом запуске.
## Обновление
Панель показывает свою версию и следит за релизами в репозитории: вкладка
«Настройки» → «Версия панели». Если вышла новая версия, там появляется кнопка
**«Обновить до …»**.
Обновление из интерфейса устроено так. Сама панель работает от
непривилегированного пользователя и не может ни заменить свой бинарник в
`/usr/local/bin`, ни дёрнуть `systemctl`. Поэтому установщик кладёт рядом
маленький root-скрипт `/usr/local/bin/pzmanager-admin` и разрешает сервисному
пользователю ровно его одну команду:
```
pzserver ALL=(root) NOPASSWD: /usr/local/bin/pzmanager-admin
```
Скрипт не повторяет логику установки, а забирает из релиза `get.sh` (сверив
его sha256) и отдаёт работу ему — поэтому кнопка обновляет всё сразу: бинарник,
скрипты установки и systemd-юнит. Панель к этим файлам доступа не имеет, она
может только попросить выполнить `update` или `restart`.
Работа уходит в отдельный разовый юнит (`systemd-run --unit=pzmanager-update`).
Иначе обновление не пережило бы само себя: панель вызывает скрипт через sudo,
тот живёт в cgroup её юнита, и остановка службы убила бы его на середине.
Побочный эффект — вывод идёт не в консоль панели, а в свой журнал:
```bash
journalctl -u pzmanager-update -f
```
Если правило sudo не установлено (панель поставлена вручную или в системе нет
`sudo`), кнопка не появится, а рядом будет показана команда для терминала:
```bash
curl -fsSL https://gitea.hsrv.site/mrleo1nid/pzmanager/raw/branch/main/deploy/get.sh | sudo bash
```
Обновление и перезапуск панели останавливают игровой сервер — панель делает
это сама, штатной командой `quit`, чтобы мир успел сохраниться.
## Доступ снаружи
Панель управляет сервером, поэтому наружу её лучше не выставлять голой. Два
разумных варианта:
**SSH-туннель (по умолчанию)** — ничего настраивать не нужно, панель слушает
только localhost.
**Доверенная локальная сеть** — `PZ_LISTEN=<ip>:8080` при установке или
`listen` в конфиге. Учтите, что HTTP не шифруется: пароль от панели идёт по
сети открытым текстом, так что для доступа через интернет этот вариант не
годится.
**HTTPS через reverse-proxy** — если панель нужна с телефона. Настройте nginx с
сертификатом (например, от Let's Encrypt):
```nginx
server {
listen 443 ssl;
server_name pz.example.com;
ssl_certificate /etc/letsencrypt/live/pz.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/pz.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
# Живая консоль работает через Server-Sent Events.
proxy_buffering off;
proxy_read_timeout 24h;
}
}
```
И запустите менеджер с флагом `--secure-cookies`, чтобы cookie сессии
передавалась только по HTTPS (добавьте флаг в `ExecStart` юнита).
## Команды
```bash
pzmanager serve # запустить панель (команда по умолчанию)
pzmanager useradd <логин> # создать пользователя панели
pzmanager passwd <логин> # сменить пароль
pzmanager users # список пользователей
```
Флаги: `--config <путь>`, `--listen <адрес>`, `--secure-cookies`.
## Конфигурация
Файл `/opt/pzmanager/config.yaml` создаёт установщик (при запуске без него
менеджер возьмёт `~/.config/pzmanager/config.yaml`). Всё, что в нём есть,
правится и через вкладку «Настройки».
```yaml
listen: 127.0.0.1:8080 # адрес веб-панели; 0.0.0.0 или IP — доступ по сети
data_dir: /opt/pzmanager/data
server_dir: /opt/pzmanager/server # куда SteamCMD ставит сервер
zomboid_dir: /opt/pzmanager/Zomboid # миры, конфиги и логи игры
steamcmd_path: "" # пусто — найти в PATH или скачать
profiles: # серверы: у каждого свой мир и настройки
- id: vanilla # vanilla -> vanilla.ini, Saves/Multiplayer/vanilla
title: Ванильный мир # как профиль подписан в панели
admin_password: "" # пароль админа игрового сервера
java_memory: 4g # -Xmx: верхняя граница кучи JVM
java_memory_min: "" # -Xms: пусто — JVM берёт память по мере надобности
extra_args: [] # доп. аргументы start-server.sh
- id: modded
title: Сборка с модами
java_memory: 8g
active_profile: vanilla # какой профиль запускается и настраивается
autostart: false # поднимать сервер вместе с менеджером
autorestart: true # поднимать сервер после падения
stop_timeout: 1m30s # сколько ждать после quit до SIGTERM
log_buffer_lines: 5000 # строк консоли в памяти
backup:
dir: /opt/pzmanager/backups
schedule: 6h # 0 — отключить автобэкапы
keep: 20 # архивов на профиль
stop_server: false # останавливать сервер на время бэкапа
```
Изменения `listen` применяются после `systemctl restart pzmanager`; остальные —
при следующем запуске игрового сервера.
## Бэкапы
В архив попадают мир активного профиля и его серверные конфиги. Имя файла —
`pz-профиль-ГГГГММДД-ЧЧММСС[-пометка].tar.gz`, так что архивы разных сборок не
путаются, а ротация (`backup.keep`) считается по каждому профилю отдельно.
Архивы, снятые до появления профилей, панель тоже читает и восстанавливает.
Бэкап на живом сервере технически корректен, но снимок может отставать от
состояния мира в памяти. Если хочется гарантированной согласованности —
включите `backup.stop_server` (игроков на время бэкапа выкинет) или отправляйте
`save` в консоль перед ручным бэкапом.
Восстановление требует остановленного сервера. Перед распаковкой менеджер сам
делает страховочную копию с пометкой `pre-restore`.
## Безопасность
- Пароли пользователей панели хранятся в виде bcrypt-хешей в
`data_dir/users.json` (права 0600).
- Сессии живут только в памяти: перезапуск менеджера разлогинивает всех.
- Первого администратора можно создать только с одноразовым кодом из журнала.
- Изменяющие запросы требуют собственный заголовок — это защита от CSRF.
- Панель не выполняет произвольные команды хоста: всё, что она умеет, — это
управление процессом сервера, SteamCMD и файлами в своих директориях.
Тем не менее доступ к панели равносилен доступу к игровому серверу, так что
пароль стоит выбрать не короче того, что вы поставили бы на SSH.
## Сборка и релизы
Репозиторий собирается Gitea Actions ([`.gitea/workflows`](.gitea/workflows)):
- **CI** — на каждый push и pull request в `main`: `gofmt`, `go vet`, тесты,
проверка сборки под Linux и синтаксиса скриптов установки.
- **Release** — на тег `v*`: собирает бинарники под `linux/amd64` и
`linux/arm64`, считает контрольные суммы и публикует релиз с ними и
скриптами установки.
Выпуск новой версии:
```bash
git tag -a v1.2.3 -m "Описание релиза" && git push origin v1.2.3
```
Версия попадает в бинарник через `-ldflags -X main.version=` и видна в
`pzmanager version` и в панели на вкладке «Настройки».
Workflow не зависят от github.com и от Node внутри контейнера: код забирается
через `git`, а релиз публикуется прямо в API Gitea. Токен Actions выдаёт сам;
если в настройках инстанса его права урезаны, заведите секрет `RELEASE_TOKEN`
с доступом на запись в репозиторий.
Для работы Actions нужен зарегистрированный runner с меткой `ubuntu-latest`,
умеющий запускать контейнеры (`docker`).
## Разработка
```bash
make test # тесты
make vet # go vet
make run # локальный запуск с конфигом в ./dev
```
Веб-интерфейс — обычные HTML, CSS и JS в `web/static`, без сборщика; при
`go build` они вшиваются в бинарник через `embed`.
Переводы лежат рядом: строки интерфейса — в `web/static/i18n.js`, подписи
параметров конфигов — в `web/static/hints-ru.json` и `hints-en.json`
(загружается только нужный язык), сообщения самого менеджера — в
`internal/i18n/catalog.go`, где ключом служит русская фраза из кода. Тесты в
`web` и `internal/i18n` следят, чтобы словари не разъезжались.
## Лицензия
[MIT](LICENSE) — делайте с кодом что угодно, но без каких-либо гарантий.