#!/bin/bash
#
# Title: Nexuiz Ninjaz - Nexuiz Server Toolz
# Version: 0.9 BETA
# Released: 10/30/08
# Created By: Tyler "-z-" Mulligan of the Nexuiz Ninjaz (www.nexuizninjaz.com)
#
# BETA RELEASE, there are some bugs and inconsistances.  I'm pressed for
# time and don't have time to explain it beyond what is written in this file
#
# This script was created to help admins manage many instances of servers by loading them
# into seperate screens they can easily call by name.  For more information check --help.
#
# Usage: ./nn_server_toolz.sh --(start[_all]|stop[_all]|restart[_all]|list|edit|rcon2irc|view|create_maplist|help)
# 		 type --help for more
#


# base directory
basedir=/home/brian/nexuiz_servers/nex242

# define a base folder for packages that run on top of nexuiz here
# unfinished in favor of a temporary solution, loading a new progs.dat
# from the config
#moddir=/home/brian/nexuiz_servers/tourney

# Used to add more functions without editing this file
#extend=true
# Name of the bash file used to extend this script
#ext_file=nn_nst_extended.sh

start_all()
{
	pgrep server_linux.sh &> /dev/null && {
		echo -e "\n[ERROR] There are running servers.  Please use --restart.  Aborting.\n"
		exit 1
	}
	
	# This can be hardcoded because it's where QuakeC puts them unless -userdir flag is set
	if [ ! -d ~/.nexuiz/data/data/oldlogs ];then mkdir -p ~/.nexuiz/data/data/oldlogs;fi
	mv ~/.nexuiz/data/data/*.log ~/.nexuiz/data/data/oldlogs

	# check if basedir exists
	if [ ! -d $basedir ]; then
		echo -e "\n[FAIL] The basedir '$basedir' is incorrect, please edit your config and try again.\n"
		exit 1
	fi
	cd $basedir

	# for each moddir find configs and load them into screens

	# List any nn_server configs found
	for cfg in $( ls data/nn_server_*.cfg ); do
		cfgname=`echo $cfg | awk -F \/ '{ print $2 }'`
		screenname=`echo $cfgname | awk -F . '{ print $1 }' | sed s/nn_server_//`
		confname="nn_rcon2irc_$screenname.conf"
		
		echo -e "\n[Starting] $screenname"
		screen -m -d -S $screenname ./server_linux.sh +exec $cfgname
		# this isn't currently being used because you can tell the server to use a different progs.dat file in the cfg but what it would do is allow you to use a 'mod directory'
		#screen -m -d -S $screenname ./server_linux.sh +exec $cfgname -userdir $moddir

		# If an rcon2irc config exists, start it.
		if [ -f $confname ]; then			
			echo -e "[Starting] rcon_$screenname\n"
			screen -m -d -S rcon_$screenname /usr/bin/perl rcon2irc.pl $confname
		else
			echo -e "\n[tip] If you create a file in your '$basedir' folder called '$confname' per div's rcon2irc requirements, it will automatically be loaded.\n"
		fi
	done
	echo
} # End start_all

start_server()
{
	if [ "$1" != "" ]; then
		screenname=$1
		cfgname="nn_server_$1.cfg"
		confname="nn_rcon2irc_$1.conf"
		
		cd $basedir
		
		if [ -f data/$cfgname ]; then
			# This can be hardcoded because it's where QuakeC puts them
			if [ ! -d ~/.nexuiz/data/data/oldlogs ];then mkdir -p ~/.nexuiz/data/data/oldlogs;fi
			mv ~/.nexuiz/data/data/$screenname*.log ~/.nexuiz/data/data/oldlogs
			
			echo -e "\n[Starting] $screenname\n"
			screen -m -d -S $screenname ./server_linux.sh \
				+exec $cfgname
		else
			echo -e "\n[ERROR] Config: $cfgname not found in $basedir/data\n"
		fi

		# If an rcon2irc config exists, start it.
		if [ -f $confname ]; then			
			echo -e "[Starting] rcon_$screenname\n"
			screen -m -d -S rcon_$screenname /usr/bin/perl rcon2irc.pl $confname
		else
			echo -e "\n[tip] If you create a file in your '$basedir' folder called '$confname' per div's rcon2irc requirements, it will automatically be loaded.\n"
		fi
	else
		echo -e "\nSyntax is: --start <server name>\n\n<server name> is built from your server cfg file name.\n\"nn_server_ctf_242.cfg\" would be titled \"ctf_242\".\nType --help for more.\n"
	fi
} #end start_server

stop_all()
{
	list_servers
	echo "[Stopping]"
	pkill nexuiz-dedicate
	echo "[Stopped]"
} # End stop_all

# stop a single server by it's session name
stop_server()
{
	if [ "$1" != "" ]; then
		gsname=$1
		requested=`ps -ef | grep SCREEN | grep server_linux.sh | grep -v grep | awk '{ print $12 }' | grep ^${gsname}$`

		if [ "$requested" != "" ]; then
			pid=`ps -ef | grep SCREEN | grep server_linux.sh | grep -v grep | grep "+exec nn_server_${gsname}.cfg" | awk '{ print $2 }'`
			pkill -P $pid
			pkill -f rcon_$gsname
			echo "[Stopped] $gsname"
		else
			echo "[ERROR] $gsname is not running"
		fi
	else
		echo -e "\nSyntax is: --stop <server name>\n\n<server name> is built from your server cfg file name.\n\"nn_server_ctf_242.cfg\" would be titled \"ctf_242\".\nType --help for more.\n"
	fi
} # End stop_server

restart_all()
{
	stop_all
	echo "waiting 5 seconds to restart"
	sleep 1; echo "4"; sleep 1; echo "3"; sleep 1; echo "2"; sleep 1; echo "1";
	echo "Restarting"
	start_all
	#list_servers # Why is the grep for the port failing?
} # End restart_all

# Restarts a specific server
restart_server()
{
	gsname=$1
	requested=`ps -ef | grep SCREEN | grep server_linux.sh | grep -v grep | awk '{ print $12 }' | grep ^${gsname}$`

	if [ "$requested" != "" ]; then
		pid=`ps -ef | grep SCREEN | grep server_linux.sh | grep -v grep | grep "+exec nn_server_${gsname}.cfg" | awk '{ print $2 }'`
		pkill -P ${pid}
		echo "Server has been killed. Restarting $gsname in 5..."
	
		sleep 1; echo "4"; sleep 1; echo "3"; sleep 1; echo "2"; sleep 1; echo "1";
		start_server $gsname
	else
		echo -e "\n[ERROR] That server is not running."
		list_servers
		echo -e "[WARNING] No server was restarted.  Please type the name EXACTLY as you read it above.\n"
	fi
} # End restart_server

list_servers() # Format all the current running servers in a easy to read way
{
	if [ "$( ps -ef | grep server_linux.sh | grep SCREEN | grep -v grep )" != "" ]; then
		echo; echo -e "Currently Running Nexuiz Servers\n----------------------------------------------------"
		gsname=`ps -ef | grep server_linux.sh | grep SCREEN | grep -v grep | awk '{ print $12 }'`
		
		ps -ef | grep server_linux.sh | grep SCREEN | grep -v grep | awk '{printf "%s %s\n", $2, $12}' | while read gspid gsname
		do
			gscfg=`echo $gsname | sed "s/\(.*\)/\1.cfg/"`
			gsport=`grep ^port -r $basedir/data/nn_server_${gscfg} | awk '{ print $2 }'`
			echo -e "Port:" $gsport "\tPID:" $gspid "\tName:" $gsname
		done; echo
	else
		echo -e "\nNo Nexuiz servers are currently running\n"
	fi
} # End list_servers

# Loads a specific server into screen
function view_server {
	#gservers=`ps -ef | grep server_linux.sh | grep SCREEN | grep -v grep | awk '{ print $12 }'`
	gsname=$1
	
	#echo "matched:"
	#screen -r | grep $gsname | awk '{ print $1 }'
	
	#screen -r | grep $gsname | awk -F . '{printf "%s\n", $1}' | sed 's/.*\([0-9]*\).*/\1/'
	#screenid=$(screen -r | grep "$gsname" | awk -F . '{printf "%s\n", $1}')
	screenid=$(screen -r | grep $gsname | awk '{ print $1 }' | grep \.$gsname$ | grep -v "rcon_" | awk -F . '{ print $1 }')
	echo -e "\n!!!IMPORTANT!!! To get out of a screen, hold ctrl, then press a, then d\n\nPress enter to continue"
	read
	screen -r ${screenid}
} # End view_server

# Loads a specific server into screen
function rcon2irc_server {
	#gservers=`ps -ef | grep server_linux.sh | grep SCREEN | grep -v grep | awk '{ print $12 }'`
	gsname=$1
	
	#echo "matched:"
	#screen -r | grep $gsname | awk '{ print $1 }'
	
	#screen -r | grep $gsname | awk -F . '{printf "%s\n", $1}' | sed 's/.*\([0-9]*\).*/\1/'
	#screenid=$(screen -r | grep "$gsname" | awk -F . '{printf "%s\n", $1}')
	screenid=$(screen -r | grep $gsname | awk '{ print $1 }' | grep \.$gsname$ | grep "rcon_" | awk -F . '{ print $1 }')
	echo -e "\n!!!IMPORTANT!!! To get out of a screen, hold ctrl, then press a, then d\n\nPress enter to continue"
	read
	screen -r ${screenid}
} # End rcon2irc_server

# Edits a specific server config based on the session name (--list name)
function edit_server {
		
	gsname=$1
	
	#vim $basedir/data/nn_server_$1.cfg
	nano $basedir/data/nn_server_$1.cfg
	echo "Do you want to restart this server now (y/n)?"
	read answer
	if [ "$answer" == "y" ]; then
		restart_server $gsname
	else
		echo "[alert] Not restarting $gsname"
	fi
} # End edit_server

# Used to dynamically build a maplist based on the pk3's in your directory rather
# than rely on the g_maplist="" emergency override
function create_maplist {
	# Start the maplist string
	i="g_maplist=\""
	
	# Get Gametype
	if [ "$1" != "" ]; then
		t=$1
	else
		echo; echo "[WARNING] No gametype has been set, setting to dm"
		t="dm"
	fi
	
	# Handle Optional Directory Parameter
	if [ "$2" != "" ]; then
		if [ -d "$2" ]; then
			d=$2
		fi
	else
		d=~/.nexuiz/data
	fi

	# Get the a list of all properly packaged bsps
	for map in $( ls $d/*.pk3 ); do
		
		# Used to tell if the package mapinfo and generate map info exist
		m=false
		m2=false
		
		# List contents, grab the name of the bsp, remove the folder name, drop any bsp not in the maps folder
		mapname=`unzip -l $map | grep .bsp | awk '{ print $4 }' | sed 's/maps\/\([A-Za-z_0-9.-]*\)\.bsp/\1/' | grep -vi .bsp`
		# If a map bsp is present
		if [ "$mapname" != "" ]; then
		
			# Check mapinfo's gametype against $t
			echo
			game_type=`unzip -p $map maps/$mapname.mapinfo | grep "^type"`
			
			if [ "$game_type" != "" ]; then
				echo "Checking package ($map) for mapinfo: [OK]"
				m=true
				
				game_type=`unzip -p $map maps/$mapname.mapinfo | grep "^type $t"`
				if [ "$game_type" == "" ]; then
					echo "Checking mapinfo for gametype compatiability ($mapname): [NO]"
				else
					# The mapinfo from the package has this gametype
					echo "Checking mapinfo for gametype compatiability ($mapname): [OK]"
				fi
			else
				echo "Checking package ($mapname) for mapinfo: [FAILED]"
			fi
			
			# If it doesn't exist, check the generated mapinfo folder
			if [ "$game_type" == "" ]; then
			
				echo "Checking ~/.nexuiz/data/data/maps/ for generated mapinfo: $mapname.mapinfo"
				cd ~/.nexuiz/data/data/maps/

				if [ ! -r "$mapname.mapinfo" ]; then
					echo "[WARNING] No generated mapinfo found for $mapname - not adding to list"
					status="warning"
				else
					# the generated mapinfo file exists
					echo "Check for generated $mapname.mapinfo file: [OK]"
					m2=true
										
					game_type=`grep "^type $t" $mapname.mapinfo`
					
					if [ "$game_type" != "" ]; then
						# The check for the generated mapinfo compatiability passed
						echo "Checking generated mapinfo for gametype compatiability ($t): [OK]"
					fi
				fi
			fi
			
			# Everything looks good, add it to the list.
			if [ "$game_type" != "" ]; then
				# Print with quotes and a comma then append to string 'i'
				echo "[ADDING] $mapname to the list"
				mapname="$t_$mapname "
				i=$i$mapname
			else
				if [ $m2 == true ]; then
					echo "Checking generated mapinfo for gametype compatiability ($t): [NO]"
				fi
			fi

		fi
	done

	# Trim the last space, add a double quote and echo maplist
	i=`echo $i | sed 's/ $/\"/'`
	
	if [ "$status" == "warning" ]; then
		echo; echo "[WARNING] Some maps weren't added because no mapinfo files were found.  Some maps may not be included!  Restart Nexuiz to generate them automatically, then run this script again."
	fi
	
	echo; echo "-- Printing $t Maplist -----------"; echo; echo $i"\""; echo
} # End create_maplist

nn_servers_help ()
{
echo "

                          Nexuiz Ninjaz Present

                          v\`     _   __                _    
                         <f     / | / /__  _  ____  __(_)___
                        .d\`    /  |/ / _ \| |/_/ / / / /_  /
                  ..    j(    / /|  /  __/>  </ /_/ / / / /_
                 jQQ,  _2    /_/ |_/\___/_/|_|\__,_/_/ /___/
              <gmQQW;  d\`
        =c :><QQQQQQk ](           _____                          
         ~{,3QQQQQQQWsf           / ___/___  ______   _____  _____
           jQQQQQQQQQW\`           \__ \/ _ \/ ___/ | / / _ \/ ___/
         <mQQQQQQWWWQh           ___/ /  __/ /   | |/ /  __/ /    
        .mQQQQQQQc)QQ#          /____/\___/_/    |___/\___/_/
        =QQQQQQQQQQQQk
         \"\$QP!\"!4QQQW'       ______            __   
          -Q;    4QQQ>      /_  __/___  ____  / /___
          jW\`     \$WQWc      / / / __ \/ __ \/ /_  /
         qQE      ]QQQ#     / / / /_/ / /_/ / / / /_
         ~\"\`      )QQQW.   /_/  \____/\____/_/ /___/
                    -\"\$L
                      )Wc    created by -z- of www.nexuizninjaz.com
                       \"$;                                    


Nexuiz Server Toolz is a collection of helpful scripts to help admins manage their Nexuiz game servers.  Following the methodology of Ruby, these scripts believe in convention over configuration.  This means using such devices as the \"nn_server_\" prefix to tell this script where your file is, instead of the classic \"configuration\" file which is edited manually.  Less work for you!  Easy to upgrade, easy to scale, easy to manage.

:::IMPORTANT USAGE NOTES:::

This script identifies your servers by the \"nn_server_\" prefix on your server config.  Furthermore, the name of the screen is constructed by the next token ending at .cfg.  That is: (nn_server)([A-Za-z0-9_-]+).cfg

Example cfg name: nn_server_ctf_242.cfg

Inside the configuration file, the conventions continue.  To prevent log errors when restarting a single server, set the following cvars, replacing 'ctf_242_' with your corresponding server name.  eventlog is also the format read by the statistics parser, so if you are utilizing that feature, you're killing two birds with one stone.

sv_eventlog 1
sv_eventlog_files 1
sv_eventlog_files_nameprefix   \"ctf_242_\"

A sample config is available for your convenience, 'nn_server_example.cfg'


General Usage: ./nn_server_toolz.sh --(start[_all]|stop[_all]|restart[_all]|list|create_maplist|help)

options are...

SERVER MANAGEMENT
--start_all					Starts all servers identified by a \"nn_server_([A-Za-z0-9_-]+).cfg\" file.
						It starts servers inside screens, using the token denoted by the () above
						to title it.  If the cfg was titled \"nn_server_ctf_242.cfg\", the screen would
						be titled \"ctf_242\".
						
--start <server name>				Same as above except it allows you to specify a server

--stop_all					Stop all currently running Nexuiz servers

--stop <server name>				Stop a specific Nexuiz server
 
--restart_all					Restart all currently running Nexuiz servers

--restart <server name>				Restart a specific Nexuiz server
 
--list						List all currently running Nexuiz servers
 
--view <server name>				View a specific server based on the name given in --list
						i.e. --view dm

--rcon2irc <server name>			View a specific server's rcon2irc based on the name given in --list
						i.e. --rcon2irc dm

CFG TOOLZ
--edit <server name>				Edit the configuration of a specific server based on the name given in --list and
						offers ability to restart.
						i.e. --edit dm

--create_maplist [gametype] [directory]		Create a maplist for a specific gametype based on the maps found
						in your data directory (default folder: ~/.nexuiz/data/)
								
						[gametype]:	(dm|tdm|ctf|lms|dom ... etc)
								default gametype is dm
										
						[directory]:	(Optional) if you wan to use a folder other than ~/.nexuiz/data"

if [ $extend == true ]; then
	nn_servers_extend --ext_help
fi

echo "INFORMATION
--help						You're lookin at it :-P - Thanks to Soulbringer for this case switch/framework.
"
} # End nn_servers_help

# Used to add more functions without editing this file
nn_servers_extend ()
{
	# Get Gametype
	if [ $extend == true ]; then
		$basedir/./$ext_file $1 $2 $3 $4
	else
		nn_servers_help
	fi
} # End nn_servers_extend

#set -- `echo "$1" | tr [:upper:] [:lower:]`
case $1 in
  --start) start_server $2;;				# start a specific server
  --start_all) start_all;; 					# start the servers defined in the top of this script
  --stop) stop_server $2;;					# stop a specific server
  --stop_all) stop_all;;					# stop the servers
  --restart) restart_server $2;;			# restart a specific server
  --restart_all) restart_all;;				# restart all servers
  --list) list_servers;;					# list all servers
  --view) view_server $2;;					# open the screen for a specific server
  --rcon2irc) rcon2irc_server $2;;				# open the rcon2irc screen for a specific server
  --edit) edit_server $2;;					# edit a specific server's cfg
  --create_maplist) create_maplist $2 $3;;	# create maplist for the passed <gametype> <directory>
  --help) nn_servers_help;;					# command line parameter help
  *) nn_servers_extend $1 $2 $3 $4;;						# gigo
esac
