Files
PupaMLupa/Server/restart-scheduled.sh
T

172 lines
7.1 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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=...
# Пароль и порт должны совпадать с тем, что слушает сервер Minecraft.
# Либо задайте только в server.properties, либо переопределите через
# переменные окружения RCON_PASSWORD и RCON_PORT (см. ниже).
# - RCON-клиент в PATH: сначала ищется mcrcon, иначе rcon (github.com/gorcon/rcon-cli)
# - Права на systemctl для сервиса (удобнее всего — crontab root)
#
# Установка mcrcon, если в apt пакета нет (Linux x86_64, статический бинарник):
# curl -fsSLO https://github.com/Tiiffi/mcrcon/releases/download/v0.7.2/mcrcon-0.7.2-linux-x86-64-static.zip
# unzip -o mcrcon-0.7.2-linux-x86-64-static.zip
# sudo install -m755 mcrcon-0.7.2-linux-x86-64-static/mcrcon /usr/local/bin/
#
# Сборка из исходников:
# sudo apt install -y git build-essential
# git clone --depth 1 https://github.com/Tiiffi/mcrcon.git && cd mcrcon && make && sudo install -m755 mcrcon /usr/local/bin/
#
# Расписание (каждые 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)
# RCON_PORT — порт RCON; если не задан — из server.properties (rcon.port)
# RCON_PASSWORD — пароль RCON; если не задан — из server.properties (rcon.password)
# STOP_WAIT_SEC — макс. ожидание выхода процесса после stop (по умолчанию: 180)
# RCON_TIMEOUT — таймаут для rcon (gorcon), напр. 60s (по умолчанию: 60s)
#
# Пример в crontab (пароль только в окружении cron, не в скрипте):
# 0 */6 * * * RCON_PASSWORD='ваш_секрет' /opt/PupaMLupa/Server/restart-scheduled.sh >>/var/log/mc-restart.log 2>&1
# (в server.properties тот же пароль в rcon.password=..., иначе сервер не примет RCON)
set -euo pipefail
# cron часто даёт урезанный PATH — mcrcon обычно в /usr/bin или /usr/local/bin
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:${PATH:-}"
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}"
RCON_TIMEOUT="${RCON_TIMEOUT:-60s}"
RCON_CLIENT=""
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"
if [[ "${RCON_CLIENT}" == "mcrcon" ]]; then
mcrcon -H "${RCON_HOST}" -P "${RCON_PORT}" -p "${RCON_PASSWORD}" "${cmd}"
else
# gorcon/rcon-cli: команда передаётся словами, как в консоли сервера (say …, save-all flush, …)
# shellcheck disable=SC2086
rcon -a "${RCON_HOST}:${RCON_PORT}" -p "${RCON_PASSWORD}" -T "${RCON_TIMEOUT}" ${cmd}
fi
}
rcon_say() {
rcon_exec "say $1"
}
# --- блокировка: не запускать два перезапуска одновременно ---
exec 9>"${LOCK_FILE}"
if ! flock -n 9; then
log "уже идёт другой запуск (flock ${LOCK_FILE}) — выход."
exit 0
fi
if command -v mcrcon >/dev/null 2>&1; then
RCON_CLIENT="mcrcon"
elif command -v rcon >/dev/null 2>&1; then
RCON_CLIENT="rcon"
else
die "нужен RCON-клиент: mcrcon или rcon (https://github.com/gorcon/rcon-cli/releases). См. комментарии в начале этого скрипта — статический mcrcon для x86_64 или сборка из git."
fi
log "RCON-клиент: ${RCON_CLIENT}"
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"
_prop_port="$(read_prop rcon.port)"
_prop_pass="$(read_prop rcon.password)"
# Переменные окружения имеют приоритет (удобно для cron).
RCON_PORT="${RCON_PORT:-${_prop_port}}"
RCON_PASSWORD="${RCON_PASSWORD:-${_prop_pass}}"
[[ -n "${RCON_PORT}" ]] || die "не задан rcon.port (server.properties или переменная RCON_PORT)"
[[ -n "${RCON_PASSWORD}" ]] || die "не задан rcon.password (server.properties или переменная 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