394 lines
12 KiB
Bash
Executable File
394 lines
12 KiB
Bash
Executable File
#!/bin/bash
|
|
# Minecraft Server Modul für gameadm
|
|
# Vollständige Implementierung mit intelligenten Features
|
|
|
|
# Konfigurationsdatei
|
|
CONFIG_FILE="/etc/minecraft-server.conf"
|
|
|
|
# Konfiguration laden
|
|
if [[ -f "$CONFIG_FILE" ]]; then
|
|
# shellcheck disable=SC1090
|
|
source "$CONFIG_FILE"
|
|
else
|
|
log "ERROR" "Konfig nicht gefunden: $CONFIG_FILE"
|
|
log "INFO" "Erstelle Standard-Konfiguration..."
|
|
exit 1
|
|
fi
|
|
|
|
# Standardwerte (mit intelligenten Defaults)
|
|
CONTAINER_NAME=${CONTAINER_NAME:-minecraft-server}
|
|
IMAGE=${IMAGE:-docker.io/itzg/minecraft-server:latest}
|
|
DATA_DIR=${DATA_DIR:-/srv/minecraft}
|
|
PORT=${PORT:-25565}
|
|
MEMORY_LIMIT=${MEMORY_LIMIT:-"2g"}
|
|
VERSION=${VERSION:-"LATEST"}
|
|
EULA=${EULA:-"TRUE"}
|
|
DIFFICULTY=${DIFFICULTY:-"normal"}
|
|
GAMEMODE=${GAMEMODE:-"survival"}
|
|
MAX_PLAYERS=${MAX_PLAYERS:-10}
|
|
MOTD=${MOTD:-"§6PP1L Minecraft Server §r§7- Powered by gameadm"}
|
|
LEVEL_NAME=${LEVEL_NAME:-"world"}
|
|
ONLINE_MODE=${ONLINE_MODE:-"true"}
|
|
ENABLE_AUTOPAUSE=${ENABLE_AUTOPAUSE:-"true"}
|
|
AUTOPAUSE_TIMEOUT_EST=${AUTOPAUSE_TIMEOUT_EST:-3600}
|
|
AUTOPAUSE_TIMEOUT_KN=${AUTOPAUSE_TIMEOUT_KN:-120}
|
|
USE_AIKAR_FLAGS=${USE_AIKAR_FLAGS:-"true"}
|
|
RCON_PASSWORD_FILE=${RCON_PASSWORD_FILE:-/root/secrets/minecraft_rcon_password}
|
|
SERVER_PASSWORD_FILE=${SERVER_PASSWORD_FILE:-/root/secrets/minecraft_server_password}
|
|
|
|
# Intelligente Hilfsfunktionen
|
|
ensure_prereqs() {
|
|
log "INFO" "Prüfe Minecraft Server Voraussetzungen..."
|
|
|
|
# Datenverzeichnis erstellen
|
|
mkdir -p "$DATA_DIR"
|
|
|
|
# EULA akzeptieren
|
|
if [[ ! -f "$DATA_DIR/eula.txt" ]]; then
|
|
echo "eula=$EULA" > "$DATA_DIR/eula.txt"
|
|
log "INFO" "EULA akzeptiert: $DATA_DIR/eula.txt"
|
|
fi
|
|
|
|
# Berechtigungen setzen
|
|
chown -R root:root "$DATA_DIR"
|
|
chmod 755 "$DATA_DIR"
|
|
|
|
# Memory Check
|
|
local available_mem=$(free -m | awk '/^Mem:/{print $7}')
|
|
local requested_mem=$(echo "$MEMORY_LIMIT" | sed 's/[gG]//' | sed 's/[mM]//')
|
|
|
|
if [[ "$MEMORY_LIMIT" =~ [gG]$ ]]; then
|
|
requested_mem=$((requested_mem * 1024))
|
|
fi
|
|
|
|
if [[ $available_mem -lt $requested_mem ]]; then
|
|
log "WARN" "Wenig verfügbarer RAM: ${available_mem}MB verfügbar, ${requested_mem}MB angefordert"
|
|
fi
|
|
|
|
log "INFO" "Voraussetzungen erfüllt ✓"
|
|
}
|
|
|
|
is_running() {
|
|
podman inspect -f '{{.State.Running}}' "$CONTAINER_NAME" 2>/dev/null | grep -q true
|
|
}
|
|
|
|
get_container_status() {
|
|
if is_running; then
|
|
echo "RUNNING"
|
|
elif podman inspect "$CONTAINER_NAME" >/dev/null 2>&1; then
|
|
local state=$(podman inspect -f '{{.State.Status}}' "$CONTAINER_NAME" 2>/dev/null)
|
|
echo "${state^^}"
|
|
else
|
|
echo "NOT_FOUND"
|
|
fi
|
|
}
|
|
|
|
get_server_info() {
|
|
if ! is_running; then
|
|
return 1
|
|
fi
|
|
|
|
local stats=$(podman stats --no-stream --format "{{.MemUsage}}" "$CONTAINER_NAME" 2>/dev/null)
|
|
echo "Memory: $stats"
|
|
}
|
|
|
|
perform_backup() {
|
|
if [[ ! -d "$DATA_DIR" ]]; then
|
|
log "ERROR" "Datenverzeichnis nicht gefunden: $DATA_DIR"
|
|
return 1
|
|
fi
|
|
|
|
local backup_dir="/srv/backups/minecraft"
|
|
local timestamp=$(date '+%Y%m%d_%H%M%S')
|
|
local backup_file="$backup_dir/minecraft_backup_$timestamp.tar.gz"
|
|
|
|
mkdir -p "$backup_dir"
|
|
|
|
log "INFO" "Erstelle Backup: $backup_file"
|
|
if tar -czf "$backup_file" -C "$(dirname "$DATA_DIR")" "$(basename "$DATA_DIR")"; then
|
|
log "INFO" "Backup erfolgreich erstellt ✓"
|
|
|
|
# Alte Backups aufräumen (behalte nur die letzten 7)
|
|
find "$backup_dir" -name "minecraft_backup_*.tar.gz" -type f -mtime +7 -delete 2>/dev/null || true
|
|
|
|
return 0
|
|
else
|
|
log "ERROR" "Backup fehlgeschlagen"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Erweiterte Befehle
|
|
cmd_start() {
|
|
ensure_prereqs
|
|
|
|
if is_running; then
|
|
log "INFO" "Bereits gestartet: $CONTAINER_NAME"
|
|
cmd_status
|
|
exit 0
|
|
fi
|
|
|
|
# Beendeten Container entfernen falls vorhanden
|
|
if podman inspect "$CONTAINER_NAME" >/dev/null 2>&1; then
|
|
log "INFO" "Entferne alten Container..."
|
|
podman rm -f "$CONTAINER_NAME" >/dev/null 2>&1 || true
|
|
fi
|
|
|
|
log "INFO" "Starte Minecraft Server..."
|
|
log "INFO" "Memory Limit: $MEMORY_LIMIT | Version: $VERSION | Port: $PORT"
|
|
|
|
# Intelligente Container-Erstellung
|
|
local podman_args=(
|
|
"run" "-d"
|
|
"--name" "$CONTAINER_NAME"
|
|
"--restart=always"
|
|
"--memory=$MEMORY_LIMIT"
|
|
"--memory-swap=$MEMORY_LIMIT"
|
|
"--oom-kill-disable"
|
|
"-p" "${PORT}:25565"
|
|
"-v" "$DATA_DIR:/data"
|
|
"-e" "EULA=$EULA"
|
|
"-e" "VERSION=$VERSION"
|
|
"-e" "MEMORY=$MEMORY_LIMIT"
|
|
"-e" "DIFFICULTY=$DIFFICULTY"
|
|
"-e" "GAMEMODE=$GAMEMODE"
|
|
"-e" "MAX_PLAYERS=$MAX_PLAYERS"
|
|
"-e" "MOTD=$MOTD"
|
|
"-e" "LEVEL=$LEVEL_NAME"
|
|
"-e" "ONLINE_MODE=$ONLINE_MODE"
|
|
"-e" "ENABLE_AUTOPAUSE=$ENABLE_AUTOPAUSE"
|
|
"-e" "AUTOPAUSE_TIMEOUT_EST=$AUTOPAUSE_TIMEOUT_EST"
|
|
"-e" "AUTOPAUSE_TIMEOUT_KN=$AUTOPAUSE_TIMEOUT_KN"
|
|
)
|
|
|
|
# Aikar Flags für Performance (wenn aktiviert)
|
|
if [[ "$USE_AIKAR_FLAGS" == "true" ]]; then
|
|
podman_args+=("-e" "USE_AIKAR_FLAGS=true")
|
|
log "INFO" "Aikar Performance Flags aktiviert ✓"
|
|
fi
|
|
|
|
# RCON aktivieren wenn Passwort-Datei existiert
|
|
if [[ -f "$RCON_PASSWORD_FILE" ]]; then
|
|
podman_args+=("-e" "ENABLE_RCON=true")
|
|
podman_args+=("-e" "RCON_PASSWORD=$(cat "$RCON_PASSWORD_FILE")")
|
|
podman_args+=("-p" "25575:25575")
|
|
log "INFO" "RCON aktiviert ✓"
|
|
fi
|
|
|
|
podman_args+=("$IMAGE")
|
|
|
|
if podman "${podman_args[@]}" >/dev/null; then
|
|
log "INFO" "Minecraft Server gestartet ✓"
|
|
log "INFO" "Warte auf Server-Initialisierung..."
|
|
sleep 5
|
|
cmd_status
|
|
else
|
|
log "ERROR" "Fehler beim Starten des Servers"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
cmd_stop() {
|
|
if ! podman inspect "$CONTAINER_NAME" >/dev/null 2>&1; then
|
|
log "INFO" "Container existiert nicht: $CONTAINER_NAME"
|
|
exit 0
|
|
fi
|
|
|
|
if is_running; then
|
|
log "INFO" "Stoppe Minecraft Server gracefully..."
|
|
|
|
# Graceful shutdown über RCON falls möglich
|
|
if [[ -f "$RCON_PASSWORD_FILE" ]]; then
|
|
local rcon_pass=$(cat "$RCON_PASSWORD_FILE")
|
|
timeout 30 podman exec "$CONTAINER_NAME" rcon-cli --password "$rcon_pass" say "Server wird in 10 Sekunden heruntergefahren..." 2>/dev/null || true
|
|
sleep 2
|
|
timeout 30 podman exec "$CONTAINER_NAME" rcon-cli --password "$rcon_pass" stop 2>/dev/null || true
|
|
sleep 8
|
|
fi
|
|
|
|
# Fallback: Standard Stop
|
|
podman stop -t 30 "$CONTAINER_NAME" >/dev/null 2>&1 || true
|
|
fi
|
|
|
|
podman rm "$CONTAINER_NAME" >/dev/null 2>&1 || true
|
|
log "INFO" "Minecraft Server gestoppt ✓"
|
|
}
|
|
|
|
cmd_restart() {
|
|
log "INFO" "Starte Minecraft Server neu..."
|
|
cmd_stop || true
|
|
sleep 2
|
|
cmd_start
|
|
}
|
|
|
|
cmd_status() {
|
|
local status=$(get_container_status)
|
|
local color=""
|
|
|
|
case "$status" in
|
|
"RUNNING") color="$GREEN" ;;
|
|
"EXITED"|"STOPPED") color="$YELLOW" ;;
|
|
"NOT_FOUND") color="$RED" ;;
|
|
*) color="$NC" ;;
|
|
esac
|
|
|
|
echo -e "${color}Status: $status${NC}"
|
|
|
|
if [[ "$status" == "RUNNING" ]]; then
|
|
podman ps --filter name="^${CONTAINER_NAME}$" --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'
|
|
|
|
# Server Info anzeigen
|
|
local info=$(get_server_info)
|
|
if [[ -n "$info" ]]; then
|
|
echo "Server Info: $info"
|
|
fi
|
|
|
|
# Online Spieler (falls RCON verfügbar)
|
|
if [[ -f "$RCON_PASSWORD_FILE" ]] && is_running; then
|
|
local rcon_pass=$(cat "$RCON_PASSWORD_FILE")
|
|
local players=$(timeout 5 podman exec "$CONTAINER_NAME" rcon-cli --password "$rcon_pass" list 2>/dev/null | head -1 || echo "RCON nicht verfügbar")
|
|
echo "Spieler: $players"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
cmd_logs() {
|
|
if ! podman inspect "$CONTAINER_NAME" >/dev/null 2>&1; then
|
|
log "ERROR" "Container $CONTAINER_NAME existiert nicht"
|
|
exit 1
|
|
fi
|
|
|
|
local lines=${1:-200}
|
|
podman logs --tail="$lines" "$CONTAINER_NAME"
|
|
}
|
|
|
|
cmd_follow() {
|
|
if ! podman inspect "$CONTAINER_NAME" >/dev/null 2>&1; then
|
|
log "ERROR" "Container $CONTAINER_NAME existiert nicht"
|
|
exit 1
|
|
fi
|
|
|
|
log "INFO" "Folge Minecraft Server Logs (Ctrl+C zum Beenden)..."
|
|
podman logs -f "$CONTAINER_NAME"
|
|
}
|
|
|
|
cmd_update() {
|
|
log "INFO" "Aktualisiere Minecraft Server Image..."
|
|
|
|
local old_id=$(podman images --format "{{.ID}}" "$IMAGE" 2>/dev/null | head -1)
|
|
|
|
if podman pull "$IMAGE"; then
|
|
local new_id=$(podman images --format "{{.ID}}" "$IMAGE" 2>/dev/null | head -1)
|
|
|
|
if [[ "$old_id" != "$new_id" ]]; then
|
|
log "INFO" "Neues Image verfügbar. Server-Neustart empfohlen."
|
|
log "INFO" "Führe 'gameadm mc restart' aus um das neue Image zu verwenden."
|
|
else
|
|
log "INFO" "Image bereits aktuell ✓"
|
|
fi
|
|
else
|
|
log "ERROR" "Fehler beim Aktualisieren des Images"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
cmd_backup() {
|
|
log "INFO" "Erstelle Minecraft Server Backup..."
|
|
|
|
if is_running; then
|
|
log "INFO" "Server läuft - erstelle Live-Backup..."
|
|
# Für Live-Backup könnten wir save-all über RCON ausführen
|
|
if [[ -f "$RCON_PASSWORD_FILE" ]]; then
|
|
local rcon_pass=$(cat "$RCON_PASSWORD_FILE")
|
|
timeout 30 podman exec "$CONTAINER_NAME" rcon-cli --password "$rcon_pass" save-all 2>/dev/null || true
|
|
sleep 2
|
|
fi
|
|
fi
|
|
|
|
perform_backup
|
|
}
|
|
|
|
cmd_console() {
|
|
if ! is_running; then
|
|
log "ERROR" "Server ist nicht gestartet"
|
|
exit 1
|
|
fi
|
|
|
|
if [[ ! -f "$RCON_PASSWORD_FILE" ]]; then
|
|
log "ERROR" "RCON nicht konfiguriert"
|
|
exit 1
|
|
fi
|
|
|
|
local rcon_pass=$(cat "$RCON_PASSWORD_FILE")
|
|
log "INFO" "Minecraft Server Konsole (RCON) - 'quit' zum Beenden"
|
|
|
|
while true; do
|
|
read -r -p "minecraft> " command
|
|
case "$command" in
|
|
"quit"|"exit"|"q") break ;;
|
|
"") continue ;;
|
|
*) timeout 10 podman exec "$CONTAINER_NAME" rcon-cli --password "$rcon_pass" "$command" 2>/dev/null || log "ERROR" "RCON Befehl fehlgeschlagen" ;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
cmd_players() {
|
|
if ! is_running; then
|
|
log "ERROR" "Server ist nicht gestartet"
|
|
exit 1
|
|
fi
|
|
|
|
if [[ ! -f "$RCON_PASSWORD_FILE" ]]; then
|
|
log "ERROR" "RCON nicht konfiguriert"
|
|
exit 1
|
|
fi
|
|
|
|
local rcon_pass=$(cat "$RCON_PASSWORD_FILE")
|
|
timeout 5 podman exec "$CONTAINER_NAME" rcon-cli --password "$rcon_pass" list 2>/dev/null || log "ERROR" "Kann Spielerliste nicht abrufen"
|
|
}
|
|
|
|
cmd_help() {
|
|
cat <<EOF
|
|
Minecraft Server Verwaltung - Vollständige Implementation
|
|
|
|
Verwendung:
|
|
gameadm mc <befehl> [optionen]
|
|
|
|
Verfügbare Befehle:
|
|
start - Startet Minecraft Server (mit intelligenten Optimierungen)
|
|
stop - Stoppt Server gracefully (mit RCON-Warnung wenn verfügbar)
|
|
restart - Startet Server neu
|
|
status - Zeigt detaillierten Server-Status und Spieler-Info
|
|
logs [n] - Zeigt Server-Logs (Standard: 200 Zeilen)
|
|
follow - Folgt Logs in Echtzeit
|
|
update - Aktualisiert Server-Image
|
|
backup - Erstellt Server-Backup
|
|
console - Interaktive Server-Konsole (RCON)
|
|
players - Zeigt Online-Spieler
|
|
help - Zeigt diese Hilfe
|
|
|
|
Features:
|
|
✓ 2GB Memory Limit mit intelligenter Überwachung
|
|
✓ Aikar Performance Flags für optimale JVM-Performance
|
|
✓ Auto-Pause bei Inaktivität (spart Ressourcen)
|
|
✓ RCON-Support für Remote-Verwaltung
|
|
✓ Automatische Backups mit Rotation
|
|
✓ Graceful Shutdown mit Spieler-Benachrichtigung
|
|
✓ Live-Status und Memory-Monitoring
|
|
✓ Sichere Passwort-Verwaltung
|
|
|
|
Konfiguration: $CONFIG_FILE
|
|
Container: $CONTAINER_NAME
|
|
Port: $PORT (TCP)
|
|
Memory: $MEMORY_LIMIT
|
|
Version: $VERSION
|
|
Daten: $DATA_DIR
|
|
|
|
Beispiele:
|
|
gameadm mc start # Server starten
|
|
gameadm mc status # Status mit Spieler-Info
|
|
gameadm mc console # Interaktive Konsole
|
|
gameadm mc backup # Backup erstellen
|
|
gameadm mc logs 50 # Letzte 50 Log-Zeilen
|
|
EOF
|
|
} |