#!/bin/bash
# /etc/init.d/vintagestory.sh
# version 0.4.2 2016-02-09 (YYYY-MM-DD)
# This shell script launches the game server on linux
#
### BEGIN INIT INFO
# Provides:   vintagestory
# 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:    vintagestory server
# Description:    Starts the vintagestory server
### END INIT INFO
#set -x
# Shamelessly adapted from http://minecraft.gamepedia.com/Tutorials/Server_startup_script

#Settings
HISTORY=1024
USERNAME='vintagestory'
VSPATH='/home/vintagestory/server'
DATAPATH='/var/vintagestory/data'

## change the SCREENNAME (and DATAPATH) when running multiple servers on the same system
SCREENNAME='vintagestory_server'
SERVICE="VintagestoryServer.dll"
OPTIONS=""


INVOCATION="dotnet ${SERVICE} --dataPath \"${DATAPATH}\" ${OPTIONS}"
PGREPTEST="dotnet ${SERVICE} --dataPath ${DATAPATH}"

#Warning
[ -n "${NO_SUPPORT_MODE}" ] && echo "Warning! You are running in 'NO_SUPPORT_MODE', disabling some requirements checks. Have fun but please avoid to ask for support!"

#Commands
command -v pgrep >/dev/null 2>&1 || { echo "Fatal! I require pgrep but it's not installed. (apt install procps)  Aborting." >&2; exit 1; }
command -v screen >/dev/null 2>&1 || { echo "Fatal! I require screen but it's not installed.  Aborting." >&2; exit 1; }
command -v dotnet >/dev/null 2>&1 && dotnet --list-runtimes | grep -q "Microsoft\.NETCore\.App 10\.0" || { echo -e "${RED}Fatal! I require dotnet 10.0 but it's not installed. (https://dotnet.microsoft.com/en-us/download/dotnet/10.0)  Aborting.${RESET}" >&2; exit 1; }

#Possibly run as user of group vintagestory
unset GROUPNAME
ME=$(whoami)
if [ "${ME}" != "${USERNAME}" ] ; then
  groups "${ME}" | grep -q "${USERNAME}" && { GROUPNAME="${USERNAME}"; USERNAME="${ME}"; }
fi

as_user() {
  if [ "${ME}" = "${USERNAME}" ] ; then
    bash -c "${1}"
    return $?
  else
    su "${USERNAME}" -s /bin/bash -c "${1}"
    return $?
  fi
}

vs_version() {
  instdir="${1}"
  if ! [ -f "${instdir}/${SERVICE}" ] ; then
    echo "Fatal! ${instdir}/${SERVICE} not found. Make sure to set a correct VSPATH and DATA in the server.sh file. Aborting." >&2
    exit 1
  fi
  if [ ! -d "${DATAPATH}" ] ; then
    mkdir -m 775 -p "${DATAPATH}" >/dev/null 2>&1 || { echo "Fatal! Cannot create new "${DATAPATH}".  Aborting." >&2; exit 1; }
  fi
  if [ "${ME}" != "${USERNAME}" ] ; then
    chown "${USERNAME}:${USERNAME}" -R "${DATAPATH}" >/dev/null 2>&1 || { echo "Fatal! ${ME} failed to adjust data privileges.  Aborting." >&2; exit 1; }
  elif [ -n "${GROUPNAME}" ] ; then
    touch "${DATAPATH}/.${PPID}" >/dev/null 2>&1
    find "${DATAPATH}" -user "${ME}" | xargs chgrp "${GROUPNAME}" > /dev/null 2>&1 || { echo "Fatal! ${ME} failed to adjust data privileges.  Aborting." >&2; exit 1; }
    rm "${DATAPATH}/.${PPID}" >/dev/null 2>&1
  fi
}


vs_start() {
  vs_version "${VSPATH}" # otherwise abort
  if  pgrep -u "${USERNAME}","${GROUPNAME:-root}" -f "${PGREPTEST}" > /dev/null ; then
    echo "${SERVICE} is already running!"
  else
    cd "${VSPATH}"
    echo "Starting ${SERVICE} ..."
    if as_user "cd \"${VSPATH}\" && screen -h ${HISTORY} -dmS ${SCREENNAME} ${INVOCATION}" ; then
      sleep 7
    else
      echo "Warning! Problems with ${SERVICE}! Make sure user ${USERNAME} has read/write access."
    fi
    if [ -n "${NO_SUPPORT_MODE}" ] ; then
	   vs_command "/announce WARNING - ${SERVICE} was started in NO_SUPPORT_MODE" > /dev/null
    fi
    cd - >/dev/null 2>&1
    if ! vs_status ; then
      echo "Error! User ${USERNAME} could not start ${SERVICE}! Please check ${DATAPATH}/Logs." >&2
      return 1
    else
      echo "Please be aware that as of 1.20, servers default configurations have changed - servers no longer register themselves to the public servers list and are invite-only (whitelisted) out of the box. If you desire so, you can enable server advertising with '/serverconfig advertise on' and disable the whitelist mode with '/serverconfig whitelistmode off'"
      return 0
    fi
  fi
}

vs_status() {
  if ! pgrep -u "${USERNAME}","${GROUPNAME:-root}" -f "${PGREPTEST}" > /dev/null ; then
    echo "${SERVICE} is not running."
    return 1
  elif statusmessage=$(vs_command "/stats") ; then
    sleep 1
    if pgrep -u "${USERNAME}","${GROUPNAME:-root}" -f "${PGREPTEST}" > /dev/null ; then
      echo "${statusmessage}"
      echo "${SERVICE} is up and running!"
      return 0
    elif ! pgrep -u "${USERNAME}","${GROUPNAME:-root}" -f "${PGREPTEST}" > /dev/null ; then
      echo "${SERVICE} is not running."
      return 1
    else
      echo "${statusmessage}"
      echo "${SERVICE} is only partially running (not as expected)."
      return 1
    fi
  else
    echo "${statusmessage}"
    echo "${SERVICE} status is indetermined."
	return 1
  fi
}

vs_stop() {
  if pgrep -u "${USERNAME}","${GROUPNAME:-root}" -f "${PGREPTEST}" > /dev/null ; then
    echo "Stopping ${SERVICE} ..."
    if as_user "screen -p 0 -S ${SCREENNAME} -X eval 'stuff \"SERVER SHUTTING DOWN IN 10 SECONDS. Saving map...\"\015'" ; then
      sleep 10
    else
      echo "Warning! ${USERNAME} encountered problems sending a server shutdown notification."
    fi
    if ! as_user "screen -p 0 -S ${SCREENNAME} -X eval 'stuff \"/stop\"\015'" ; then
      echo "Warning! ${USERNAME} encountered problems executing the server stop command."
    fi
    sleep 7
    if pgrep -u "${USERNAME}","${GROUPNAME:-root}" -f "${PGREPTEST}" > /dev/null ; then
      if ! as_user "screen -p 0 -S ${SCREENNAME} -X quit" ; then
        echo "Warning! ${USERNAME} encountered problems terminating the ${SERVICE} processes."
      fi
    fi
  else
    echo "${SERVICE} was not running."
    return 0
  fi
  if [ $(pgrep -u "${USERNAME}","${GROUPNAME:-root}" -f "${PGREPTEST}" -c) = "0" ] ; then
    echo "${SERVICE} is stopped."
    return 0
  else
    echo "Error! ${SERVICE} could not be stopped." >&2
    return 1
  fi
}

vs_command() {
  command="${1}"
  servicelog="${DATAPATH}/Logs/server-main.log"
  if pgrep -u "${USERNAME}","${GROUPNAME:-root}" -f "${PGREPTEST}" > /dev/null ; then
    touch "${servicelog}" >/dev/null 2>&1
    pre_log_len=$(wc -l "${servicelog}" | awk '{print $1}')
    echo "${SERVICE} process found ... executing command"
    if as_user "screen -p 0 -S ${SCREENNAME} -X eval 'stuff \"/$command\"\015'" ; then
      sleep .1 # assumes that the command will run and print to the log file in less than .1 seconds
      # print output, number of lines computed by arithmetic expansion
      if [ -f "${servicelog}" ] ; then
        tail -n $(( $(wc -l "${servicelog}" | awk '{print $1}') - ${pre_log_len} )) "${servicelog}"
        return 0
      else
        echo "Error! Commmand output could not be read from ${servicelog}." >&2
        return 1
      fi
    else
      echo "Error! ${USERNAME} encountered problems with command execution." >&2
      return 1
    fi
  fi
}

vs_update () {
  echo "Auto update is disabled because unreliable."
  echo "Personal recommendation: Copy aside the server.sh once, e.g. to the parent folder, then for every update do (assuming your data path is somewhere else):"
  echo "1. ./server.sh stop"
  echo "2. rm -rf *"
  echo "3. wget [server archive link copied from account maanger]"
  echo "4. tar -zxvf [hit tab to get the downloaded file name]"
  echo "5. cp ../server.sh ."
  echo "6. ./server.sh start"
}


vs_setup() {
  read -p "Please confirm setup of basic server environment: user ${1} path ${2:-/home/${1}/server} (y/n) " -n 1 -r ; echo
  if [ "${REPLY,,}" = "y" ] ; then
    USERNAME="${1}"
    VSPATH="${2:-/home/${1}/server}"
	if [ "${ME}" != "${USERNAME}" ] ; then
      if ! getent passwd "${USERNAME}" > /dev/null ; then
        useradd -r -s "/bin/false" -U "${USERNAME}" >/dev/null 2>&1 || { echo "Fatal! User setup failed.  Aborting." >&2; exit 1; }
        echo "Setup new user: $(getent passwd "${USERNAME}")"
      fi
      if ! getent group "${USERNAME}" > /dev/null ; then
        groupadd -r "${USERNAME}" >/dev/null 2>&1 || { echo "Fatal! Group setup failed.  Aborting." >&2; exit 1; }
        echo "Setup new group: $(getent group "${USERNAME}")"
      fi
      if ! [ -d "${VSPATH}" ] ; then
        mkdir -m 775 -p "${VSPATH}" >/dev/null || { echo "Fatal! Server path setup failed.  Aborting." >&2; exit 1; }
        chown "${USERNAME}:${USERNAME}" -R "${VSPATH%/*}" >/dev/null 2>&1 || { echo "Fatal! Server privileges setup failed.  Aborting." >&2; exit 1; }
        echo "Setup new server path: $(ls -ld "${VSPATH}")"
      fi
	  if [ ! -d "${DATAPATH%/*}" ] ; then
		mkdir -m 775 -p "${DATAPATH%/*}" >/dev/null || { echo "Fatal! Data path setup failed.  Aborting." >&2; exit 1; }
        chown "${USERNAME}:${USERNAME}" -R "${DATAPATH%/*}" >/dev/null 2>&1 || { echo "Fatal! Data privileges setup failed.  Aborting." >&2; exit 1; }
        echo "Setup new data path: $(ls -ld "${DATAPATH%/*}")"
	  fi
    else
      if ! getent group "${USERNAME}" > /dev/null 2>&1 ; then
        echo "Fatal! User ${USERNAME} cannot setup missing group.  Aborting." >&2
        exit 1
      elif ! id -nG | grep "${USERNAME}" > /dev/null 2>&1 ; then
        echo "Fatal! User cannot use existing group ${USERNAME}.  Aborting." >&2
        exit 1
      fi
      if ! [ -d "${VSPATH}" ] ; then
        mkdir -m 775 -p "${VSPATH}"  > /dev/null 2>&1 || { echo "Fatal! Server path setup failed.  Aborting." >&2; exit 1; }
        chgrp "${USERNAME}" -R "${VSPATH}"  > /dev/null 2>&1 || { echo "Fatal! Server privileges setup failed.  Aborting." >&2; exit 1; }
        echo "Setup new server path: $(ls -ld "${VSPATH}")"
      fi
      if ! [ -d "${DATAPATH%/*}" ] ; then
        mkdir -m 775 -p "${DATAPATH%/*}"  > /dev/null 2>&1 || { echo "Fatal! Data path setup failed.  Aborting." >&2; exit 1; }
        chgrp "${USERNAME}" -R "${DATAPATH%/*}"  > /dev/null 2>&1 || { echo "Fatal! Data privileges setup failed.  Aborting." >&2; exit 1; }
        echo "Setup new data path: $(ls -ld "${DATAPATH%/*}")"
      fi
    fi
    echo "Basic environment setup finished. Now check server.sh header if all info is correct."
	return 0
  else
	echo "Fatal! Setup for user ${1} cancelled.  Aborting." >&2
	return 1
  fi
}

#Installation check
unset missing
getent passwd "${USERNAME}" > /dev/null || missing=" user ${USERNAME}"
getent group "${USERNAME}" > /dev/null || missing="${missing} group ${USERNAME}"
[ -d "${VSPATH}" ] || missing="${missing} path ${VSPATH}"
if [ -n "${missing}" ] ; then
  echo "Username, Group or data path missing. Server environment setup needed (create${missing})"
  vs_setup "${USERNAME}"
fi

# main
case "${1}" in
  start)
    vs_start
    exit $?
    ;;
  stop)
    vs_stop
    exit $?
    ;;
  restart)
    vs_stop && vs_start
    exit $?
    ;;
  update)
    vs_update
    exit $?
    ;;
  setup)
    if [ "$(id -u)" = 0 -o "${ME}" = "${2:-${GROUPNAME:-$USERNAME}}" ] ; then
      vs_setup "${2:-${GROUPNAME:-$USERNAME}}" "${3}"
      exit $?
    else
      echo "The setup command is only available for the users root and ${2:-${GROUPNAME:-$USERNAME}}."
      exit 1
    fi
    ;;
  status)
    vs_version "${VSPATH}" # otherwise abort
    echo "Data path is ${DATAPATH}"
    vs_status
    exit $?
    ;;
  command)
    vs_version "${VSPATH}" # otherwise abort
    if [ $# -gt 1 ] ; then
      shift
      vs_command "$*"
      exit $?
    else
      echo "Must specify server command (try 'help'?)"
	  exit 1
    fi
    ;;
  *)
    echo "Usage: ${0} {start|stop|status|update|restart|setup [\"user name\"] [\"path\"]|command \"server command\"}"
    exit 1
    ;;
esac

exit 1
