gameadm/modules/mc.sh

491 lines
15 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"
# Installation/Konfiguration für Minecraft Server
cmd_install() {
local force_install="$1"
log "INFO" "Minecraft Server Installation/Konfiguration gestartet"
# Prüfe ob bereits konfiguriert
if [[ -f "$CONFIG_FILE" ]]; then
log "WARN" "Minecraft bereits konfiguriert in: $CONFIG_FILE"
# Force-Installation oder interaktive Abfrage
if [[ "$force_install" == "--force" ]] || [[ "$force_install" == "-f" ]]; then
log "INFO" "Force-Installation: Überschreibe Konfiguration"
else
read -p "Überschreiben? [y/N]: " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
log "INFO" "Installation abgebrochen."
return 0
fi
fi
fi
# Erstelle Konfiguration
log "INFO" "Erstelle Minecraft Server Konfiguration..."
mkdir -p "$(dirname "$CONFIG_FILE")"
cat > "$CONFIG_FILE" << 'EOF'
# Minecraft Server Konfiguration für gameadm
# Automatisch erstellt - kann angepasst werden
# Container Einstellungen
CONTAINER_NAME="minecraft-server"
IMAGE="docker.io/itzg/minecraft-server:latest"
DATA_DIR="/srv/minecraft"
PORT="25565"
# Server Einstellungen
MEMORY_LIMIT="2g"
VERSION="LATEST"
EULA="TRUE"
DIFFICULTY="normal"
GAMEMODE="survival"
MAX_PLAYERS="20"
MOTD="§6PP1L Minecraft Server §r§7- Powered by gameadm"
LEVEL_NAME="world"
ONLINE_MODE="true"
# Performance Optimierungen
ENABLE_AUTOPAUSE="true"
AUTOPAUSE_TIMEOUT_EST="3600"
AUTOPAUSE_TIMEOUT_KN="120"
USE_AIKAR_FLAGS="true"
# Sicherheit (optional - Pfade zu Secret-Dateien)
RCON_PASSWORD_FILE="/root/secrets/minecraft_rcon_password"
SERVER_PASSWORD_FILE="/root/secrets/minecraft_server_password"
EOF
log "INFO" "Konfiguration erstellt: $CONFIG_FILE"
# Erstelle Datenverzeichnis
mkdir -p "/srv/minecraft"
log "INFO" "Datenverzeichnis erstellt: /srv/minecraft"
# EULA akzeptieren
echo "eula=TRUE" > "/srv/minecraft/eula.txt"
log "INFO" "EULA akzeptiert: /srv/minecraft/eula.txt"
# Erstelle Secret-Verzeichnis (optional)
mkdir -p "/root/secrets"
if [[ ! -f "/root/secrets/minecraft_rcon_password" ]]; then
# Generiere zufälliges RCON Passwort
openssl rand -base64 32 2>/dev/null > "/root/secrets/minecraft_rcon_password" || echo "rcon$(date +%s)" > "/root/secrets/minecraft_rcon_password"
chmod 600 "/root/secrets/minecraft_rcon_password"
log "INFO" "RCON Passwort generiert: /root/secrets/minecraft_rcon_password"
fi
log "INFO" "Minecraft Server erfolgreich konfiguriert!"
log "INFO" ""
log "INFO" "Nächste Schritte:"
log "INFO" " 1. Konfiguration anpassen: $CONFIG_FILE"
log "INFO" " 2. Server starten: gameadm mc start"
log "INFO" " 3. Status prüfen: gameadm mc status"
log "INFO" " 4. Logs anzeigen: gameadm mc logs"
log "INFO" ""
log "INFO" "Server läuft auf Port 25565"
log "INFO" "Daten gespeichert in: /srv/minecraft"
return 0
}
# Konfiguration laden (nur wenn Datei existiert)
if [[ -f "$CONFIG_FILE" ]]; then
# shellcheck disable=SC1090
source "$CONFIG_FILE"
else
# Fallback-Werte für andere Befehle wenn keine Konfiguration existiert
CONTAINER_NAME="minecraft-server"
IMAGE="docker.io/itzg/minecraft-server:latest"
DATA_DIR="/srv/minecraft"
PORT="25565"
MEMORY_LIMIT="2g"
VERSION="LATEST"
EULA="TRUE"
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
}