Преглед изворни кода

feat(newserver): add Xonotic (#4633)

* Adding default_cfg and Distro Requirements

* Adding Shellscripts for Xonotic

* Fixing serverlist.csv

* Fixing again serverlist.csv

* Fixed Typo

* Changing Shortname to XNT

* Fixing missed _default.cfg change

* Fixing _default.cfg

* Updating Modules and Server installation

* Updating paths and install script

* Fixing install Scripts Issues

* Update info_game.sh

* Updating Game Info Curling

* fix bug with npm download of gamedig on branches

* styling fix

* move to fix

* typo

* bug

* add missing details

* edit using config

* add missing servercfgfullpath

* bug

* config settings update

* update cp command based off warn message

* fix dir

* update dirs

* fix exit code

* fix dir install

* remove -v

* update settings

* update info game

* update config type

* fix dir

* update config parsing

* change to remove config

* add server.cfg

* remove serverconfigdefault

* add new default

* move to use an updater

* send key to update function

* typo

* change extract location

* update directory location

* filename fix

* update dirs

* fix extraction

* creatre datadir if missing

* consoleinteract yes

* lint

---------

Co-authored-by: Stephan Schaffner <stephan.schaffner@check24.de>
Co-authored-by: Daniel Gibbs <me@danielgibbs.co.uk>
Stephan Schaffner пре 1 година
родитељ
комит
ce27b7301b

+ 171 - 0
lgsm/config-default/config-lgsm/xntserver/_default.cfg

@@ -0,0 +1,171 @@
+##################################
+######## Default Settings ########
+##################################
+# DO NOT EDIT, ANY CHANGES WILL BE OVERWRITTEN!
+# Copy settings from here and use them in either:
+# common.cfg - applies settings to every instance.
+# [instance].cfg - applies settings to a specific instance.
+
+#### Game Server Settings ####
+
+## Predefined Parameters | https://docs.linuxgsm.com/configuration/start-parameters
+
+## Server Parameters | https://docs.linuxgsm.com/configuration/start-parameters#additional-parameters
+startparameters="-userdir ${systemdir}/${selfname}"
+
+#### LinuxGSM Settings ####
+
+## LinuxGSM Stats
+# Send useful stats to LinuxGSM developers.
+# https://docs.linuxgsm.com/configuration/linuxgsm-stats
+# (on|off)
+stats="off"
+
+## Notification Alerts
+# (on|off)
+
+# Display IP | https://docs.linuxgsm.com/alerts#display-ip
+displayip=""
+
+# More info | https://docs.linuxgsm.com/alerts#more-info
+postalert="off"
+
+# Alert on Start/Stop/Restart
+statusalert="off"
+
+# Discord Alerts | https://docs.linuxgsm.com/alerts/discord
+discordalert="off"
+discordwebhook="webhook"
+
+# Email Alerts | https://docs.linuxgsm.com/alerts/email
+emailalert="off"
+email="email@example.com"
+emailfrom=""
+
+# Gotify Alerts | https://docs.linuxgsm.com/alerts/gotify
+gotifyalert="off"
+gotifytoken="token"
+gotifywebhook="webhook"
+
+# IFTTT Alerts | https://docs.linuxgsm.com/alerts/ifttt
+iftttalert="off"
+ifttttoken="accesstoken"
+iftttevent="linuxgsm_alert"
+
+# Pushbullet Alerts | https://docs.linuxgsm.com/alerts/pushbullet
+pushbulletalert="off"
+pushbullettoken="accesstoken"
+channeltag=""
+
+# Pushover Alerts | https://docs.linuxgsm.com/alerts/pushover
+pushoveralert="off"
+pushovertoken="accesstoken"
+pushoveruserkey="userkey"
+
+# Rocket.Chat Alerts | https://docs.linuxgsm.com/alerts/rocket.chat
+rocketchatalert="off"
+rocketchatwebhook="webhook"
+
+# Slack Alerts | https://docs.linuxgsm.com/alerts/slack
+slackalert="off"
+slackwebhook="webhook"
+
+# Telegram Alerts | https://docs.linuxgsm.com/alerts/telegram
+# You can add a custom cURL string eg proxy (useful in Russia) in "curlcustomstring".
+# For example "--socks5 ipaddr:port" for socks5 proxy see more in "curl --help all".
+telegramapi="api.telegram.org"
+telegramalert="off"
+telegramtoken="accesstoken"
+telegramchatid=""
+telegramthreadid=""
+telegramsilentnotification="false"
+curlcustomstring=""
+
+## Backup | https://docs.linuxgsm.com/commands/backup
+maxbackups="4"
+maxbackupdays="30"
+stoponbackup="on"
+
+## Logging | https://docs.linuxgsm.com/features/logging
+consolelogging="on"
+logdays="7"
+
+## Monitor | https://docs.linuxgsm.com/commands/monitor
+# Query delay time
+querydelay="1"
+
+## ANSI Colors | https://docs.linuxgsm.com/features/ansi-colors
+ansi="on"
+
+#### Advanced Settings ####
+
+## Message Display Time | https://docs.linuxgsm.com/features/message-display-time
+sleeptime="0.5"
+
+## Stop Mode | https://docs.linuxgsm.com/features/stop-mode
+# 1: tmux kill
+# 2: CTRL+c
+# 3: quit
+# 4: quit 120s
+# 5: stop
+# 6: q
+# 7: exit
+# 8: 7 Days to Die
+# 9: GoldSrc
+# 10: Avorion
+# 11: end
+stopmode="2"
+
+## Query mode
+# 1: session only
+# 2: gamedig (gsquery fallback)
+# 3: gamedig
+# 4: gsquery
+# 5: tcp
+querymode="3"
+querytype="xonotic"
+
+## Console type
+consoleverbose="yes"
+consoleinteract="yes"
+
+## Game Server Details
+# Do not edit
+gamename="Xonotic"
+engine="unreal3"
+glibc="2.29"
+
+#### Directories ####
+# Edit with care
+
+## Game Server Directories
+systemdir="${serverfiles}"
+executabledir="${systemdir}"
+executable="./xonotic-linux64-dedicated"
+servercfgdir="${systemdir}/${selfname}/data"
+servercfg="server.cfg"
+servercfgdefault="${systemdir}/server.cfg"
+servercfgfullpath="${servercfgdir}/${servercfg}"
+
+## Backup Directory
+backupdir="${lgsmdir}/backup"
+
+## Logging Directories
+[ -n "${LGSM_LOGDIR}" ] && logdir="${LGSM_LOGDIR}" || logdir="${rootdir}/log"
+gamelogdir="${logdir}/server"
+lgsmlogdir="${logdir}/script"
+consolelogdir="${logdir}/console"
+gamelog="${gamelogdir}/${selfname}-game.log"
+lgsmlog="${lgsmlogdir}/${selfname}-script.log"
+consolelog="${consolelogdir}/${selfname}-console.log"
+alertlog="${lgsmlogdir}/${selfname}-alert.log"
+postdetailslog="${lgsmlogdir}/${selfname}-postdetails.log"
+
+## Logs Naming
+lgsmlogdate="${lgsmlogdir}/${selfname}-script-$(date '+%Y-%m-%d-%H:%M:%S').log"
+consolelogdate="${consolelogdir}/${selfname}-console-$(date '+%Y-%m-%d-%H:%M:%S').log"
+gamelogdate="${gamelogdir}/${selfname}-game-$(date '+%Y-%m-%d-%H:%M:%S').log"
+
+## Log Parameters
+logtimestamp="off"
+logtimestampformat="%Y-%m-%d %H:%M:%S"

+ 1 - 0
lgsm/data/almalinux-8.csv

@@ -134,5 +134,6 @@ wet
 wf
 wmc,java-21-openjdk
 wurm,xorg-x11-server-Xvfb
+xnt
 zmr,ncurses-libs.i686
 zps,ncurses-libs.i686

+ 1 - 0
lgsm/data/almalinux-9.csv

@@ -134,5 +134,6 @@ wet
 wf
 wmc,java-21-openjdk
 wurm,xorg-x11-server-Xvfb
+xnt
 zmr,ncurses-libs.i686
 zps,ncurses-libs.i686

+ 1 - 0
lgsm/data/centos-7.csv

@@ -134,5 +134,6 @@ wet
 wf
 wmc,java-11-openjdk
 wurm,xorg-x11-server-Xvfb
+xnt
 zmr,ncurses-libs.i686
 zps,ncurses-libs.i686

+ 1 - 0
lgsm/data/centos-8.csv

@@ -134,5 +134,6 @@ wet
 wf
 wmc,java-17-openjdk
 wurm,xorg-x11-server-Xvfb
+xnt
 zmr,ncurses-libs.i686
 zps,ncurses-libs.i686

+ 1 - 0
lgsm/data/centos-9.csv

@@ -134,5 +134,6 @@ wet
 wf
 wmc,java-17-openjdk
 wurm,xorg-x11-server-Xvfb
+xnt
 zmr,ncurses-libs.i686
 zps,ncurses-libs.i686

+ 1 - 0
lgsm/data/debian-10.csv

@@ -134,5 +134,6 @@ wet
 wf
 wmc,openjdk-11-jre
 wurm,xvfb
+xnt
 zmr,libtinfo5:i386
 zps,libtinfo5:i386

+ 1 - 0
lgsm/data/debian-11.csv

@@ -134,5 +134,6 @@ wet
 wf
 wmc,openjdk-17-jre
 wurm,xvfb
+xnt
 zmr,libtinfo5:i386
 zps,libtinfo5:i386

+ 1 - 0
lgsm/data/debian-12.csv

@@ -134,5 +134,6 @@ wet
 wf
 wmc,openjdk-17-jre
 wurm,xvfb
+xnt
 zmr,libtinfo5:i386
 zps,libtinfo5:i386

+ 1 - 0
lgsm/data/debian-13.csv

@@ -133,5 +133,6 @@ wet
 wf
 wmc,openjdk21-jre
 wurm,xvfb
+xnt
 zmr,libtinfo5:i386
 zps,libtinfo5:i386

+ 1 - 0
lgsm/data/debian-9.csv

@@ -134,5 +134,6 @@ wet
 wf
 wmc,openjdk-8-jre
 wurm,xvfb
+xnt
 zmr,libtinfo5:i386
 zps,libtinfo5:i386

+ 1 - 0
lgsm/data/rhel-7.csv

@@ -134,5 +134,6 @@ wet
 wf
 wmc,java-11-openjdk
 wurm,xorg-x11-server-Xvfb
+xnt
 zmr,ncurses-libs.i686
 zps,ncurses-libs.i686

+ 1 - 0
lgsm/data/rhel-8.csv

@@ -134,5 +134,6 @@ wet
 wf
 wmc,java-21-openjdk
 wurm,xorg-x11-server-Xvfb
+xnt
 zmr,ncurses-libs.i686
 zps,ncurses-libs.i686

+ 1 - 0
lgsm/data/rhel-9.csv

@@ -134,5 +134,6 @@ wet
 wf
 wmc,java-21-openjdk
 wurm,xorg-x11-server-Xvfb
+xnt
 zmr,ncurses-libs.i686
 zps,ncurses-libs.i686

+ 1 - 0
lgsm/data/rocky-8.csv

@@ -134,5 +134,6 @@ wet
 wf
 wmc,java-21-openjdk
 wurm,xorg-x11-server-Xvfb
+xnt
 zmr,ncurses-libs.i686
 zps,ncurses-libs.i686

+ 1 - 0
lgsm/data/rocky-9.csv

@@ -134,5 +134,6 @@ wet
 wf
 wmc,java-21-openjdk
 wurm,xorg-x11-server-Xvfb
+xnt
 zmr,ncurses-libs.i686
 zps,ncurses-libs.i686

+ 1 - 0
lgsm/data/serverlist.csv

@@ -133,5 +133,6 @@ wet,wetserver,Wolfenstein: Enemy Territory,ubuntu-22.04
 wf,wfserver,Warfork,ubuntu-22.04
 wmc,wmcserver,WaterfallMC,ubuntu-22.04
 wurm,wurmserver,Wurm Unlimited,ubuntu-22.04
+xnt,xntserver,Xonotic,ubuntu-22.04
 zmr,zmrserver,Zombie Master: Reborn,ubuntu-22.04
 zps,zpsserver,Zombie Panic! Source,ubuntu-22.04

+ 1 - 0
lgsm/data/ubuntu-16.04.csv

@@ -134,5 +134,6 @@ wet
 wf
 wmc,openjdk-8-jre
 wurm,xvfb
+xnt
 zmr,libtinfo5:i386
 zps,libtinfo5:i386

+ 1 - 0
lgsm/data/ubuntu-18.04.csv

@@ -134,5 +134,6 @@ wet
 wf
 wmc,openjdk-11-jre
 wurm,xvfb
+xnt
 zmr,libtinfo5:i386
 zps,libtinfo5:i386

+ 1 - 0
lgsm/data/ubuntu-20.04.csv

@@ -134,5 +134,6 @@ wet
 wf
 wmc,openjdk-21-jre
 wurm,xvfb
+xnt
 zmr,libtinfo5:i386
 zps,libtinfo5:i386

+ 1 - 0
lgsm/data/ubuntu-22.04.csv

@@ -134,5 +134,6 @@ wet
 wf
 wmc,openjdk-21-jre
 wurm,xvfb
+xnt
 zmr,libtinfo5:i386
 zps,libtinfo5:i386

+ 1 - 0
lgsm/data/ubuntu-23.04.csv

@@ -134,5 +134,6 @@ wet
 wf
 wmc,openjdk-21-jre
 wurm,xvfb
+xnt
 zmr,libtinfo5:i386
 zps,libtinfo5:i386

+ 1 - 0
lgsm/data/ubuntu-23.10.csv

@@ -134,5 +134,6 @@ wet
 wf
 wmc,openjdk-21-jre
 wurm,xvfb
+xnt
 zmr,libtinfo5:i386
 zps,libtinfo5:i386

+ 1 - 0
lgsm/data/ubuntu-24.04.csv

@@ -134,5 +134,6 @@ wet
 wf
 wmc,openjdk-21-jre
 wurm,xvfb
+xnt
 zmr,libtinfo5:i386
 zps,libtinfo5:i386

+ 2 - 2
lgsm/modules/check_gamedig.sh

@@ -10,10 +10,10 @@ if [ "$(command -v node)" ] && [ "$(node -v | cut -d 'v' -f 2 | cut -d '.' -f 1)
 	echo -e "${bold}${lightyellow}Installing Gamedig${default}"
 	fn_script_log_info "Installing Gamedig"
 	cd "${lgsmdir}" || exit
-	curl -L -o package.json "https://raw.githubusercontent.com/GameServerManagers/LinuxGSM/${githubbranch}/package.json"
+	curl -L -o package.json "https://raw.githubusercontent.com/${githubuser}/${githubrepo}/${githubbranch}/package.json"
 	npm install
 elif [ "$(command -v node)" ] && [ "$(node -v | cut -d 'v' -f 2 | cut -d '.' -f 1)" -ge 16 ]; then
 	cd "${lgsmdir}" || exit
-	curl -s -L -o package.json "https://raw.githubusercontent.com/GameServerManagers/LinuxGSM/${githubbranch}/package.json"
+	curl -s -L -o package.json "https://raw.githubusercontent.com/${githubuser}/${githubrepo}/${githubbranch}/package.json"
 	npm update > /dev/null 2>&1
 fi

+ 2 - 0
lgsm/modules/command_update.sh

@@ -33,6 +33,8 @@ elif [ "${shortname}" == "vints" ]; then
 	update_vints.sh
 elif [ "${shortname}" == "ut99" ]; then
 	update_ut99.sh
+elif [ "${shortname}" == "xnt" ]; then
+	update_xnt.sh
 else
 	update_steamcmd.sh
 fi

+ 4 - 1
lgsm/modules/core_dl.sh

@@ -251,7 +251,10 @@ fn_dl_extract() {
 		fi
 	elif [ "${mime}" == "application/zip" ]; then
 		if [ -n "${extractsrc}" ]; then
-			extractcmd=$(unzip -qoj -d "${extractdest}" "${local_filedir}/${local_filename}" "${extractsrc}"/*)
+			temp_extractdir="${tmpdir}/Xonotic"
+			extractcmd=$(unzip -qo "${local_filedir}/${local_filename}" "${extractsrc}/*" -d "${temp_extractdir}")
+			find "${temp_extractdir}/${extractsrc}" -mindepth 1 -maxdepth 1 -exec mv -t "${extractdest}" {} +
+			rm -rf "${temp_extractdir}"
 		else
 			extractcmd=$(unzip -qo -d "${extractdest}" "${local_filedir}/${local_filename}")
 		fi

+ 15 - 0
lgsm/modules/core_modules.sh

@@ -531,6 +531,11 @@ fix_wurm.sh() {
 	fn_fetch_module
 }
 
+fix_xnt.sh() {
+	modulefile="${FUNCNAME[0]}"
+	fn_fetch_module
+}
+
 fix_zmr.sh() {
 	modulefile="${FUNCNAME[0]}"
 	fn_fetch_module
@@ -695,6 +700,11 @@ update_ut99.sh() {
 	fn_fetch_module
 }
 
+update_xnt.sh() {
+	modulefile="${FUNCNAME[0]}"
+	fn_fetch_module
+}
+
 fn_update_modules.sh() {
 	modulefile="${FUNCNAME[0]}"
 	fn_fetch_module
@@ -821,6 +831,11 @@ if [ ! -d "${lockdir}" ]; then
 	mkdir -p "${lockdir}"
 fi
 
+# Creates data dir if missing
+if [ ! -d "${datadir}" ]; then
+	mkdir -p "${datadir}"
+fi
+
 # if $USER id missing set to whoami
 if [ -z "${USER}" ]; then
 	USER="$(whoami)"

+ 1 - 1
lgsm/modules/fix.sh

@@ -52,7 +52,7 @@ fn_apply_fix() {
 	fi
 }
 
-apply_pre_start_fix=(arma3 armar ark av bt bo csgo cmw dst hw ins kf nmrih onset pvr ro rust rw samp sdtd sfc sm sof2 squad st tf2 terraria ts3 mcb mta unt vh wurm zmr)
+apply_pre_start_fix=(arma3 armar ark av bt bo csgo cmw dst hw ins kf nmrih onset pvr ro rust rw samp sdtd sfc sm sof2 squad st tf2 terraria ts3 mcb mta unt vh wurm xnt zmr)
 apply_post_install_fix=(av kf kf2 ro ut2k4 ut ut3)
 
 # validate registered fixes for safe development

+ 13 - 0
lgsm/modules/fix_xnt.sh

@@ -0,0 +1,13 @@
+#!/bin/bash
+# LinuxGSM fix_xnt.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Install Xonotic Default Config
+
+moduleselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+# Create the directory if it doesn't exist
+if [ ! -d "${systemdir}/${selfname}/data" ]; then
+	mkdir -p "${systemdir}/${selfname}/data"
+fi

+ 19 - 0
lgsm/modules/info_game.sh

@@ -2242,6 +2242,23 @@ fn_info_game_wf() {
 	servername="${servername:-"NOT SET"}"
 }
 
+# Config Type: QuakeC
+# Comment: //
+# Filetype: cfg
+fn_info_game_xnt() {
+	if [ -f "${servercfgfullpath}" ]; then
+		fn_info_game_keyvalue_pairs_space "maxplayers" "maxplayers"
+		fn_info_game_keyvalue_pairs_space "port" "port"
+		fn_info_game_keyvalue_pairs_space "rconpassword" "rcon_password"
+		fn_info_game_keyvalue_pairs_space "servername" "hostname"
+	fi
+	maxplayers="${maxplayers:-"8"}"
+	port="${port:-"0"}"
+	queryport="${port}"
+	rconpassword="${rconpassword:-"NOT SET"}"
+	servername="${servername:-"NOT SET"}"
+}
+
 fn_info_game_wmc() {
 	if [ ! -f "${servercfgfullpath}" ]; then
 		servername="${unavailable}"
@@ -2476,6 +2493,8 @@ elif [ "${engine}" == "source" ] || [ "${engine}" == "goldsrc" ]; then
 	fn_info_game_source
 elif [ "${engine}" == "unreal2" ]; then
 	fn_info_game_unreal2
+elif [ "${shortname}" == "xnt" ]; then
+	fn_info_game_xnt
 fi
 
 # Public IP address

+ 12 - 2
lgsm/modules/info_messages.sh

@@ -668,7 +668,7 @@ fn_info_messages_ports_edit() {
 
 	startparameterslocation="${red}UNKNOWN${default}"
 	# engines/games that require editing in the config file.
-	local ports_edit_array=("ac" "arma3" "armar" "bo" "bt" "ct" "dst" "eco" "idtech2" "idtech3" "idtech3_ql" "jc2" "jc3" "lwjgl2" "mcb" "nec" "pc" "pc2" "prism3d" "pz" "qw" "refractor" "renderware" "rw" "sb" "sdtd" "st" "stn" "ts3" "tw" "terraria" "unreal" "unreal2" "unreal3" "vints" "wurm")
+	local ports_edit_array=("ac" "arma3" "armar" "bo" "bt" "ct" "dst" "eco" "idtech2" "idtech3" "idtech3_ql" "jc2" "jc3" "lwjgl2" "mcb" "nec" "pc" "pc2" "prism3d" "pz" "qw" "refractor" "renderware" "rw" "sb" "sdtd" "st" "stn" "ts3" "tw" "terraria" "unreal" "unreal2" "unreal3" "vints" "xnt" "wurm")
 	for port_edit in "${ports_edit_array[@]}"; do
 		if [ "${shortname}" == "ut3" ]; then
 			startparameterslocation="${servercfgdir}/UTWeb.ini"
@@ -1548,6 +1548,14 @@ fn_info_messages_st() {
 	} | column -s $'\t' -t
 }
 
+fn_info_messages_stn() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+	} | column -s $'\t' -t
+}
+
 fn_info_messages_ti() {
 	{
 		fn_port "header"
@@ -1728,7 +1736,7 @@ fn_info_messages_wurm() {
 	} | column -s $'\t' -t
 }
 
-fn_info_messages_stn() {
+fn_info_messages_xnt() {
 	{
 		fn_port "header"
 		fn_port "Game" port udp
@@ -1914,6 +1922,8 @@ fn_info_messages_select_engine() {
 		fn_info_messages_wf
 	elif [ "${shortname}" == "wurm" ]; then
 		fn_info_messages_wurm
+	elif [ "${shortname}" == "xnt" ]; then
+		fn_info_messages_xnt
 	elif [ "${engine}" == "goldsrc" ]; then
 		fn_info_messages_goldsrc
 	elif [ "${engine}" == "prism3d" ]; then

+ 22 - 8
lgsm/modules/install_config.sh

@@ -41,15 +41,15 @@ fn_default_config_remote() {
 		fn_script_log_info "Copying ${servercfg} config file."
 		if [ "${config}" == "${servercfgdefault}" ]; then
 			mkdir -p "${servercfgdir}"
-			cp -nv "${lgsmdir}/config-default/config-game/${config}" "${servercfgfullpath}"
+			cp -v --update=none "${lgsmdir}/config-default/config-game/${config}" "${servercfgfullpath}"
 		elif [ "${shortname}" == "arma3" ] && [ "${config}" == "${networkcfgdefault}" ]; then
 			mkdir -p "${servercfgdir}"
-			cp -nv "${lgsmdir}/config-default/config-game/${config}" "${networkcfgfullpath}"
+			cp -v --update=none "${lgsmdir}/config-default/config-game/${config}" "${networkcfgfullpath}"
 		elif [ "${shortname}" == "dst" ] && [ "${config}" == "${clustercfgdefault}" ]; then
-			cp -nv "${lgsmdir}/config-default/config-game/${clustercfgdefault}" "${clustercfgfullpath}"
+			cp -v --update=none "${lgsmdir}/config-default/config-game/${clustercfgdefault}" "${clustercfgfullpath}"
 		else
 			mkdir -p "${servercfgdir}"
-			cp -nv "${lgsmdir}/config-default/config-game/${config}" "${servercfgdir}/${config}"
+			cp -v --update=none "${lgsmdir}/config-default/config-game/${config}" "${servercfgdir}/${config}"
 		fi
 	done
 	fn_sleep_time
@@ -62,14 +62,21 @@ fn_default_config_local() {
 	fn_messages_separator
 	echo -e "Copying default configs."
 	fn_check_cfgdir
-	echo -en "copying config file [ ${italic}${servercfgdefault}${default} ]"
-	cp -n "${servercfgdir}/${servercfgdefault}" "${servercfgfullpath}"
+
+	# Check if the directory for ${servercfgfullpath} exists, if not, create it
+	if [ ! -d "$(dirname "${servercfgfullpath}")" ]; then
+		mkdir -p "$(dirname "${servercfgfullpath}")"
+	fi
+
+	echo -en "copying config file [ ${italic}${servercfgdefault}${default} ]: "
+	cp --update=none "${servercfgdir}/${servercfgdefault}" "${servercfgfullpath}"
+	exitcode=$?
 	if [ "${exitcode}" != 0 ]; then
 		fn_print_fail_eol
-		fn_script_log_fail "copying config file [ ${servercfgdefault} ]"
+		fn_script_log_fail "copying config file [ ${servercfgdefault} ]: "
 	else
 		fn_print_ok_eol
-		fn_script_log_pass "copying config file [ ${servercfgdefault} ]"
+		fn_script_log_pass "copying config file [ ${servercfgdefault} ]: "
 	fi
 }
 
@@ -835,6 +842,13 @@ elif [ "${shortname}" == "wmc" ]; then
 	fn_fetch_default_config
 	fn_default_config_remote
 	fn_set_config_vars
+	fn_list_config_locations
+elif [ "${shortname}" == "xnt" ]; then
+	array_configs+=(server.cfg)
+	fn_fetch_default_config
+	fn_default_config_remote
+	fn_set_config_vars
+	fn_list_config_locations
 elif [ "${shortname}" == "wurm" ]; then
 	array_configs+=(server.cfg)
 	fn_fetch_default_config

+ 58 - 28
lgsm/modules/install_server_files.sh

@@ -12,196 +12,224 @@ fn_install_server_files() {
 		remote_fileurl="http://linuxgsm.download/ActionHalfLife/action_halflife-1.0.tar.xz"
 		local_filedir="${tmpdir}"
 		local_filename="action_halflife-1.0.tar.xz"
-		chmodx="nochmodx" run="norun"
+		chmodx="nochmodx"
+		run="norun"
 		force="noforce"
 		md5="61d7b79fd714888b6d65944fdaafa94a"
 	elif [ "${shortname}" == "bf1942" ]; then
 		remote_fileurl="http://linuxgsm.download/BattleField1942/bf1942_lnxded-1.61-hacked-to-1.612.full.tar.xz"
 		local_filedir="${tmpdir}"
 		local_filename="bf1942_lnxded-1.61-hacked-to-1.612.full.tar.xz"
-		chmodx="nochmodx" run="norun"
+		chmodx="nochmodx"
+		run="norun"
 		force="noforce"
 		md5="4223bf4ed85f5162c24b2cba51249b9e"
 	elif [ "${shortname}" == "bfv" ]; then
 		remote_fileurl="http://linuxgsm.download/BattlefieldVietnam/bfv_linded-v1.21-20041207_patch.tar.xz"
 		local_filedir="${tmpdir}"
 		local_filename="bfv_linded-v1.21-20041207_patch.tar.xz"
-		chmodx="nochmodx" run="norun"
+		chmodx="nochmodx"
+		run="norun"
 		force="noforce"
 		md5="e3b4962cdd9d41e23c6fed65101bccde"
 	elif [ "${shortname}" == "bb" ]; then
 		remote_fileurl="http://linuxgsm.download/BrainBread/brainbread-v1.2-linuxserver.tar.xz"
 		local_filedir="${tmpdir}"
 		local_filename="brainbread-v1.2-linuxserver.tar.xz"
-		chmodx="nochmodx" run="norun"
+		chmodx="nochmodx"
+		run="norun"
 		force="noforce"
 		md5="55f227183b736397806d5b6db6143f15"
 	elif [ "${shortname}" == "cod" ]; then
 		remote_fileurl="http://linuxgsm.download/CallOfDuty/cod-lnxded-1.5b-full.tar.xz"
 		local_filedir="${tmpdir}"
 		local_filename="cod-lnxded-1.5-large.tar.xz"
-		chmodx="nochmodx" run="norun"
+		chmodx="nochmodx"
+		run="norun"
 		force="noforce"
 		md5="ee0ad1ccbfa1fd27fde01a4a431a5c2f"
 	elif [ "${shortname}" == "coduo" ]; then
 		remote_fileurl="http://linuxgsm.download/CallOfDutyUnitedOffensive/coduo-lnxded-1.51b-full.tar.xz"
 		local_filedir="${tmpdir}"
 		local_filename="coduo-lnxded-1.51b-full.tar.xz"
-		chmodx="nochmodx" run="norun"
+		chmodx="nochmodx"
+		run="norun"
 		force="noforce"
 		md5="35cabccd67adcda44aaebc59405915b9"
 	elif [ "${shortname}" == "cod2" ]; then
 		remote_fileurl="http://linuxgsm.download/CallOfDuty2/cod2-lnxded-1.3-full.tar.xz"
 		local_filedir="${tmpdir}"
 		local_filename="cod2-lnxded-1.3-full.tar.xz"
-		chmodx="nochmodx" run="norun"
+		chmodx="nochmodx"
+		run="norun"
 		force="noforce"
 		md5="b8c4c611f01627dd43348e78478a3d41"
 	elif [ "${shortname}" == "cod4" ]; then
 		remote_fileurl="http://linuxgsm.download/CallOfDuty4/cod4x18_lnxded.tar.xz"
 		local_filedir="${tmpdir}"
 		local_filename="cod4x18_lnxded.tar.xz"
-		chmodx="nochmodx" run="norun"
+		chmodx="nochmodx"
+		run="norun"
 		force="noforce"
 		md5="d255b59b9756d7dbead67718208512ee"
 	elif [ "${shortname}" == "codwaw" ]; then
 		remote_fileurl="http://linuxgsm.download/CallOfDutyWorldAtWar/codwaw-lnxded-1.7-full.tar.xz"
 		local_filedir="${tmpdir}"
 		local_filename="codwaw-lnxded-1.7-full.tar.xz"
-		chmodx="nochmodx" run="norun"
+		chmodx="nochmodx"
+		run="norun"
 		force="noforce"
 		md5="2c6be1bb66ea631b9b2e7ae6216c6680"
 	elif [ "${shortname}" == "etl" ]; then
 		remote_fileurl="http://linuxgsm.download/WolfensteinEnemyTerritory/etlegacy-v2.78.1-i386-et-260b.tar.xz"
 		local_filedir="${tmpdir}"
 		local_filename="etlegacy-v2.78.1-i386-et-260b.tar.xz"
-		chmodx="nochmodx" run="norun"
+		chmodx="nochmodx"
+		run="norun"
 		force="noforce"
 		md5="7c08b52cb09b30eadb98ea05ef780fc7"
 	elif [ "${shortname}" == "mohaa" ]; then
 		remote_fileurl="http://linuxgsm.download/MedalofHonorAlliedAssault/moh_revival_v1.12_RC3.5.1.tar.xz"
 		local_filedir="${tmpdir}"
 		local_filename="moh_revival_v1.12_RC3.5.1.tar.xz"
-		chmodx="nochmodx" run="norun"
+		chmodx="nochmodx"
+		run="norun"
 		force="noforce"
 		md5="7c664538999252eeaf2b6d9949416480"
 	elif [ "${shortname}" == "ns" ]; then
 		remote_fileurl="http://linuxgsm.download/NaturalSelection/ns_dedicated_server_v32.tar.xz"
 		local_filedir="${tmpdir}"
 		local_filename="ns_dedicated_server_v32.tar.xz"
-		chmodx="nochmodx" run="norun"
+		chmodx="nochmodx"
+		run="norun"
 		force="noforce"
 		md5="23ec3cadd93d8bb1c475bad5b9cce370"
 	elif [ "${shortname}" == "q2" ]; then
 		remote_fileurl="http://linuxgsm.download/Quake2/quake2-3.20-glibc-i386-full-linux2.0.tar.xz"
 		local_filedir="${tmpdir}"
 		local_filename="quake2-3.20-glibc-i386-full-linux2.0.tar.xz"
-		chmodx="nochmodx" run="norun"
+		chmodx="nochmodx"
+		run="norun"
 		force="noforce"
 		md5="2908164a32d4808bb720f2161f6b0c82"
 	elif [ "${shortname}" == "q3" ]; then
 		remote_fileurl="http://linuxgsm.download/Quake3/quake3-1.32c-x86-full-linux.tar.xz"
 		local_filedir="${tmpdir}"
 		local_filename="quake3-1.32c-x86-full-linux.tar.xz"
-		chmodx="nochmodx" run="norun"
+		chmodx="nochmodx"
+		run="norun"
 		force="noforce"
 		md5="b0e26d8919fe9313fb9d8ded2360f3db"
 	elif [ "${shortname}" == "q4" ]; then
 		remote_fileurl="http://linuxgsm.download/Quake4/quake4-1.4.2-x86-linuxded.tar.xz"
 		local_filedir="${tmpdir}"
 		local_filename="quake4-1.4.2-x86-linuxded.tar.xz"
-		chmodx="nochmodx" run="norun"
+		chmodx="nochmodx"
+		run="norun"
 		force="noforce"
 		md5="afe30b44f23c8ae2ce6f0f464473d8ba"
 	elif [ "${shortname}" == "qw" ]; then
 		remote_fileurl="http://linuxgsm.download/QuakeWorld/nquake.server.linux.190506.full.tar.xz"
 		local_filedir="${tmpdir}"
 		local_filename="nquake.server.linux.190506.full.tar.xz"
-		chmodx="nochmodx" run="norun"
+		chmodx="nochmodx"
+		run="norun"
 		force="noforce"
 		md5="82055b7d973206c13a606db8ba288d03"
 	elif [ "${shortname}" == "rtcw" ]; then
 		remote_fileurl="http://linuxgsm.download/ReturnToCastleWolfenstein/iortcw-1.51c-x86_64-server-linux-20190507.tar.xz"
 		local_filedir="${tmpdir}"
 		local_filename="iortcw-1.51c-x86_64-server-linux-20190507.tar.xz"
-		chmodx="nochmodx" run="norun"
+		chmodx="nochmodx"
+		run="norun"
 		force="noforce"
 		md5="df6ff664d37dd0d22787848bdb3cac5f"
 	elif [ "${shortname}" == "sfc" ]; then
 		remote_fileurl="http://linuxgsm.download/SourceFortsClassic/SFClassic-1.0-RC7-fix.tar.xz"
 		local_filedir="${tmpdir}"
 		local_filename="SFClassic-1.0-RC7-fix.tar.xz"
-		chmodx="nochmodx" run="norun"
+		chmodx="nochmodx"
+		run="norun"
 		force="noforce"
 		md5="70077137185700e28fe6bbb6021d12bc"
 	elif [ "${shortname}" == "sof2" ]; then
 		remote_fileurl="http://linuxgsm.download/SoldierOfFortune2/sof2gold-1.03.tar.xz"
 		local_filedir="${tmpdir}"
 		local_filename="sof2gold-1.03.tar.xz"
-		chmodx="nochmodx" run="norun"
+		chmodx="nochmodx"
+		run="norun"
 		force="noforce"
 		md5="201e23bab04207d00ce813d001c483d9"
 	elif [ "${shortname}" == "ts" ]; then
 		remote_fileurl="http://linuxgsm.download/TheSpecialists/ts-3-linux-final.tar.xz"
 		local_filedir="${tmpdir}"
 		local_filename="ts-3-linux-final.tar.xz"
-		chmodx="nochmodx" run="norun"
+		chmodx="nochmodx"
+		run="norun"
 		force="noforce"
 		md5="3c66ecff6e3644f7ac88015732a0fb93"
 	elif [ "${shortname}" == "ut2k4" ]; then
 		remote_fileurl="http://linuxgsm.download/UnrealTournament2004/ut2004-server-3369-3-ultimate-linux.tar.xz"
 		local_filedir="${tmpdir}"
 		local_filename="ut2004-server-3369-3-ultimate-linux.tar.xz"
-		chmodx="nochmodx" run="norun"
+		chmodx="nochmodx"
+		run="norun"
 		force="noforce"
 		md5="9fceaab68554749f4b45be66613b9a15"
 	elif [ "${shortname}" == "ut99" ]; then
 		remote_fileurl="http://linuxgsm.download/UnrealTournament99/ut99-server-469b-ultimate-linux.tar.xz"
 		local_filedir="${tmpdir}"
 		local_filename="ut99-server-469b-ultimate-linux.tar.xz"
-		chmodx="nochmodx" run="norun"
+		chmodx="nochmodx"
+		run="norun"
 		force="noforce"
 		md5="dba3f1122a5e60ee45ece7422fcf78f5"
 	elif [ "${shortname}" == "ut" ]; then
 		remote_fileurl="http://linuxgsm.download/UnrealTournament/UnrealTournament-Server-XAN-3525360-Linux.tar.xz"
 		local_filedir="${tmpdir}"
 		local_filename="UnrealTournament-Server-XAN-3525360-Linux.tar.xz"
-		chmodx="noexecute" run="norun"
+		chmodx="noexecute"
+		run="norun"
 		force="noforce"
 		md5="41dd92015713a78211eaccf503b72393"
 	elif [ "${shortname}" == "ut3" ]; then
 		remote_fileurl="http://linuxgsm.download/UnrealTournament3/UT3-linux-server-2.1.tar.xz"
 		local_filedir="${tmpdir}"
 		local_filename="UT3-linux-server-2.1.tar.xz"
-		chmodx="nochmodx" run="norun"
+		chmodx="nochmodx"
+		run="norun"
 		force="noforce"
 		md5="8876cca61e3f83ea08db25208bde6ac6"
 	elif [ "${shortname}" == "vs" ]; then
 		remote_fileurl="http://linuxgsm.download/VampireSlayer/vs_l-6.0_full.tar.xz"
 		local_filedir="${tmpdir}"
 		local_filename="vs_l-6.0_full.tar.xz"
-		chmodx="nochmodx" run="norun"
+		chmodx="nochmodx"
+		run="norun"
 		force="noforce"
 		md5="b322f79e0abd31847493c52acf802667"
 	elif [ "${shortname}" == "wet" ]; then
 		remote_fileurl="http://linuxgsm.download/WolfensteinEnemyTerritory/enemy-territory.260b.tar.xz"
 		local_filedir="${tmpdir}"
 		local_filename="enemy-territory.260b.tar.xz"
-		chmodx="nochmodx" run="norun"
+		chmodx="nochmodx"
+		run="norun"
 		force="noforce"
 		md5="581a333cc7eacda2f56d5a00fe11eafa"
 	elif [ "${shortname}" == "samp" ]; then
 		remote_fileurl="https://files.samp-sc.com/samp037svr_R2-1.tar.gz"
 		local_filedir="${tmpdir}"
 		local_filename="samp037svr_R2-1.tar.gz"
-		chmodx="nochmodx" run="norun"
+		chmodx="nochmodx"
+		run="norun"
 		force="noforce"
 		md5="93705e165550c97484678236749198a4"
 	elif [ "${shortname}" == "zmr" ]; then
 		remote_fileurl="http://linuxgsm.download/ZombieMasterReborn/zombie_master_reborn_b6_1.tar.xz"
 		local_filedir="${tmpdir}"
 		local_filename="zombie_master_reborn_b6_1.tar.xz"
-		chmodx="nochmodx" run="norun"
+		chmodx="nochmodx"
+		run="norun"
 		force="noforce"
 		md5="0188ae86dbc9376f11ae3032dba2d665"
 	else
@@ -247,6 +275,8 @@ elif [ "${shortname}" == "vints" ]; then
 elif [ "${shortname}" == "ut99" ]; then
 	fn_install_server_files
 	update_ut99.sh
+elif [ "${shortname}" == "xnt" ]; then
+	update_xnt.sh
 elif [ -z "${appid}" ] || [ "${shortname}" == "ahl" ] || [ "${shortname}" == "bb" ] || [ "${shortname}" == "q4" ] || [ "${shortname}" == "ns" ] || [ "${shortname}" == "sfc" ] || [ "${shortname}" == "ts" ] || [ "${shortname}" == "vs" ] || [ "${shortname}" == "zmr" ]; then
 	if [ "${shortname}" == "ut" ]; then
 		install_eula.sh

+ 173 - 0
lgsm/modules/update_xnt.sh

@@ -0,0 +1,173 @@
+#!/bin/bash
+# LinuxGSM command_ut99.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Handles updating of Unreal Tournament 99 servers.
+
+module_selfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+fn_update_dl() {
+	# Download and extract files to serverfiles.
+	fn_fetch_file "${remotebuildurl}" "" "" "" "${tmpdir}" "${remotebuildfilename}" "nochmodx" "norun" "force" "${remotebuildhash}"
+	fn_dl_extract "${tmpdir}" "${remotebuildfilename}" "${serverfiles}" "Xonotic"
+	fn_clear_tmp
+}
+
+fn_update_localbuild() {
+	# Gets local build info.
+	fn_print_dots "Checking local build: ${remotelocation}"
+
+	# Send version command to Xonotic server.
+	tmux -L "${socketname}" send-keys -t "${sessionname}" "version\r" > /dev/null 2>&1
+	fn_sleep_time_1
+
+	# Uses log file to get local build.
+	localbuild=$(grep "SVQC version: xonotic-v" "${consolelogdir}"/* 2> /dev/null | tail -1 | sed 's/.*SVQC version: \(xonotic-v[0-9.]*\).*/\1/' | tr -d '\000-\011\013-\037')
+	if [ -z "${localbuild}" ]; then
+		fn_print_error "Checking local build: ${remotelocation}: missing local build info"
+		fn_script_log_error "Missing local build info"
+		fn_script_log_error "Set localbuild to 0"
+		localbuild="0"
+	else
+		fn_print_ok "Checking local build: ${remotelocation}"
+		fn_script_log_pass "Checking local build"
+	fi
+}
+
+fn_update_remotebuild() {
+	# Get remote build info.
+	apiurl="https://api.github.com/repos/xonotic/xonotic/tags"
+	remotebuildresponse=$(curl -s "${apiurl}")
+	remotebuildtag=$(echo "${remotebuildresponse}" | jq -r '.[0].name')
+	remotebuildfilename=$(echo "${remotebuildtag}" | tr -d 'v')
+	remotebuildfilename="${remotebuildfilename}.zip"
+	remotebuildurl="https://dl.xonotic.org/${remotebuildfilename}"
+
+	remotebuildversion="${remotebuildtag}"
+
+	if [ "${firstcommandname}" != "INSTALL" ]; then
+		fn_print_dots "Checking remote build: ${remotelocation}"
+		# Checks if remotebuildversion variable has been set.
+		if [ -z "${remotebuildversion}" ] || [ "${remotebuildversion}" == "null" ]; then
+			fn_print_fail "Checking remote build: ${remotelocation}"
+			fn_script_log_fail "Checking remote build"
+			core_exit.sh
+		else
+			fn_print_ok "Checking remote build: ${remotelocation}"
+			fn_script_log_pass "Checking remote build"
+		fi
+	else
+		# Checks if remotebuild variable has been set.
+		if [ -z "${remotebuildversion}" ] || [ "${remotebuildversion}" == "null" ]; then
+			fn_print_failure "Unable to get remote build"
+			fn_script_log_fail "Unable to get remote build"
+			core_exit.sh
+		fi
+	fi
+}
+
+fn_update_compare() {
+	fn_print_dots "Checking for update: ${remotelocation}"
+	# Update has been found or force update.
+	if [ "${localbuild}" != "${remotebuildversion}" ] || [ "${forceupdate}" == "1" ]; then
+		# Create update lockfile.
+		date '+%s' > "${lockdir:?}/update.lock"
+		fn_print_ok_nl "Checking for update: ${remotelocation}"
+		echo -en "\n"
+		echo -e "Update available"
+		echo -e "* Local build: ${red}${localbuild}${default}"
+		echo -e "* Remote build: ${green}${remotebuildversion}${default}"
+		if [ -n "${branch}" ]; then
+			echo -e "* Branch: ${branch}"
+		fi
+		if [ -f "${rootdir}/.dev-debug" ]; then
+			echo -e "Remote build info"
+			echo -e "* apiurl: ${apiurl}"
+			echo -e "* remotebuildfilename: ${remotebuildfilename}"
+			echo -e "* remotebuildurl: ${remotebuildurl}"
+			echo -e "* remotebuildversion: ${remotebuildversion}"
+		fi
+		echo -en "\n"
+		fn_script_log_info "Update available"
+		fn_script_log_info "Local build: ${localbuild}"
+		fn_script_log_info "Remote build: ${remotebuildversion}"
+		if [ -n "${branch}" ]; then
+			fn_script_log_info "Branch: ${branch}"
+		fi
+		fn_script_log_info "${localbuild} > ${remotebuildversion}"
+
+		if [ "${commandname}" == "UPDATE" ]; then
+			date +%s > "${lockdir}/last-updated.lock"
+			unset updateonstart
+			check_status.sh
+			# If server stopped.
+			if [ "${status}" == "0" ]; then
+				fn_update_dl
+				if [ "${localbuild}" == "0" ]; then
+					exitbypass=1
+					command_start.sh
+					fn_firstcommand_reset
+					exitbypass=1
+					fn_sleep_time_5
+					command_stop.sh
+					fn_firstcommand_reset
+				fi
+			# If server started.
+			else
+				fn_print_restart_warning
+				exitbypass=1
+				command_stop.sh
+				fn_firstcommand_reset
+				exitbypass=1
+				fn_update_dl
+				exitbypass=1
+				command_start.sh
+				fn_firstcommand_reset
+			fi
+			unset exitbypass
+			alert="update"
+		elif [ "${commandname}" == "CHECK-UPDATE" ]; then
+			alert="check-update"
+		fi
+		alert.sh
+	else
+		fn_print_ok_nl "Checking for update: ${remotelocation}"
+		echo -en "\n"
+		echo -e "No update available"
+		echo -e "* Local build: ${green}${localbuild}${default}"
+		echo -e "* Remote build: ${green}${remotebuildversion}${default}"
+		if [ -n "${branch}" ]; then
+			echo -e "* Branch: ${branch}"
+		fi
+		echo -en "\n"
+		fn_script_log_info "No update available"
+		fn_script_log_info "Local build: ${localbuild}"
+		fn_script_log_info "Remote build: ${remotebuildversion}"
+		if [ -n "${branch}" ]; then
+			fn_script_log_info "Branch: ${branch}"
+		fi
+		if [ -f "${rootdir}/.dev-debug" ]; then
+			echo -e "Remote build info"
+			echo -e "* apiurl: ${apiurl}"
+			echo -e "* remotebuildfilename: ${remotebuildfilename}"
+			echo -e "* remotebuildurl: ${remotebuildurl}"
+			echo -e "* remotebuildversion: ${remotebuildversion}"
+		fi
+	fi
+}
+
+# The location where the builds are checked and downloaded.
+remotelocation="github.com"
+
+if [ "${firstcommandname}" == "INSTALL" ]; then
+	fn_update_remotebuild
+	fn_update_dl
+else
+	fn_print_dots "Checking for update"
+	fn_print_dots "Checking for update: ${remotelocation}"
+	fn_script_log_info "Checking for update: ${remotelocation}"
+	fn_update_localbuild
+	fn_update_remotebuild
+	fn_update_compare
+fi