Files
pzmanager/README.md
T
mrleo1nidandClaude Opus 5 ae8c511a9a
CI / test (push) Successful in 24s
Release / release (push) Successful in 33s
Импорт пресета не терял моды: полный набор и активный профиль
В пакете сборки рядом с комплектом сервера лежит пресет песочницы одиночной
игры. Наборы сортировались по имени, и пресет песочницы часто оказывался
первым — панель предлагала его по умолчанию, а модов и карт в нём нет.
Теперь первым идёт набор с конфигом сервера, а в списке выбора видно, что
внутри каждого.

Вкладки показывают активный профиль, поэтому импорт в новый выглядел так,
будто ничего не подтянулось. Появилась галочка «Сделать этот профиль
активным»; если её снять, панель говорит, где искать импортированное.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-06 20:21:41 +03:00

36 KiB
Raw Permalink Blame History

PZ Manager

release License: MIT Go Platform last commit

Веб-панель для управления выделенным сервером Project Zomboid на Ubuntu. Один бинарник без зависимостей: веб-интерфейс вшит внутрь, Node.js и Python на сервере не нужны.

Что умеет

  • Профили серверов — несколько независимых сборок (свой мир, конфиг, песочница, моды и лимит памяти) с переключением одной кнопкой.
  • Управление сервером — запуск, остановка (через команду quit, мир сохраняется), перезапуск, автоподъём после падения, автозапуск вместе с менеджером. «Работает» панель показывает не раньше, чем сервер напечатает *** SERVER STARTED ****: пока идёт загрузка мира, состояние остаётся «Запускается».
  • Живая консоль — вывод сервера в реальном времени и отправка любых команд (players, save, servermsg, kickuser, …).
  • Мониторинг — состояние, аптайм, CPU и память процесса сервера, свободные ОЗУ и диск на хосте, список игроков онлайн.
  • Редактор конфиговservertest.ini и SandboxVars.lua прямо в браузере: параметры разложены по разделам, у каждого видно описание из самого конфига, поиск идёт и по названию, и по описанию.
  • Мод-менеджер — моды списком с галочками и перетаскиванием порядка загрузки, добавление по ссылке и разворачивание коллекций Steam целиком.
  • Импорт пресетов сборок — ZIP-пакет сборки загружается прямо в панель: конфиг, песочница и точки появления попадают в новый или существующий профиль, свои порты и пароли при этом сохраняются. Отдельно принимаются пресет песочницы одиночной игры (.cfg) и строка со списком модов из игровой кнопки «Поделиться».
  • Бэкапы — ручные и по расписанию, отдельно по каждому профилю, с ротацией, скачиванием и восстановлением (перед восстановлением автоматически снимается страховочная копия).
  • Два языка — русский и английский, с выбором в шапке и автоопределением по языку браузера.
  • Установка и обновление сервера через SteamCMD прямо из панели.
  • Обновление самой панели — кнопкой из интерфейса или одной командой в терминале.

Как это устроено

Менеджер запускает start-server.sh как дочерний процесс и держит его stdin, stdout и stderr. Отсюда следует всё остальное: команды консоли пишутся прямо в stdin (RCON не нужен), логи стримятся в браузер через SSE, а остановка идёт по цепочке quitSIGTERMSIGKILL для всей группы процессов.

Сам менеджер живёт под systemd, который его и перезапускает.

systemd ──> pzmanager ──> start-server.sh ──> java (GameServer)
                │
                ├── HTTP + SSE ──> браузер
                └── SteamCMD, бэкапы, конфиги

Быстрый старт

На чистом Ubuntu/Debian всё ставится одной командой — она возьмёт последний релиз, проверит контрольные суммы и настроит systemd:

curl -fsSL https://gitea.hsrv.site/mrleo1nid/pzmanager/raw/branch/main/deploy/get.sh | sudo bash

По умолчанию панель слушает 127.0.0.1:8080 — только с самой машины. Чтобы она была доступна в локальной сети, укажите адрес сервера:

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.

Установщик поставит зависимости (SteamCMD, JRE, SDL2), заведёт пользователя pzserver без shell, положит бинарник в /usr/local/bin и создаст структуру:

/opt/pzmanager/
├── config.yaml     настройки менеджера
├── data/           учётки панели и скачанный SteamCMD
├── server/         серверные файлы Project Zomboid
├── Zomboid/        миры, конфиги и логи игры
└── backups/        архивы

Обновление — та же команда или кнопка в панели (вкладка «Настройки» → «Версия панели»). Конфиг, миры и учётные записи не трогаются. Повторный запуск с той же версией службу не трогает вовсе: если бинарник не изменился, установщик не станет ради него ронять игровой сервер.

Дальше:

  1. Откройте панель в браузере и создайте администратора, введя код первичной настройки — установщик печатает его последним сообщением. Если панель слушает localhost, пробросьте порт:

    ssh -L 8080:127.0.0.1:8080 пользователь@сервер
    

    Код одноразовый и перестаёт работать сразу после создания администратора. Если вывод установщика потерялся, код есть в журнале:

    journalctl -u pzmanager -n 30 --no-pager
    
  2. Во вкладке «Обзор» нажмите Установить / обновить через SteamCMD и дождитесь окончания — прогресс виден во вкладке «Консоль».

  3. Запустите сервер. При первом запуске PZ создаст Zomboid/Server/*.ini — после этого станут доступны вкладки «Конфиг сервера», «Песочница» и «Моды».

Порты, которые нужно открыть:

sudo ufw allow 8080/tcp     # веб-панель, если она слушает не localhost
sudo ufw allow 16261/udp    # игровой порт
sudo ufw allow 16262/udp    # второй игровой порт

Установка из исходников

Если релизам предпочитаете свою сборку (нужен Go 1.24+, собирать можно на любой ОС, включая Windows):

make build-linux

Скопируйте pzmanager и папку deploy/ на сервер и запустите установщик:

sudo PZ_LISTEN=10.10.1.142:8080 ./deploy/install.sh

Удаление

Убрать панель, оставив миры и настройки:

sudo /opt/pzmanager/install.sh --uninstall

Удалить всё вместе с мирами, бэкапами и учётными записями:

sudo /opt/pzmanager/install.sh --uninstall --purge

--purge спрашивает подтверждение; в неинтерактивном запуске нужен ещё и --yes. Каталог сносится, только если похож на установку панели — иначе опечатка в PZ_HOME стоила бы чужих файлов.

Установщик кладёт свою копию в /opt/pzmanager/install.sh, так что для удаления ничего скачивать не нужно. Если копии нет, те же аргументы принимает и установочная команда:

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.

Импорт пресетов сборок

Крупные сборки модов раздают готовый комплект настроек архивом: внутри лежат папка Server с <имя>.ini, <имя>_SandboxVars.lua и <имя>_spawnregions.lua, а рядом — файлы для одиночной игры, которые серверу не нужны.

На вкладке «Профили» есть «Импорт пресета сборки»: выберите ZIP, и панель покажет, что в нём нашла — название сервера, сколько модов и пакетов мастерской, какие карты, сколько параметров конфига и песочницы. Ничего не записывается, пока вы не нажмёте «Импортировать».

Если наборов в архиве несколько, появится список выбора. Первым идёт самый полный набор — тот, где есть конфиг сервера: только в нём лежат моды и карты. Рядом с именем написано, что внутри, чтобы случайно не импортировать пресет песочницы вместо комплекта сервера.

Импортировать можно в новый профиль (идентификатор предлагается по имени файлов из архива) или поверх существующего. Прежние конфиги остаются рядом с расширением .bak, мир не трогается. Профиль, который сейчас работает, импорт не принимает — сначала остановите сервер, иначе PZ перезапишет файлы своими при выходе.

Галочка «Сделать этот профиль активным» включена по умолчанию: вкладки «Моды» и «Конфиг сервера» показывают активный профиль, и без переключения кажется, что импорт ничего не сделал. Если галочку снять, панель напомнит, под каким профилем искать импортированное.

Галочки «Оставить свои значения» переносят в импортированный конфиг то, что относится к вашей машине, а не к сборке:

группа параметры
Порты и RCON DefaultPort, UDPPort, RCONPort, RCONPassword
Пароль сервера и объявляемый IP Password, server_browser_announced_ip
Имя и описание сервера PublicName, PublicDescription

Для нового профиля эти значения берутся из активного: порты и пароли на машине общие. Всё остальное — списки модов, карты, правила и песочница — приходит из пресета.

Посторонние .ini из архива (настройки модов, например) не принимаются за конфиг сервера: панель опознаёт его по знакомым параметрам.

Пресеты песочницы одиночной игры

Сборки нередко раздают только пресет для одиночной игры — файл .cfg из папки Sandbox Presets. Панель принимает и его: как отдельный файл, и как часть архива. Внутри это плоский список Параметр=значение с точками вместо вложенности (ZombieLore.Speed=2), а серверу нужна таблица Lua, поэтому значения переносятся по одному.

Если у профиля уже есть свой SandboxVars.lua, панель меняет в нём только значения — комментарии игры, порядок и остальные параметры остаются на месте. Параметры, которых в файле нет (обычно это настройки модов, пока моды не установлены и сервер ни разу не запускался), пропускаются, и панель говорит, сколько перенесла и сколько пропустила. Если файла ещё нет, он создаётся целиком из пресета.

Список модов из кнопки «Поделиться»

Игра умеет отдавать набор модов одной строкой: Название:ModA;ModB;ModC. На вкладке «Моды» есть поле «Импорт списка модов» — вставьте её туда, и список включённых модов заменится на этот, в том же порядке загрузки. Список попадает в конфиг только после кнопки «Сохранить», так что до неё всё можно отменить, просто переключив вкладку.

В такой строке нет идентификаторов мастерской, поэтому сама по себе она не заставит сервер что-то скачать: панель добавит пакеты только тех модов, чьи файлы уже лежат на диске, и скажет, для скольких модов файлов не нашлось. Пакеты для них добавляются коллекцией на той же вкладке.

Моды

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 её юнита, и остановка службы убила бы его на середине. Побочный эффект — вывод идёт не в консоль панели, а в свой журнал:

journalctl -u pzmanager-update -f

Если правило sudo не установлено (панель поставлена вручную или в системе нет sudo), кнопка не появится, а рядом будет показана команда для терминала:

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):

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 юнита).

Команды

pzmanager serve                  # запустить панель (команда по умолчанию)
pzmanager useradd <логин>        # создать пользователя панели
pzmanager passwd <логин>         # сменить пароль
pzmanager users                  # список пользователей

Флаги: --config <путь>, --listen <адрес>, --secure-cookies.

Конфигурация

Файл /opt/pzmanager/config.yaml создаёт установщик (при запуске без него менеджер возьмёт ~/.config/pzmanager/config.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):

  • CI — на каждый push и pull request в main: gofmt, go vet, тесты, проверка сборки под Linux и синтаксиса скриптов установки.
  • Release — на тег v*: собирает бинарники под linux/amd64 и linux/arm64, считает контрольные суммы и публикует релиз с ними и скриптами установки.

Выпуск новой версии:

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).

Разработка

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 — делайте с кодом что угодно, но без каких-либо гарантий.