Script zum Management von Minecraft Servern

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
Knogle
Beiträge: 466
Registriert: 06.05.2016 19:29:00
Lizenz eigener Beiträge: MIT Lizenz

Script zum Management von Minecraft Servern

Beitrag von Knogle » 03.11.2018 16:24:52

Ich betreibe bei mir Zuhause fuer mich und Kollegen inzwischen mehrere Minecraft Server.
Dabei ist es ein ziemlicher Aufwand mehrere zu betreiben, da ich bisher fuer jeden ein einzelnes Start/Stop Script angelegt habe, als auch einen einzelnen User etc.

Das hier ist mein bisherigen Script

Code: Alles auswählen

#!/bin/bash
# /etc/init.d/minecraft
# version 0.4.2 2016-02-09 (YYYY-MM-DD)
#
### BEGIN INIT INFO
# Provides:   minecraft
# Required-Start: $local_fs $remote_fs screen-cleanup
# Required-Stop:  $local_fs $remote_fs
# Should-Start:   $network
# Should-Stop:    $network
# Default-Start:  2 3 4 5
# Default-Stop:   0 1 6
# Short-Description:    Minecraft server
# Description:    Starts the minecraft server
### END INIT INFO

#Settings
SERVICE='forge-1.12.2-14.23.4.2709-universal.jar'
SCREENNAME='minecraft_modded_server'
OPTIONS='nogui'
USERNAME='root'
WORLD='world'
MCPATH='/home/minecraftmod'
MINHEAP=4096
HISTORY=4096
CPU_COUNT=16
Xms=1G
JAVACMD="java"
MAX_RAM="8192M"     # -Xmx
JAVA_PARAMETERS="-XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSIncrementalPacing -XX:+CMSClassUnloadingEnabled -XX:ParallelGCThreads=${CPU_COUNT} -XX:MinHeapFreeRatio=5 -XX:MaxHeapFreeRatio=10 -XX:+AggressiveOpts -Xms${Xms}"
INVOCATION="${JAVACMD} -server -Xmx${MAX_RAM} ${JAVA_PARAMETERS} -jar ${SERVICE} nogui"
Backuppath="/var/www/repo.openknogle.eu/Minecraft/modded/"
Zielordner="${Backuppath}Minecraft_Modded_Backup_$(date +%Y-%m-%d_%H-%M).7z"
Password="FASkog4AmtO1XIvGbkh8sqppYs51Eo7E7drbVpCSXOIsX4yyaUsIXvVEnLGQELMh3n1eqI23QiOGVhnsnlCX7GGMQKVapKCfoqnei2u8Ssb7ZDgbbbfYgpoo0DTrfraae"




ME=`whoami`
as_user() {
  if [ "$ME" = "$USERNAME" ] ; then
    bash -c "$1"
  else
    su - "$USERNAME" -c "$1"
  fi
}

mc_start() {
  if  pgrep -u $USERNAME -f $SERVICE > /dev/null ; then
    echo "$SERVICE is already running!"
  else
    echo "Starting $SERVICE..."
    cd $MCPATH
    as_user "cd $MCPATH && screen -h $HISTORY -dmS ${SCREENNAME} $INVOCATION"
    sleep 7
    if pgrep -u $USERNAME -f $SERVICE > /dev/null ; then
      echo "$SERVICE is now running."
    else
      echo "Error! Could not start $SERVICE!"
    fi
  fi
}

mc_saveoff() {
  if pgrep -u $USERNAME -f $SERVICE > /dev/null ; then
    echo "$SERVICE is running... suspending saves"
   as_user "screen -p 0 -S ${SCREENNAME} -X eval 'stuff \"say SERVER BACKUP STARTING. Server going readonly...\"\015'"
    as_user "screen -p 0 -S ${SCREENNAME} -X eval 'stuff \"save-off\"\015'"
    as_user "screen -p 0 -S ${SCREENNAME} -X eval 'stuff \"save-all\"\015'"
    sync
    sleep 10
  else
    echo "$SERVICE is not running. Not suspending saves."
  fi
}

mc_saveon() {
  if pgrep -u $USERNAME -f $SERVICE > /dev/null ; then
    echo "$SERVICE is running... re-enabling saves"
    as_user "screen -p 0 -S ${SCREENNAME} -X eval 'stuff \"save-on\"\015'"
    as_user "screen -p 0 -S ${SCREENNAME} -X eval 'stuff \"say SERVER BACKUP ENDED. Server going read-write...\"\015'"
  else
    echo "$SERVICE is not running. Not resuming saves."
  fi
}

mc_stop() {
  if pgrep -u $USERNAME -f $SERVICE > /dev/null ; then
    echo "Stopping $SERVICE"
    as_user "screen -p 0 -S ${SCREENNAME} -X eval 'stuff \"say SERVER SHUTTING DOWN IN 10 SECONDS. Saving map...\"\015'"
    as_user "screen -p 0 -S ${SCREENNAME} -X eval 'stuff \"save-all\"\015'"
    sleep 10
    as_user "screen -p 0 -S ${SCREENNAME} -X eval 'stuff \"stop\"\015'"
    sleep 7
  else
    echo "$SERVICE was not running."
  fi
  if pgrep -u $USERNAME -f $SERVICE > /dev/null ; then
    echo "Error! $SERVICE could not be stopped."
  else
    echo "$SERVICE is stopped."
  fi
}

mc_update() {
  if pgrep -u $USERNAME -f $SERVICE > /dev/null ; then
    echo "$SERVICE is running! Will not start update."
  else
    as_user "cd $MCPATH && wget -q -O $MCPATH/versions --no-check-certificate https://launchermeta.mojang.com/mc/game/version_manifest.json"
    if [ "$1" == "snapshot" ] ; then
      JSONVERSION=`cd $MCPATH && cat versions | python -c "exec(\"import json,sys\nobj=json.load(sys.stdin)\nversion=obj['latest']['snapshot']\nfor v in obj['versions']:\n   if v['id']==version:\n    print(v['url'])\")"`
    else
      JSONVERSION=`cd $MCPATH && cat versions | python -c "exec(\"import json,sys\nobj=json.load(sys.stdin)\nversion=obj['latest']['release']\nfor v in obj['versions']:\n   if v['id']==version:\n    print(v['url'])\")"`
    fi
    as_user "cd $MCPATH && wget -q -O $MCPATH/versions --no-check-certificate $JSONVERSION"
    MC_SERVER_URL=`cd $MCPATH && cat versions | python -c 'import json,sys;obj=json.load(sys.stdin);print(obj["downloads"]["server"]["url"])'`
    as_user "rm $MCPATH/versions"
    as_user "cd $MCPATH && wget -q -O $MCPATH/minecraft_server.jar.update --no-check-certificate $MC_SERVER_URL"
    if [ -f $MCPATH/minecraft_server.jar.update ] ; then
      if `diff $MCPATH/$SERVICE $MCPATH/minecraft_server.jar.update >/dev/null` ; then
        echo "You are already running the latest version of $SERVICE."
      else
        as_user "mv $MCPATH/minecraft_server.jar.update $MCPATH/$SERVICE"
        echo "Minecraft successfully updated."
      fi
    else
      echo "Minecraft update could not be downloaded."
    fi
  fi
}

mc_backup() {
mc_saveoff

if [ ! -d "${MCPATH}" ]; then
    echo "MCPATH existiert nicht!"
    exit 1
fi
if [ -d "${Zielordner}" ]; then
    echo "Zielordner bereits vorhanden!"
    exit 1
fi

7z a -t7z -mx=0 -m0=LZMA2 -mmt=${CPU_COUNT} -p"${Password}" "${Zielordner}" "${MCPATH}" >> "${Backuppath}placeholder.txt"
7z h -scrcsha256 "${Zielordner}" >> "${Backuppath}placeholder.txt"
mc_saveon
}

mc_command() {
  command="$1";
  if pgrep -u $USERNAME -f $SERVICE > /dev/null ; then
    pre_log_len=`wc -l "$MCPATH/logs/latest.log" | awk '{print $1}'`
    echo "$SERVICE is running... executing command"
    as_user "screen -p 0 -S ${SCREENNAME} -X eval 'stuff \"$command\"\015'"
    sleep .1 # assumes that the command will run and print to the log file in less than .1 seconds
    # print output
    tail -n $[`wc -l "$MCPATH/logs/latest.log" | awk '{print $1}'`-$pre_log_len] "$MCPATH/logs/latest.log"
  fi
}

#Start-Stop here
case "$1" in
  start)
    mc_start
    ;;
  stop)
    mc_stop
    ;;
  restart)
    mc_stop
    mc_start
    ;;
  update)
    mc_stop
    mc_backup
    mc_update $2
    mc_start
    ;;
  backup)
    mc_backup
    ;;
  status)
    if pgrep -u $USERNAME -f $SERVICE > /dev/null ; then
      echo "$SERVICE is running."
    else
      echo "$SERVICE is not running."
    fi
    ;;
  command)
    if [ $# -gt 1 ] ; then
      shift
      mc_command "$*"
    else
      echo "Must specify server command (try 'help'?)"
    fi
    ;;

  *)
  echo "Usage: $0 {start|stop|update|backup|status|restart|command \"server command\"}"
  exit 1
  ;;
esac

exit 0

Dieses Script ermoeglicht es, bspw. mit dem Befehl "/etc/init.d/minecraft start" den entsprechenden Minecraft Server zu starten.
Das Problem: Ich brauche fuer jeden Server ein eigenes Script, weshalb es das ganze inzwischen extrem unuebersichtlich macht.
Da das einzige, was abweicht eigentlich die Pfade sind, wuerde ich fuer jeden Server gerne eine Datei erstellen, welche dann eingelesen wird, und der entsprechende Server gestartet wird.
Es ist bei mir nun so strukturiert, dass der Serverordner genauso heisst wie der Service der gestartet werden soll, im jeweiligen Ordner.
D.h., die einzige benoetige Information ist der Pfad.
Daher will ich bspw. fuer den Server, abgelegt in /home/minecraft_servers/ftb_continuum, in irgendeinem Bereich eine Datei mit dem Namen ftb_continuum erstellen, in welcher nur der Pfad ftb_continuum abgelegt ist.
Das Script soll dann die in dem Ordner /home/minecraft_servers/ftb_continuum/ die Datei ftb_continuum.jar starten.
Im Optimalfall kann ich mit einem String alle Server einrichten, da ja nur dieser benoetigt wird aufgrund des Ordneraufbaus.

Aber wie setze ich das um?
Weiterhin ist es wichtig, dass die erstellten Screen Sessions, auch den String beinhalten, damit ich beispielsweise mit screen -r ftb_continuum auf diesen Server zugreifen kann, oder mit /etc/init.d/minecraft stop ftb_continuum diesen verwalten kann.

Ich habe von Python leider nicht viel Ahnung weshalb ich schon extrem lange gebraucht habe, um dieses Script auf meine Beduerfnisse anzupassen.
In C habe ich einen grossen Erfahrungsschatz, jedoch stellt mich das dann wiederum vor einen sehr grossen Aufwand bash Befehle auszugeben und einzulesen.

Eine Idee die mir eventuell gerade reingekommen ist, waere ein Array zu erstellen mit den entsprechenden Strings, welche den jeweiligen Servern zugewiesen werden, in Richtung

Code: Alles auswählen

SERVICE=["ftb_continuum","ftb_continuum_last","lost+souls","final+frontier","sky_adventure"]
Das Array beim Starten zu durchlaufen, und den entsprechenden Screen Sessions diese auch zuzuweisen.

Ich hoffe auf Tipps und Hilfe

DeletedUserReAsG

Re: Script zum Management von Minecraft Servern

Beitrag von DeletedUserReAsG » 03.11.2018 16:34:36

Knogle hat geschrieben: ↑ zum Beitrag ↑
03.11.2018 16:24:52
Ich habe von Python leider nicht viel Ahnung weshalb ich schon extrem lange gebraucht habe, um dieses Script auf meine Beduerfnisse anzupassen.
… mag daran liegen, dass es ein Bash-Script ist ;)

Wobei ich heute kein solches Start/Stop-Script nehmen, sondern ’ne systemd-Unit erstellen würde. Die könnte dann ein Script (oder ein Programm, geschrieben in der Sprache deiner Wahl – etwa C) aufrufen, das die gewünschten Jobs tut.

Knogle
Beiträge: 466
Registriert: 06.05.2016 19:29:00
Lizenz eigener Beiträge: MIT Lizenz

Re: Script zum Management von Minecraft Servern

Beitrag von Knogle » 03.11.2018 18:03:44

Alles klar!
Habe gerade versucht was in C zu schreiben, der Aufwand ist jedoch extrem.
Habe das Bash Script nun so umgebaut, dass man mit einem 2. Parameter den Server angeben kann den man starten kann.

Wie kann ich das nun mit dem systemd Service genau einrichten?
Fuer jeden Server brauche ich letztendlich so eine Syntax: /etc/init.d/minecraft "start/stop" "servername"

Ist es auch moeglich, mit so einem Systemd service zeitgesteuert ein Script auszufuehren ueber cron?

Ist es weiterhin noch moeglich, das Script "minecraft" global aufrufen zu koennen, und nicht immer mit /etc/init.d/ davor?

cronoik
Beiträge: 2049
Registriert: 18.03.2012 21:13:42
Lizenz eigener Beiträge: GNU Free Documentation License

Re: Script zum Management von Minecraft Servern

Beitrag von cronoik » 03.11.2018 22:48:34

Knogle hat geschrieben: ↑ zum Beitrag ↑
03.11.2018 18:03:44
Wie kann ich das nun mit dem systemd Service genau einrichten?
Folge doch einer Anleitung (beispielsweise [1]) und setzt den Thread bei konkreten Problem fort. Dann hilft man/ich dir gerne.
Knogle hat geschrieben: ↑ zum Beitrag ↑
03.11.2018 18:03:44
Ist es auch moeglich, mit so einem Systemd service zeitgesteuert ein Script auszufuehren ueber cron?
Nein, aber es gibt Systemd Timer Units und mit diesen kannst du, ohne cron, zeitgesteuert Dinge tun [2].
Knogle hat geschrieben: ↑ zum Beitrag ↑
03.11.2018 18:03:44
Ist es weiterhin noch moeglich, das Script "minecraft" global aufrufen zu koennen, und nicht immer mit /etc/init.d/ davor?
Ja. Du koenntest beispielsweise einen bash alias definieren (/etc/bash.bashrc global fuer alle; ~/.bashrc dann fuer den Nutzer).


[1] https://schroeffu.ch/2017/05/einfache-s ... amspeak-3/
[2] https://wiki.archlinux.org/index.php/Sy ... unit_files
Hilf mit unser Wiki zu verbessern!

Knogle
Beiträge: 466
Registriert: 06.05.2016 19:29:00
Lizenz eigener Beiträge: MIT Lizenz

Re: Script zum Management von Minecraft Servern

Beitrag von Knogle » 06.11.2018 16:10:03

Vielen Dank!
Konnte dank eurer Tipps alles zufriedenstellend lösen.

Antworten