Index: /scripts/etc/7dtd.conf
===================================================================
--- /scripts/etc/7dtd.conf	(revision 16)
+++ /scripts/etc/7dtd.conf	(revision 17)
@@ -1,4 +1,4 @@
 #!/bin/sh
-# Version 1
+# Version 4
 
 # Root directory where steamcmd should be placed
@@ -7,8 +7,5 @@
 export STEAM_PASS=
 
-# Root directory of the engine
-export SDTD_ROOT=/home/sdtd/7dtd
-
-# Root of the 7dtd folders, containing e.g. the "instances" folder
+# Root of the 7dtd folders, containing e.g. the "engine", "instances" folders
 export SDTD_BASE=/home/sdtd
 
@@ -21,9 +18,5 @@
 
 # Paths to binaries. Use "which BINARYNAME" to find the path of a single binary
-export CP=/bin/cp
 export RSYNC=/usr/bin/rsync
-export DU=/usr/bin/du
-export FREE=/usr/bin/free
-export AWK=/usr/bin/awk
-export PIDOF=/bin/pidof
 export WINE=/usr/bin/wine
+export XMLSTARLET=/usr/bin/xmlstarlet
Index: /scripts/etc/bash_completion.d/7dtd
===================================================================
--- /scripts/etc/bash_completion.d/7dtd	(revision 17)
+++ /scripts/etc/bash_completion.d/7dtd	(revision 17)
@@ -0,0 +1,24 @@
+. /usr/local/lib/7dtd/common.sh
+
+_sdtd() {
+	local cur prev opts
+	COMPREPLY=()
+	cur="${COMP_WORDS[COMP_CWORD]}"
+	prev="${COMP_WORDS[COMP_CWORD-1]}"
+
+	opts="help $(listCommands)"
+	
+	case "${COMP_CWORD}" in
+		1)
+			COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
+			return 0
+			;;
+		*)
+			if [ "$(type -t sdtdCommand$(camelcasePrep ${COMP_WORDS[1]})Expects)" = "function" ]; then
+				COMPREPLY=( $(compgen -W "$(sdtdCommand$(camelcasePrep ${COMP_WORDS[1]})Expects $COMP_CWORD)" -- ${cur}) )
+				return 0
+			fi
+			;;
+	esac
+}
+complete -F _sdtd 7dtd.sh
Index: /scripts/etc/init.d/7dtd.sh
===================================================================
--- /scripts/etc/init.d/7dtd.sh	(revision 16)
+++ /scripts/etc/init.d/7dtd.sh	(revision 17)
@@ -1,4 +1,4 @@
 #!/bin/sh
-# Version 3
+# Version 4
 
 ### BEGIN INIT INFO
@@ -14,28 +14,13 @@
 ### END INIT INFO
 
-. /usr/local/bin/7dtd-common.sh
-checkRootLoadConf
-
 case "$1" in
     start)
-    	echo "Starting all 7dtd instances:"
-        for I in $(getInstanceList); do
-            echo "Starting: $I"
-            /usr/local/bin/7dtd-start.sh $I
-        done
-        echo "All done"
+    	/usr/local/bin/7dtd.sh start "!"
     ;;
     stop)
-    	echo "Stopping all 7dtd instances:"
-        for I in $(getInstanceList); do
-            echo "Stopping: $I"
-            /usr/local/bin/7dtd-kill.sh $I
-            echo
-            echo
-        done
-        echo "All done"
+    	/usr/local/bin/7dtd.sh kill "!"
     ;;
     status)
-        /usr/local/bin/7dtd-instances.sh
+    	/usr/local/bin/7dtd.sh instances
     ;;
     *)
Index: ripts/usr/local/bin/7dtd-backup.sh
===================================================================
--- /scripts/usr/local/bin/7dtd-backup.sh	(revision 16)
+++ 	(revision )
@@ -1,24 +1,0 @@
-#!/bin/bash
-# Version 3
-. /etc/7dtd.conf
-
-DT=`date "+%Y-%m-%d_%H-%M"`
-NewBackup=$SDTD_BACKUP_ROOT/$DT
-
-# Check for backup folder existence
-if [ -e $SDTD_BACKUP_ROOT ]; then
-	# Exists, copy(link) latest backup
-	unset -v LatestBackup
-	for file in "$SDTD_BACKUP_ROOT"/*; do
-		[[ $file -nt $LatestBackup ]] && LatestBackup=$file
-	done
-	$CP -al $LatestBackup $NewBackup
-else
-	# Create new backup dir
-	mkdir $SDTD_BACKUP_ROOT
-fi
-
-$RSYNC -a --delete --numeric-ids --delete-excluded $SDTD_BASE/instances/./ $NewBackup
-touch $NewBackup
-
-#echo "Backup folder disk usage: `$DU -sh $SDTD_BACKUP_ROOT`"
Index: ripts/usr/local/bin/7dtd-common.sh
===================================================================
--- /scripts/usr/local/bin/7dtd-common.sh	(revision 16)
+++ 	(revision )
@@ -1,126 +1,0 @@
-#!/bin/bash
-# Version 3
-
-# Provides common functions for 7dtd-scripts. Not intended to be run directly.
-
-# Check if the script is run as root (exit otherwise) and load global config
-checkRootLoadConf() {
-	if [ `id -u` -ne 0 ]; then
-		echo "This script has to be run as root!"
-		exit 10
-	fi
-	. /etc/7dtd.conf
-}
-
-# Get the config path for the given instance
-# Params:
-#   1: Instance name
-# Returns:
-#   Config path for instance
-getInstancePath() {
-	echo $SDTD_BASE/instances/$1
-}
-
-# Check if the given instance name is an existing instance
-# Params:
-#   1: Instance name
-# Returns:
-#   0/1 instance not valid/valid
-isValidInstance() {
-	if [ -z $1 ]; then
-		echo 0
-	else
-		if [ ! -d $(getInstancePath $1) ]; then
-			echo 0
-		else
-			if [ ! -f $(getInstancePath $1)/config.xml ]; then
-				echo 0
-			else
-				echo 1
-			fi
-		fi
-	fi
-}
-
-# Check if the given instance is valid, exit the script otherwise
-# Params:
-#   1: instance name
-checkInstanceValid() {
-	if [ -z $1 ]; then
-		echo "Missing parameter <instance>"
-		exit 1
-	fi
-	if [ $(isValidInstance $1) -eq 0 ]; then
-		echo "'$1' is not a valid instance"
-		exit 1
-	fi
-}
-
-# Check if the given instance is currently running
-# Params:
-#   1: Instance name
-# Returns:
-#   0 = not running
-#   1 = running
-isRunning() {
-	start-stop-daemon --status --pidfile $(getInstancePath $1)/7dtd.pid
-	if [ $? -eq 0 ]; then
-		echo 1
-	else
-		echo 0
-	fi
-}
-
-# Get list of defined instances
-# Returns:
-#   List of instances
-getInstanceList() {
-	for IF in $SDTD_BASE/instances/*; do
-		I=`basename $IF`
-		if [ $(isValidInstance $I) -eq 1 ]; then
-			echo $I
-		fi
-	done
-}
-
-# Get the PID of the instance if it is running, 0 otherwise
-# Params:
-#   1: Instance name
-# Returns:
-#   0 if not running
-#   PID otherwise
-getInstancePID() {
-	if [ $(isRunning $1) -eq 1 ]; then
-		cat $(getInstancePath $1)/7dtd.pid
-	else
-		echo 0
-	fi
-}
-
-# Get a single value from a serverconfig
-# Params:
-#   1: Instance name
-#   2: Property name
-# Returns:
-#   Property value
-getConfigValue() {
-	CONF=$(getInstancePath $1)/config.xml
-	xmlstarlet sel -t -v "/ServerSettings/property[@name='$2']/@value" $CONF
-}
-
-# Send a single command to the telnet port
-# Params:
-#   1: Instance name
-#   2: Command
-# Returns:
-#   String of telnet output
-telnetCommand() {
-	TEL_ENABLED=$(getConfigValue $1 TelnetEnabled)
-	TEL_PORT=$(getConfigValue $1 TelnetPort)
-	TEL_PASS=$(getConfigValue $1 TelnetPassword)	
-	if [ "$TEL_ENABLED" = "true" ] && [ -n "$TEL_PASS" ]; then
-		echo -e "$TEL_PASS\n$2\nexit" | nc -q 2 127.0.0.1 $TEL_PORT
-	else
-		echo "Telnet not enabled or no password set."
-	fi
-}
Index: ripts/usr/local/bin/7dtd-instances.sh
===================================================================
--- /scripts/usr/local/bin/7dtd-instances.sh	(revision 16)
+++ 	(revision )
@@ -1,105 +1,0 @@
-#!/bin/bash
-# Version 3
-
-# Lists available 7dtd instances.
-# Returns:
-#  0
-
-. /usr/local/bin/7dtd-common.sh
-checkRootLoadConf
-
-listInstances() {
-	printf "%-*s | %-*s | %-*s | %-*s\n" 20 "Instance name" 8 "Running" 7 "Players" 5 "Port"
-	printf -v line "%*s-+-%*s-+-%*s-+-%*s\n" 20 " " 8 " " 7 " " 5 " "
-	echo ${line// /-}
-	for I in $(getInstanceList); do
-		if [ $(isRunning $I) -eq 1 ]; then
-			run="yes"
-			tel=$(telnetCommand $I lp)
-			cur=`echo $tel | sed "s/\r/\n/g" | sed "s/^ //g" | grep "Total of " | cut -d\  -f 3`
-		else
-			run="no"
-			cur="-"
-		fi
-
-		max=$(getConfigValue $I ServerMaxPlayerCount)
-		port=$(getConfigValue $I ServerPort)
-
-		printf "%-*s | %*s |   %2s/%2d | %5d\n" 20 "$I" 8 "$run" $cur $max $port
-	done
-	exit 0
-}
-
-showInfo() {
-	checkInstanceValid $1
-
-	line() {
-		printf "    %-*s %s\n" 15 "$1" "$2"
-	}
-	
-	echo
-
-	echo "7dtd instance: $1"
-	echo
-
-	if [ $(isRunning $1) -eq 1 ]; then
-		echo "Status: Running"
-		echo "Open ports:"
-		netstat -nlp | grep $(getInstancePID $1) | sed -r 's/^([^ ]*)\s+.*[^ :]*:([^ ]*).*[^ :]*:[^ ]*.*/    \2 (\1)/g' | sort
-		tel=$(telnetCommand $1 lp)
-		cur=`echo $tel | sed "s/\r/\n/g" | sed "s/^ //g" | grep "Total of " | cut -d\  -f 3`
-		echo "Players: $cur"
-	else
-		echo "Status: NOT running"
-	fi
-
-	echo
-	echo "Game info:"
-	line "Server name:" $(getConfigValue $1 ServerName)
-	line "Password:" $(getConfigValue $1 ServerPassword)
-	line "Max players:" $(getConfigValue $1 ServerMaxPlayerCount)
-	line "World:" $(getConfigValue $1 GameWorld)
-
-	echo
-	echo "Network info:"
-	line "Port:" $(getConfigValue $1 ServerPort)
-	line "Public:" $(getConfigValue $1 ServerIsPublic)
-	if [ "$(getConfigValue $1 ControlPanelEnabled)" = "false" ]; then
-		cp="off"
-	else
-		cp="Port $(getConfigValue $1 ControlPanelPort), Pass $(getConfigValue $1 ControlPanelPassword)"
-	fi
-	line "Control Panel:" "$cp"
-	if [ "$(getConfigValue $1 TelnetEnabled)" = "false" ]; then
-		tn="off"
-	else
-		tn="Port $(getConfigValue $1 TelnetPort), Pass $(getConfigValue $1 TelnetPassword)"
-	fi
-	line "Telnet:" "$tn"
-
-	echo
-	exit 0
-}
-
-showHelp() {
-	name=`basename $0`
-	echo "Unknown parameter: $1"
-	echo "Usage:"
-	echo "  $name [list]          - List all instances"
-	echo "  $name show <instance> - Show detailed info on the given instance"
-	exit 0
-}
-
-COMMAND=${1:-list}
-
-case "$COMMAND" in
-	list)
-		listInstances
-	;;
-	show)
-		showInfo $2
-	;;
-	*)
-		showHelp $COMMAND
-esac
-exit 1
Index: ripts/usr/local/bin/7dtd-kill.sh
===================================================================
--- /scripts/usr/local/bin/7dtd-kill.sh	(revision 16)
+++ 	(revision )
@@ -1,40 +1,0 @@
-#!/bin/bash
-# Version 3
-
-# Tries to stop the 7dtd instance given as first parameter.
-# Returns:
-#  0 : Done
-#  1 : Was not running
-#  2 : No instance name given
-#  3 : No such instance
-
-. /usr/local/bin/7dtd-common.sh
-checkRootLoadConf
-
-checkInstanceValid $1
-
-res=$(isRunning $1)
-if [ $res -eq 1 ]; then
-	echo "Trying to gracefully shutdown..."
-	tmp=$(telnetCommand $1 shutdown)
-	echo "Waiting for server to shut down..."
-	
-	waittime=0
-	maxwait=5
-	until [ $(isRunning $1) -eq 0 ] || [ $waittime -eq $maxwait ]; do
-		(( waittime++ ))
-		sleep 1
-		echo $waittime/$maxwait
-	done
-	
-	if [ $(isRunning $1) -eq 1 ]; then
-		echo "Failed, force closing server..."
-		start-stop-daemon --stop --pidfile $(getInstancePath $1)/7dtd.pid
-	fi
-	rm $(getInstancePath $1)/7dtd.pid
-	echo "Done"	
-	exit 0
-else
-	echo "7dtd instance $1 is NOT running"
-	exit 1
-fi
Index: ripts/usr/local/bin/7dtd-running.sh
===================================================================
--- /scripts/usr/local/bin/7dtd-running.sh	(revision 16)
+++ 	(revision )
@@ -1,24 +1,0 @@
-#!/bin/bash
-# Version 3
-
-# Checks if the 7dtd instance given as first parameter is running.
-# Returns:
-#  0 : Running
-#  1 : Not running
-#  2 : No instance name given
-#  3 : No such instance
-
-. /usr/local/bin/7dtd-common.sh
-checkRootLoadConf
-
-checkInstanceValid $1
-
-if [ $(isRunning $1) -eq 1 ]; then
-	echo "7dtd instance $1 is running"
-	echo "Open ports:"
-	netstat -nlp | grep 15161 | sed -r 's/^.*[^ :]*:([^ ]*).*[^ :]*:[^ ]*.*/    \1/g' | sort
-	exit 0
-else
-	echo "7dtd instance $1 is NOT running"
-	exit 1
-fi
Index: ripts/usr/local/bin/7dtd-start.sh
===================================================================
--- /scripts/usr/local/bin/7dtd-start.sh	(revision 16)
+++ 	(revision )
@@ -1,43 +1,0 @@
-#!/bin/bash
-# Version 3
-
-# Tries to start the 7dtd instance given as first parameter.
-# Returns:
-#  0 : Done
-#  1 : Was already running
-#  2 : No instance name given
-#  3 : No such instance
-#  5 : Unknown error when starting engine
-
-. /usr/local/bin/7dtd-common.sh
-checkRootLoadConf
-
-checkInstanceValid $1
-
-if [ $(isRunning $1) -eq 0 ]; then
-	if [ ! `pgrep Xvfb` ]; then
-		echo "Xvfb not yet running. Starting..."
-		su -c "/usr/bin/Xvfb :1 -screen 0 640x480x16" $SDTD_USER 2>&1 | grep -v "Could not init font path element" &
-		sleep 3
-	fi
-	export DISPLAY=localhost:1.0
-	
-	SSD_PID="--pidfile $(getInstancePath $1)/7dtd.pid --make-pidfile"
-	SSD_DAEMON="--background --no-close"
-	SSD_USER="--chuid $SDTD_USER:$SDTD_GROUP --user $SDTD_USER"
-	OPTS="-quit -batchmode -nographics -configfile=$(getInstancePath $1)/config.xml -dedicated"
-	
-	start-stop-daemon --start $SSD_PID $SSD_DAEMON $SSD_USER --chdir $SDTD_ROOT --exec $WINE -- $SDTD_ROOT/7DaysToDie.exe $OPTS > $(getInstancePath $1)/stdout.log 2>&1
-	sleep 1
-	if [ $(isRunning $1) -eq 1 ]; then
-		echo "Done!"
-		exit 0
-	else
-		echo "Failed!"
-		rm -f $(getInstancePath $1)/7dtd.pid
-		exit 5
-	fi
-else
-	echo "7dtd instance $1 is already running"
-	exit 1
-fi
Index: ripts/usr/local/bin/7dtd-update.sh
===================================================================
--- /scripts/usr/local/bin/7dtd-update.sh	(revision 16)
+++ 	(revision )
@@ -1,32 +1,0 @@
-#!/bin/bash
-# Version 3
-
-. /usr/local/bin/7dtd-common.sh
-checkRootLoadConf
-
-for I in $(getInstanceList); do
-	if [ $(isRunning $I) -eq 1 ]; then
-		echo "At least one instance is still running."
-		echo "Before updating the engine please stop all instances!"
-		exit 1
-	fi
-done
-
-if [ ! -e $STEAMCMD_ROOT ]; then
-	mkdir $STEAMCMD_ROOT
-	cd /tmp
-	wget http://media.steampowered.com/installer/steamcmd_linux.tar.gz
-	tar -xvzf steamcmd_linux.tar.gz -C $STEAMCMD_ROOT
-	cd $STEAMCMD_ROOT
-	./steamcmd.sh +quit
-fi
-
-cd $STEAMCMD_ROOT
-
-./steamcmd.sh +@sSteamCmdForcePlatformType windows +login $STEAM_USER $STEAM_PASS +force_install_dir $SDTD_ROOT "+app_update 251570" validate +quit
-chown $SDTD_USER.$SDTD_GROUP -R $SDTD_ROOT
-
-cp $SDTD_ROOT/Install/32bit/7DaysToDie.exe $SDTD_ROOT/
-cp $SDTD_ROOT/Install/32bit/mono.dll $SDTD_ROOT/7DaysToDie_Data/Mono/
-cp $SDTD_ROOT/Install/32bit/SteamworksManaged.dll $SDTD_ROOT/7DaysToDie_Data/Managed/
-cp $SDTD_BASE/msvcr100.dll $SDTD_ROOT/
Index: /scripts/usr/local/bin/7dtd.sh
===================================================================
--- /scripts/usr/local/bin/7dtd.sh	(revision 17)
+++ /scripts/usr/local/bin/7dtd.sh	(revision 17)
@@ -0,0 +1,36 @@
+#!/bin/bash
+# Version 4
+
+. /usr/local/lib/7dtd/common.sh
+checkRootLoadConf
+
+if [ -z $1 ]; then
+	genericHelp
+else
+	CMD=$(camelcasePrep "$1")
+	shift
+	
+	if [ "$CMD" = "Help" ]; then
+		if [ -z $1 ]; then
+			genericHelp
+		else
+			HELPCMD=$(camelcasePrep "$1")
+			if [ "$(type -t sdtdCommand${HELPCMD}Help)" = "function" ]; then
+				sdtdCommand${HELPCMD}Help
+			else
+				echo "Command \"$1\" does not exist!"
+				exit 1
+			fi
+		fi
+	else
+		if [ "$(type -t sdtdCommand${CMD})" = "function" ]; then
+			sdtdCommand${CMD} "$@"
+		else
+			echo "Command \"$CMD\" does not exist!"
+			exit 1
+		fi
+	fi
+fi
+
+exit 0
+
Index: /scripts/usr/local/lib/7dtd/commands/backup.sh
===================================================================
--- /scripts/usr/local/lib/7dtd/commands/backup.sh	(revision 17)
+++ /scripts/usr/local/lib/7dtd/commands/backup.sh	(revision 17)
@@ -0,0 +1,37 @@
+#!/bin/bash
+# Version 4
+
+# Backups game data files.
+
+sdtdCommandBackup() {
+	DT=`date "+%Y-%m-%d_%H-%M"`
+	NewBackup=$SDTD_BACKUP_ROOT/$DT
+
+	# Check for backup folder existence
+	if [ -e $SDTD_BACKUP_ROOT ]; then
+		# Exists, copy(link) latest backup
+		unset -v LatestBackup
+		for file in "$SDTD_BACKUP_ROOT"/*; do
+			[[ $file -nt $LatestBackup ]] && LatestBackup=$file
+		done
+		cp -al $LatestBackup $NewBackup
+	else
+		# Create new backup dir
+		mkdir $SDTD_BACKUP_ROOT
+	fi
+
+	$RSYNC -a --delete --numeric-ids --delete-excluded $SDTD_BASE/instances/./ $NewBackup
+	touch $NewBackup
+
+	#echo "Backup folder disk usage: `$DU -sh $SDTD_BACKUP_ROOT`"
+}
+
+sdtdCommandBackupHelp() {
+	echo "Usage: $(basename $0) backup"
+	echo
+	echo "Backups all data files (instance configurations, save data, logs)."
+}
+
+sdtdCommandBackupDescription() {
+	echo "Backup game data files"
+}
Index: /scripts/usr/local/lib/7dtd/commands/instances.sh
===================================================================
--- /scripts/usr/local/lib/7dtd/commands/instances.sh	(revision 17)
+++ /scripts/usr/local/lib/7dtd/commands/instances.sh	(revision 17)
@@ -0,0 +1,35 @@
+#!/bin/bash
+# Version 4
+
+
+sdtdCommandInstances() {
+	printf "%-*s | %-*s | %-*s | %-*s\n" 20 "Instance name" 8 "Running" 7 "Players" 5 "Port"
+	printf -v line "%*s-+-%*s-+-%*s-+-%*s\n" 20 " " 8 " " 7 " " 5 " "
+	echo ${line// /-}
+	for I in $(getInstanceList); do
+		if [ $(isRunning $I) -eq 1 ]; then
+			run="yes"
+			tel=$(telnetCommand $I lp)
+			cur=`echo $tel | sed "s/\r/\n/g" | sed "s/^ //g" | grep "Total of " | cut -d\  -f 3`
+		else
+			run="no"
+			cur="-"
+		fi
+
+		max=$(getConfigValue $I ServerMaxPlayerCount)
+		port=$(getConfigValue $I ServerPort)
+
+		printf "%-*s | %*s |   %2s/%2d | %5d\n" 20 "$I" 8 "$run" $cur $max $port
+	done
+	exit 0
+}
+
+sdtdCommandInstancesHelp() {
+	echo "Usage: $(basename $0) instances"
+	echo
+	echo "List all defined instances and their status."
+}
+
+sdtdCommandInstancesDescription() {
+	echo "List all defined instances"
+}
Index: /scripts/usr/local/lib/7dtd/commands/start.sh
===================================================================
--- /scripts/usr/local/lib/7dtd/commands/start.sh	(revision 17)
+++ /scripts/usr/local/lib/7dtd/commands/start.sh	(revision 17)
@@ -0,0 +1,71 @@
+#!/bin/bash
+# Version 4
+
+# Tries to start the 7dtd instance.
+
+sdtdCommandStart() {
+	if [ "$1" = "!" ]; then
+		echo "Starting all instances:"
+		for I in $(getInstanceList); do
+			printf "%-*s: " 10 "$I"
+			sdtdCommandStart $I
+		done
+		echo "All done"
+		return
+	fi
+
+	if [ $(isValidInstance $1) -eq 0 ]; then
+		echo "No instance given or not a valid instance!"
+		return
+	fi
+
+	if [ $(isRunning $1) -eq 0 ]; then
+		if [ ! `pgrep Xvfb` ]; then
+			echo "Xvfb not yet running. Starting..."
+			su -c "/usr/bin/Xvfb :1 -screen 0 640x480x16" $SDTD_USER 2>&1 | grep -v "Could not init font path element" &
+			sleep 3
+		fi
+		export DISPLAY=localhost:1.0
+		
+		setAllPlayersOffline $1
+		
+		SSD_PID="--pidfile $(getInstancePath $1)/7dtd.pid --make-pidfile"
+		SSD_DAEMON="--background --no-close"
+		SSD_USER="--chuid $SDTD_USER:$SDTD_GROUP --user $SDTD_USER"
+		OPTS="-quit -batchmode -nographics -configfile=$(getInstancePath $1)/config.xml -dedicated"
+		
+		start-stop-daemon --start $SSD_PID $SSD_DAEMON $SSD_USER --chdir $SDTD_BASE/engine --exec $WINE -- $SDTD_BASE/engine/7DaysToDie.exe $OPTS > $(getInstancePath $1)/stdout.log 2>&1
+		sleep 1
+		if [ $(isRunning $1) -eq 1 ]; then
+			SSD_MONITOR_PID="--pidfile $(getInstancePath $1)/monitor.pid --make-pidfile"
+			SSD_MONITOR_DAEMON="--background"
+			start-stop-daemon --start $SSD_PID $SSD_DAEMON --exec "/usr/local/lib/monitor-log.sh" -- "$1"
+			echo "Done!"
+		else
+			echo "Failed!"
+			rm -f $(getInstancePath $1)/7dtd.pid
+		fi
+	else
+		echo "Instance $1 is already running"
+	fi
+}
+
+sdtdCommandStartHelp() {
+	echo "Usage: $(basename $0) start <instance>"
+	echo
+	echo "Starts the given instance."
+	echo "If <instance> is \"!\" all defined instances are started."
+}
+
+sdtdCommandStartDescription() {
+	echo "Start the given instance"
+}
+
+sdtdCommandStartExpects() {
+	case $1 in
+		2)
+			echo "! $(getInstanceList)"
+			;;
+	esac
+}
+
Index: /scripts/usr/local/lib/7dtd/commands/status.sh
===================================================================
--- /scripts/usr/local/lib/7dtd/commands/status.sh	(revision 17)
+++ /scripts/usr/local/lib/7dtd/commands/status.sh	(revision 17)
@@ -0,0 +1,75 @@
+#!/bin/bash
+# Version 4
+
+# Print status of given instance.
+
+sdtdCommandStatus() {
+	if [ $(isValidInstance $1) -eq 0 ]; then
+		echo "No instance given or not a valid instance!"
+		return
+	fi
+
+	line() {
+		printf "    %-*s %s\n" 15 "$1" "$2"
+	}
+	
+	echo "Instance: $1"
+	echo
+
+	if [ $(isRunning $1) -eq 1 ]; then
+		echo "Status: Running"
+		echo "Open ports:"
+		netstat -nlp | grep $(getInstancePID $1) | sed -r 's/^([^ ]*)\s+.*[^ :]*:([^ ]*).*[^ :]*:[^ ]*.*/    \2 (\1)/g' | sort
+		tel=$(telnetCommand $1 lp)
+		cur=`echo $tel | sed "s/\r/\n/g" | sed "s/^ //g" | grep "Total of " | cut -d\  -f 3`
+		echo "Players: $cur"
+	else
+		echo "Status: NOT running"
+	fi
+
+	echo
+	echo "Game info:"
+	line "Server name:" $(getConfigValue $1 ServerName)
+	line "Password:" $(getConfigValue $1 ServerPassword)
+	line "Max players:" $(getConfigValue $1 ServerMaxPlayerCount)
+	line "World:" $(getConfigValue $1 GameWorld)
+
+	echo
+	echo "Network info:"
+	line "Port:" $(getConfigValue $1 ServerPort)
+	line "Public:" $(getConfigValue $1 ServerIsPublic)
+	if [ "$(getConfigValue $1 ControlPanelEnabled)" = "false" ]; then
+		cp="off"
+	else
+		cp="Port $(getConfigValue $1 ControlPanelPort), Pass $(getConfigValue $1 ControlPanelPassword)"
+	fi
+	line "Control Panel:" "$cp"
+	if [ "$(getConfigValue $1 TelnetEnabled)" = "false" ]; then
+		tn="off"
+	else
+		tn="Port $(getConfigValue $1 TelnetPort), Pass $(getConfigValue $1 TelnetPassword)"
+	fi
+	line "Telnet:" "$tn"
+
+	echo
+	exit 0
+}
+
+sdtdCommandStatusHelp() {
+	echo "Usage: $(basename $0) status <instance>"
+	echo
+	echo "Print status information for the given instance."
+}
+
+sdtdCommandStatusDescription() {
+	echo "Print status for the given instance"
+}
+
+sdtdCommandStatusExpects() {
+	case $1 in
+		2)
+			getInstanceList
+			;;
+	esac
+}
+
Index: /scripts/usr/local/lib/7dtd/commands/stop.sh
===================================================================
--- /scripts/usr/local/lib/7dtd/commands/stop.sh	(revision 17)
+++ /scripts/usr/local/lib/7dtd/commands/stop.sh	(revision 17)
@@ -0,0 +1,76 @@
+#!/bin/bash
+# Version 4
+
+# Tries to stop the 7dtd instance given as first parameter.
+# Returns:
+#  0 : Done
+#  1 : Was not running
+#  2 : No instance name given
+#  3 : No such instance
+
+sdtdCommandKill() {
+	if [ "$1" = "!" ]; then
+		echo "Stopping all instances:"
+		for I in $(getInstanceList); do
+			printf "%s:\n" "$I"
+			sdtdCommandKill $I
+			echo
+		done
+		echo "All done"
+		return
+	fi
+
+	if [ $(isValidInstance $1) -eq 0 ]; then
+		echo "No instance given or not a valid instance!"
+		return
+	fi
+
+	res=$(isRunning $1)
+	if [ $res -eq 1 ]; then
+		echo "Trying to gracefully shutdown..."
+		tmp=$(telnetCommand $1 shutdown)
+		echo "Waiting for server to shut down..."
+	
+		waittime=0
+		maxwait=5
+		until [ $(isRunning $1) -eq 0 ] || [ $waittime -eq $maxwait ]; do
+			(( waittime++ ))
+			sleep 1
+			echo $waittime/$maxwait
+		done
+	
+		if [ $(isRunning $1) -eq 1 ]; then
+			echo "Failed, force closing server..."
+			start-stop-daemon --stop --pidfile $(getInstancePath $1)/7dtd.pid
+		fi
+
+		start-stop-daemon --stop --pidfile $(getInstancePath $1)/monitor.pid
+		rm $(getInstancePath $1)/monitor.pid
+		setAllPlayersOffline $1
+
+		rm $(getInstancePath $1)/7dtd.pid
+		echo "Done"	
+	else
+		echo "Instance $1 is NOT running"
+	fi
+}
+
+sdtdCommandKillHelp() {
+	echo "Usage: $(basename $0) kill <instance>"
+	echo
+	echo "Stops the given instance."
+	echo "If <instance> is \"!\" all defined instances are stopped."
+}
+
+sdtdCommandKillDescription() {
+	echo "Stop the given instance"
+}
+
+sdtdCommandKillExpects() {
+	case $1 in
+		2)
+			echo "! $(getInstanceList)"
+			;;
+	esac
+}
+
Index: /scripts/usr/local/lib/7dtd/commands/updateengine.sh
===================================================================
--- /scripts/usr/local/lib/7dtd/commands/updateengine.sh	(revision 17)
+++ /scripts/usr/local/lib/7dtd/commands/updateengine.sh	(revision 17)
@@ -0,0 +1,43 @@
+#!/bin/bash
+# Version 4
+
+# Tries to start the 7dtd instance.
+
+sdtdCommandUpdateengine() {
+	for I in $(getInstanceList); do
+		if [ $(isRunning $I) -eq 1 ]; then
+			echo "At least one instance is still running."
+			echo "Before updating the engine please stop all instances!"
+			return
+		fi
+	done
+
+	if [ ! -e $STEAMCMD_ROOT ]; then
+		mkdir $STEAMCMD_ROOT
+		cd /tmp
+		wget http://media.steampowered.com/installer/steamcmd_linux.tar.gz
+		tar -xvzf steamcmd_linux.tar.gz -C $STEAMCMD_ROOT
+		cd $STEAMCMD_ROOT
+		./steamcmd.sh +quit
+	fi
+
+	cd $STEAMCMD_ROOT
+
+	./steamcmd.sh +@sSteamCmdForcePlatformType windows +login $STEAM_USER $STEAM_PASS +force_install_dir $SDTD_BASE/engine "+app_update 251570" validate +quit
+	chown $SDTD_USER.$SDTD_GROUP -R $SDTD_BASE/engine
+
+	cp $SDTD_BASE/engine/Install/32bit/7DaysToDie.exe $SDTD_BASE/engine/
+	cp $SDTD_BASE/engine/Install/32bit/mono.dll $SDTD_BASE/engine/7DaysToDie_Data/Mono/
+	cp $SDTD_BASE/engine/Install/32bit/SteamworksManaged.dll $SDTD_BASE/engine/7DaysToDie_Data/Managed/
+	cp $SDTD_BASE/msvcr100.dll $SDTD_BASE/engine/
+}
+
+sdtdCommandUpdateengineHelp() {
+	echo "Usage: $(basename $0) updateengine"
+	echo
+	echo "Update the engine (aka game) files of 7dtd."
+}
+
+sdtdCommandUpdateengineDescription() {
+	echo "Update the 7dtd engine files"
+}
Index: /scripts/usr/local/lib/7dtd/common.sh
===================================================================
--- /scripts/usr/local/lib/7dtd/common.sh	(revision 17)
+++ /scripts/usr/local/lib/7dtd/common.sh	(revision 17)
@@ -0,0 +1,208 @@
+#!/bin/bash
+# Version 4
+
+# Provides common functions for 7dtd-scripts. Not intended to be run directly.
+
+# Check if the script is run as root (exit otherwise) and load global config
+checkRootLoadConf() {
+	if [ `id -u` -ne 0 ]; then
+		echo "This script has to be run as root!"
+		exit 10
+	fi
+	. /etc/7dtd.conf
+}
+
+# Get the config path for the given instance
+# Params:
+#   1: Instance name
+# Returns:
+#   Config path for instance
+getInstancePath() {
+	echo $SDTD_BASE/instances/$1
+}
+
+# Check if the given instance name is an existing instance
+# Params:
+#   1: Instance name
+# Returns:
+#   0/1 instance not valid/valid
+isValidInstance() {
+	if [ -z $1 ]; then
+		echo 0
+	else
+		if [ ! -d $(getInstancePath $1) ]; then
+			echo 0
+		else
+			if [ ! -f $(getInstancePath $1)/config.xml ]; then
+				echo 0
+			else
+				echo 1
+			fi
+		fi
+	fi
+}
+
+# Check if the given instance is currently running
+# Params:
+#   1: Instance name
+# Returns:
+#   0 = not running
+#   1 = running
+isRunning() {
+	start-stop-daemon --status --pidfile $(getInstancePath $1)/7dtd.pid
+	if [ $? -eq 0 ]; then
+		echo 1
+	else
+		echo 0
+	fi
+}
+
+# Get list of defined instances
+# Returns:
+#   List of instances
+getInstanceList() {
+	for IF in $SDTD_BASE/instances/*; do
+		I=`basename $IF`
+		if [ $(isValidInstance $I) -eq 1 ]; then
+			echo $I
+		fi
+	done
+}
+
+# Get the PID of the instance if it is running, 0 otherwise
+# Params:
+#   1: Instance name
+# Returns:
+#   0 if not running
+#   PID otherwise
+getInstancePID() {
+	if [ $(isRunning $1) -eq 1 ]; then
+		cat $(getInstancePath $1)/7dtd.pid
+	else
+		echo 0
+	fi
+}
+
+# Get a single value from a serverconfig
+# Params:
+#   1: Instance name
+#   2: Property name
+# Returns:
+#   Property value
+getConfigValue() {
+	CONF=$(getInstancePath $1)/config.xml
+	$XMLSTARLET sel -t -v "/ServerSettings/property[@name='$2']/@value" $CONF
+}
+
+# Update a single value in a serverconfig
+# Params:
+#   1: Instance name
+#   2: Property name
+#   3: New value
+setConfigValue() {
+	CONF=$(getInstancePath $1)/config.xml
+	$XMLSTARLET ed -L -u "/ServerSettings/property[@name='$2']/@value" -v "$3" $CONF
+}
+
+# Check if a given port range (baseport, baseport+1, baseport+2 each udp)
+# is already in use by any instance
+# Params:
+#   1: Baseport
+# Returns:
+#   0/1 not in use/in use
+checkGamePortUsed() {
+	PORTMIN=$1
+	PORTMAX=$(( $1 + 2 ))
+	for I in $(getInstanceList); do
+		CURPORTMIN=$(getConfigValue $I "ServerPort")
+		CURPORTMAX=$(( $CURPORTMIN + 2 ))
+		if [ $PORTMAX -ge $CURPORTMIN -a $PORTMIN -le $CURPORTMAX ]; then
+			echo 1
+			return
+		fi
+	done
+	echo 0
+}
+
+# Check if a given telnet port is already in use by any instance
+# Params:
+#   1: Port
+# Returns:
+#   0/1 not in use/in use
+checkTelnetPortUsed() {
+	for I in $(getInstanceList); do
+		CURENABLED=$(getConfigValue $I "TelnetEnabled")
+		CURPORT=$(getConfigValue $I "TelnetPort")
+		if [ "$CURENABLED" = "true" -a $CURPORT -eq $1 ]; then
+			echo 1
+			return
+		fi
+	done
+	echo 0
+}
+
+# Send a single command to the telnet port
+# Params:
+#   1: Instance name
+#   2: Command
+# Returns:
+#   String of telnet output
+telnetCommand() {
+	TEL_ENABLED=$(getConfigValue $1 TelnetEnabled)
+	TEL_PORT=$(getConfigValue $1 TelnetPort)
+	TEL_PASS=$(getConfigValue $1 TelnetPassword)	
+	if [ "$TEL_ENABLED" = "true" ] && [ -n "$TEL_PASS" ]; then
+		echo -e "$TEL_PASS\n$2\nexit" | nc -q 2 127.0.0.1 $TEL_PORT
+	else
+		echo "Telnet not enabled or no password set."
+	fi
+}
+
+# Get all hook files for the given hook-name
+# Params:
+#   1: Hook name
+# Returns:
+#   Names of hook files
+getHooksFor() {
+	if [ -d $SDTD_BASE/hooks/$1 ]; then
+		for H in $SDTD_BASE/hooks/$1/*.sh; do
+			echo "$H"
+		done
+	fi
+}
+
+# Lowercase passed string
+# Params:
+#   1: String
+# Returns:
+#   Lowercased string
+lowercase() {
+	echo "${1}" | tr "[:upper:]" "[:lower:]"
+}
+
+# Prepare passed string as part of camelcase, i.e. first char upper case, others
+# lowercase
+# Params:
+#   1: String
+# Returns:
+#   Transformed string
+camelcasePrep() {
+	echo $(echo "${1:0:1}" | tr "[:lower:]" "[:upper:]")$(echo "${1:1}" | tr "[:upper:]" "[:lower:]")
+}
+
+listCommands() {
+	for C in $(declare -F | cut -d\  -f3 | grep "^sdtdCommand"\
+			| grep -v "Help$"\
+			| grep -v "Description$"\
+			| grep -v "Expects$"); do
+		CMD=$(lowercase "${C#sdtdCommand}")
+		printf "%s " "$CMD"
+	done
+}
+
+. /usr/local/lib/7dtd/help.sh
+. /usr/local/lib/7dtd/playerlog.sh
+for M in /usr/local/lib/7dtd/commands/*.sh; do
+	. $M
+done
+
Index: /scripts/usr/local/lib/7dtd/help.sh
===================================================================
--- /scripts/usr/local/lib/7dtd/help.sh	(revision 17)
+++ /scripts/usr/local/lib/7dtd/help.sh	(revision 17)
@@ -0,0 +1,29 @@
+#!/bin/bash
+# Version 4
+
+genericHelp() {
+	line() {
+		printf "  %-*s %s\n" 15 "$1" "$2"
+	}
+	
+	echo
+	if [ ! -z $1 ]; then
+		echo "Unknown command: $1"
+	fi
+	echo "Usage: $(basename $0) <command> [parameters]"
+	echo
+	echo "Commands are:"
+	
+	for C in $(listCommands); do
+		if [ "$(type -t sdtdCommand$(camelcasePrep $C)Description)" = "function" ]; then
+			line "${C}" "$(sdtdCommand$(camelcasePrep $C)Description)"
+		else
+			line "${C}" "TODO: Description"
+		fi
+	done
+	
+	echo
+	echo "Use \"$(basename $0) help <command>\" to get further details on a specific command."
+}
+
+
Index: /scripts/usr/local/lib/7dtd/monitor-log.sh
===================================================================
--- /scripts/usr/local/lib/7dtd/monitor-log.sh	(revision 17)
+++ /scripts/usr/local/lib/7dtd/monitor-log.sh	(revision 17)
@@ -0,0 +1,70 @@
+#!/bin/bash
+# Version 4
+
+. /usr/local/lib/7dtd/common.sh
+checkRootLoadConf
+
+if [ $(isValidInstance $1) -eq 0 ]; then
+	echo "No instance given or not a valid instance!"
+	return
+fi
+
+INSTANCE=$1
+LOG=$(getInstancePath $INSTANCE)/output.log
+
+timestamp() {
+	date '+%Y.%m.%d %H:%M:%S'
+}
+
+handleConnect() {
+	tel=$(telnetCommand $INSTANCE lp)
+	playerline=$(echo "$tel" | tr -d '\r' | grep "id=$2,")
+	nickname=$(echo "$playerline" | sed -r 's/^.* id=[0-9]*, ([^,]*), pos=.*$/\1/')
+	steamid=$(echo "$playerline" | sed -r 's/^.*, health=[0-9]*, ([0-9]*)$/\1/')
+	
+	if [ -z "$steamid" ]; then
+		return
+	fi
+
+	logPlayerConnect $INSTANCE $2 $steamid $nickname
+
+	for H in $(getHooksFor playerConnect); do
+		$H $INSTANCE $1 $2 $nickname $steamid
+	done
+}
+
+handleDisconnect() {
+	logPlayerDisconnect $INSTANCE $2
+
+	for H in $(getHooksFor playerDisconnect); do
+		$H $INSTANCE $1 $2 $NICKNAME $STEAMID
+	done
+}
+
+echo >> $LOG
+echo >> $LOG
+echo "Starting instance $INSTANCE at $(timestamp)" >> $LOG
+echo >> $LOG
+
+NOBUF="stdbuf -e0 -o0"
+
+$NOBUF tail -n 5000 -F $SDTD_BASE/engine/7DaysToDie_Data/output_log.txt |
+$NOBUF tr '\\' '/' |
+$NOBUF tr -d '\r' |
+$NOBUF grep -v "^(Filename: " |
+while read line ; do
+	if [ -n "$line" ]; then
+		echo "$(timestamp): $line" >> $LOG
+		if [ -n "$(echo "$line" | grep '^RequestToSpawnPlayer:')" ]; then
+			read entityId playerId playerName unknown <<< "$(echo $line | sed -r 's/^RequestToSpawnPlayer: (.*), (.*), (.*), (.*)$/\1 \2 \3 \4/')"
+			sleep 1
+			handleConnect $playerId $entityId $playerName
+			unset entityId playerId playerName unknown
+		fi
+		if [ -n "$(echo "$line" | grep '^Removing player with id ')" ]; then
+			read playerId entityId <<< "$(echo $line | sed -r 's/^Removing player with id clientId=(.*), entityId=(.*)$/\1 \2/')"
+			handleDisconnect $playerId $entityId
+			unset playerId entityId
+		fi
+	fi
+done
Index: /scripts/usr/local/lib/7dtd/playerlog.sh
===================================================================
--- /scripts/usr/local/lib/7dtd/playerlog.sh	(revision 17)
+++ /scripts/usr/local/lib/7dtd/playerlog.sh	(revision 17)
@@ -0,0 +1,101 @@
+#!/bin/bash
+# Version 4
+
+timestamp() {
+	date '+%Y.%m.%d %H:%M:%S'
+}
+
+# Create empty player list if not existing
+# Params:
+#   1: Instance name
+createPlayerList() {
+	PLAYERLIST=$(getInstancePath $1)/players.xml
+	if [ ! -f $PLAYERLIST ]; then
+		echo "<Players/>" > $PLAYERLIST
+	fi
+}
+
+# Set all players for an instance to offline (on startup/shutdown)
+# Params:
+#   1: Instance name
+setAllPlayersOffline() {
+	PLAYERLIST=$(getInstancePath $1)/players.xml
+	createPlayerList "$1"
+	$XMLSTARLET ed -L \
+		-u "/Players/Player/@online" -v "false" \
+		$PLAYERLIST
+}
+
+# Handle a player connect for logging/tracking
+# Params:
+#   1: Instance name
+#   2: Entity ID
+#   3: Steam ID
+#   4: Nick name
+logPlayerConnect() {
+	PLAYERLOG=$(getInstancePath $1)/players.log
+	PLAYERLIST=$(getInstancePath $1)/players.xml
+	ENTITYID=$2
+	STEAMID=$3
+	NICKNAME=$4
+
+	echo "$(timestamp) +++ $ENTITYID $NICKNAME $STEAMID" >> $PLAYERLOG
+
+	createPlayerList "$1"
+	
+	XPATHBASE="/Players/Player[@steamid='$STEAMID']"
+
+	if [ -z $($XMLSTARLET sel -t -v "$XPATHBASE/@steamid" $PLAYERLIST) ]; then
+		$XMLSTARLET ed -L \
+			-s "/Players" -t elem -n "Player" -v "" \
+			-i "/Players/Player[not(@steamid)]" -t attr -n "steamid" -v "$STEAMID" \
+			-i "$XPATHBASE" -t attr -n "nick" -v "$NICKNAME" \
+			-i "$XPATHBASE" -t attr -n "playtime" -v "0" \
+			-i "$XPATHBASE" -t attr -n "logins" -v "1" \
+			-i "$XPATHBASE" -t attr -n "lastlogin" -v "$(date '+%s')" \
+			-i "$XPATHBASE" -t attr -n "online" -v "true" \
+			-i "$XPATHBASE" -t attr -n "entityid" -v "$ENTITYID" \
+			$PLAYERLIST
+	else
+		LOGINS=$($XMLSTARLET sel -t -v "$XPATHBASE/@logins" $PLAYERLIST)
+		(( LOGINS++ ))
+		$XMLSTARLET ed -L \
+			-u "$XPATHBASE/@lastlogin" -v "$(date '+%s')" \
+			-u "$XPATHBASE/@online" -v "true" \
+			-u "$XPATHBASE/@entityid" -v "$ENTITYID" \
+			-u "$XPATHBASE/@logins" -v "$LOGINS" \
+			$PLAYERLIST
+	fi
+}
+
+# Handle a player disconnect for logging/tracking
+# Params:
+#   1: Instance name
+#   2: Entity ID
+logPlayerDisconnect() {
+	PLAYERLOG=$(getInstancePath $1)/players.log
+	PLAYERLIST=$(getInstancePath $1)/players.xml
+	ENTITYID=$2
+
+	createPlayerList "$1"
+
+	XPATHBASE="/Players/Player[@entityid='$ENTITYID'][@online='true']"
+
+	if [ -f $PLAYERLIST ]; then
+		if [ ! -z $($XMLSTARLET sel -t -v "$XPATHBASE/@steamid" $PLAYERLIST) ]; then
+			NICKNAME=$($XMLSTARLET sel -t -v "$XPATHBASE/@nick" $PLAYERLIST)
+			STEAMID=$($XMLSTARLET sel -t -v "$XPATHBASE/@steamid" $PLAYERLIST)
+			LOGINTIME=$($XMLSTARLET sel -t -v "$XPATHBASE/@lastlogin" $PLAYERLIST)
+			PLAYTIME=$($XMLSTARLET sel -t -v "$XPATHBASE/@playtime" $PLAYERLIST)
+			NOW=$(date '+%s')
+			PLAYTIME=$(( PLAYTIME + NOW - LOGINTIME ))
+			$XMLSTARLET ed -L \
+				-u "$XPATHBASE/@playtime" -v "$PLAYTIME" \
+				-u "$XPATHBASE/@online" -v "false" \
+				$PLAYERLIST
+		fi
+	fi
+
+	echo "$(timestamp) --- $ENTITYID $NICKNAME $STEAMID" >> $PLAYERLOG
+}
+
