#!/usr/bin/env bash # Плановый перезапуск Minecraft-сервера с предупреждениями в чат (RCON) и # корректной остановкой (save-all flush + stop). После stop unit systemd # сам не поднимется при Restart=on-failure — скрипт делает systemctl start. # # Требования: # - В server.properties: enable-rcon=true, rcon.password=..., rcon.port=... # - Утилита mcrcon в PATH (см. README.md в этом каталоге) # - Права на systemctl для сервиса (удобнее всего — crontab root) # # Расписание (каждые 6 часов: 00:00, 06:00, 12:00, 18:00): # crontab -e # 0 */6 * * * /opt/PupaMLupa/Server/restart-scheduled.sh >>/var/log/mc-restart.log 2>&1 # # Переменные окружения (необязательно): # MINECRAFT_SERVICE — имя unit (по умолчанию: minecraft) # RCON_HOST — хост RCON (по умолчанию: 127.0.0.1) # STOP_WAIT_SEC — макс. ожидание выхода процесса после stop (по умолчанию: 180) set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "${SCRIPT_DIR}" MINECRAFT_SERVICE="${MINECRAFT_SERVICE:-minecraft}" RCON_HOST="${RCON_HOST:-127.0.0.1}" STOP_WAIT_SEC="${STOP_WAIT_SEC:-180}" LOCK_FILE="${SCRIPT_DIR}/.restart-scheduled.lock" PROPS="${SCRIPT_DIR}/server.properties" log() { echo "[$(date -Iseconds)] $*" } die() { log "ОШИБКА: $*" >&2 exit 1 } read_prop() { local key="$1" [[ -f "${PROPS}" ]] || die "нет файла ${PROPS}" grep -E "^[[:space:]]*${key}[[:space:]]*=" "${PROPS}" 2>/dev/null | head -n1 \ | cut -d= -f2- | tr -d '\r' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' } systemctl_wrap() { if [[ "${EUID:-0}" -eq 0 ]]; then systemctl "$@" elif command -v sudo >/dev/null 2>&1 && sudo -n true 2>/dev/null; then sudo -n systemctl "$@" else systemctl "$@" fi } rcon_exec() { local cmd="$1" mcrcon -H "${RCON_HOST}" -P "${RCON_PORT}" -p "${RCON_PASSWORD}" "${cmd}" } rcon_say() { rcon_exec "say $1" } # --- блокировка: не запускать два перезапуска одновременно --- exec 9>"${LOCK_FILE}" if ! flock -n 9; then log "уже идёт другой запуск (flock ${LOCK_FILE}) — выход." exit 0 fi command -v mcrcon >/dev/null 2>&1 || die "нужна утилита mcrcon (например: apt install mcrcon или сборка с https://github.com/Tiiffi/mcrcon)" if ! systemctl_wrap is-active --quiet "${MINECRAFT_SERVICE}"; then log "сервис ${MINECRAFT_SERVICE} не active — перезапуск не выполняется." exit 0 fi rc_en="$(read_prop enable-rcon)" [[ "${rc_en,,}" == "true" ]] || die "в server.properties включите enable-rcon=true" RCON_PORT="$(read_prop rcon.port)" RCON_PASSWORD="$(read_prop rcon.password)" [[ -n "${RCON_PORT}" ]] || die "не задан rcon.port" [[ -n "${RCON_PASSWORD}" ]] || die "не задан rcon.password" # Проверка RCON rcon_exec "list" >/dev/null || die "RCON не отвечает (хост ${RCON_HOST}, порт ${RCON_PORT})" log "Начало планового перезапуска (${MINECRAFT_SERVICE})." rcon_say "[Сервер] Плановый перезапуск через 10 минут!" sleep 300 rcon_say "[Сервер] Перезапуск через 5 минут!" sleep 240 rcon_say "[Сервер] Перезапуск через 1 минуту!" sleep 30 rcon_say "[Сервер] Перезапуск через 30 секунд!" sleep 25 for n in 5 4 3 2 1; do rcon_say "[Сервер] $n" sleep 1 done log "Сохранение мира и остановка..." rcon_exec "save-all flush" || true sleep 3 rcon_exec "stop" _waited=0 while systemctl_wrap is-active --quiet "${MINECRAFT_SERVICE}"; do if ((_waited >= STOP_WAIT_SEC)); then die "таймаут ${STOP_WAIT_SEC}s: сервис всё ещё active после stop" fi sleep 2 _waited=$((_waited + 2)) done log "Процесс остановлен, запускаю сервис..." systemctl_wrap start "${MINECRAFT_SERVICE}" sleep 2 if systemctl_wrap is-active --quiet "${MINECRAFT_SERVICE}"; then log "Готово: ${MINECRAFT_SERVICE} снова active." else die "systemctl start не поднял сервис — проверьте: systemctl status ${MINECRAFT_SERVICE}" fi