#!/usr/bin/env sh

PROGNAME=${0##*/}
VERSION="0.1"

red="\e[0;91m"
blue="\e[0;34m"
green="\e[0;92m"
white="\e[0;97m"
yellow="\e[0;33m"
cyan="\e[0;96m"
reset="\e[0m"

export DISTRIB=""

clean_up() { # Perform pre-exit housekeeping
    return
}

getos() {
    if [ -f /etc/lsb-release ]; then
        #Read the config file line by line
        while IFS=$'\n\r' read -r line || [[ -n "$line" ]]; do
            #Fliters out comments and empty lines
            if [[ ${line:0:1} != ';' ]] && [[ $line = *[!\ ]* ]] && [[ $line == DISTRIB_DESCRIPTION* ]]; then
                export DISTRIB=$(echo "$line" | cut -d '=' -f 2-)
            fi
        done < /etc/lsb-release
    fi
}

error_exit() {
    echo -e "${PROGNAME}: ${1:-"Unknown Error"}" >&2
    clean_up
    exit 1
}

graceful_exit() {
    clean_up
    exit
}

signal_exit() { # Handle trapped signals
    case $1 in
        INT)
            error_exit "Program interrupted by user" ;;
        TERM)
            echo -e "\n$PROGNAME: Program terminated" >&2
            graceful_exit ;;
        *)
            error_exit "$PROGNAME: Terminating on unknown signal" ;;
    esac
}

usage() {
    echo -e "Usage: $PROGNAME [-h|--help]"
}

help_message() {
    cat <<- _EOF_
    $PROGNAME ver. $VERSION
    Minecraft Server Start/Auto-Restart UNIX Script

    $(usage)

    Options:
    -h, --help  Display this help message and exit.

_EOF_
    return
}

# Trap signals
trap "signal_exit TERM" TERM HUP
trap "signal_exit INT"  INT

# Parse command-line
while [[ -n $1 ]]; do
    case $1 in
        -h | --help)
            help_message; graceful_exit ;;
        -* | --*)
            usage
            error_exit "Unknown option $1" ;;
        *)
            echo "Argument $1 to process..." ;;
    esac
    shift
done

# Main logic
clear
echo -e "${yellow}--------------------------------------------------${reset}"
echo -e "${blue}TASK ${white}| ${yellow}Verify Bash Version ${white} ----------> ${green}SUCESSFULL${reset}"
if [ ! "$BASH_VERSION" ] ; then
    echo "Please do not use sh to run this script ($0). Use bash instead (or execute it directly)" 1>&2
    graceful_exit
fi
sleep 2s
echo -e "   ${cyan}Verified ${white}--> ${green}v$BASH_VERSION${reset}"
sleep 1s
echo ""
echo -e "${blue}TASK ${white}| ${yellow}Getting OS Type ${white} --------------> ${green}SUCESSFULL${reset}"
getos
case "$(uname -s)" in
    Linux*)     MC_SERVER_environment=Linux;;
    Darwin*)    MC_SERVER_environment=Mac;;
    CYGWIN*)    MC_SERVER_environment=Cygwin;;
    MINGW*)     MC_SERVER_environment=MinGw;;
    *)          MC_SERVER_environment="UNKNOWN:$(uname -s)"
esac
sleep 2s
echo -e "   ${cyan}Verified ${white}--> ${green}$DISTRIB${reset}"
echo ""
sleep 1s
#Makes sure that the script is being ran from the server directory
cd "${0%/*}" || exit
mkdir -p logs

echo "INFO: Starting Server script at $(date -u +%Y-%m-%d_%H:%M:%S)" >logs/jvm.log

#Default Settings
export MC_SERVER_MAX_RAM=16G
export MC_SERVER_GAME_ARGS=nogui
export MC_SERVER_JAVA_ARGS='-server -XX:+AggressiveOpts -XX:ParallelGCThreads=3 -XX:+UseConcMarkSweepGC -XX:+UnlockExperimentalVMOptions -XX:+UseParNewGC -XX:+ExplicitGCInvokesConcurrent -XX:MaxGCPauseMillis=10 -XX:GCPauseIntervalMillis=50 -XX:+UseFastAccessorMethods -XX:+OptimizeStringConcat -XX:NewSize=84m -XX:+UseAdaptiveGCBoundary -XX:NewRatio=3'
export MC_SERVER_CRASH_COUNT=5
export MC_SERVER_CRASH_TIMER=600
export MC_SERVER_JAVA_PATH=DISABLE
export MC_SERVER_RUN_FROM_BAD_FOLDER=0
export MC_SERVER_IGNORE_OFFLINE=0
export MC_SERVER_IGNORE_JAVA_CHECK=0
export MC_SERVER_USE_SPONGE=1
export MC_SERVER_HIGH_CPU_PRIORITY=0
export MC_SERVER_JMX_ENABLE=0
export MC_SERVER_JMX_PORT=9010
export MC_SERVER_JMX_HOST=127.0.0.1
export MC_SERVER_JMX_ALLOW_REMOTE=0
export MC_SERVER_MODPACK_NAME=LAPITOS
export MC_SERVER_DEFAULT_WORLD_TYPE=DEFAULT
export MC_SERVER_MCVER=1.12.2
export MC_SERVER_FORGEVER=14.23.5.2847
export MC_SERVER_FORGEURL=DISABLE

if [ -f ./settings.cfg ]; then
    #Read the config file line by line
    while IFS=$'\n\r' read -r line || [[ -n "$line" ]]; do
        #Fliters out comments and empty lines
        if [[ ${line:0:1} != ';' ]] && [[ $line = *[!\ ]* ]]; then
            var="MC_SERVER_"$(echo "$line" | cut -d '=' -f 1)
            value=$(echo "$line" | cut -d '=' -f 2-)
            export "$var"="$value"
        fi
    done < ./settings.cfg
else
    echo "Failed to find settings file default settings will be used may cause issues"
    echo "WARN: Failed to find settings.cfg" >>logs/jvm.log
fi
echo -e "${blue}TASK ${white}| ${yellow}Load Variables from Settings ${white}--> ${green}SUCESSFULL${reset}"
sleep 2s

export MC_SERVER_FORGESHORT=${MC_SERVER_FORGEVER: - 4}

if [[ "$MC_SERVER_JAVA_PATH" == "DISABLE" ]]; then
    export MC_SERVER_JAVA=java
else
    command -v $MC_SERVER_JAVA_PATH/java >/dev/null
    if [[ $? == "0" ]]; then
        export MC_SERVER_JAVA=$MC_SERVER_JAVA_PATH/java
    else
        echo "Could not find java if path in given in settings using the deflut option"
        echo "WARN: Could not find java if path in given in settings" >>logs/jvm.log
        export MC_SERVER_JAVA=java
    fi
fi
{
    echo "DEBUG: Dumping starting variables: "
    echo "DEBUG: MC_SERVER_MAX_RAM=$MC_SERVER_MAX_RAM"
    echo "DEBUG: MC_SERVER_GAME_ARGS=$MC_SERVER_GAME_ARGS"
    echo "DEBUG: MC_SERVER_JAVA_ARGS=$MC_SERVER_JAVA_ARGS"
    echo "DEBUG: MC_SERVER_CRASH_COUNT=$MC_SERVER_CRASH_COUNT"
    echo "DEBUG: MC_SERVER_CRASH_TIMER=$MC_SERVER_CRASH_TIMER"
    echo "DEBUG: MC_SERVER_JAVA_PATH=$MC_SERVER_JAVA_PATH"
    echo "DEBUG: MC_SERVER_RUN_FROM_BAD_FOLDER=$MC_SERVER_RUN_FROM_BAD_FOLDER"
    echo "DEBUG: MC_SERVER_IGNORE_OFFLINE=$MC_SERVER_IGNORE_OFFLINE"
    echo "DEBUG: MC_SERVER_IGNORE_JAVA_CHECK=$MC_SERVER_IGNORE_JAVA_CHECK"
    echo "DEBUG: MC_SERVER_USE_SPONGE=$MC_SERVER_USE_SPONGE"
    echo "DEBUG: MC_SERVER_HIGH_CPU_PRIORITY=$MC_SERVER_HIGH_CPU_PRIORITY"
    echo "DEBUG: MC_SERVER_JMX_ENABLE=$MC_SERVER_JMX_ENABLE"
    echo "DEBUG: MC_SERVER_JMX_PORT=$MC_SERVER_JMX_PORT"
    echo "DEBUG: MC_SERVER_JMX_ALLOW_REMOTE=$MC_SERVER_JMX_ALLOW_REMOTE"
    echo "DEBUG: MC_SERVER_MODPACK_NAME=$MC_SERVER_MODPACK_NAME"
    echo "DEBUG: MC_SERVER_DEFAULT_WORLD_TYPE=$MC_SERVER_DEFAULT_WORLD_TYPE"
    echo "DEBUG: MC_SERVER_MCVER=$MC_SERVER_MCVER"
    echo "DEBUG: MC_SERVER_FORGEVER=$MC_SERVER_FORGEVER"
    echo "DEBUG: MC_SERVER_FORGESHORT=$MC_SERVER_FORGESHORT"
    echo "DEBUG: MC_SERVER_FORGEURL=$MC_SERVER_FORGEURL"
    echo "DEBUG: Basic System Info:  $(uname -a)"
    
    if [[ $MC_SERVER_environment == "Mac" ]]; then
        echo "DEBUG: Total RAM estimate: $(sysctl hw.memsize | awk 'BEGIN {total = 1} {if (NR == 1 || NR == 3) total *=$NF} END {print total / 1024 / 1024" MB"}')"
    else
        echo "DEBUG: Total RAM estimate: $(getconf -a | grep PAGES | awk 'BEGIN {total = 1} {if (NR == 1 || NR == 3) total *=$NF} END {print total / 1024 / 1024" MB"}')"
    fi
    
    echo "DEBUG: Java Version info: "
    echo "DEBUG: Java path: $MC_SERVER_JAVA"
    $MC_SERVER_JAVA -version
    echo "DEBUG: Dumping current directory listing"
    ls -s1h
}>>logs/jvm.log 2>&1

# loop to restart server and check crash frequency
a=0
last_crash=$((SECONDS))
remote=FALSE

if [[ ${MC_SERVER_JMX_ENABLE} == 1 ]]; then
    if [[ ${MC_SERVER_JMX_ALLOW_REMOTE} == 1 ]]; then
        export MC_SERVER_JMX_HOST=`dig @resolver4.opendns.com myip.opendns.com +short`
        remote=TRUE
    fi
    echo "INFO: JMX Enabled on port ${MC_SERVER_JMX_PORT}" >>logs/jvm.log 2>&1
    export MC_SERVER_ARGS="${MC_SERVER_JAVA_ARGS} -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=${MC_SERVER_JMX_PORT} -Dcom.sun.management.jmxremote.rmi.port=${MC_SERVER_JMX_PORT} -Dcom.sun.management.jmxremote.local.only=false -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=$MC_SERVER_JMX_HOST"
    echo ""
    echo -e "${yellow}--------------------------------------------------${reset}"
    echo -e "${cyan}JMX Feature Enabled${reset}"
    echo -e "   ${yellow}Host: ${green}${MC_SERVER_JMX_HOST}${reset}"
    echo -e "   ${yellow}Port: ${green}${MC_SERVER_JMX_PORT}${reset}"
    echo -e "   ${yellow}Remote Allowed: ${green}${remote}${reset}"
else
    export MC_SERVER_ARGS=${MC_SERVER_JAVA_ARGS}
fi

run=true
while $run ; do
    
    #Testing some stuff before the server starts
    
    #Checks if script is running in temp
    echo "DEBUG: Current directory is $(pwd)" >>logs/jvm.log
    
    if [ "$(pwd)" = "/tmp" ] || [ "$(pwd)" = "/var/tmp" ]; then
        echo "Current directory appears to be TMP"
        echo "WARN: Current DIR is TEMP"  >>logs/jvm.log
        if [ ${MC_SERVER_RUN_FROM_BAD_FOLDER} -eq 0 ]; then
            echo "ERROR: Stopping due to bad folder (TMP)" >>logs/jvm.log
            echo "RUN_FROM_BAD_FOLDER setting is off, exiting script"
            exit 0
        else
            echo "WARN: Bad folder (TMP) but continuing anyway" >>logs/jvm.log
            echo "Bypassing cd=temp halt per script settings"
        fi
    fi
    
    #Checks if users has full read/write access to the server dir
    if [ ! -r . ] || [ ! -w . ]; then
        echo "WARN: Not full R/W access on current directory"
        echo "You do not have full R/W access to current directory"
        if [ ${MC_SERVER_RUN_FROM_BAD_FOLDER} -eq 0 ]; then
            echo "ERROR: Stopping due to bad folder (R/W access)" >>logs/jvm.log
            echo "RUN_FROM_BAD_FOLDER setting is off, exiting script"
            exit 0
        else
            echo "WARN: Bad folder (R/W) cut continuing anyway" >>logs/jvm.log
            echo "Bypassing no R/W halt (per script settings)"
        fi
    fi
    
    #Checks java version
    if [[ ${MC_SERVER_IGNORE_JAVA_CHECK} == 1 ]]; then
        echo "WARN: Skipping validation of proper Java install/version..".
        echo "IF Java is not installed, too old, or not 64-bit, the server probably won't start/run correctly"
        echo "WARN: Skipping validation of Java install..." >>logs/jvm.log
    else
        command -v $MC_SERVER_JAVA >> /dev/null 2>&1
        if [ $? -eq 0 ]; then
            echo "DEBUG: Java found" >>logs/jvm.log
            if [[ "$(${MC_SERVER_JAVA} -version 2>&1 | awk -F ' ' '/Bit/ {print $2}')"  == "64-Bit" ]] || [[ "$(${MC_SERVER_JAVA} -version 2>&1 | awk -F ' ' '/Bit/ {print $3}')"  == "64-Bit" ]]; then
                echo "DEBUG: 64-bit Java found" >>logs/jvm.log
            else
                echo "ERROR: 64-bit Java not found"
                echo "ERROR: 64-bit Java not found" >>logs/jvm.log
                exit 1
            fi
            if [ "$(${MC_SERVER_JAVA} -version 2>&1 | awk -F '"' '/version/ {print $2}' | cut -c1-3)" = "1.8" ]; then
                echo "DEBUG: Java 8 Found" >>logs/jvm.log
            else
                echo "ERROR: Java 8 not found"
                echo "ERROR: Java 8 not found" >>logs/jvm.log
                exit 1
            fi
        else
            echo "ERROR: Java is not installed install Java before continuing"
            echo "ERROR: No Java detected" >>logs/jvm.log
            exit 1
        fi
    fi
    
    #Check internet connection
    if [ ${MC_SERVER_IGNORE_OFFLINE} -eq 1 ]; then
        echo "WARN: Internet connectivity checking is disabled" >>logs/jvm.log
        echo "Skipping internet connectivity check"
    else
        command -v ping >> /dev/null 2>&1
        if [ $? -eq 0 ]; then
            echo "DEBUG: Ping found on system" >>logs/jvm.log
            if ping -c 1 8.8.8.8 >> /dev/null 2>&1; then
                echo "INFO: Ping to Google DNS successful" >>logs/jvm.log
                echo -e "${blue}TASK ${white}| ${yellow}Check Ping to Google DNS ${white}------> ${green}SUCESSFULL${reset}"
            else
                echo "ERROR: Ping to Google DNS failed. No internet access?" >>logs/jvm.log
                echo -e "${blue}TASK ${white}| ${yellow}Check Ping to Google DNS ${white}------> ${red}FAILED${reset}"
                
                if ping -c 1 4.2.2.1 >> /dev/null 2>&1; then
                    echo "INFO: Ping to L4 successful" >>logs/jvm.log
                    echo "Ping to L4 successful"
                else
                    echo "ERROR: Ping to L4 failed. No internet access?"  >>logs/jvm.log
                    echo "Ping to L4 failed. No internet access?"
                    echo "IGNORE_OFFLINE is set to off exiting"
                    exit 1
                fi
            fi
        else
            echo "WARN: Ping not install can not check network connection" >>logs/jvm.log
            echo "Ping is not install can not assure internet connection"
        fi
    fi
    
    sleep 2s

    #Checking minecraft has the files its needs to run
    if [ ! -d ./libraries ] ; then
        echo "WARN: library directory not found" >>logs/jvm.log
        echo "Required files not found, need to install Forge"
    fi
    forge=$(ls forge*"$MC_SERVER_MCVER"*"$MC_SERVER_FORGEVER"*universal.jar 2>>logs/jvm.log)
    if [[ $? != 0 ]] ; then
        echo "WARN: no forge jar for MCVER: $MC_SERVER_MCVER and FORGEVER: $MC_SERVER_FORGEVER found"  >>logs/jvm.log
        echo "Required files not found, need to install Forge"
    else
        export MC_SERVER_FORGE_JAR="${forge[0]}"
        echo "DEBUG: Found forge jar: $MC_SERVER_FORGE_JAR" >>logs/jvm.log
    fi
    
    if [ ! -f ./minecraft_server.${MC_SERVER_MCVER}.jar ] ; then
        echo "WARN: minecraft_server.${MC_SERVER_MCVER}.jar not found" >>logs/jvm.log
        echo "Required files not found, need to install Forge"
    fi
    
    #Checks if the EULA exists if not generates it
    if [ ! -f eula.txt ]; then
        echo "Could not find eula.txt"
    else
        if grep -Fxq "eula=true" eula.txt; then
            echo "INFO: Found 'eula=true' in 'eula.txt'" >>logs/jvm.log
        else
            echo "Could not find 'eula=true' in 'eula.txt'"
        fi
    fi
    
    #Checks if settings.properties exists and if not adds some default values
    if [ ! -f server.properties ]; then
        echo "Could not find server.properties, creating initial copy..."
        echo "INFO: server.properties not found... populating default" >>logs/jvm.log
        {
            echo "view-distance=8"
            echo "allow-flight=true"
            echo "level-type=$MC_SERVER_DEFAULT_WORLD_TYPE"
            echo "snooper-enabled=false"
            echo "max-tick-time=90000"
            echo "motd=$MC_SERVER_MODPACK_NAME"
        }>> server.properties
    fi
    echo -e "${yellow}--------------------------------------------------${reset}"
    echo ""

    echo -e "${green}Starting Minecraft Server${reset}"
    if [[ $MC_SERVER_JMX_ENABLE == 1 ]]; then
        echo "JMX Enabled on port ${MC_SERVER_JMX_PORT}"
    fi
    echo "INFO: Starting Server at $(date -u +%Y-%m-%d_%H:%M:%S)" >>logs/jvm.log 2>&1
    "$MC_SERVER_JAVA" -Xmx"$MC_SERVER_MAX_RAM" "$MC_SERVER_ARGS" -jar "$MC_SERVER_FORGE_JAR" "$MC_SERVER_GAME_ARGS"
    b=$?
    b=1
    if [[ $b == 0 ]]; then
        echo "DEBUG: Server ended with code 0" >>logs/jvm.log
        a=0
    else
        now=$((SECONDS))
        diff=$now-$last_crash
        if [[ $diff -gt $MC_SERVER_CRASH_TIMER ]]; then
            a=1
        else
            a=$((a+1))
        fi
        last_crash=$((SECONDS))
    fi
    if [[ "$a" == "$MC_SERVER_CRASH_COUNT" ]]; then
        echo "The server has crashed too many times"
        echo "ERROR: Server has failed to start too many times in a row." >>logs/jvm.log
        exit 1
    fi
    
    export answer="y"
    echo ""
    echo "------------------------------------------------------------"
    echo "Server will restart in ~20 seconds. No input needed..."
    read -r -t 22 -p "Press (n) then (enter) to return to Terminal:  >" answer

    if [[ "$answer" =~ ^([nN][oO]|[nN])+$ ]]; then
        echo "INFO: User cancelled restart; exiting to shell" >>logs/jvm.log
        echo ""
        exit 0
    fi
    echo ""
    echo "INFO: Server-auto-restart commencing"  >>logs/jvm.log
    echo "Rebooting now!"
    clear
done