Explorar el Código

fix: add all modules from v23.2.3 to functions dir

This will reduce chances of migration issues
functions dir will eventually be removed
Daniel Gibbs hace 3 años
padre
commit
c2e31552a4
Se han modificado 100 ficheros con 11039 adiciones y 0 borrados
  1. 17 0
      lgsm/functions/README.md
  2. 238 0
      lgsm/functions/alert.sh
  3. 59 0
      lgsm/functions/alert_discord.sh
  4. 25 0
      lgsm/functions/alert_email.sh
  5. 30 0
      lgsm/functions/alert_gotify.sh
  6. 29 0
      lgsm/functions/alert_ifttt.sh
  7. 32 0
      lgsm/functions/alert_mailgun.sh
  8. 30 0
      lgsm/functions/alert_pushbullet.sh
  9. 33 0
      lgsm/functions/alert_pushover.sh
  10. 50 0
      lgsm/functions/alert_rocketchat.sh
  11. 75 0
      lgsm/functions/alert_slack.sh
  12. 30 0
      lgsm/functions/alert_telegram.sh
  13. 101 0
      lgsm/functions/check.sh
  14. 35 0
      lgsm/functions/check_config.sh
  15. 363 0
      lgsm/functions/check_deps.sh
  16. 20 0
      lgsm/functions/check_executable.sh
  17. 29 0
      lgsm/functions/check_glibc.sh
  18. 63 0
      lgsm/functions/check_ip.sh
  19. 26 0
      lgsm/functions/check_last_update.sh
  20. 233 0
      lgsm/functions/check_permissions.sh
  21. 10 0
      lgsm/functions/check_status.sh
  22. 22 0
      lgsm/functions/check_steamcmd.sh
  23. 22 0
      lgsm/functions/check_system_dir.sh
  24. 55 0
      lgsm/functions/check_system_requirements.sh
  25. 33 0
      lgsm/functions/check_tmuxception.sh
  26. 23 0
      lgsm/functions/check_version.sh
  27. 268 0
      lgsm/functions/command_backup.sh
  28. 39 0
      lgsm/functions/command_check_update.sh
  29. 58 0
      lgsm/functions/command_console.sh
  30. 142 0
      lgsm/functions/command_debug.sh
  31. 41 0
      lgsm/functions/command_details.sh
  32. 24 0
      lgsm/functions/command_dev_clear_functions.sh
  33. 23 0
      lgsm/functions/command_dev_debug.sh
  34. 231 0
      lgsm/functions/command_dev_detect_deps.sh
  35. 92 0
      lgsm/functions/command_dev_detect_glibc.sh
  36. 61 0
      lgsm/functions/command_dev_detect_ldd.sh
  37. 280 0
      lgsm/functions/command_dev_query_raw.sh
  38. 26 0
      lgsm/functions/command_donate.sh
  39. 442 0
      lgsm/functions/command_fastdl.sh
  40. 52 0
      lgsm/functions/command_install.sh
  41. 32 0
      lgsm/functions/command_install_resources_mta.sh
  42. 135 0
      lgsm/functions/command_mods_install.sh
  43. 153 0
      lgsm/functions/command_mods_remove.sh
  44. 109 0
      lgsm/functions/command_mods_update.sh
  45. 245 0
      lgsm/functions/command_monitor.sh
  46. 78 0
      lgsm/functions/command_postdetails.sh
  47. 18 0
      lgsm/functions/command_restart.sh
  48. 41 0
      lgsm/functions/command_send.sh
  49. 23 0
      lgsm/functions/command_skeleton.sh
  50. 224 0
      lgsm/functions/command_start.sh
  51. 283 0
      lgsm/functions/command_stop.sh
  52. 19 0
      lgsm/functions/command_test_alert.sh
  53. 57 0
      lgsm/functions/command_ts3_server_pass.sh
  54. 40 0
      lgsm/functions/command_update.sh
  55. 49 0
      lgsm/functions/command_validate.sh
  56. 183 0
      lgsm/functions/command_wipe.sh
  57. 35 0
      lgsm/functions/compress_unreal2_maps.sh
  58. 35 0
      lgsm/functions/compress_ut99_maps.sh
  59. 88 0
      lgsm/functions/fix.sh
  60. 62 0
      lgsm/functions/fix_ark.sh
  61. 16 0
      lgsm/functions/fix_arma3.sh
  62. 17 0
      lgsm/functions/fix_armar.sh
  63. 19 0
      lgsm/functions/fix_av.sh
  64. 10 0
      lgsm/functions/fix_bo.sh
  65. 24 0
      lgsm/functions/fix_bt.sh
  66. 23 0
      lgsm/functions/fix_cmw.sh
  67. 42 0
      lgsm/functions/fix_csgo.sh
  68. 17 0
      lgsm/functions/fix_dst.sh
  69. 10 0
      lgsm/functions/fix_hw.sh
  70. 20 0
      lgsm/functions/fix_ins.sh
  71. 36 0
      lgsm/functions/fix_kf.sh
  72. 20 0
      lgsm/functions/fix_kf2.sh
  73. 17 0
      lgsm/functions/fix_lo.sh
  74. 11 0
      lgsm/functions/fix_mcb.sh
  75. 16 0
      lgsm/functions/fix_mta.sh
  76. 17 0
      lgsm/functions/fix_nmrih.sh
  77. 19 0
      lgsm/functions/fix_onset.sh
  78. 39 0
      lgsm/functions/fix_ro.sh
  79. 32 0
      lgsm/functions/fix_rust.sh
  80. 10 0
      lgsm/functions/fix_rw.sh
  81. 34 0
      lgsm/functions/fix_samp.sh
  82. 10 0
      lgsm/functions/fix_sdtd.sh
  83. 48 0
      lgsm/functions/fix_sfc.sh
  84. 11 0
      lgsm/functions/fix_sof2.sh
  85. 26 0
      lgsm/functions/fix_squad.sh
  86. 11 0
      lgsm/functions/fix_st.sh
  87. 141 0
      lgsm/functions/fix_steamcmd.sh
  88. 10 0
      lgsm/functions/fix_terraria.sh
  89. 16 0
      lgsm/functions/fix_tf2.sh
  90. 33 0
      lgsm/functions/fix_ts3.sh
  91. 10 0
      lgsm/functions/fix_unt.sh
  92. 13 0
      lgsm/functions/fix_ut.sh
  93. 36 0
      lgsm/functions/fix_ut2k4.sh
  94. 20 0
      lgsm/functions/fix_ut3.sh
  95. 35 0
      lgsm/functions/fix_vh.sh
  96. 23 0
      lgsm/functions/fix_wurm.sh
  97. 48 0
      lgsm/functions/fix_zmr.sh
  98. 281 0
      lgsm/functions/info_distro.sh
  99. 2600 0
      lgsm/functions/info_game.sh
  100. 1847 0
      lgsm/functions/info_messages.sh

+ 17 - 0
lgsm/functions/README.md

@@ -0,0 +1,17 @@
+# LinuxGSM - Modules
+
+These modules are scripts that are called upon by the primary script linuxgsm.sh
+
+## Module Names
+
+Modules have been named to give an idea of what the function does.
+
+-   core: Essential modules that will always run first.
+-   command: Primary command function.
+-   check: Runs checks that will either halt on or fix an issue.
+-   dev: development modules.
+-   fix: Applies a game server specific fix.
+-   info: retrieves information from a source such as config file or the OS.
+-   install: modules related to the installer.
+-   monitor: modules related to monitor.
+-   update: modules that update the game server.

+ 238 - 0
lgsm/functions/alert.sh

@@ -0,0 +1,238 @@
+#!/bin/bash
+# LinuxGSM alert.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Overall function for managing alerts.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+# Generates alert log of the details at the time of the alert.
+# Used with email alerts.
+fn_alert_log() {
+	info_distro.sh
+	info_game.sh
+	info_messages.sh
+	if [ -f "${alertlog}" ]; then
+		rm -f "${alertlog:?}"
+	fi
+
+	{
+		fn_info_message_head
+		fn_info_message_distro
+		fn_info_message_server_resource
+		fn_info_message_gameserver_resource
+		fn_info_message_gameserver
+		fn_info_logs
+	} | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g" | tee -a "${alertlog}" > /dev/null 2>&1
+}
+
+fn_alert_test() {
+	fn_script_log_info "Sending test alert"
+	alertsubject="Alert - ${selfname} - Test"
+	alertemoji="🚧"
+	alertsound="1"
+	alerturl="not enabled"
+	alertbody="Testing LinuxGSM Alert. No action to be taken."
+}
+
+fn_alert_restart() {
+	fn_script_log_info "Sending alert: Restarted: ${executable} not running"
+	alertsubject="Alert - ${selfname} - Restarted"
+	alertemoji="🚨"
+	alertsound="2"
+	alerturl="not enabled"
+	alertbody="${selfname} ${executable} not running"
+}
+
+fn_alert_restart_query() {
+	fn_script_log_info "Sending alert: Restarted: ${selfname}"
+	alertsubject="Alert - ${selfname} - Restarted"
+	alertemoji="🚨"
+	alertsound="2"
+	alerturl="not enabled"
+	alertbody="Unable to query: ${selfname}"
+}
+
+fn_alert_update() {
+	fn_script_log_info "Sending alert: Updated"
+	alertsubject="Alert - ${selfname} - Updated"
+	alertemoji="🎮"
+	alertsound="1"
+	alerturl="not enabled"
+	alertbody="${gamename} received update: ${remotebuildversion}"
+}
+
+fn_alert_check_update() {
+	fn_script_log_info "Sending alert: Update available"
+	alertsubject="Alert - ${selfname} - Update available"
+	alertemoji="🎮"
+	alertsound="1"
+	alerturl="not enabled"
+	alertbody="${gamename} update available: ${remotebuildversion}"
+}
+
+fn_alert_permissions() {
+	fn_script_log_info "Sending alert: Permissions error"
+	alertsubject="Alert - ${selfname}: Permissions error"
+	alertemoji="❗"
+	alertsound="2"
+	alerturl="not enabled"
+	alertbody="${selfname} has permissions issues"
+}
+
+fn_alert_config() {
+	fn_script_log_info "Sending alert: New _default.cfg"
+	alertsubject="Alert - ${selfname} - New _default.cfg"
+	alertemoji="🎮"
+	alertsound="1"
+	alerturl="not enabled"
+	alertbody="${selfname} has received a new _default.cfg. Check file for changes."
+}
+
+if [ "${alert}" == "permissions" ]; then
+	fn_alert_permissions
+elif [ "${alert}" == "restart" ]; then
+	fn_alert_restart
+elif [ "${alert}" == "restartquery" ]; then
+	fn_alert_restart_query
+elif [ "${alert}" == "test" ]; then
+	fn_alert_test
+elif [ "${alert}" == "update" ]; then
+	fn_alert_update
+elif [ "${alert}" == "check-update" ]; then
+	fn_alert_check_update
+elif [ "${alert}" == "config" ]; then
+	fn_alert_config
+fi
+
+# Generate alert log.
+fn_alert_log
+
+# Generates the more info link.
+if [ "${postalert}" == "on" ] && [ -n "${postalert}" ]; then
+	exitbypass=1
+	command_postdetails.sh
+	fn_firstcommand_reset
+	unset exitbypass
+elif [ "${postalert}" != "on" ] && [ "${commandname}" == "TEST-ALERT" ]; then
+	fn_print_warn_nl "More Info not enabled"
+	fn_script_log_warn "More Info alerts not enabled"
+fi
+
+if [ "${discordalert}" == "on" ] && [ -n "${discordalert}" ]; then
+	alert_discord.sh
+elif [ "${discordalert}" != "on" ] && [ "${commandname}" == "TEST-ALERT" ]; then
+	fn_print_warn_nl "Discord alerts not enabled"
+	fn_script_log_warn "Discord alerts not enabled"
+elif [ -z "${discordtoken}" ] && [ "${commandname}" == "TEST-ALERT" ]; then
+	fn_print_error_nl "Discord token not set"
+	echo -e "* https://docs.linuxgsm.com/alerts/discord"
+	fn_script_error "Discord token not set"
+fi
+
+if [ "${emailalert}" == "on" ] && [ -n "${email}" ]; then
+	alert_email.sh
+elif [ "${emailalert}" != "on" ] && [ "${commandname}" == "TEST-ALERT" ]; then
+	fn_print_warn_nl "Email alerts not enabled"
+	fn_script_log_warn "Email alerts not enabled"
+elif [ -z "${email}" ] && [ "${commandname}" == "TEST-ALERT" ]; then
+	fn_print_error_nl "Email not set"
+	fn_script_log_error "Email not set"
+fi
+
+if [ "${gotifyalert}" == "on" ] && [ -n "${gotifyalert}" ]; then
+	alert_gotify.sh
+elif [ "${gotifyalert}" != "on" ] && [ "${commandname}" == "TEST-ALERT" ]; then
+	fn_print_warn_nl "Gotify alerts not enabled"
+	fn_script_log_warn "Gotify alerts not enabled"
+elif [ -z "${gotifytoken}" ] && [ "${commandname}" == "TEST-ALERT" ]; then
+	fn_print_error_nl "Gotify token not set"
+	echo -e "* https://docs.linuxgsm.com/alerts/gotify"
+	fn_script_error "Gotify token not set"
+elif [ -z "${gotifywebhook}" ] && [ "${commandname}" == "TEST-ALERT" ]; then
+	fn_print_error_nl "Gotify webhook not set"
+	echo -e "* https://docs.linuxgsm.com/alerts/gotify"
+	fn_script_error "Gotify webhook not set"
+fi
+
+if [ "${iftttalert}" == "on" ] && [ -n "${iftttalert}" ]; then
+	alert_ifttt.sh
+elif [ "${iftttalert}" != "on" ] && [ "${commandname}" == "TEST-ALERT" ]; then
+	fn_print_warn_nl "IFTTT alerts not enabled"
+	fn_script_log_warn "IFTTT alerts not enabled"
+elif [ -z "${ifttttoken}" ] && [ "${commandname}" == "TEST-ALERT" ]; then
+	fn_print_error_nl "IFTTT token not set"
+	echo -e "* https://docs.linuxgsm.com/alerts/ifttt"
+	fn_script_error "IFTTT token not set"
+fi
+
+if [ "${mailgunalert}" == "on" ] && [ -n "${mailgunalert}" ]; then
+	alert_mailgun.sh
+elif [ "${mailgunalert}" != "on" ] && [ "${commandname}" == "TEST-ALERT" ]; then
+	fn_print_warn_nl "Mailgun alerts not enabled"
+	fn_script_log_warn "Mailgun alerts not enabled"
+elif [ -z "${mailguntoken}" ] && [ "${commandname}" == "TEST-ALERT" ]; then
+	fn_print_error_nl "Mailgun token not set"
+	echo -e "* https://docs.linuxgsm.com/alerts/mailgun"
+	fn_script_error "Mailgun token not set"
+fi
+
+if [ "${pushbulletalert}" == "on" ] && [ -n "${pushbullettoken}" ]; then
+	alert_pushbullet.sh
+elif [ "${pushbulletalert}" != "on" ] && [ "${commandname}" == "TEST-ALERT" ]; then
+	fn_print_warn_nl "Pushbullet alerts not enabled"
+	fn_script_log_warn "Pushbullet alerts not enabled"
+elif [ -z "${pushbullettoken}" ] && [ "${commandname}" == "TEST-ALERT" ]; then
+	fn_print_error_nl "Pushbullet token not set"
+	echo -e "* https://docs.linuxgsm.com/alerts/pushbullet"
+	fn_script_error "Pushbullet token not set"
+fi
+
+if [ "${pushoveralert}" == "on" ] && [ -n "${pushoveralert}" ]; then
+	alert_pushover.sh
+elif [ "${pushoveralert}" != "on" ] && [ "${commandname}" == "TEST-ALERT" ]; then
+	fn_print_warn_nl "Pushover alerts not enabled"
+	fn_script_log_warn "Pushover alerts not enabled"
+elif [ -z "${pushovertoken}" ] && [ "${commandname}" == "TEST-ALERT" ]; then
+	fn_print_error_nl "Pushover token not set"
+	echo -e "* https://docs.linuxgsm.com/alerts/pushover"
+	fn_script_error "Pushover token not set"
+fi
+
+if [ "${telegramalert}" == "on" ] && [ -n "${telegramtoken}" ]; then
+	alert_telegram.sh
+elif [ "${telegramalert}" != "on" ] && [ "${commandname}" == "TEST-ALERT" ]; then
+	fn_print_warn_nl "Telegram Messages not enabled"
+	fn_script_log_warn "Telegram Messages not enabled"
+elif [ -z "${telegramtoken}" ] && [ "${commandname}" == "TEST-ALERT" ]; then
+	fn_print_error_nl "Telegram token not set."
+	echo -e "* https://docs.linuxgsm.com/alerts/telegram"
+	fn_script_error "Telegram token not set."
+elif [ -z "${telegramchatid}" ] && [ "${commandname}" == "TEST-ALERT" ]; then
+	fn_print_error_nl "Telegram chat id not set."
+	echo -e "* https://docs.linuxgsm.com/alerts/telegram"
+	fn_script_error "Telegram chat id not set."
+fi
+
+if [ "${rocketchatalert}" == "on" ] && [ -n "${rocketchatalert}" ]; then
+	alert_rocketchat.sh
+elif [ "${rocketchatalert}" != "on" ] && [ "${commandname}" == "TEST-ALERT" ]; then
+	fn_print_warn_nl "Rocketchat alerts not enabled"
+	fn_script_log_warn "Rocketchat alerts not enabled"
+elif [ -z "${rocketchattoken}" ] && [ "${commandname}" == "TEST-ALERT" ]; then
+	fn_print_error_nl "Rocketchat token not set"
+	#echo -e "* https://docs.linuxgsm.com/alerts/slack"
+	fn_script_error "Rocketchat token not set"
+fi
+
+if [ "${slackalert}" == "on" ] && [ -n "${slackalert}" ]; then
+	alert_slack.sh
+elif [ "${slackalert}" != "on" ] && [ "${commandname}" == "TEST-ALERT" ]; then
+	fn_print_warn_nl "Slack alerts not enabled"
+	fn_script_log_warn "Slack alerts not enabled"
+elif [ -z "${slacktoken}" ] && [ "${commandname}" == "TEST-ALERT" ]; then
+	fn_print_error_nl "Slack token not set"
+	echo -e "* https://docs.linuxgsm.com/alerts/slack"
+	fn_script_error "Slack token not set"
+fi

+ 59 - 0
lgsm/functions/alert_discord.sh

@@ -0,0 +1,59 @@
+#!/bin/bash
+# LinuxGSM alert_discord.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Sends Discord alert.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+json=$(
+	cat << EOF
+{
+	"username":"LinuxGSM",
+	"avatar_url":"https://raw.githubusercontent.com/${githubuser}/${githubrepo}/${githubbranch}/lgsm/data/alert_discord_logo.jpg",
+	"file":"content",
+	"embeds": [{
+		"color": "2067276",
+		"author": {
+			"name": "${alertemoji} ${alertsubject} ${alertemoji}",
+			"icon_url": "https://raw.githubusercontent.com/${githubuser}/${githubrepo}/${githubbranch}/lgsm/data/alert_discord_logo.jpg"
+		},
+		"title": "${servername}",
+		"description": "${alertbody} \n More info: ${alerturl}",
+		"url": "",
+		"type": "content",
+		"thumbnail": {},
+		"fields": [
+			{
+				"name": "Game",
+				"value": "${gamename}",
+				"inline": true
+			},
+			{
+				"name": "Server IP",
+				"value": "[${alertip}:${port}](https://www.gametracker.com/server_info/${alertip}:${port})",
+				"inline": true
+			},
+			{
+				"name": "Hostname",
+				"value": "${HOSTNAME}",
+				"inline": true
+			}
+		]
+	}]
+}
+EOF
+)
+
+fn_print_dots "Sending Discord alert"
+
+discordsend=$(curl --connect-timeout 10 -sSL -H "Content-Type: application/json" -X POST -d "$(echo -n "${json}" | jq -c .)" "${discordwebhook}")
+
+if [ -n "${discordsend}" ]; then
+	fn_print_fail_nl "Sending Discord alert: ${discordsend}"
+	fn_script_log_fatal "Sending Discord alert: ${discordsend}"
+else
+	fn_print_ok_nl "Sending Discord alert"
+	fn_script_log_pass "Sending Discord alert"
+fi

+ 25 - 0
lgsm/functions/alert_email.sh

@@ -0,0 +1,25 @@
+#!/bin/bash
+# LinuxGSM alert_email.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Sends email alert.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+fn_print_dots "Sending Email alert: ${email}"
+fn_sleep_time
+
+if [ -n "${emailfrom}" ]; then
+	mail -s "${alertsubject}" -r "${emailfrom}" "${email}" < "${alertlog}"
+else
+	mail -s "${alertsubject}" "${email}" < "${alertlog}"
+fi
+exitcode=$?
+if [ "${exitcode}" == "0" ]; then
+	fn_print_ok_nl "Sending Email alert: ${email}"
+	fn_script_log_pass "Sending Email alert: ${email}"
+else
+	fn_print_fail_nl "Sending Email alert: ${email}"
+	fn_script_log_fatal "Sending Email alert: ${email}"
+fi

+ 30 - 0
lgsm/functions/alert_gotify.sh

@@ -0,0 +1,30 @@
+#!/bin/bash
+# LinuxGSM alert_gotify.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Sends Gotify alert.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+json=$(
+	cat << EOF
+{
+	"title": "${alertemoji} ${alertsubject} ${alertemoji}",
+	"message": "Server name\n${servername}\n\nMessage\n${alertbody}\n\nGame\n${gamename}\n\nServer IP\n${alertip}:${port}\n\nHostname\n${HOSTNAME}\n\nMore info\n${alerturl}",
+	"priority": 5
+}
+EOF
+)
+
+fn_print_dots "Sending Gotify alert"
+
+gotifysend=$(curl --connect-timeout 10 -sSL "${gotifywebhook}/message"?token="${gotifytoken}" -H "Content-Type: application/json" -X POST -d "$(echo -n "${json}" | jq -c .)")
+
+if [ -n "${gotifysend}" ]; then
+	fn_print_ok_nl "Sending Gotify alert"
+	fn_script_log_pass "Sending Gotify alert"
+else
+	fn_print_fail_nl "Sending Gotify alert: ${gotifysend}"
+	fn_script_log_fatal "Sending Gotify alert: ${gotifysend}"
+fi

+ 29 - 0
lgsm/functions/alert_ifttt.sh

@@ -0,0 +1,29 @@
+#!/bin/bash
+# LinuxGSM alert_ifttt.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Sends IFTTT alert.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+json=$(
+	cat << EOF
+{
+	"value1": "${selfname}",
+	"value2": "${alertemoji} ${alertsubject} ${alertemoji}",
+	"value3": "Message: \n${alertbody}\n\nGame: \n${gamename}\n\nServer name: \n${servername}\n\nHostname: \n${HOSTNAME}\n\nServer IP: \n${alertip}:${port}\n\nMore info: \n${alerturl}"
+}
+EOF
+)
+
+fn_print_dots "Sending IFTTT alert"
+iftttsend=$(curl --connect-timeout 10 -sSL -H "Content-Type: application/json" -X POST -d "$(echo -n "${json}" | jq -c .)" "https://maker.ifttt.com/trigger/${iftttevent}/with/key/${ifttttoken}" | grep "Bad Request")
+
+if [ -n "${iftttsend}" ]; then
+	fn_print_fail_nl "Sending IFTTT alert: ${pushbulletsend}"
+	fn_script_log_fatal "Sending IFTTT alert: ${pushbulletsend}"
+else
+	fn_print_ok_nl "Sending IFTTT alert"
+	fn_script_log_pass "Sent IFTTT alert"
+fi

+ 32 - 0
lgsm/functions/alert_mailgun.sh

@@ -0,0 +1,32 @@
+#!/bin/bash
+# LinuxGSM alert_mailgun.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Sends Mailgun Email alert.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+if [ "${mailgunapiregion}" == "eu" ]; then
+	mailgunapiurl="https://api.eu.mailgun.net"
+else
+	mailgunapiurl="https://api.mailgun.net"
+fi
+
+fn_print_dots "Sending Email alert: Mailgun: ${mailgunemail}"
+
+mailgunsend=$(curl --connect-timeout 10 -s --user "api:${mailguntoken}" \
+	-F from="LinuxGSM <${mailgunemailfrom}>" \
+	-F to="LinuxGSM Admin <${mailgunemail}>" \
+	-F subject="${alertemoji} ${alertsubject} ${alertemoji}" \
+	-F o:tag='alert' \
+	-F o:tag='LinuxGSM' \
+	-F text="$(cat "${alertlog}")" "${mailgunapiurl}/v3/${mailgundomain}/messages")
+
+if [ -z "${mailgunsend}" ]; then
+	fn_print_fail_nl "Sending Email alert: Mailgun: ${mailgunemail}"
+	fn_script_log_fatal "Sending Email alert: Mailgun: ${mailgunemail}"
+else
+	fn_print_ok_nl "Sending Email alert: Mailgun: ${mailgunemail}"
+	fn_script_log_pass "Sending Email alert: Mailgun: ${mailgunemail}"
+fi

+ 30 - 0
lgsm/functions/alert_pushbullet.sh

@@ -0,0 +1,30 @@
+#!/bin/bash
+# LinuxGSM alert_pushbullet.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Sends Pushbullet Messenger alert.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+json=$(
+	cat << EOF
+{
+	"channel_tag": "${channeltag}",
+	"type": "note",
+	"title": "${alertemoji} ${alertsubject} ${alertemoji}",
+	"body": "Server name\n${servername}\n\nMessage\n${alertbody}\n\nGame\n${gamename}\n\nServer IP\n${alertip}:${port}\n\nHostname\n${HOSTNAME}\n\nMore info\n${alerturl}"
+}
+EOF
+)
+
+fn_print_dots "Sending Pushbullet alert"
+pushbulletsend=$(curl --connect-timeout 10 -sSL -u """${pushbullettoken}"":" -H "Content-Type: application/json" -X POST -d "$(echo -n "${json}" | jq -c .)" "https://api.pushbullet.com/v2/pushes" | grep "error_code")
+
+if [ -n "${pushbulletsend}" ]; then
+	fn_print_fail_nl "Sending Pushbullet alert: ${pushbulletsend}"
+	fn_script_log_fatal "Sending Pushbullet alert: ${pushbulletsend}"
+else
+	fn_print_ok_nl "Sending Pushbullet alert"
+	fn_script_log_pass "Sent Pushbullet alert"
+fi

+ 33 - 0
lgsm/functions/alert_pushover.sh

@@ -0,0 +1,33 @@
+#!/bin/bash
+# LinuxGSM alert_pushover.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Sends Pushover alert.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+fn_print_dots "Sending Pushover alert"
+
+# Different alerts are given different priorities and notification sounds.
+if [ "${alertsound}" == "1" ]; then
+	alertsound=""
+	alertpriority="0"
+elif [ "${alertsound}" == "2" ]; then
+	# restarted.
+	alertsound="siren"
+	alertpriority="1"
+else
+	alertsound=""
+	alertpriority="0"
+fi
+
+pushoversend=$(curl --connect-timeout 10 -sS -F token="${pushovertoken}" -F user="${pushoveruserkey}" -F html="1" -F sound="${alertsound}" -F priority="${alertpriority}" -F title="${alertemoji} ${alertsubject} ${alertemoji}" -F message=" <b>Server name</b><br>${servername}<br><br><b>Message</b><br>${alertbody}<br><br><b>Game</b><br>${gamename}<br><br><b>Server IP</b><br><a href='https://www.gametracker.com/server_info/${alertip}:${port}'>${alertip}:${port}</a><br><br><b>Hostname</b><br>${HOSTNAME}<br><br><b>More info</b><br><a href='${alerturl}'>${alerturl}</a>" "https://api.pushover.net/1/messages.json" | grep errors)
+
+if [ -n "${pushoversend}" ]; then
+	fn_print_fail_nl "Sending Pushover alert: ${pushoversend}"
+	fn_script_log_fatal "Sending Pushover alert: ${pushoversend}"
+else
+	fn_print_ok_nl "Sending Pushover alert"
+	fn_script_log_pass "Sent Pushover alert"
+fi

+ 50 - 0
lgsm/functions/alert_rocketchat.sh

@@ -0,0 +1,50 @@
+#!/bin/bash
+# LinuxGSM alert_rocketchat.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Sends Rocketchat alert.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+json=$(
+	cat << EOF
+{
+	"alias": "LinuxGSM",
+	"text": "*${alertemoji} ${alertsubject} ${alertemoji}* \n *${servername}* \n ${alertbody} \n More info: ${alerturl}",
+	"attachments": [
+		{
+			"fields": [
+				{
+					"short": true,
+					"title": "Game:",
+					"value": "${gamename}"
+				},
+				{
+					"short": true,
+					"title": "Server IP:",
+					"value": "${alertip}:${port}"
+				},
+				{
+					"short": true,
+					"title": "Hostname:",
+					"value": "${HOSTNAME}"
+				}
+			]
+		}
+	]
+}
+EOF
+)
+
+fn_print_dots "Sending Rocketchat alert"
+
+rocketchatsend=$(curl --connect-timeout 10 -sSL -H "Content-Type: application/json" -X POST -d "$(echo -n "${json}" | jq -c .)" "${rocketchatwebhook}")
+
+if [ -n "${rocketchatsend}" ]; then
+	fn_print_ok_nl "Sending Rocketchat alert"
+	fn_script_log_pass "Sending Rocketchat alert"
+else
+	fn_print_fail_nl "Sending Rocketchat alert: ${rocketchatsend}"
+	fn_script_log_fatal "Sending Rocketchat alert: ${rocketchatsend}"
+fi

+ 75 - 0
lgsm/functions/alert_slack.sh

@@ -0,0 +1,75 @@
+#!/bin/bash
+# LinuxGSM alert_slack.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Sends Slack alert.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+json=$(
+	cat << EOF
+{
+		"attachments": [
+			{
+				"color": "#36a64f",
+				"blocks": [
+					{
+										"type": "section",
+										"text": {
+												"type": "mrkdwn",
+												"text": "*LinuxGSM Alert*"
+										}
+								},
+								{
+										"type": "section",
+										"text": {
+												"type": "mrkdwn",
+												"text": "*${alertemoji} ${alertsubject}* \n ${alertbody}"
+										}
+								},
+								{
+										"type": "divider"
+								},
+								{
+										"type": "section",
+										"fields": [
+												{
+														"type": "mrkdwn",
+														"text": "*Game:* \n ${gamename}"
+												},
+												{
+														"type": "mrkdwn",
+														"text": "*Server IP:* \n ${alertip}:${port}"
+												},
+												{
+														"type": "mrkdwn",
+														"text": "*Server Name:* \n ${servername}"
+												}
+										]
+								},
+					 {
+										"type": "section",
+										"text": {
+														"type": "mrkdwn",
+														"text": "Hostname: ${HOSTNAME} / More info: ${alerturl}"
+										}
+								}
+						]
+			}
+		]
+}
+EOF
+)
+
+fn_print_dots "Sending Slack alert"
+
+slacksend=$(curl --connect-timeout 10 -sSL -H "Content-Type: application/json" -X POST -d "$(echo -n "${json}" | jq -c .)" "${slackwebhook}")
+
+if [ "${slacksend}" == "ok" ]; then
+	fn_print_ok_nl "Sending Slack alert"
+	fn_script_log_pass "Sending Slack alert"
+else
+	fn_print_fail_nl "Sending Slack alert: ${slacksend}"
+	fn_script_log_fatal "Sending Slack alert: ${slacksend}"
+fi

+ 30 - 0
lgsm/functions/alert_telegram.sh

@@ -0,0 +1,30 @@
+#!/bin/bash
+# LinuxGSM alert_telegram.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Sends Telegram Messenger alert.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+json=$(
+	cat << EOF
+{
+	"chat_id": "${telegramchatid}",
+	"parse_mode": "HTML",
+	"text": "<b>${alertemoji} ${alertsubject} ${alertemoji}</b>\n\n<b>Server name</b>\n${servername}\n\n<b>Message</b>\n${alertbody}\n\n<b>Game</b>\n${gamename}\n\n<b>Server IP</b>\n<a href='https://www.gametracker.com/server_info/${alertip}:${port}'>${alertip}:${port}</a>\n\n<b>Hostname</b>\n${HOSTNAME}\n\n<b>More info</b>\n<a href='${alerturl}'>${alerturl}</a>",
+	"disable_web_page_preview": "yes"
+}
+EOF
+)
+
+fn_print_dots "Sending Telegram alert"
+telegramsend=$(curl --connect-timeout 10 -sSL -H "Content-Type: application/json" -X POST -d "$(echo -n "${json}" | jq -c .)" ${curlcustomstring} "https://${telegramapi}/bot${telegramtoken}/sendMessage" | grep "error_code")
+
+if [ -n "${telegramsend}" ]; then
+	fn_print_fail_nl "Sending Telegram alert: ${telegramsend}"
+	fn_script_log_fatal "Sending Telegram alert: ${telegramsend}"
+else
+	fn_print_ok_nl "Sending Telegram alert"
+	fn_script_log_pass "Sent Telegram alert"
+fi

+ 101 - 0
lgsm/functions/check.sh

@@ -0,0 +1,101 @@
+#!/bin/bash
+# LinuxGSM check.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Overall function for managing checks.
+# Runs checks that will either halt on or fix an issue.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+# Every command that requires checks just references check.sh.
+# check.sh selects which checks to run by using arrays.
+
+if [ "${commandname}" != "INSTALL" ]; then
+	check_root.sh
+fi
+
+if [ "${commandname}" != "UPDATE-LGSM" ]; then
+	check_version.sh
+fi
+
+check_tmuxception.sh
+
+if [ "$(whoami)" != "root" ] || [ -f /.dockerenv ]; then
+	if [ "${commandname}" != "MONITOR" ]; then
+		check_permissions.sh
+	fi
+fi
+
+if [ "${commandname}" != "INSTALL" ] && [ "${commandname}" != "UPDATE-LGSM" ] && [ "${commandname}" != "DETAILS" ] && [ "${commandname}" != "POST-DETAILS" ]; then
+	check_system_dir.sh
+fi
+
+allowed_commands_array=(START DEBUG)
+for allowed_command in "${allowed_commands_array[@]}"; do
+	if [ "${allowed_command}" == "${commandname}" ]; then
+		check_executable.sh
+	fi
+done
+
+if [ "$(whoami)" != "root" ] || [ -f /.dockerenv ]; then
+	allowed_commands_array=(DEBUG START INSTALL)
+	for allowed_command in "${allowed_commands_array[@]}"; do
+		if [ "${allowed_command}" == "${commandname}" ]; then
+			check_glibc.sh
+		fi
+	done
+fi
+
+allowed_commands_array=(BACKUP CONSOLE DEBUG DETAILS MAP-COMPRESSOR FASTDL MODS-INSTALL MODS-REMOVE MODS-UPDATE MONITOR POST-DETAILS RESTART START STOP TEST-ALERT CHANGE-PASSWORD UPDATE UPDATE-LGSM VALIDATE WIPE)
+for allowed_command in "${allowed_commands_array[@]}"; do
+	if [ "${allowed_command}" == "${commandname}" ]; then
+		check_logs.sh
+	fi
+done
+
+allowed_commands_array=(DEBUG START)
+for allowed_command in "${allowed_commands_array[@]}"; do
+	if [ "${allowed_command}" == "${commandname}" ]; then
+		check_deps.sh
+	fi
+done
+
+allowed_commands_array=(CONSOLE DEBUG MONITOR START STOP)
+for allowed_command in "${allowed_commands_array[@]}"; do
+	if [ "${allowed_command}" == "${commandname}" ]; then
+		check_config.sh
+	fi
+done
+
+allowed_commands_array=(DEBUG DETAILS DEV-QUERY-RAW MONITOR POST_DETAILS START STOP POST-DETAILS)
+for allowed_command in "${allowed_commands_array[@]}"; do
+	if [ "${allowed_command}" == "${commandname}" ]; then
+		if [ -z "${installflag}" ]; then
+			check_ip.sh
+		fi
+	fi
+done
+
+allowed_commands_array=(DEBUG START UPDATE VALIDATE CHECK-UPDATE)
+for allowed_command in "${allowed_commands_array[@]}"; do
+	if [ "${allowed_command}" == "${commandname}" ]; then
+		if [ "${appid}" ]; then
+			check_steamcmd.sh
+		fi
+	fi
+done
+
+allowed_commands_array=(CHANGE-PASSWORD DETAILS MONITOR START STOP UPDATE VALIDATE POST-DETAILS)
+for allowed_command in "${allowed_commands_array[@]}"; do
+	if [ "${allowed_command}" == "${commandname}" ]; then
+		check_status.sh
+	fi
+done
+
+allowed_commands_array=(DEBUG START INSTALL)
+for allowed_command in "${allowed_commands_array[@]}"; do
+	if [ "${allowed_command}" == "${commandname}" ]; then
+		check_system_requirements.sh
+	fi
+done

+ 35 - 0
lgsm/functions/check_config.sh

@@ -0,0 +1,35 @@
+#!/bin/bash
+# LinuxGSM check_config.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Checks if the server config is missing and warns the user if needed.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+if [ -n "${servercfgfullpath}" ] && [ ! -f "${servercfgfullpath}" ]; then
+	fn_print_dots ""
+	fn_print_warn_nl "Configuration file missing!"
+	echo -e "${servercfgfullpath}"
+	fn_script_log_warn "Configuration file missing!"
+	fn_script_log_warn "${servercfgfullpath}"
+	install_config.sh
+fi
+
+if [ "${shortname}" == "rust" ] && [ -v rconpassword ] && [ -z "${rconpassword}" ]; then
+	fn_print_dots ""
+	fn_print_fail_nl "RCON password is not set"
+	fn_script_log_warn "RCON password is not set"
+elif [ -v rconpassword ] && [ "${rconpassword}" == "CHANGE_ME" ]; then
+	fn_print_dots ""
+	fn_print_warn_nl "Default RCON Password detected"
+	fn_script_log_warn "Default RCON Password detected"
+fi
+
+if [ "${shortname}" == "vh" ] && [ -z "${serverpassword}" ]; then
+	fn_print_fail_nl "serverpassword is not set"
+	fn_script_log_fatal "serverpassword is not set"
+elif [ "${shortname}" == "vh" ] && [ "${#serverpassword}" -le "4" ]; then
+	fn_print_fail_nl "serverpassword is to short (min 5 chars)"
+	fn_script_log_fatal "serverpassword is to short (min 5 chars)"
+fi

+ 363 - 0
lgsm/functions/check_deps.sh

@@ -0,0 +1,363 @@
+#!/bin/bash
+# LinuxGSM check_deps.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Checks and installs missing dependencies.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+fn_install_mono_repo() {
+	if [ "${autodepinstall}" == "0" ]; then
+		fn_print_information_nl "Automatically adding Mono repository."
+		fn_script_log_info "Automatically adding Mono repository."
+		echo -en ".\r"
+		sleep 1
+		echo -en "..\r"
+		sleep 1
+		echo -en "...\r"
+		sleep 1
+		echo -en "   \r"
+		if [ "${distroid}" == "ubuntu" ]; then
+			if [ "${distroversion}" == "20.04" ]; then
+				cmd="sudo apt install gnupg ca-certificates;sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF;echo 'deb https://download.mono-project.com/repo/ubuntu stable-focal main' | sudo tee /etc/apt/sources.list.d/mono-official-stable.list;sudo apt update"
+			elif [ "${distroversion}" == "18.04" ]; then
+				cmd="sudo apt install gnupg ca-certificates;sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF;echo 'deb https://download.mono-project.com/repo/ubuntu stable-bionic main' | sudo tee /etc/apt/sources.list.d/mono-official-stable.list;sudo apt update"
+			elif [ "${distroversion}" == "16.04" ]; then
+				cmd="sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF;sudo apt install apt-transport-https ca-certificates;echo 'deb https://download.mono-project.com/repo/ubuntu stable-xenial main' | sudo tee /etc/apt/sources.list.d/mono-official-stable.list;sudo apt update"
+			else
+				monoautoinstall="1"
+			fi
+		elif [ "${distroid}" == "debian" ]; then
+			if [ "${distroversion}" == "10" ]; then
+				cmd="sudo apt install apt-transport-https dirmngr gnupg ca-certificates;sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF;echo 'deb https://download.mono-project.com/repo/debian stable-buster main' | sudo tee /etc/apt/sources.list.d/mono-official-stable.list;sudo apt update"
+			elif [ "${distroversion}" == "9" ]; then
+				cmd="sudo apt install apt-transport-https dirmngr gnupg ca-certificates;sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF;echo 'deb https://download.mono-project.com/repo/debian stable-stretch main' | sudo tee /etc/apt/sources.list.d/mono-official-stable.list;sudo apt update"
+			else
+				monoautoinstall="1"
+			fi
+		elif [ "${distroid}" == "centos" ] || [ "${distroid}" == "almalinux" ] || [ "${distroid}" == "rocky" ]; then
+			if [ "${distroversion}" == "8" ]; then
+				cmd="sudo rpmkeys --import 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF';su -c 'curl https://download.mono-project.com/repo/centos8-stable.repo | tee /etc/yum.repos.d/mono-centos8-stable.repo'"
+			elif [ "${distroversion}" == "7" ]; then
+				cmd="sudo rpmkeys --import 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF';su -c 'curl https://download.mono-project.com/repo/centos7-stable.repo | tee /etc/yum.repos.d/mono-centos7-stable.repo'"
+			else
+				monoautoinstall="1"
+			fi
+		elif [ "${distroid}" == "fedora" ]; then
+			if [ "${distroversion}" -ge "29" ]; then
+				cmd="sudo rpm --import 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF';su -c 'curl https://download.mono-project.com/repo/centos8-stable.repo | tee /etc/yum.repos.d/mono-centos8-stable.repo';dnf update"
+			else
+				cmd="sudo rpm --import 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF';su -c 'curl https://download.mono-project.com/repo/centos7-stable.repo | tee /etc/yum.repos.d/mono-centos7-stable.repo';dnf update"
+			fi
+		else
+			monoautoinstall="1"
+		fi
+
+		# Run the mono repo install.
+		eval "${cmd}"
+
+		# Did Mono repo install correctly?
+		if [ "${monoautoinstall}" != "1" ]; then
+			if [ $? != 0 ]; then
+				fn_print_failure_nl "Unable to install Mono repository."
+				fn_script_log_fatal "Unable to install Mono repository."
+			else
+				fn_print_complete_nl "Installing Mono repository completed."
+				fn_script_log_pass "Installing Mono repository completed."
+			fi
+		fi
+
+		# Mono can not be auto installed with this distro.
+		if [ "${monoautoinstall}" == "1" ]; then
+			fn_print_warning_nl "Mono auto install not available for ${distroname}."
+			echo -e "Follow instructions on Mono website to install."
+			echo -e "https://www.mono-project.com/download/stable/#download-lin"
+			fn_script_log_warn "Unable to install Mono repository. Mono auto install not available for ${distroname}."
+		fi
+
+	else
+		fn_print_information_nl "Installing Mono repository."
+		fn_print_warning_nl "$(whoami) does not have sudo access."
+		echo -e "Follow instructions on Mono website to install."
+		echo -e "https://www.mono-project.com/download/stable/#download-lin"
+		fn_script_log_warn "Unable to install Mono repository. $(whoami) does not have sudo access."
+	fi
+}
+
+fn_deps_email() {
+	# Adds postfix to required dependencies if email alert is enabled.
+	if [ "${emailalert}" == "on" ]; then
+		if [ -f /usr/bin/mailx ]; then
+			if [ -d /etc/exim4 ]; then
+				array_deps_required+=(exim4)
+			elif [ -d /etc/sendmail ]; then
+				array_deps_required+=(sendmail)
+			elif [ "$(command -v dpkg-query 2> /dev/null)" ]; then
+				array_deps_required+=(mailutils postfix)
+			elif [ "$(command -v rpm 2> /dev/null)" ]; then
+				array_deps_required+=(mailx postfix)
+			fi
+		else
+			if [ "$(command -v dpkg-query 2> /dev/null)" ]; then
+				array_deps_required+=(mailutils postfix)
+			elif [ "$(command -v rpm 2> /dev/null)" ]; then
+				array_deps_required+=(mailx postfix)
+			fi
+		fi
+	fi
+}
+
+fn_install_missing_deps() {
+	# If any dependencies are not installed.
+	if [ "${#array_deps_missing[*]}" != "0" ]; then
+		if [ "${commandname}" == "INSTALL" ]; then
+			fn_print_warning_nl "Missing dependencies: ${red}${array_deps_missing[*]}${default}"
+			fn_script_log_warn "Missing dependencies: ${array_deps_missing[*]}"
+		else
+			fn_print_dots "Missing dependencies"
+			fn_print_warn "Missing dependencies: ${red}${array_deps_missing[*]}${default}"
+			fn_script_log_warn "Missing dependencies: ${array_deps_missing[*]}"
+		fi
+		fn_sleep_time
+
+		# Attempt automatic dependency installation
+		if [ "${autoinstall}" == "1" ]; then
+			sudo -n true > /dev/null 2>&1
+		else
+			sudo -v > /dev/null 2>&1
+		fi
+		autodepinstall="$?"
+
+		if [ "${monostatus}" == "1" ]; then
+			fn_install_mono_repo
+		fi
+
+		if [ "${commandname}" == "INSTALL" ]; then
+			if [ "${autodepinstall}" == "0" ]; then
+				fn_print_information_nl "$(whoami) has sudo access."
+				fn_script_log_info "$(whoami) has sudo access."
+			else
+				fn_print_warning_nl "$(whoami) does not have sudo access. Manually install dependencies."
+				fn_script_log_warn "$(whoami) does not have sudo access. Manually install dependencies."
+			fi
+		fi
+
+		# Add sudo dpkg --add-architecture i386 if using i386 packages.
+		if [ "$(command -v dpkg-query 2> /dev/null)" ]; then
+			if printf '%s\n' "${array_deps_required[@]}" | grep -q -P 'i386'; then
+				i386installcommand="sudo dpkg --add-architecture i386; "
+			fi
+		fi
+
+		# If automatic dependency install is available
+		if [ "${autodepinstall}" == "0" ]; then
+			fn_print_information_nl "Automatically installing missing dependencies."
+			fn_script_log_info "Automatically installing missing dependencies."
+			echo -en ".\r"
+			sleep 1
+			echo -en "..\r"
+			sleep 1
+			echo -en "...\r"
+			sleep 1
+			echo -en "   \r"
+			if [ "$(command -v dpkg-query 2> /dev/null)" ]; then
+				cmd="echo steamcmd steam/question select \"I AGREE\" | sudo debconf-set-selections; echo steamcmd steam/license note '' | sudo debconf-set-selections; ${i386installcommand}sudo apt-get update; sudo apt-get -y install ${array_deps_missing[*]}"
+				eval "${cmd}"
+			elif [ "$(command -v dnf 2> /dev/null)" ]; then
+				cmd="sudo dnf -y install ${array_deps_missing[*]}"
+				eval "${cmd}"
+			elif [ "$(command -v yum 2> /dev/null)" ]; then
+				cmd="sudo yum -y install ${array_deps_missing[*]}"
+				eval "${cmd}"
+			fi
+			autodepinstall="$?"
+
+			# If auto install passes remove steamcmd install failure.
+			if [ "${autodepinstall}" == "0" ]; then
+				unset steamcmdfail
+			fi
+		fi
+
+		# If automatic dependency install is unavailable.
+		if [ "${autodepinstall}" != "0" ]; then
+			if [ "$(command -v dpkg-query 2> /dev/null)" ]; then
+				echo -e "${i386installcommand}sudo apt update; sudo apt install ${array_deps_missing[*]}"
+			elif [ "$(command -v dnf 2> /dev/null)" ]; then
+				echo -e "sudo dnf install ${array_deps_missing[*]}"
+			elif [ "$(command -v yum 2> /dev/null)" ]; then
+				echo -e "sudo yum install ${array_deps_missing[*]}"
+			fi
+		fi
+
+		if [ "${steamcmdfail}" ]; then
+			if [ "${commandname}" == "INSTALL" ]; then
+				fn_print_failure_nl "Missing dependencies required to run SteamCMD."
+				fn_script_log_fatal "Missing dependencies required to run SteamCMD."
+				core_exit.sh
+			else
+				fn_print_error_nl "Missing dependencies required to run SteamCMD."
+				fn_script_log_error "Missing dependencies required to run SteamCMD."
+			fi
+		fi
+
+	else
+		if [ "${commandname}" == "INSTALL" ]; then
+			fn_print_information_nl "Required dependencies already installed."
+			fn_script_log_info "Required dependencies already installed."
+		fi
+	fi
+}
+
+fn_check_loop() {
+	# Loop though required depenencies checking if they are installed.
+	for deptocheck in ${array_deps_required[*]}; do
+		fn_deps_detector
+	done
+
+	# user will be informed of any missing dependencies.
+	fn_install_missing_deps
+}
+
+# Checks if dependency is installed or not.
+fn_deps_detector() {
+	## Check.
+	# SteamCMD: Will be removed from required array if no appid is present or non-free repo is not available.
+	# This will cause SteamCMD to be installed using tar.
+	if [ "${deptocheck}" == "libsdl2-2.0-0:i386" ] && [ -z "${appid}" ]; then
+		array_deps_required=("${array_deps_required[@]/libsdl2-2.0-0:i386/}")
+		steamcmdstatus=1
+	elif [ "${deptocheck}" == "steamcmd" ] && [ -z "${appid}" ]; then
+		array_deps_required=("${array_deps_required[@]/steamcmd/}")
+		steamcmdstatus=1
+	elif [ "${deptocheck}" == "steamcmd" ] && [ "${distroid}" == "debian" ] && ! grep -qE "^deb .*non-free" /etc/apt/sources.list; then
+		array_deps_required=("${array_deps_required[@]/steamcmd/}")
+		steamcmdstatus=1
+	# Java: Added for users using Oracle JRE to bypass check.
+	elif [[ ${deptocheck} == "openjdk"* ]] || [[ ${deptocheck} == "java"* ]]; then
+		# Is java already installed?
+		if [ -n "${javaversion}" ]; then
+			# Added for users using Oracle JRE to bypass check.
+			depstatus=0
+			deptocheck="${javaversion}"
+		else
+			depstatus=1
+		fi
+	# Mono: A Mono repo needs to be installed.
+	elif [ "${deptocheck}" == "mono-complete" ]; then
+		if [ -n "${monoversion}" ] && [ "${monoversion}" -ge "5" ]; then
+			# Mono >= 5.0.0 already installed.
+			depstatus=0
+			monostatus=0
+		else
+			# Mono not installed or installed Mono < 5.0.0.
+			depstatus=1
+			monostatus=1
+		fi
+	elif [ "$(command -v dpkg-query 2> /dev/null)" ]; then
+		dpkg-query -W -f='${Status}' "${deptocheck}" 2> /dev/null | grep -q -P '^install ok installed'
+		depstatus=$?
+	elif [ "$(command -v dnf 2> /dev/null)" ]; then
+		dnf list installed "${deptocheck}" > /dev/null 2>&1
+		depstatus=$?
+	elif [ "$(command -v rpm 2> /dev/null)" ]; then
+		rpm -q "${deptocheck}" > /dev/null 2>&1
+		depstatus=$?
+	fi
+
+	# Outcome of Check.
+	if [ "${steamcmdstatus}" == "1" ]; then
+		# If SteamCMD is not available in repo dont check for it.
+		unset steamcmdstatus
+	elif [ "${depstatus}" == "0" ]; then
+		# If dependency is found.
+		missingdep=0
+		if [ "${commandname}" == "INSTALL" ]; then
+			echo -e "${green}${deptocheck}${default}"
+			sleep 0.1
+		fi
+	elif [ "${depstatus}" != "0" ]; then
+		# If dependency is not found.
+		missingdep=1
+		if [ "${commandname}" == "INSTALL" ]; then
+			echo -e "${red}${deptocheck}${default}"
+			sleep 0.1
+		fi
+		# If SteamCMD requirements are not met install will fail.
+		if [ -n "${appid}" ]; then
+			for steamcmddeptocheck in ${array_deps_required_steamcmd[*]}; do
+				if [ "${deptocheck}" != "steamcmd" ] && [ "${deptocheck}" == "${steamcmddeptocheck}" ]; then
+					steamcmdfail=1
+				fi
+			done
+		fi
+	fi
+	unset depstatus
+
+	# Missing dependencies are added to array_deps_missing.
+	if [ "${missingdep}" == "1" ]; then
+		array_deps_missing+=("${deptocheck}")
+	fi
+}
+
+if [ "${commandname}" == "INSTALL" ]; then
+	if [ "$(whoami)" == "root" ] && [ ! -f /.dockerenv ]; then
+		echo -e ""
+		echo -e "${lightyellow}Checking Dependencies as root${default}"
+		echo -e "================================="
+		fn_print_information_nl "Checking any missing dependencies for ${gamename} server only."
+		fn_print_information_nl "This will NOT install a ${gamename} server."
+		fn_sleep_time
+	else
+		echo -e ""
+		echo -e "${lightyellow}Checking Dependencies${default}"
+		echo -e "================================="
+	fi
+fi
+
+# Will warn user if their distro is no longer supported by the vendor.
+if [ -n "${distrosupport}" ]; then
+	if [ "${distrosupport}" == "unsupported" ]; then
+		fn_print_warning_nl "${distroname} is no longer supported by the vendor. Upgrading is recommended."
+		fn_script_log_warn "${distroname} is no longer supported by the vendor. Upgrading is recommended."
+	fi
+fi
+
+info_distro.sh
+
+if [ ! -f "${tmpdir}/dependency-no-check.tmp" ] && [ ! -f "${datadir}/${distroid}-${distroversioncsv}.csv" ]; then
+	# Check that the distro dependency csv file exists.
+	fn_check_file_github "lgsm/data" "${distroid}-${distroversioncsv}.csv"
+	if [ -n "${checkflag}" ] && [ "${checkflag}" == "0" ]; then
+		fn_fetch_file_github "lgsm/data" "${distroid}-${distroversioncsv}.csv" "lgsm/data" "chmodx" "norun" "noforce" "nohash"
+	fi
+fi
+
+# If the file successfully downloaded run the dependency check.
+if [ -f "${datadir}/${distroid}-${distroversioncsv}.csv" ]; then
+	depall=$(awk -F, '$1=="all" {$1=""; print $0}' "${datadir}/${distroid}-${distroversioncsv}.csv")
+	depsteamcmd=$(awk -F, '$1=="steamcmd" {$1=""; print $0}' "${datadir}/${distroid}-${distroversioncsv}.csv")
+	depshortname=$(awk -v shortname="${shortname}" -F, '$1==shortname {$1=""; print $0}' "${datadir}/${distroid}-${distroversioncsv}.csv")
+
+	# Generate array of missing deps.
+	array_deps_missing=()
+
+	array_deps_required=("${depall} ${depsteamcmd} ${depshortname}")
+	array_deps_required_steamcmd=("${depsteamcmd}")
+	fn_deps_email
+	# Unique sort dependency array.
+	IFS=" " read -r -a array_deps_required <<< "$(echo "${array_deps_required[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' ')"
+
+	fn_check_loop
+# Warn the user that dependency checking is unavailable for their distro.
+elif [ "${commandname}" == "INSTALL" ] || [ -n "${checkflag}" ] && [ "${checkflag}" != "0" ]; then
+	fn_print_warning_nl "LinuxGSM dependency checking currently unavailable for ${distroname}."
+	# Prevent future dependency checking if unavailable for the distro.
+	echo "${version}" > "${tmpdir}/dependency-no-check.tmp"
+elif [ -f "${tmpdir}/dependency-no-check.tmp" ]; then
+	# Allow LinuxGSM to try a dependency check if LinuxGSM has been recently updated.
+	nocheckversion=$(cat "${tmpdir}/dependency-no-check.tmp")
+	if [ "${version}" != "${nocheckversion}" ]; then
+		rm -f "${tmpdir:?}/dependency-no-check.tmp"
+	fi
+fi

+ 20 - 0
lgsm/functions/check_executable.sh

@@ -0,0 +1,20 @@
+#!/bin/bash
+# LinuxGSM check_executable.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Checks if server executable exists.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+# Check if executable exists
+execname=$(basename "${executable}")
+if [ ! -f "${executabledir}/${execname}" ]; then
+	fn_print_fail_nl "executable was not found"
+	echo -e "* ${executabledir}/${execname}"
+	if [ -d "${lgsmlogdir}" ]; then
+		fn_script_log_fatal "Executable was not found: ${executabledir}/${execname}"
+	fi
+	unset exitbypass
+	core_exit.sh
+fi

+ 29 - 0
lgsm/functions/check_glibc.sh

@@ -0,0 +1,29 @@
+#!/bin/bash
+# LinuxGSM check_glibc.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Checks if the server has the correct Glibc version.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+info_distro.sh
+
+if [ "${glibc}" == "null" ]; then
+	# Glibc is not required.
+	:
+elif [ -z "${glibc}" ]; then
+	fn_print_dots "glibc"
+	fn_print_error_nl "glibc requirement unknown"
+	fn_script_log_error "glibc requirement unknown"
+elif [ "$(printf '%s\n'${glibc}'\n' "${glibcversion}" | sort -V | head -n 1)" != "${glibc}" ]; then
+	fn_print_dots "glibc"
+	fn_print_error_nl "glibc requirements not met"
+	fn_script_log_error "glibc requirements not met"
+	echo -en "\n"
+	echo -e "	* glibc required: ${glibc}"
+	echo -e "	* glibc installed: ${red}${glibcversion}${default}"
+	echo -en "\n"
+	fn_print_information_nl "distro upgrade is required"
+	fn_script_log_info "distro upgrade is required"
+fi

+ 63 - 0
lgsm/functions/check_ip.sh

@@ -0,0 +1,63 @@
+#!/bin/bash
+# LinuxGSM check_ip.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Automatically identifies the server interface IP.
+# If multiple interfaces are detected the user will need to manually set using ip="0.0.0.0".
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+info_game.sh
+
+ip_commands_array=("/bin/ip" "/usr/sbin/ip" "ip")
+for ip_command in "${ip_commands_array[@]}"; do
+	if [ "$(command -v "${ip_command}" 2> /dev/null)" ]; then
+		ipcommand="${ip_command}"
+		break
+	fi
+done
+
+ethtool_commands_array=("/bin/ethtool" "/usr/sbin/ethtool" "ethtool")
+for ethtool_command in "${ethtool_commands_array[@]}"; do
+	if [ "$(command -v "${ethtool_command}" 2> /dev/null)" ]; then
+		ethtoolcommand="${ethtool_command}"
+		break
+	fi
+done
+
+mapfile -t current_ips < <(${ipcommand} -o -4 addr | awk '{print $4}' | grep -oe '\([0-9]\{1,3\}\.\?\)\{4\}' | sort -u | grep -v 127.0.0)
+
+function fn_is_valid_ip() {
+	local ip="${1}"
+	# excluding 0.* ips also
+	grep -qEe '^[1-9]+[0-9]*\.[0-9]+\.[0-9]+\.[0-9]+$' <<< "${ip}"
+}
+
+# Check if server has multiple IP addresses
+
+# If the IP variable has been set by user.
+if fn_is_valid_ip "${ip}"; then
+	queryips=("${ip}")
+	webadminip=("${ip}")
+	telnetip=("${ip}")
+# If game config does have an IP set.
+elif fn_is_valid_ip "${configip}"; then
+	queryips=("${configip}")
+	ip="${configip}"
+	webadminip=("${configip}")
+	telnetip=("${configip}")
+# If there is only 1 server IP address.
+# Some IP details can automaticly use the one IP
+elif [ "${#current_ips[@]}" == "1" ]; then
+	queryips=("127.0.0.1" "${current_ips[@]}")
+	ip="0.0.0.0"
+	webadminip=("${current_ips[@]}")
+	telnetip=("${current_ips[@]}")
+# If no ip is set by the user and server has more than one IP.
+else
+	queryips=("127.0.0.1" "${current_ips[@]}")
+	ip="0.0.0.0"
+	webadminip=("${ip}")
+	telnetip=("${ip}")
+fi

+ 26 - 0
lgsm/functions/check_last_update.sh

@@ -0,0 +1,26 @@
+#!/bin/bash
+# LinuxGSM check_last_update.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Checks lock file to see when last update happened.
+# Will reboot server if instance not rebooted since update.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+if [ -f "${lockdir}/${selfname}-laststart.lock" ]; then
+	laststart=$(cat "${lockdir}/${selfname}-laststart.lock")
+fi
+if [ -f "${lockdir}/lastupdate.lock" ]; then
+	lastupdate=$(cat "${lockdir}/lastupdate.lock")
+fi
+
+check_status.sh
+if [ -f "${lockdir}/lastupdate.lock" ] && [ "${status}" != "0" ]; then
+	if [ ! -f "${lockdir}/${selfname}-laststart.lock" ] || [ "${laststart}" -lt "${lastupdate}" ]; then
+		fn_print_info "${selfname} has not been restarted since last update"
+		fn_script_log_info "${selfname} has not been restarted since last update"
+		command_restart.sh
+		fn_firstcommand_reset
+	fi
+fi

+ 233 - 0
lgsm/functions/check_permissions.sh

@@ -0,0 +1,233 @@
+#!/bin/bash
+# LinuxGSM check_permissions.sh
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Checks ownership & permissions of scripts, files and directories.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+fn_check_ownership() {
+	if [ -f "${rootdir}/${selfname}" ]; then
+		if [ "$(find "${rootdir}/${selfname}" -not -user "$(whoami)" | wc -l)" -ne "0" ]; then
+			selfownissue=1
+		fi
+	fi
+	if [ -d "${functionsdir}" ]; then
+		if [ "$(find "${functionsdir}" -not -user "$(whoami)" | wc -l)" -ne "0" ]; then
+			funcownissue=1
+		fi
+	fi
+	if [ -d "${serverfiles}" ]; then
+		if [ "$(find "${serverfiles}" -not -user "$(whoami)" | wc -l)" -ne "0" ]; then
+			filesownissue=1
+		fi
+	fi
+	if [ "${selfownissue}" == "1" ] || [ "${funcownissue}" == "1" ] || [ "${filesownissue}" == "1" ]; then
+		fn_print_fail_nl "Ownership issues found"
+		fn_script_log_fatal "Ownership issues found"
+		fn_print_information_nl "The current user ($(whoami)) does not have ownership of the following files:"
+		fn_script_log_info "The current user ($(whoami)) does not have ownership of the following files:"
+		{
+			echo -e "User\tGroup\tFile\n"
+			if [ "${selfownissue}" == "1" ]; then
+				find "${rootdir}/${selfname}" -not -user "$(whoami)" -printf "%u\t%g\t%p\n"
+			fi
+			if [ "${funcownissue}" == "1" ]; then
+				find "${functionsdir}" -not -user "$(whoami)" -printf "%u\t%g\t%p\n"
+			fi
+			if [ "${filesownissue}" == "1" ]; then
+				find "${serverfiles}" -not -user "$(whoami)" -printf "%u\t%g\t%p\n"
+			fi
+
+		} | column -s $'\t' -t | tee -a "${lgsmlog}"
+		echo -e ""
+		fn_print_information_nl "please see https://docs.linuxgsm.com/support/faq#fail-starting-game-server-permission-issues-found"
+		fn_script_log "For more information, please see https://docs.linuxgsm.com/support/faq#fail-starting-game-server-permission-issues-found"
+		if [ "${monitorflag}" == 1 ]; then
+			alert="permissions"
+			alert.sh
+		fi
+		core_exit.sh
+	fi
+}
+
+fn_check_permissions() {
+	if [ -d "${functionsdir}" ]; then
+		if [ "$(find "${functionsdir}" -type f -not -executable | wc -l)" -ne "0" ]; then
+			fn_print_fail_nl "Permissions issues found"
+			fn_script_log_fatal "Permissions issues found"
+			fn_print_information_nl "The following files are not executable:"
+			fn_script_log_info "The following files are not executable:"
+			{
+				echo -e "File\n"
+				find "${functionsdir}" -type f -not -executable -printf "%p\n"
+			} | column -s $'\t' -t | tee -a "${lgsmlog}"
+			if [ "${monitorflag}" == 1 ]; then
+				alert="permissions"
+				alert.sh
+			fi
+			core_exit.sh
+		fi
+	fi
+
+	# Check rootdir permissions.
+	if [ "${rootdir}" ]; then
+		# Get permission numbers on directory under the form 775.
+		rootdirperm=$(stat -c %a "${rootdir}")
+		# Grab the first and second digit for user and group permission.
+		userrootdirperm="${rootdirperm:0:1}"
+		grouprootdirperm="${rootdirperm:1:1}"
+		if [ "${userrootdirperm}" != "7" ] && [ "${grouprootdirperm}" != "7" ]; then
+			fn_print_fail_nl "Permissions issues found"
+			fn_script_log_fatal "Permissions issues found"
+			fn_print_information_nl "The following directory does not have the correct permissions:"
+			fn_script_log_info "The following directory does not have the correct permissions:"
+			fn_script_log_info "${rootdir}"
+			ls -l "${rootdir}"
+			if [ "${monitorflag}" == 1 ]; then
+				alert="permissions"
+				alert.sh
+			fi
+			core_exit.sh
+		fi
+	fi
+	# Check if executable is executable and attempt to fix it.
+	# First get executable name.
+	execname=$(basename "${executable}")
+	if [ -f "${executabledir}/${execname}" ]; then
+		# Get permission numbers on file under the form 775.
+		execperm=$(stat -c %a "${executabledir}/${execname}")
+		# Grab the first and second digit for user and group permission.
+		userexecperm="${execperm:0:1}"
+		groupexecperm="${execperm:1:1}"
+		# Check for invalid user permission.
+		if [ "${userexecperm}" == "0" ] || [ "${userexecperm}" == "2" ] || [ "${userexecperm}" == "4" ] || [ "${userexecperm}" == "6" ]; then
+			# If user permission is invalid, then check for invalid group permissions.
+			if [ "${groupexecperm}" == "0" ] || [ "${groupexecperm}" == "2" ] || [ "${groupexecperm}" == "4" ] || [ "${groupexecperm}" == "6" ]; then
+				# If permission issues are found.
+				fn_print_warn_nl "Permissions issue found"
+				fn_script_log_warn "Permissions issue found"
+				fn_print_information_nl "The following file is not executable:"
+				ls -l "${executabledir}/${execname}"
+				fn_script_log_info "The following file is not executable:"
+				fn_script_log_info "${executabledir}/${execname}"
+				fn_print_information_nl "Applying chmod u+x,g+x ${executabledir}/${execname}"
+				fn_script_log_info "Applying chmod u+x,g+x ${execperm}"
+				# Make the executable executable.
+				chmod u+x,g+x "${executabledir}/${execname}"
+				# Second check to see if it's been successfully applied.
+				# Get permission numbers on file under the form 775.
+				execperm=$(stat -c %a "${executabledir}/${execname}")
+				# Grab the first and second digit for user and group permission.
+				userexecperm="${execperm:0:1}"
+				groupexecperm="${execperm:1:1}"
+				if [ "${userexecperm}" == "0" ] || [ "${userexecperm}" == "2" ] || [ "${userexecperm}" == "4" ] || [ "${userexecperm}" == "6" ]; then
+					if [ "${groupexecperm}" == "0" ] || [ "${groupexecperm}" == "2" ] || [ "${groupexecperm}" == "4" ] || [ "${groupexecperm}" == "6" ]; then
+						# If errors are still found.
+						fn_print_fail_nl "The following file could not be set executable:"
+						ls -l "${executabledir}/${execname}"
+						fn_script_log_warn "The following file could not be set executable:"
+						fn_script_log_info "${executabledir}/${execname}"
+						if [ "${monitorflag}" == "1" ]; then
+							alert="permissions"
+							alert.sh
+						fi
+						core_exit.sh
+					fi
+				fi
+			fi
+		fi
+	fi
+}
+
+## The following fn_sys_perm_* functions checks for permission errors in /sys directory.
+
+# Checks for permission errors in /sys directory.
+fn_sys_perm_errors_detect() {
+	# Reset test variables.
+	sysdirpermerror="0"
+	classdirpermerror="0"
+	netdirpermerror="0"
+	# Check permissions.
+	# /sys, /sys/class and /sys/class/net should be readable & executable.
+	if [ ! -r "/sys" ] || [ ! -x "/sys" ]; then
+		sysdirpermerror="1"
+	fi
+	if [ ! -r "/sys/class" ] || [ ! -x "/sys/class" ]; then
+		classdirpermerror="1"
+	fi
+	if [ ! -r "/sys/class/net" ] || [ ! -x "/sys/class/net" ]; then
+		netdirpermerror="1"
+	fi
+}
+
+# Display a message on how to fix the issue manually.
+fn_sys_perm_fix_manually_msg() {
+	echo -e ""
+	fn_print_information_nl "This error causes servers to fail starting properly"
+	fn_script_log_info "This error causes servers to fail starting properly."
+	echo -e "	* To fix this issue, run the following command as root:"
+	fn_script_log_info "To fix this issue, run the following command as root:"
+	echo -e "	  chmod a+rx /sys /sys/class /sys/class/net"
+	fn_script_log "chmod a+rx /sys /sys/class /sys/class/net"
+	fn_sleep_time
+	if [ "${monitorflag}" == 1 ]; then
+		alert="permissions"
+		alert.sh
+	fi
+	core_exit.sh
+}
+
+# Attempt to fix /sys related permission errors if sudo is available, exits otherwise.
+fn_sys_perm_errors_fix() {
+	if sudo -n true > /dev/null 2>&1; then
+		fn_print_dots "Automatically fixing /sys permissions"
+		fn_script_log_info "Automatically fixing /sys permissions."
+		if [ "${sysdirpermerror}" == "1" ]; then
+			sudo chmod a+rx "/sys"
+		fi
+		if [ "${classdirpermerror}" == "1" ]; then
+			sudo chmod a+rx "/sys/class"
+		fi
+		if [ "${netdirpermerror}" == "1" ]; then
+			sudo chmod a+rx "/sys/class/net"
+		fi
+		# Run check again to see if it's fixed.
+		fn_sys_perm_errors_detect
+		if [ "${sysdirpermerror}" == "1" ] || [ "${classdirpermerror}" == "1" ] || [ "${netdirpermerror}" == "1" ]; then
+			fn_print_error "Could not fix /sys permissions"
+			fn_script_log_error "Could not fix /sys permissions."
+			fn_sleep_time
+			# Show the user how to fix.
+			fn_sys_perm_fix_manually_msg
+		else
+			fn_print_ok_nl "Automatically fixing /sys permissions"
+			fn_script_log_pass "Permissions in /sys fixed"
+		fi
+	else
+		# Show the user how to fix.
+		fn_sys_perm_fix_manually_msg
+	fi
+}
+
+# Processes to the /sys related permission errors check & fix/info.
+fn_sys_perm_error_process() {
+	fn_sys_perm_errors_detect
+	# If any error was found.
+	if [ "${sysdirpermerror}" == "1" ] || [ "${classdirpermerror}" == "1" ] || [ "${netdirpermerror}" == "1" ]; then
+		fn_print_error_nl "Permission error(s) found in /sys"
+		fn_script_log_error "Permission error(s) found in /sys"
+		# Run the fix
+		fn_sys_perm_errors_fix
+	fi
+}
+
+## Run permisions checks when not root or docker.
+if [ "$(whoami)" != "root" ] && [ ! -f /.dockerenv ]; then
+	fn_check_ownership
+	fn_check_permissions
+	if [ "${commandname}" == "START" ]; then
+		fn_sys_perm_error_process
+	fi
+fi

+ 10 - 0
lgsm/functions/check_status.sh

@@ -0,0 +1,10 @@
+#!/bin/bash
+# LinuxGSM check_status.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Checks the process status of the server. Either online or offline.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+status=$(tmux list-sessions -F "#{session_name}" 2> /dev/null | grep -Ecx "^${sessionname}")

+ 22 - 0
lgsm/functions/check_steamcmd.sh

@@ -0,0 +1,22 @@
+#!/bin/bash
+# LinuxGSM check_steamcmd.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Checks if SteamCMD is installed correctly.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+# init steamcmd functions
+core_steamcmd.sh
+
+fn_check_steamcmd_clear
+fn_check_steamcmd
+if [ "${shortname}" == "ark" ]; then
+	fn_check_steamcmd_ark
+fi
+fn_check_steamcmd_dir
+fn_check_steamcmd_dir_legacy
+fn_check_steamcmd_steamapp
+fn_check_steamcmd_user
+fn_check_steamcmd_exec

+ 22 - 0
lgsm/functions/check_system_dir.sh

@@ -0,0 +1,22 @@
+#!/bin/bash
+# LinuxGSM check_system_dir.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Checks if systemdir/serverfiles is accessible.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+if [ "${commandname}" != "VALIDATE" ]; then
+	checkdir="${serverfiles}"
+else
+	checkdir="${systemdir}"
+fi
+
+if [ ! -d "${checkdir}" ]; then
+	fn_print_fail_nl "Cannot access ${checkdir}: No such directory"
+	if [ -d "${lgsmlogdir}" ]; then
+		fn_script_log_fatal "Cannot access ${checkdir}: No such directory."
+	fi
+	core_exit.sh
+fi

+ 55 - 0
lgsm/functions/check_system_requirements.sh

@@ -0,0 +1,55 @@
+#!/bin/bash
+# LinuxGSM check_system_requirements.sh
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Checks RAM requirements.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+info_distro.sh
+
+# RAM requirements in megabytes for each game or engine.
+
+if [ "${shortname}" == "ark" ]; then
+	ramrequirementmb="4000"
+	ramrequirementgb="4"
+elif [ "${shortname}" == "bt" ]; then
+	ramrequirementmb="1000"
+	ramrequirementgb="1"
+elif [ "${shortname}" == "mh" ]; then
+	ramrequirementmb="4000"
+	ramrequirementgb="4"
+elif [ "${shortname}" == "arma3" ]; then
+	ramrequirementmb="1000"
+	ramrequirementgb="1"
+elif [ "${shortname}" == "rust" ]; then
+	ramrequirementmb="4000"
+	ramrequirementgb="4"
+elif [ "${shortname}" == "mc" ] || [ "${shortname}" == "pmc" ] || [ "${shortname}" == "wmc" ]; then
+	ramrequirementmb="1000"
+	ramrequirementgb="1"
+elif [ "${shortname}" == "pstbs" ]; then
+	ramrequirementmb="2000"
+	ramrequirementgb="2"
+elif [ "${shortname}" == "ns2" ] || [ "${shortname}" == "ns2c" ]; then
+	ramrequirementmb="1000"
+	ramrequirementgb="1"
+elif [ "${shortname}" == "st" ]; then
+	ramrequirementmb="1000"
+	ramrequirementgb="1"
+elif [ "${shortname}" == "pvr" ]; then
+	ramrequirementmb="2000"
+	ramrequirementgb="2"
+fi
+
+# If the game or engine has a minimum RAM Requirement, compare it to system's available RAM.
+if [ "${ramrequirementmb}" ]; then
+	if [ "${physmemtotalmb}" -lt "${ramrequirementmb}" ]; then
+		fn_print_dots "Check RAM"
+		# Warn the user.
+		fn_print_warn_nl "Check RAM: ${ramrequirementgb}G required, ${physmemtotal} available"
+		echo "* ${gamename} server may fail to run or experience poor performance."
+		fn_sleep_time
+	fi
+fi

+ 33 - 0
lgsm/functions/check_tmuxception.sh

@@ -0,0 +1,33 @@
+#!/bin/bash
+# LinuxGSM check_config.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Checks if run from tmux or screen.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+fn_check_is_in_tmux() {
+	if [ "${TMUX}" ]; then
+		fn_print_fail_nl "tmuxception error: Sorry Cobb you cannot start a tmux session inside of a tmux session."
+		fn_script_log_fatal "Tmuxception error: Attempted to start a tmux session inside of a tmux session."
+		fn_print_information_nl "LinuxGSM creates a tmux session when starting the server."
+		echo -e "It is not possible to run a tmux session inside another tmux session"
+		echo -e "https://docs.linuxgsm.com/requirements/tmux#tmuxception"
+		core_exit.sh
+	fi
+}
+
+fn_check_is_in_screen() {
+	if [ "${STY}" ]; then
+		fn_print_fail_nl "tmuxception error: Sorry Cobb you cannot start a tmux session inside of a screen session."
+		fn_script_log_fatal "Tmuxception error: Attempted to start a tmux session inside of a screen session."
+		fn_print_information_nl "LinuxGSM creates a tmux session when starting the server."
+		echo -e "It is not possible to run a tmux session inside screen session"
+		echo -e "https://docs.linuxgsm.com/requirements/tmux#tmuxception"
+		core_exit.sh
+	fi
+}
+
+fn_check_is_in_tmux
+fn_check_is_in_screen

+ 23 - 0
lgsm/functions/check_version.sh

@@ -0,0 +1,23 @@
+#!/bin/bash
+# LinuxGSM command_version.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Will run update-lgsm if gameserver.sh and modules version does not match
+# this will allow gameserver.sh to update - useful for multi instance servers.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+if [ -n "${modulesversion}" ] && [ -n "${version}" ] && [ "${version}" != "${modulesversion}" ]; then
+	exitbypass=1
+	echo -e ""
+	fn_print_error_nl "LinuxGSM version mismatch"
+	echo -e ""
+	echo -e "* ${selfname}: ${version}"
+	echo -e "* modules: ${modulesversion}"
+	echo -e ""
+	fn_sleep_time
+	fn_script_log_error "LinuxGSM Version mismatch: ${selfname}: ${version}: modules: ${modulesversion}"
+	command_update_linuxgsm.sh
+	fn_firstcommand_reset
+fi

+ 268 - 0
lgsm/functions/command_backup.sh

@@ -0,0 +1,268 @@
+#!/bin/bash
+# LinuxGSM command_backup.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Creates a .tar.gz file in the backup directory.
+
+commandname="BACKUP"
+commandaction="Backing up"
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+fn_firstcommand_set
+
+check.sh
+
+# Trap to remove lockfile on quit.
+fn_backup_trap() {
+	echo -e ""
+	echo -en "backup ${backupname}.tar.gz..."
+	fn_print_canceled_eol_nl
+	fn_script_log_info "Backup ${backupname}.tar.gz: CANCELED"
+	rm -f "${backupdir:?}/${backupname}.tar.gz" | tee -a "${lgsmlog}"
+	echo -en "backup ${backupname}.tar.gz..."
+	fn_print_removed_eol_nl
+	fn_script_log_info "Backup ${backupname}.tar.gz: REMOVED"
+	# Remove lock file.
+	rm -f "${lockdir:?}/backup.lock"
+	fn_backup_start_server
+	unset exitbypass
+	core_exit.sh
+}
+
+# Check if a backup is pending or has been aborted using backup.lock.
+fn_backup_check_lockfile() {
+	if [ -f "${lockdir}/backup.lock" ]; then
+		fn_print_info_nl "Lock file found: Backup is currently running"
+		fn_script_log_error "Lock file found: Backup is currently running: ${lockdir}/backup.lock"
+		core_exit.sh
+	fi
+}
+
+# Initialisation.
+fn_backup_init() {
+	# Backup file name with selfname and current date.
+	backupname="${selfname}-$(date '+%Y-%m-%d-%H%M%S')"
+
+	info_distro.sh
+	fn_print_dots "Backup starting"
+	fn_script_log_info "Backup starting"
+	fn_print_ok_nl "Backup starting"
+	if [ ! -d "${backupdir}" ] || [ "${backupcount}" == "0" ]; then
+		fn_print_info_nl "There are no previous backups"
+	else
+		if [ "${lastbackupdaysago}" == "0" ]; then
+			daysago="less than 1 day ago"
+		elif [ "${lastbackupdaysago}" == "1" ]; then
+			daysago="1 day ago"
+		else
+			daysago="${lastbackupdaysago} days ago"
+		fi
+		echo -e "* Previous backup was created ${daysago}, total size ${lastbackupsize}"
+	fi
+}
+
+# Check if server is started and whether to stop it.
+fn_backup_stop_server() {
+	check_status.sh
+	# Server is running but will not be stopped.
+	if [ "${stoponbackup}" == "off" ]; then
+		fn_print_warn_nl "${selfname} is currently running"
+		echo -e "* Although unlikely; creating a backup while ${selfname} is running might corrupt the backup."
+		fn_script_log_warn "${selfname} is currently running"
+		fn_script_log_warn "Although unlikely; creating a backup while ${selfname} is running might corrupt the backup"
+	# Server is running and will be stopped if stoponbackup=on or unset.
+	# If server is started
+	elif [ "${status}" != "0" ]; then
+		fn_print_restart_warning
+		startserver="1"
+		exitbypass=1
+		command_stop.sh
+		fn_firstcommand_reset
+	fi
+}
+
+# Create required folders.
+fn_backup_dir() {
+	# Create backupdir if it doesn't exist.
+	if [ ! -d "${backupdir}" ]; then
+		mkdir -p "${backupdir}"
+	fi
+}
+
+# Migrate Backups from old dir before refactor
+fn_backup_migrate_olddir() {
+	# Check if old backup dir is there before the refactor and move the backups
+	if [ -d "${rootdir}/backups" ]; then
+		if [ "${rootdir}/backups" != "${backupdir}" ]; then
+			fn_print_dots "Backup directory is being migrated"
+			fn_script_log_info "Backup directory is being migrated"
+			fn_script_log_info "${rootdir}/backups > ${backupdir}"
+			mv "${rootdir}/backups/"* "${backupdir}" 2> /dev/null
+			exitcode=$?
+			if [ "${exitcode}" == 0 ]; then
+				rmdir "${rootdir}/backups" 2> /dev/null
+				exitcode=$?
+			fi
+			if [ "${exitcode}" != 0 ]; then
+				fn_print_error_nl "Backup directory is being migrated"
+				fn_script_log_error "Backup directory is being migrated"
+			else
+
+				fn_print_ok_nl "Backup directory is being migrated"
+				fn_script_log_pass "Backup directory is being migrated"
+			fi
+		fi
+	fi
+}
+
+fn_backup_create_lockfile() {
+	# Create lockfile.
+	date '+%s' > "${lockdir}/backup.lock"
+	fn_script_log_info "Lockfile generated"
+	fn_script_log_info "${lockdir}/backup.lock"
+	# trap to remove lockfile on quit.
+	trap fn_backup_trap INT
+}
+
+# Compressing files.
+fn_backup_compression() {
+	# Tells how much will be compressed using rootdirduexbackup value from info_distro and prompt for continue.
+	fn_print_info "A total of ${rootdirduexbackup} will be compressed."
+	fn_script_log_info "A total of ${rootdirduexbackup} will be compressed: ${backupdir}/${backupname}.tar.gz"
+	fn_print_dots "Backup (${rootdirduexbackup}) ${backupname}.tar.gz, in progress..."
+	fn_script_log_info "backup ${rootdirduexbackup} ${backupname}.tar.gz, in progress"
+	excludedir=$(fn_backup_relpath)
+
+	# Check that excludedir is a valid path.
+	if [ ! -d "${excludedir}" ]; then
+		fn_print_fail_nl "Problem identifying the previous backup directory for exclusion."
+		fn_script_log_fatal "Problem identifying the previous backup directory for exclusion"
+		core_exit.sh
+	fi
+
+	tar -czf "${backupdir}/${backupname}.tar.gz" -C "${rootdir}" --exclude "${excludedir}" --exclude "${lockdir}/backup.lock" ./.
+	local exitcode=$?
+	if [ "${exitcode}" != 0 ]; then
+		fn_print_fail_eol
+		fn_script_log_fatal "Backup in progress: FAIL"
+		echo -e "${extractcmd}" | tee -a "${lgsmlog}"
+		fn_print_fail_nl "Starting backup"
+		fn_script_log_fatal "Starting backup"
+	else
+		fn_print_ok_eol
+		fn_print_ok_nl "Completed: ${backupname}.tar.gz, total size $(du -sh "${backupdir}/${backupname}.tar.gz" | awk '{print $1}')"
+		fn_script_log_pass "Backup created: ${backupname}.tar.gz, total size $(du -sh "${backupdir}/${backupname}.tar.gz" | awk '{print $1}')"
+	fi
+	# Remove lock file
+	rm -f "${lockdir:?}/backup.lock"
+}
+
+# Clear old backups according to maxbackups and maxbackupdays variables.
+fn_backup_prune() {
+	# Clear if backup variables are set.
+	if [ "${maxbackups}" ] && [ -n "${maxbackupdays}" ]; then
+		# How many backups there are.
+		info_distro.sh
+		# How many backups exceed maxbackups.
+		backupquotadiff=$((backupcount - maxbackups))
+		# How many backups exceed maxbackupdays.
+		backupsoudatedcount=$(find "${backupdir}"/ -type f -name "*.tar.gz" -mtime +"${maxbackupdays}" | wc -l)
+		# If anything can be cleared.
+		if [ "${backupquotadiff}" -gt "0" ] || [ "${backupsoudatedcount}" -gt "0" ]; then
+			fn_print_dots "Pruning"
+			fn_script_log_info "Backup pruning activated"
+			fn_print_ok_nl "Pruning"
+			# If maxbackups greater or equal to backupsoutdatedcount, then it is over maxbackupdays.
+			if [ "${backupquotadiff}" -ge "${backupsoudatedcount}" ]; then
+				# Display how many backups will be cleared.
+				echo -e "* Pruning: ${backupquotadiff} backup(s) has exceeded the ${maxbackups} backups limit"
+				fn_script_log_info "Pruning: ${backupquotadiff} backup(s) has exceeded the ${maxbackups} backups limit"
+				fn_sleep_time
+				fn_print_dots "Pruning: Clearing ${backupquotadiff} backup(s)"
+				fn_script_log_info "Pruning: Clearing ${backupquotadiff} backup(s)"
+				# Clear backups over quota.
+				find "${backupdir}"/ -type f -name "*.tar.gz" -printf '%T@ %p\n' | sort -rn | tail -${backupquotadiff} | cut -f2- -d" " | xargs rm
+				fn_print_ok_nl "Pruning: Clearing ${backupquotadiff} backup(s)"
+				fn_script_log_pass "Pruning: Cleared ${backupquotadiff} backup(s)"
+			# If maxbackupdays is used over maxbackups.
+			elif [ "${backupquotadiff}" -lt "${backupsoudatedcount}" ]; then
+				# Display how many backups will be cleared.
+				echo -e "* Pruning: ${backupsoudatedcount} backup(s) are older than ${maxbackupdays} days."
+				fn_script_log_info "Pruning: ${backupsoudatedcount} backup(s) older than ${maxbackupdays} days."
+				fn_sleep_time
+				fn_print_dots "Pruning: Clearing ${backupquotadiff} backup(s)."
+				fn_script_log_info "Pruning: Clearing ${backupquotadiff} backup(s)"
+				# Clear backups over quota
+				find "${backupdir}"/ -type f -mtime +"${maxbackupdays}" -exec rm -f {} \;
+				fn_print_ok_nl "Pruning: Clearing ${backupquotadiff} backup(s)"
+				fn_script_log_pass "Pruning: Cleared ${backupquotadiff} backup(s)"
+			fi
+		fi
+	fi
+}
+
+fn_backup_relpath() {
+	# Written by CedarLUG as a "realpath --relative-to" alternative in bash.
+	# Populate an array of tokens initialized from the rootdir components.
+	declare -a rdirtoks=($(readlink -f "${rootdir}" | sed "s/\// /g"))
+	if [ ${#rdirtoks[@]} -eq 0 ]; then
+		fn_print_fail_nl "Problem assessing rootdir during relative path assessment"
+		fn_script_log_fatal "Problem assessing rootdir during relative path assessment: ${rootdir}"
+		core_exit.sh
+	fi
+
+	# Populate an array of tokens initialized from the backupdir components.
+	declare -a bdirtoks=($(readlink -f "${backupdir}" | sed "s/\// /g"))
+	if [ ${#bdirtoks[@]} -eq 0 ]; then
+		fn_print_fail_nl "Problem assessing backupdir during relative path assessment"
+		fn_script_log_fatal "Problem assessing backupdir during relative path assessment: ${rootdir}"
+		core_exit.sh
+	fi
+
+	# Compare the leading entries of each array.  These common elements will be clipped off.
+	# for the relative path output.
+	for ((base = 0; base < ${#rdirtoks[@]}; base++)); do
+		[[ "${rdirtoks[$base]}" != "${bdirtoks[$base]}" ]] && break
+	done
+
+	# Next, climb out of the remaining rootdir location with updir references.
+	for ((x = base; x < ${#rdirtoks[@]}; x++)); do
+		echo -n "../"
+	done
+
+	# Climb down the remaining components of the backupdir location.
+	for ((x = base; x < $((${#bdirtoks[@]} - 1)); x++)); do
+		echo -n "${bdirtoks[$x]}/"
+	done
+
+	# In the event there were no directories left in the backupdir above to
+	# traverse down, just add a newline. Otherwise at this point, there is
+	# one remaining directory component in the backupdir to navigate.
+	if (("$base" < "${#bdirtoks[@]}")); then
+		echo -e "${bdirtoks[$((${#bdirtoks[@]} - 1))]}"
+	else
+		echo
+	fi
+}
+
+# Start the server if it was stopped for the backup.
+fn_backup_start_server() {
+	if [ -n "${startserver}" ]; then
+		exitbypass=1
+		command_start.sh
+		fn_firstcommand_reset
+	fi
+}
+
+# Run functions.
+fn_backup_check_lockfile
+fn_backup_init
+fn_backup_stop_server
+fn_backup_dir
+fn_backup_migrate_olddir
+fn_backup_create_lockfile
+fn_backup_compression
+fn_backup_prune
+fn_backup_start_server
+
+core_exit.sh

+ 39 - 0
lgsm/functions/command_check_update.sh

@@ -0,0 +1,39 @@
+#!/bin/bash
+# LinuxGSM command_check_update.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Handles updating of servers.
+
+commandname="CHECK-UPDATE"
+commandaction="Check for Update"
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+fn_firstcommand_set
+
+fn_print_dots ""
+check.sh
+core_logs.sh
+
+if [ "${shortname}" == "ts3" ]; then
+	update_ts3.sh
+elif [ "${shortname}" == "mc" ]; then
+	update_minecraft.sh
+elif [ "${shortname}" == "mcb" ]; then
+	update_minecraft_bedrock.sh
+elif [ "${shortname}" == "pmc" ] || [ "${shortname}" == "vpmc" ] || [ "${shortname}" == "wmc" ]; then
+	update_papermc.sh
+elif [ "${shortname}" == "fctr" ]; then
+	update_factorio.sh
+elif [ "${shortname}" == "mta" ]; then
+	update_mta.sh
+elif [ "${shortname}" == "jk2" ]; then
+	update_jediknight2.sh
+elif [ "${shortname}" == "vints" ]; then
+	update_vintagestory.sh
+elif [ "${shortname}" == "ut99" ]; then
+	update_ut99.sh
+else
+	update_steamcmd.sh
+fi
+
+core_exit.sh

+ 58 - 0
lgsm/functions/command_console.sh

@@ -0,0 +1,58 @@
+#!/bin/bash
+# LinuxGSM command_console.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Gives access to the server tmux console.
+
+commandname="CONSOLE"
+commandaction="Access console"
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+fn_firstcommand_set
+
+check.sh
+fn_print_header
+
+if [ "${consoleverbose}" == "yes" ]; then
+	echo -e "* Verbose output: ${lightgreen}yes${default}"
+elif [ "${consoleverbose}" == "no" ]; then
+	echo -e "* Verbose output: ${red}no${default}"
+else
+	echo -e "* Verbose output: ${red}unknown${default}"
+fi
+
+if [ "${consoleinteract}" == "yes" ]; then
+	echo -e "* Interactive output: ${lightgreen}yes${default}"
+elif [ "${consoleinteract}" == "no" ]; then
+	echo -e "* Interactive output: ${red}no${default}"
+else
+	echo -e "* Interactive output: ${red}unknown${default}"
+fi
+echo ""
+fn_print_information_nl "Press \"CTRL+b\" then \"d\" to exit console."
+fn_print_warning_nl "Do NOT press CTRL+c to exit."
+echo -e "* https://docs.linuxgsm.com/commands/console"
+echo -e ""
+if ! fn_prompt_yn "Continue?" Y; then
+	exitcode=0
+	core_exit.sh
+fi
+fn_print_dots "Accessing console"
+check_status.sh
+if [ "${status}" != "0" ]; then
+	fn_print_ok_nl "Accessing console"
+	fn_script_log_pass "Console accessed"
+	tmux attach-session -t "${sessionname}"
+	fn_print_ok_nl "Closing console"
+	fn_script_log_pass "Console closed"
+else
+	fn_print_error_nl "Server not running"
+	fn_script_log_error "Failed to access: Server not running"
+	if fn_prompt_yn "Do you want to start the server?" Y; then
+		exitbypass=1
+		command_start.sh
+		fn_firstcommand_reset
+	fi
+fi
+
+core_exit.sh

+ 142 - 0
lgsm/functions/command_debug.sh

@@ -0,0 +1,142 @@
+#!/bin/bash
+# LinuxGSM command_debug.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Runs the server without tmux and directly from the terminal.
+
+commandname="DEBUG"
+commandaction="Debuging"
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+fn_firstcommand_set
+
+# Trap to remove lockfile on quit.
+fn_lockfile_trap() {
+	# Remove lockfile.
+	rm -f "${lockdir:?}/${selfname}.lock"
+	# resets terminal. Servers can sometimes mess up the terminal on exit.
+	reset
+	fn_print_dots "Stopping debug"
+	fn_print_ok_nl "Stopping debug"
+	fn_script_log_pass "Stopping debug"
+	# remove trap.
+	trap - INT
+	core_exit.sh
+}
+
+check.sh
+fix.sh
+info_distro.sh
+info_game.sh
+fn_print_header
+{
+	echo -e "${lightblue}Distro:\t\t${default}${distroname}"
+	echo -e "${lightblue}Architecture:\t\t${default}${arch}"
+	echo -e "${lightblue}Kernel:\t\t${default}${kernel}"
+	echo -e "${lightblue}Hostname:\t\t${default}${HOSTNAME}"
+	echo -e "${lightblue}tmux:\t\t${default}${tmuxv}"
+	echo -e "${lightblue}Avg Load:\t\t${default}${load}"
+	echo -e "${lightblue}Free Memory:\t\t${default}${physmemfree}"
+	echo -e "${lightblue}Free Disk:\t\t${default}${availspace}"
+} | column -s $'\t' -t
+
+# glibc required.
+if [ -n "${glibc}" ]; then
+	if [ "${glibc}" == "null" ]; then
+		# Glibc is not required.
+		:
+	elif [ -z "${glibc}" ]; then
+		echo -e "${lightblue}glibc required:\t${red}UNKNOWN${default}"
+	elif [ "$(printf '%s\n'${glibc}'\n' ${glibcversion} | sort -V | head -n 1)" != "${glibc}" ]; then
+		echo -e "${lightblue}glibc required:\t${red}${glibc} ${default}(${red}distro glibc ${glibcversion} too old${default})"
+	else
+		echo -e "${lightblue}glibc required:\t${green}${glibc}${default}"
+	fi
+fi
+
+# Server IP.
+echo -e "${lightblue}Game Server IP:\t${default}${ip}:${port}"
+
+# External server IP.
+if [ "${extip}" ]; then
+	if [ "${ip}" != "${extip}" ]; then
+		echo -e "${lightblue}Internet IP:\t${default}${extip}:${port}"
+	fi
+fi
+
+# Server password.
+if [ "${serverpassword}" ]; then
+	echo -e "${lightblue}Server password:\t${default}${serverpassword}"
+fi
+
+fn_reload_startparameters
+echo -e "${lightblue}Start parameters:${default}"
+if [ "${engine}" == "source" ] || [ "${engine}" == "goldsrc" ]; then
+	echo -e "${executable} ${startparameters} -debug"
+elif [ "${engine}" == "quake" ]; then
+	echo -e "${executable} ${startparameters} -condebug"
+else
+	echo -e "${preexecutable} ${executable} ${startparameters}"
+fi
+echo -e ""
+echo -e "Use debug for identifying server issues only!"
+echo -e "Press CTRL+c to drop out of debug mode."
+fn_print_warning_nl "If ${selfname} is already running it will be stopped."
+echo -e ""
+if ! fn_prompt_yn "Continue?" Y; then
+	exitcode=0
+	core_exit.sh
+fi
+
+fn_print_info_nl "Stopping any running servers"
+fn_script_log_info "Stopping any running servers"
+exitbypass=1
+command_stop.sh
+fn_firstcommand_reset
+unset exitbypass
+fn_print_dots "Starting debug"
+fn_script_log_info "Starting debug"
+fn_print_ok_nl "Starting debug"
+
+# Create lockfile.
+date '+%s' > "${lockdir}/${selfname}.lock"
+echo "${version}" >> "${lockdir}/${selfname}.lock"
+echo "${port}" >> "${lockdir}/${selfname}.lock"
+fn_script_log_info "Lockfile generated"
+fn_script_log_info "${lockdir}/${selfname}.lock"
+
+if [ "${shortname}" == "av" ]; then
+	cd "${systemdir}" || exit
+else
+	cd "${executabledir}" || exit
+fi
+
+# Note: do not add double quotes to ${executable} ${startparameters}.
+if [ "${engine}" == "source" ] || [ "${engine}" == "goldsrc" ]; then
+	eval "${executable} ${startparameters} -debug"
+elif [ "${engine}" == "quake" ]; then
+	eval "${executable} ${startparameters} -condebug"
+else
+	# shellcheck disable=SC2086
+	eval "${preexecutable} ${executable} ${startparameters}"
+fi
+
+if [ $? -ne 0 ]; then
+	fn_print_error_nl "Server has stopped: exit code: $?"
+	fn_script_log_error "Server has stopped: exit code: $?"
+	fn_print_error_nl "Press ENTER to exit debug mode"
+	read -r
+else
+	fn_print_ok_nl "Server has stopped"
+	fn_script_log_pass "Server has stopped"
+	fn_print_ok_nl "Press ENTER to exit debug mode"
+	read -r
+fi
+
+fn_lockfile_trap
+
+fn_print_dots "Stopping debug"
+fn_print_ok_nl "Stopping debug"
+fn_script_log_info "Stopping debug"
+
+core_exit.sh

+ 41 - 0
lgsm/functions/command_details.sh

@@ -0,0 +1,41 @@
+#!/bin/bash
+# LinuxGSM command_details.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Displays server information.
+
+commandname="DETAILS"
+commandaction="Viewing details"
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+fn_firstcommand_set
+
+# Run checks and gathers details to display.
+check.sh
+info_distro.sh
+info_game.sh
+info_messages.sh
+if [ "${querymode}" == "2" ] || [ "${querymode}" == "3" ]; then
+	for queryip in "${queryips[@]}"; do
+		query_gamedig.sh
+		if [ "${querystatus}" == "0" ]; then
+			break
+		fi
+	done
+fi
+fn_info_message_distro
+fn_info_message_server_resource
+fn_info_message_gameserver_resource
+fn_info_message_gameserver
+fn_info_message_script
+fn_info_message_backup
+# Some game servers do not have parms.
+if [ "${shortname}" != "jc2" ] && [ "${shortname}" != "dst" ] && [ "${shortname}" != "pz" ] && [ "${engine}" != "renderware" ]; then
+	fn_info_message_commandlineparms
+fi
+fn_info_message_ports_edit
+fn_info_message_ports
+fn_info_message_select_engine
+fn_info_message_statusbottom
+
+core_exit.sh

+ 24 - 0
lgsm/functions/command_dev_clear_functions.sh

@@ -0,0 +1,24 @@
+#!/bin/bash
+# LinuxGSM command_dev_clear_functions.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Deletes the contents of the functions dir.
+
+commandname="DEV-CLEAR-MODULES"
+commandaction="Clearing modules"
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+fn_firstcommand_set
+
+echo -e "================================="
+echo -e "Clear Functions"
+echo -e "================================="
+echo -e ""
+if fn_prompt_yn "Do you want to delete all functions?" Y; then
+	rm -rfv "${functionsdir:?}/"*
+	rm -rfv "${configdirdefault:?}/"*
+	fn_script_log_info "Cleared modules directory"
+	fn_script_log_info "Cleared default config directory"
+fi
+
+core_exit.sh

+ 23 - 0
lgsm/functions/command_dev_debug.sh

@@ -0,0 +1,23 @@
+#!/bin/bash
+# LinuxGSM command_dev_debug.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Dev only: Enables debugging log to be saved to dev-debug.log.
+
+commandname="DEV-DEBUG"
+commandaction="Developer debug"
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+fn_firstcommand_reset
+
+if [ -f "${rootdir}/.dev-debug" ]; then
+	rm -f "${rootdir:?}/.dev-debug"
+	fn_print_ok_nl "Disabled dev-debug"
+	fn_script_log_info "Disabled dev-debug"
+else
+	date '+%s' > "${rootdir}/.dev-debug"
+	fn_print_ok_nl "Enabled dev-debug"
+	fn_script_log_info "Enabled dev-debug"
+fi
+
+core_exit.sh

+ 231 - 0
lgsm/functions/command_dev_detect_deps.sh

@@ -0,0 +1,231 @@
+#!/bin/bash
+# LinuxGSM command_dev_detect_deps.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Detects dependencies the server binary requires.
+
+commandname="DEV-DETECT-DEPS"
+commandaction="Developer detect deps"
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+fn_firstcommand_set
+
+echo -e "================================="
+echo -e "Dependencies Checker"
+echo -e "================================="
+echo -e "Checking directory: "
+echo -e "${serverfiles}"
+if [ "$(command -v eu-readelf 2> /dev/null)" ]; then
+	readelf=eu-readelf
+elif [ "$(command -v readelf 2> /dev/null)" ]; then
+	readelf=readelf
+else
+	echo -e "readelf/eu-readelf not installed"
+fi
+files=$(find "${serverfiles}" | wc -l)
+find "${serverfiles}" -type f -print0 \
+	| while IFS= read -r -d $'\0' line; do
+		if [ "${readelf}" == "eu-readelf" ]; then
+			${readelf} -d "${line}" 2> /dev/null | grep NEEDED | awk '{ print $4 }' | sed 's/\[//g;s/\]//g' >> "${tmpdir}/.depdetect_readelf"
+		else
+			${readelf} -d "${line}" 2> /dev/null | grep NEEDED | awk '{ print $5 }' | sed 's/\[//g;s/\]//g' >> "${tmpdir}/.depdetect_readelf"
+		fi
+		echo -n "${i} / ${files}" $'\r'
+		((i++))
+	done
+
+sort "${tmpdir}/.depdetect_readelf" | uniq > "${tmpdir}/.depdetect_readelf_uniq"
+
+touch "${tmpdir}/.depdetect_centos_list"
+touch "${tmpdir}/.depdetect_ubuntu_list"
+touch "${tmpdir}/.depdetect_debian_list"
+
+while read -r lib; do
+	echo -e "${lib}"
+	libs_array=(libm.so.6 libc.so.6 libtcmalloc_minimal.so.4 libpthread.so.0 libdl.so.2 libnsl.so.1 libgcc_s.so.1 librt.so.1 ld-linux.so.2 libdbus-glib-1.so.2 libgio-2.0.so.0 libglib-2.0.so.0 libGL.so.1 libgobject-2.0.so.0 libnm-glib.so.4 libnm-util.so.2)
+	for lib_file in "${libs_array[@]}"; do
+		if [ "${lib}" == "${lib_file}" ]; then
+			echo -e "glibc.i686" >> "${tmpdir}/.depdetect_centos_list"
+			echo -e "lib32gcc1" >> "${tmpdir}/.depdetect_ubuntu_list"
+			echo -e "lib32gcc1" >> "${tmpdir}/.depdetect_debian_list"
+			libdetected=1
+		fi
+	done
+
+	libs_array=(libawt.so libjava.so libjli.so libjvm.so libnet.so libnio.so libverify.so)
+	for lib_file in "${libs_array[@]}"; do
+		if [ "${lib}" == "${lib_file}" ]; then
+			echo -e "java-1.8.0-openjdk" >> "${tmpdir}/.depdetect_centos_list"
+			echo -e "default-jre" >> "${tmpdir}/.depdetect_ubuntu_list"
+			echo -e "default-jre" >> "${tmpdir}/.depdetect_debian_list"
+			libdetected=1
+		fi
+	done
+
+	libs_array=(libtier0.so libtier0_srv.so libvstdlib_srv.so Core.so libvstdlib.so libtier0_s.so Editor.so Engine.so liblua.so libsteam_api.so ld-linux-x86-64.so.2 libPhysX3_x86.so libPhysX3Common_x86.so libPhysX3Cooking_x86.so)
+	for lib_file in "${libs_array[@]}"; do
+		# Known shared libs what dont requires dependencies.
+		if [ "${lib}" == "${lib_file}" ]; then
+			libdetected=1
+		fi
+	done
+
+	if [ "${lib}" == "libstdc++.so.6" ]; then
+		echo -e "libstdc++.i686" >> "${tmpdir}/.depdetect_centos_list"
+		echo -e "libstdc++6:i386" >> "${tmpdir}/.depdetect_ubuntu_list"
+		echo -e "libstdc++6:i386" >> "${tmpdir}/.depdetect_debian_list"
+		libdetected=1
+	elif [ "${lib}" == "libstdc++.so.5" ]; then
+		echo -e "compat-libstdc++-33.i686" >> "${tmpdir}/.depdetect_centos_list"
+		echo -e "libstdc++5:i386" >> "${tmpdir}/.depdetect_ubuntu_list"
+		echo -e "libstdc++5:i386" >> "${tmpdir}/.depdetect_debian_list"
+		libdetected=1
+	elif [ "${lib}" == "libcurl-gnutls.so.4" ]; then
+		echo -e "libcurl.i686" >> "${tmpdir}/.depdetect_centos_list"
+		echo -e "libcurl4-gnutls-dev:i386" >> "${tmpdir}/.depdetect_ubuntu_list"
+		echo -e "libcurl4-gnutls-dev:i386" >> "${tmpdir}/.depdetect_debian_list"
+		libdetected=1
+	elif [ "${lib}" == "libspeex.so.1" ] || [ "${lib}" == "libspeexdsp.so.1" ]; then
+		echo -e "speex.i686" >> "${tmpdir}/.depdetect_centos_list"
+		echo -e "speex:i386" >> "${tmpdir}/.depdetect_ubuntu_list"
+		echo -e "speex:i386" >> "${tmpdir}/.depdetect_debian_list"
+		libdetected=1
+	elif [ "${lib}" == "./libSDL-1.2.so.0" ] || [ "${lib}" == "libSDL-1.2.so.0" ]; then
+		echo -e "SDL.i686" >> "${tmpdir}/.depdetect_centos_list"
+		echo -e "libsdl1.2debian" >> "${tmpdir}/.depdetect_ubuntu_list"
+		echo -e "libsdl1.2debian" >> "${tmpdir}/.depdetect_debian_list"
+		libdetected=1
+	elif [ "${lib}" == "libtbb.so.2" ]; then
+		echo -e "tbb.i686" >> "${tmpdir}/.depdetect_centos_list"
+		echo -e "libtbb2" >> "${tmpdir}/.depdetect_ubuntu_list"
+		echo -e "libtbb2" >> "${tmpdir}/.depdetect_debian_list"
+		libdetected=1
+
+	elif [ "${lib}" == "libXrandr.so.2" ]; then
+		echo -e "libXrandr" >> "${tmpdir}/.depdetect_centos_list"
+		echo -e "libxrandr2" >> "${tmpdir}/.depdetect_ubuntu_list"
+		echo -e "libxrandr2" >> "${tmpdir}/.depdetect_debian_list"
+		libdetected=1
+	elif [ "${lib}" == "libXext.so.6" ]; then
+		echo -e "libXext" >> "${tmpdir}/.depdetect_centos_list"
+		echo -e "libxext6" >> "${tmpdir}/.depdetect_ubuntu_list"
+		echo -e "libxext6" >> "${tmpdir}/.depdetect_debian_list"
+		libdetected=1
+	elif [ "${lib}" == "libXtst.so.6" ]; then
+		echo -e "libXtst" >> "${tmpdir}/.depdetect_centos_list"
+		echo -e "libxtst6" >> "${tmpdir}/.depdetect_ubuntu_list"
+		echo -e "libxtst6" >> "${tmpdir}/.depdetect_debian_list"
+		libdetected=1
+	elif [ "${lib}" == "libpulse.so.0" ]; then
+		echo -e "pulseaudio-libs" >> "${tmpdir}/.depdetect_centos_list"
+		echo -e "libpulse0" >> "${tmpdir}/.depdetect_ubuntu_list"
+		echo -e "libpulse0" >> "${tmpdir}/.depdetect_debian_list"
+		libdetected=1
+	elif [ "${lib}" == "libopenal.so.1" ]; then
+		echo -e "" >> "${tmpdir}/.depdetect_centos_list"
+		echo -e "libopenal1" >> "${tmpdir}/.depdetect_ubuntu_list"
+		echo -e "libopenal1" >> "${tmpdir}/.depdetect_debian_list"
+		libdetected=1
+	elif [ "${lib}" == "libgconf-2.so.4" ]; then
+		echo -e "GConf2" >> "${tmpdir}/.depdetect_centos_list"
+		echo -e "libgconf2-4" >> "${tmpdir}/.depdetect_ubuntu_list"
+		echo -e "libgconf2-4" >> "${tmpdir}/.depdetect_debian_list"
+		libdetected=1
+	elif [ "${lib}" == "libz.so.1" ]; then
+		echo -e "zlib" >> "${tmpdir}/.depdetect_centos_list"
+		echo -e "zlib1g" >> "${tmpdir}/.depdetect_ubuntu_list"
+		echo -e "zlib1g" >> "${tmpdir}/.depdetect_debian_list"
+		libdetected=1
+	elif [ "${lib}" == "libatk-1.0.so.0" ]; then
+		echo -e "atk" >> "${tmpdir}/.depdetect_centos_list"
+		echo -e "libatk1.0-0" >> "${tmpdir}/.depdetect_ubuntu_list"
+		echo -e "libatk1.0-0" >> "${tmpdir}/.depdetect_debian_list"
+		libdetected=1
+	elif [ "${lib}" == "libcairo.so.2" ]; then
+		echo -e "cairo" >> "${tmpdir}/.depdetect_centos_list"
+		echo -e "libcairo2" >> "${tmpdir}/.depdetect_ubuntu_list"
+		echo -e "libcairo2" >> "${tmpdir}/.depdetect_debian_list"
+		libdetected=1
+	elif [ "${lib}" == "libfontconfig.so.1" ]; then
+		echo -e "fontconfig" >> "${tmpdir}/.depdetect_centos_list"
+		echo -e "libfontconfig1" >> "${tmpdir}/.depdetect_ubuntu_list"
+		echo -e "libfontconfig1" >> "${tmpdir}/.depdetect_debian_list"
+		libdetected=1
+	elif [ "${lib}" == "libfreetype.so.6" ]; then
+		echo -e "freetype" >> "${tmpdir}/.depdetect_centos_list"
+		echo -e "libfreetype6" >> "${tmpdir}/.depdetect_ubuntu_list"
+		echo -e "libfreetype6" >> "${tmpdir}/.depdetect_debian_list"
+		libdetected=1
+	elif [ "${lib}" == "libc++.so.1" ]; then
+		echo -e "libcxx" >> "${tmpdir}/.depdetect_centos_list"
+		echo -e "libc++1" >> "${tmpdir}/.depdetect_ubuntu_list"
+		echo -e "libc++1" >> "${tmpdir}/.depdetect_debian_list"
+		libdetected=1
+	fi
+
+	if [ "${libdetected}" != "1" ]; then
+		unknownlib=1
+		echo -e "${lib}" >> "${tmpdir}/.depdetect_unknown"
+	fi
+	unset libdetected
+done < "${tmpdir}/.depdetect_readelf_uniq"
+
+sort "${tmpdir}/.depdetect_centos_list" | uniq >> "${tmpdir}/.depdetect_centos_list_uniq"
+sort "${tmpdir}/.depdetect_ubuntu_list" | uniq >> "${tmpdir}/.depdetect_ubuntu_list_uniq"
+sort "${tmpdir}/.depdetect_debian_list" | uniq >> "${tmpdir}/.depdetect_debian_list_uniq"
+if [ "${unknownlib}" == "1" ]; then
+	sort "${tmpdir}/.depdetect_unknown" | uniq >> "${tmpdir}/.depdetect_unknown_uniq"
+fi
+
+awk -vORS='' '{ print $1,$2 }' "${tmpdir}/.depdetect_centos_list_uniq" > "${tmpdir}/.depdetect_centos_line"
+awk -vORS='' '{ print $1,$2 }' "${tmpdir}/.depdetect_ubuntu_list_uniq" > "${tmpdir}/.depdetect_ubuntu_line"
+awk -vORS='' '{ print $1,$2 }' "${tmpdir}/.depdetect_debian_list_uniq" > "${tmpdir}/.depdetect_debian_line"
+echo -e ""
+echo -e ""
+echo -e "Required Dependencies"
+echo -e "================================="
+echo -e "${executable}"
+echo -e ""
+echo -e "CentOS"
+echo -e "================================="
+cat "${tmpdir}/.depdetect_centos_line"
+echo -e ""
+echo -e ""
+echo -e "Ubuntu"
+echo -e "================================="
+cat "${tmpdir}/.depdetect_ubuntu_line"
+echo -e ""
+echo -e ""
+echo -e "Debian"
+echo -e "================================="
+cat "${tmpdir}/.depdetect_debian_line"
+echo -e ""
+if [ "${unknownlib}" == "1" ]; then
+	echo -e ""
+	echo -e "Unknown shared Library"
+	echo -e "================================="
+	cat "${tmpdir}/.depdetect_unknown"
+fi
+echo -e ""
+echo -e "Required Librarys"
+echo -e "================================="
+sort "${tmpdir}/.depdetect_readelf" | uniq
+echo -en "\n"
+rm -f "${tmpdir:?}/.depdetect_centos_line"
+rm -f "${tmpdir:?}/.depdetect_centos_list"
+rm -f "${tmpdir:?}/.depdetect_centos_list_uniq"
+
+rm -f "${tmpdir:?}/.depdetect_debian_line"
+rm -f "${tmpdir:?}/.depdetect_debian_list"
+rm -f "${tmpdir:?}/.depdetect_debian_list_uniq"
+
+rm -f "${tmpdir:?}/.depdetect_ubuntu_line"
+rm -f "${tmpdir:?}/.depdetect_ubuntu_list"
+rm -f "${tmpdir:?}/.depdetect_ubuntu_list_uniq"
+
+rm -f "${tmpdir:?}/.depdetect_readelf"
+rm -f "${tmpdir:?}/.depdetect_readelf_uniq"
+rm -f "${tmpdir:?}/.depdetect_unknown"
+rm -f "${tmpdir:?}/.depdetect_unknown_uniq"
+
+core_exit.sh

+ 92 - 0
lgsm/functions/command_dev_detect_glibc.sh

@@ -0,0 +1,92 @@
+#!/bin/bash
+# LinuxGSM command_dev_detect_glibc.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Automatically detects the version of GLIBC that is required.
+# Can check a file or directory recursively.
+
+commandname="DEV-DETECT-GLIBC"
+commandaction="Developer detect glibc"
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+fn_firstcommand_set
+
+echo -e "================================="
+echo -e "glibc Requirements Checker"
+echo -e "================================="
+
+if [ ! "$(command -v objdump)" ]; then
+	fn_print_failure_nl "objdump is missing"
+	fn_script_log_fatal "objdump is missing"
+	core_exit.sh
+fi
+
+if [ -z "${serverfiles}" ]; then
+	dir=$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")
+fi
+
+if [ -d "${serverfiles}" ]; then
+	echo -e "Checking directory: "
+	echo -e "${serverfiles}"
+elif [ -f "${serverfiles}" ]; then
+	echo -e "Checking file: "
+	echo -e "${serverfiles}"
+fi
+echo -e ""
+
+glibc_check_dir_array=(steamcmddir serverfiles)
+for glibc_check_var in "${glibc_check_dir_array[@]}"; do
+	if [ "${glibc_check_var}" == "serverfiles" ]; then
+		glibc_check_dir="${serverfiles}"
+		glibc_check_name="${gamename}"
+	elif [ "${glibc_check_var}" == "steamcmddir" ]; then
+		glibc_check_dir="${steamcmddir}"
+		glibc_check_name="SteamCMD"
+	fi
+
+	if [ -d "${glibc_check_dir}" ]; then
+		glibc_check_files=$(find "${glibc_check_dir}" | wc -l)
+		find "${glibc_check_dir}" -type f -print0 \
+			| while IFS= read -r -d $'\0' line; do
+				glibcversion=$(objdump -T "${line}" 2> /dev/null | grep -oP "GLIBC[^ ]+" | grep -v GLIBCXX | sort | uniq | sort -r --version-sort | head -n 1)
+				if [ "${glibcversion}" ]; then
+					echo -e "${glibcversion}: ${line}" >> "${tmpdir}/detect_glibc_files_${glibc_check_var}.tmp"
+				fi
+				objdump -T "${line}" 2> /dev/null | grep -oP "GLIBC[^ ]+" >> "${tmpdir}/detect_glibc_${glibc_check_var}.tmp"
+				echo -n "${i} / ${glibc_check_files}" $'\r'
+				((i++))
+			done
+		echo -e ""
+		echo -e ""
+		echo -e "${glibc_check_name} glibc Requirements"
+		echo -e "================================="
+		if [ -f "${tmpdir}/detect_glibc_files_${glibc_check_var}.tmp" ]; then
+			echo -e "Required glibc"
+			cat "${tmpdir}/detect_glibc_${glibc_check_var}.tmp" | sort | uniq | sort -r --version-sort | head -1 | tee -a "${tmpdir}/detect_glibc_highest.tmp"
+			echo -e ""
+			echo -e "Files requiring GLIBC"
+			echo -e "Highest verion required: filename"
+			cat "${tmpdir}/detect_glibc_files_${glibc_check_var}.tmp"
+			echo -e ""
+			echo -e "All required GLIBC versions"
+			cat "${tmpdir}/detect_glibc_${glibc_check_var}.tmp" | sort | uniq | sort -r --version-sort
+			rm -f "${tmpdir:?}/detect_glibc_${glibc_check_var}.tmp"
+			rm -f "${tmpdir:?}/detect_glibc_files_${glibc_check_var}.tmp"
+		else
+			fn_print_information_nl "glibc is not required"
+		fi
+	else
+		fn_print_information_nl "${glibc_check_name} is not installed"
+	fi
+done
+echo -e ""
+echo -e "Final glibc Requirement"
+echo -e "================================="
+if [ -f "${tmpdir}/detect_glibc_highest.tmp" ]; then
+	cat "${tmpdir}/detect_glibc_highest.tmp" | sort | uniq | sort -r --version-sort | head -1
+	rm -f "${tmpdir:?}/detect_glibc_highest.tmp"
+else
+	fn_print_information_nl "glibc is not required"
+fi
+
+core_exit.sh

+ 61 - 0
lgsm/functions/command_dev_detect_ldd.sh

@@ -0,0 +1,61 @@
+#!/bin/bash
+# LinuxGSM command_dev_detect_ldd.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Automatically detects required deps using ldd.
+# Can check a file or directory recursively.
+
+commandname="DEV-DETECT-LDD"
+commandaction="Developer detect ldd"
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+fn_firstcommand_set
+
+echo -e "================================="
+echo -e "Shared Object dependencies Checker"
+echo -e "================================="
+
+if [ -z "${serverfiles}" ]; then
+	dir=$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")
+fi
+
+if [ -d "${serverfiles}" ]; then
+	echo -e "Checking directory: "
+	echo -e "${serverfiles}"
+elif [ -f "${serverfiles}" ]; then
+	echo -e "Checking file: "
+	echo -e "${serverfiles}"
+fi
+echo -e ""
+touch "${tmpdir}/detect_ldd.tmp"
+touch "${tmpdir}/detect_ldd_not_found.tmp"
+
+files=$(find "${serverfiles}" | wc -l)
+find "${serverfiles}" -type f -print0 \
+	| while IFS= read -r -d $'\0' line; do
+		if ldd "${line}" 2> /dev/null | grep -v "not a dynamic executable"; then
+			echo -e "${line}" >> "${tmpdir}/detect_ldd.tmp"
+			ldd "${line}" 2> /dev/null | grep -v "not a dynamic executable" >> "${tmpdir}/detect_ldd.tmp"
+			if ldd "${line}" 2> /dev/null | grep -v "not a dynamic executable" | grep "not found"; then
+				echo -e "${line}" >> "${tmpdir}/detect_ldd_not_found.tmp"
+				ldd "${line}" 2> /dev/null | grep -v "not a dynamic executable" | grep "not found" >> "${tmpdir}/detect_ldd_not_found.tmp"
+			fi
+		fi
+		echo -n "$i / $files" $'\r'
+		((i++))
+	done
+echo -e ""
+echo -e ""
+echo -e "All"
+echo -e "================================="
+cat "${tmpdir}/detect_ldd.tmp"
+
+echo -e ""
+echo -e "Not Found"
+echo -e "================================="
+cat "${tmpdir}/detect_ldd_not_found.tmp"
+
+rm -f "${tmpdir:?}/detect_ldd.tmp"
+rm -f "${tmpdir:?}/detect_ldd_not_found.tmp"
+
+core_exit.sh

+ 280 - 0
lgsm/functions/command_dev_query_raw.sh

@@ -0,0 +1,280 @@
+#!/bin/bash
+# LinuxGSM command_dev_query_raw.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Raw gamedig output of the server.
+
+commandname="DEV-QUERY-RAW"
+commandaction="Developer query raw"
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+fn_firstcommand_set
+
+check.sh
+info_game.sh
+info_distro.sh
+info_messages.sh
+
+echo -e ""
+echo -e "${lightgreen}Query IP Addresses${default}"
+echo -e "=================================================================="
+echo -e ""
+for queryip in "${queryips[@]}"; do
+	echo -e "${queryip}"
+done
+echo -e ""
+echo -e "${lightgreen}Game Server Ports${default}"
+echo -e "=================================================================="
+{
+	echo -e "${lightblue}Port Name \tPort Number \tStatus \tTCP \tUDP${default}"
+	if [ -v port ]; then
+		echo -e "Game: \t${port} \t$(ss -tupl | grep -c ${port}) \t$(ss -tupl | grep ${port} | grep tcp | awk '{ print $2 }') \t$(ss -tupl | grep ${port} | grep udp | awk '{ print $2 }')"
+	else
+		echo -e "Game:"
+	fi
+	if [ "${shortname}" == "rw" ]; then
+		if [ -v port2 ]; then
+			echo -e "Game+1: \t${port2} \t$(ss -tupl | grep -c ${port}) \t$(ss -tupl | grep ${port2} | grep tcp | awk '{ print $2 }') \t$(ss -tupl | grep ${port2} | grep udp | awk '{ print $2 }')"
+		else
+			echo -e "Game+1:"
+		fi
+
+		if [ -v port3 ]; then
+			echo -e "Game+2: \t${port3} \t$(ss -tupl | grep -c ${port}) \t$(ss -tupl | grep ${port3} | grep tcp | awk '{ print $2 }') \t$(ss -tupl | grep ${port3} | grep udp | awk '{ print $2 }')"
+		else
+			echo -e "Game+2:"
+		fi
+
+		if [ -v port4 ]; then
+			echo -e "Game+3: \t${port4} \t$(ss -tupl | grep -c ${port}) \t$(ss -tupl | grep ${port4} | grep tcp | awk '{ print $2 }') \t$(ss -tupl | grep ${port4} | grep udp | awk '{ print $2 }')"
+		else
+			echo -e "Game+3:"
+		fi
+	fi
+
+	if [ -v port401 ]; then
+		echo -e "Game+400: \t${port401} \t$(ss -tupl | grep -c ${port401}) \t$(ss -tupl | grep ${port401} | grep tcp | awk '{ print $2 }') \t$(ss -tupl | grep ${port401} | grep udp | awk '{ print $2 }')"
+	else
+		echo -e "Game+400:"
+	fi
+
+	if [ -v portipv6 ]; then
+		echo -e "Game ipv6: \t${portipv6} \t$(ss -tupl | grep -c ${portipv6}) \t$(ss -tupl | grep ${portipv6} | grep tcp | awk '{ print $2 }') \t$(ss -tupl | grep ${portipv6} | grep udp | awk '{ print $2 }')"
+	else
+		echo -e "Game ipv6:"
+	fi
+
+	if [ -v queryport ]; then
+		echo -e "Query: \t${queryport} \t$(ss -tupl | grep -c ${queryport}) \t$(ss -tupl | grep ${queryport} | grep tcp | awk '{ print $2 }') \t$(ss -tupl | grep ${queryport} | grep udp | awk '{ print $2 }')"
+	else
+		echo -e "Query:"
+	fi
+
+	if [ -v httpport ]; then
+		echo -e "HTTP: \t${httpport} \t$(ss -tupl | grep -c ${httpport}) \t$(ss -tupl | grep ${httpport} | grep tcp | awk '{ print $2 }') \t$(ss -tupl | grep ${httpport} | grep udp | awk '{ print $2 }')"
+	else
+		echo -e "HTTP:"
+	fi
+
+	if [ -v httpqueryport ]; then
+		echo -e "HTTP Query: \t${httpqueryport} \t$(ss -tupl | grep -c ${httpqueryport}) \t$(ss -tupl | grep ${httpqueryport} | grep tcp | awk '{ print $2 }') \t$(ss -tupl | grep ${httpqueryport} | grep udp | awk '{ print $2 }')"
+	else
+		echo -e "HTTP Query:"
+	fi
+
+	if [ -v webadminport ]; then
+		echo -e "Web Admin: \t${webadminport} \t$(ss -tupl | grep -c ${webadminport}) \t$(ss -tupl | grep ${webadminport} | grep tcp | awk '{ print $2 }') \t$(ss -tupl | grep ${webadminport} | grep udp | awk '{ print $2 }')"
+	else
+		echo -e "Web Admin:"
+	fi
+
+	if [ -v clientport ]; then
+		echo -e "Client: \t${clientport} \t$(ss -tupl | grep -c ${clientport}) \t$(ss -tupl | grep ${clientport} | grep tcp | awk '{ print $2 }') \t$(ss -tupl | grep ${clientport} | grep udp | awk '{ print $2 }')"
+	else
+		echo -e "Client:"
+	fi
+
+	if [ -v rconport ]; then
+		echo -e "RCON: \t${rconport} \t$(ss -tupl | grep -c ${rconport}) \t$(ss -tupl | grep ${rconport} | grep tcp | awk '{ print $2 }') \t$(ss -tupl | grep ${rconport} | grep udp | awk '{ print $2 }')"
+	else
+		echo -e "RCON:"
+	fi
+
+	if [ -v rawport ]; then
+		echo -e "RAW UDP Socket: \t${rawport} \t$(ss -tupl | grep -c ${rawport}) \t$(ss -tupl | grep ${rawport} | grep tcp | awk '{ print $2 }') \t$(ss -tupl | grep ${rawport} | grep udp | awk '{ print $2 }')"
+	else
+		echo -e "RAW UDP Socket:"
+	fi
+
+	if [ -v masterport ]; then
+		echo -e "Game: Master: \t${masterport} \t$(ss -tupl | grep -c ${masterport}) \t$(ss -tupl | grep ${masterport} | grep tcp | awk '{ print $2 }') \t$(ss -tupl | grep ${masterport} | grep udp | awk '{ print $2 }')"
+	else
+		echo -e "Game: Master:"
+	fi
+
+	if [ -v steamport ]; then
+		echo -e "Steam: \t${steamport} \t$(ss -tupl | grep -c ${steamport}) \t$(ss -tupl | grep ${steamport} | grep tcp | awk '{ print $2 }') \t$(ss -tupl | grep ${steamport} | grep udp | awk '{ print $2 }')"
+	else
+		echo -e "Steam:"
+	fi
+
+	if [ -v steamauthport ]; then
+		echo -e "Steam: Auth: \t${steamauthport} \t$(ss -tupl | grep -c ${steamauthport}) \t$(ss -tupl | grep ${steamauthport} | grep tcp | awk '{ print $2 }') \t$(ss -tupl | grep ${steamauthport} | grep udp | awk '{ print $2 }')"
+	else
+		echo -e "Steam: Auth:"
+	fi
+
+	if [ -v steammasterport ]; then
+		echo -e "Steam: Master: \t${steammasterport} \t$(ss -tupl | grep -c ${steammasterport}) \t$(ss -tupl | grep ${steammasterport} | grep tcp | awk '{ print $2 }') \t$(ss -tupl | grep ${steammasterport} | grep udp | awk '{ print $2 }')"
+	else
+		echo -e "Steam: Master:"
+	fi
+
+	if [ -v steamqueryport ]; then
+		echo -e "Steam: Query: \t${steamqueryport} \t$(ss -tupl | grep -c ${steamqueryport}) \t$(ss -tupl | grep ${steamqueryport} | grep tcp | awk '{ print $2 }') \t$(ss -tupl | grep ${steamqueryport} | grep udp | awk '{ print $2 }')"
+	else
+		echo -e "Steam: Query:"
+	fi
+	if [ -v beaconport ]; then
+		echo -e "Beacon: \t${beaconport} \t$(ss -tupl | grep -c ${beaconport}) \t$(ss -tupl | grep ${beaconport} | grep tcp | awk '{ print $2 }') \t$(ss -tupl | grep ${beaconport} | grep udp | awk '{ print $2 }')"
+	else
+		echo -e "Beacon:"
+	fi
+
+	if [ -v appport ]; then
+		echo -e "App: \t${appport} \t$(ss -tupl | grep -c ${appport}) \t$(ss -tupl | grep ${appport} | grep tcp | awk '{ print $2 }') \t$(ss -tupl | grep ${appport} | grep udp | awk '{ print $2 }')"
+	else
+		echo -e "App:"
+	fi
+
+	if [ -v telnetport ]; then
+		echo -e "Telnet: \t${telnetport} \t$(ss -tupl | grep -c ${telnetport}) \t$(ss -tupl | grep ${telnetport} | grep tcp | awk '{ print $2 }') \t$(ss -tupl | grep ${telnetport} | grep udp | awk '{ print $2 }')"
+	else
+		echo -e "Telnet:"
+	fi
+
+	if [ -v sourcetvport ]; then
+		echo -e "SourceTV: \t${sourcetvport} \t$(ss -tupl | grep -c ${sourcetvport}) \t$(ss -tupl | grep ${sourcetvport} | grep tcp | awk '{ print $2 }') \t$(ss -tupl | grep ${sourcetvport} | grep udp | awk '{ print $2 }')"
+	else
+		echo -e "SourceTV:"
+	fi
+
+	if [ -v fileport ]; then
+		echo -e "File: \t${fileport} \t$(ss -tupl | grep -c ${fileport}) \t$(ss -tupl | grep ${fileport} | grep tcp | awk '{ print $2 }') \t$(ss -tupl | grep ${fileport} | grep udp | awk '{ print $2 }')"
+	else
+		echo -e "File:"
+	fi
+
+	if [ -v udplinkport ]; then
+		echo -e "UDP Link: \t${udplinkport} \t$(ss -tupl | grep -c ${udplinkport}) \t$(ss -tupl | grep ${udplinkport} | grep tcp | awk '{ print $2 }') \t$(ss -tupl | grep ${udplinkport} | grep udp | awk '{ print $2 }')"
+	else
+		echo -e "UDP Link:"
+	fi
+
+	if [ -v voiceport ]; then
+		echo -e "Voice: \t${voiceport} \t$(ss -tupl | grep -c ${voiceport}) \t$(ss -tupl | grep ${voiceport} | grep tcp | awk '{ print $2 }') \t$(ss -tupl | grep ${voiceport} | grep udp | awk '{ print $2 }')"
+	else
+		echo -e "Voice:"
+	fi
+
+	if [ -v voiceunusedport ]; then
+		echo -e "Voice (Unused): \t${voiceunusedport} \t$(ss -tupl | grep -c ${voiceunusedport}) \t$(ss -tupl | grep ${voiceunusedport} | grep tcp | awk '{ print $2 }') \t$(ss -tupl | grep ${voiceunusedport} | grep udp | awk '{ print $2 }')"
+	else
+		echo -e "Voice (Unused):"
+	fi
+
+	if [ -v battleeyeport ]; then
+		echo -e "BattleEye: \t${battleeyeport} \t$(ss -tupl | grep -c ${battleeyeport}) \t$(ss -tupl | grep ${battleeyeport} | grep tcp | awk '{ print $2 }') \t$(ss -tupl | grep ${battleeyeport} | grep udp | awk '{ print $2 }')"
+	else
+		echo -e "BattleEye:"
+	fi
+
+	if [ -v statsport ]; then
+		echo -e "Stats: \t${battleeyeport} \t$(ss -tupl | grep -c ${statsport}) \t$(ss -tupl | grep ${statsport} | grep tcp | awk '{ print $2 }') \t$(ss -tupl | grep ${statsport} | grep udp | awk '{ print $2 }')"
+	else
+		echo -e "Stats:"
+	fi
+
+} | column -s $'\t' -t
+echo -e ""
+echo -e "${lightgreen}SS Output${default}"
+echo -e "================================="
+fn_info_message_ports
+echo -e ""
+echo -e "${lightgreen}Query Port - Raw Output${default}"
+echo -e "=================================================================="
+echo -e ""
+echo -e "PORT: ${port}"
+echo -e "QUERY PORT: ${queryport}"
+echo -e ""
+echo -e "${lightgreen}Gamedig Raw Output${default}"
+echo -e "================================="
+echo -e ""
+if [ ! "$(command -v gamedig 2> /dev/null)" ]; then
+	fn_print_failure_nl "gamedig not installed"
+fi
+if [ ! "$(command -v jq 2> /dev/null)" ]; then
+	fn_print_failure_nl "jq not installed"
+fi
+for queryip in "${queryips[@]}"; do
+	query_gamedig.sh
+	echo -e "${gamedigcmd}"
+	echo""
+	echo "${gamedigraw}" | jq
+done
+echo -e ""
+echo -e "${lightgreen}gsquery Raw Output${default}"
+echo -e "================================="
+echo -e ""
+for queryip in "${queryips[@]}"; do
+	echo -e "./query_gsquery.py -a \"${queryip}\" -p \"${queryport}\" -e \"${querytype}\""
+	echo -e ""
+	if [ ! -f "${functionsdir}/query_gsquery.py" ]; then
+		fn_fetch_file_github "lgsm/functions" "query_gsquery.py" "${functionsdir}" "chmodx" "norun" "noforce" "nohash"
+	fi
+	"${functionsdir}"/query_gsquery.py -a "${queryip}" -p "${queryport}" -e "${querytype}"
+done
+echo -e ""
+echo -e "${lightgreen}TCP Raw Output${default}"
+echo -e "================================="
+echo -e ""
+for queryip in "${queryips[@]}"; do
+	echo -e "bash -c 'exec 3<> /dev/tcp/'${queryip}'/'${queryport}''"
+	echo -e ""
+	timeout 3 bash -c 'exec 3<> /dev/tcp/'${queryip}'/'${queryport}''
+	querystatus="$?"
+	echo -e ""
+	if [ "${querystatus}" == "0" ]; then
+		echo -e "TCP query PASS"
+	else
+		echo -e "TCP query FAIL"
+	fi
+done
+echo -e ""
+echo -e "${lightgreen}Game Port - Raw Output${default}"
+echo -e "=================================================================="
+echo -e ""
+echo -e "${lightgreen}TCP Raw Output${default}"
+echo -e "================================="
+echo -e ""
+for queryip in "${queryips[@]}"; do
+	echo -e "bash -c 'exec 3<> /dev/tcp/'${queryip}'/'${port}''"
+	echo -e ""
+	timeout 3 bash -c 'exec 3<> /dev/tcp/'${queryip}'/'${port}''
+	querystatus="$?"
+	echo -e ""
+	if [ "${querystatus}" == "0" ]; then
+		echo -e "TCP query PASS"
+	else
+		echo -e "TCP query FAIL"
+	fi
+done
+echo -e ""
+echo -e "${lightgreen}Steam Master Server Response${default}"
+echo -e "=================================================================="
+echo -e ""
+echo -e "Response: ${displaymasterserver}"
+echo -e ""
+
+exitcode=0
+core_exit.sh

+ 26 - 0
lgsm/functions/command_donate.sh

@@ -0,0 +1,26 @@
+#!/bin/bash
+# LinuxGSM command_donate.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Shows ways to donate.
+
+commandname="DONATE"
+commandaction="Donate"
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+fn_firstcommand_set
+
+fn_print_ascii_logo
+echo -e "${lightyellow}Support LinuxGSM${default}"
+echo -e "================================="
+echo -e ""
+echo -e "Been using LinuxGSM?"
+echo -e "Consider donating to support development."
+echo -e ""
+echo -e "* ${lightblue}Patreon:${default} https://linuxgsm.com/patreon"
+echo -e "* ${lightblue}GitHub:${default} https://github.com/sponsors/dgibbs64"
+echo -e "* ${lightblue}PayPal:${default} https://linuxgsm.com/paypal"
+echo -e ""
+echo -e "LinuxGSM est. 2012"
+
+core_exit.sh

+ 442 - 0
lgsm/functions/command_fastdl.sh

@@ -0,0 +1,442 @@
+#!/bin/bash
+# LinuxGSM command_fastdl.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Creates a FastDL directory.
+
+commandname="FASTDL"
+commandaction="Fastdl"
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+fn_firstcommand_set
+
+check.sh
+
+# Directories.
+if [ -z "${webdir}" ]; then
+	webdir="${rootdir}/public_html"
+fi
+fastdldir="${webdir}/fastdl"
+addonsdir="${systemdir}/addons"
+# Server lua autorun dir, used to autorun lua on client connect to the server.
+luasvautorundir="${systemdir}/lua/autorun/server"
+luafastdlfile="lgsm_cl_force_fastdl.lua"
+luafastdlfullpath="${luasvautorundir}/${luafastdlfile}"
+
+# Check if bzip2 is installed.
+if [ ! "$(command -v bzip2 2> /dev/null)" ]; then
+	fn_print_fail "bzip2 is not installed"
+	fn_script_log_fatal "bzip2 is not installed"
+	core_exit.sh
+fi
+
+# Header
+fn_print_header
+echo -e "More info: https://docs.linuxgsm.com/commands/fastdl"
+echo -e ""
+
+# Prompts user for FastDL creation settings.
+echo -e "${commandaction} setup"
+echo -e "================================="
+
+# Prompt for clearing old files if directory was already here.
+if [ -d "${fastdldir}" ]; then
+	fn_print_warning_nl "FastDL directory already exists."
+	echo -e "${fastdldir}"
+	echo -e ""
+	if fn_prompt_yn "Overwrite existing directory?" Y; then
+		fn_script_log_info "Overwrite existing directory: YES"
+	else
+		core_exit.sh
+	fi
+fi
+
+# Garry's Mod Specific.
+if [ "${shortname}" == "gmod" ]; then
+	# Prompt for download enforcer, which is using a .lua addfile resource generator.
+	if fn_prompt_yn "Force clients to download files?" Y; then
+		luaresource="on"
+		fn_script_log_info "Force clients to download files: YES"
+	else
+		luaresource="off"
+		fn_script_log_info "Force clients to download filesr: NO"
+	fi
+fi
+
+# Clears any fastdl directory content.
+fn_clear_old_fastdl() {
+	# Clearing old FastDL.
+	if [ -d "${fastdldir}" ]; then
+		echo -en "clearing existing FastDL directory ${fastdldir}..."
+		rm -rf "${fastdldir:?}"
+		exitcode=$?
+		if [ "${exitcode}" != 0 ]; then
+			fn_print_fail_eol_nl
+			fn_script_log_fatal "Clearing existing FastDL directory ${fastdldir}"
+			core_exit.sh
+		else
+			fn_print_ok_eol_nl
+			fn_script_log_pass "Clearing existing FastDL directory ${fastdldir}"
+		fi
+	fi
+}
+
+fn_fastdl_dirs() {
+	# Check and create directories.
+	if [ ! -d "${webdir}" ]; then
+		echo -en "creating web directory ${webdir}..."
+		mkdir -p "${webdir}"
+		exitcode=$?
+		if [ "${exitcode}" != 0 ]; then
+			fn_print_fail_eol_nl
+			fn_script_log_fatal "Creating web directory ${webdir}"
+			core_exit.sh
+		else
+			fn_print_ok_eol_nl
+			fn_script_log_pass "Creating web directory ${webdir}"
+		fi
+	fi
+	if [ ! -d "${fastdldir}" ]; then
+		echo -en "creating fastdl directory ${fastdldir}..."
+		mkdir -p "${fastdldir}"
+		exitcode=$?
+		if [ "${exitcode}" != 0 ]; then
+			fn_print_fail_eol_nl
+			fn_script_log_fatal "Creating fastdl directory ${fastdldir}"
+			core_exit.sh
+		else
+			fn_print_ok_eol_nl
+			fn_script_log_pass "Creating fastdl directory ${fastdldir}"
+		fi
+	fi
+}
+
+# Using this gist https://gist.github.com/agunnerson-ibm/efca449565a3e7356906
+fn_human_readable_file_size() {
+	local abbrevs=(
+		$((1 << 60)):ZB
+		$((1 << 50)):EB
+		$((1 << 40)):TB
+		$((1 << 30)):GB
+		$((1 << 20)):MB
+		$((1 << 10)):KB
+		$((1)):bytes
+	)
+
+	local bytes="${1}"
+	local precision="${2}"
+
+	if [[ "${bytes}" == "1" ]]; then
+		echo -e "1 byte"
+	else
+		for item in "${abbrevs[@]}"; do
+			local factor="${item%:*}"
+			local abbrev="${item#*:}"
+			if [[ "${bytes}" -ge "${factor}" ]]; then
+				size=$(bc -l <<< "${bytes} / ${factor}")
+				printf "%.*f %s\n" "${precision}" "${size}" "${abbrev}"
+				break
+			fi
+		done
+	fi
+}
+
+# Provides info about the fastdl directory content and prompts for confirmation.
+fn_fastdl_preview() {
+	# Remove any file list.
+	if [ -f "${tmpdir}/fastdl_files_to_compress.txt" ]; then
+		rm -f "${tmpdir:?}/fastdl_files_to_compress.txt"
+	fi
+	echo -e "analysing required files"
+	fn_script_log_info "Analysing required files"
+	# Garry's Mod
+	if [ "${shortname}" == "gmod" ]; then
+		cd "${systemdir}" || exit
+		allowed_extentions_array=("*.ain" "*.bsp" "*.mdl" "*.mp3" "*.ogg" "*.otf" "*.pcf" "*.phy" "*.png" "*.svg" "*.vtf" "*.vmt" "*.vtx" "*.vvd" "*.ttf" "*.wav")
+		for allowed_extention in "${allowed_extentions_array[@]}"; do
+			fileswc=0
+			tput sc
+			while read -r ext; do
+				((fileswc++))
+				tput rc
+				tput el
+				echo -e "gathering ${allowed_extention} : ${fileswc}..."
+				echo -e "${ext}" >> "${tmpdir}/fastdl_files_to_compress.txt"
+			done < <(find . -type f -iname "${allowed_extention}")
+			if [ ${fileswc} != 0 ]; then
+				fn_print_ok_eol_nl
+			else
+				fn_print_info_eol_nl
+			fi
+		done
+	# Source engine
+	else
+		fastdl_directories_array=("maps" "materials" "models" "particles" "sound" "resources")
+		for directory in "${fastdl_directories_array[@]}"; do
+			if [ -d "${systemdir}/${directory}" ]; then
+				if [ "${directory}" == "maps" ]; then
+					local allowed_extentions_array=("*.bsp" "*.ain" "*.nav" "*.jpg" "*.txt")
+				elif [ "${directory}" == "materials" ]; then
+					local allowed_extentions_array=("*.vtf" "*.vmt" "*.vbf" "*.png" "*.svg")
+				elif [ "${directory}" == "models" ]; then
+					local allowed_extentions_array=("*.vtx" "*.vvd" "*.mdl" "*.phy" "*.jpg" "*.png" "*.vmt" "*.vtf")
+				elif [ "${directory}" == "particles" ]; then
+					local allowed_extentions_array=("*.pcf")
+				elif [ "${directory}" == "sound" ]; then
+					local allowed_extentions_array=("*.wav" "*.mp3" "*.ogg")
+				fi
+				for allowed_extention in "${allowed_extentions_array[@]}"; do
+					fileswc=0
+					tput sc
+					while read -r ext; do
+						((fileswc++))
+						tput rc
+						tput el
+						echo -e "gathering ${directory} ${allowed_extention} : ${fileswc}..."
+						echo -e "${ext}" >> "${tmpdir}/fastdl_files_to_compress.txt"
+					done < <(find "${systemdir}/${directory}" -type f -iname "${allowed_extention}")
+					tput rc
+					tput el
+					echo -e "gathering ${directory} ${allowed_extention} : ${fileswc}..."
+					if [ ${fileswc} != 0 ]; then
+						fn_print_ok_eol_nl
+					else
+						fn_print_info_eol_nl
+					fi
+				done
+			fi
+		done
+	fi
+	if [ -f "${tmpdir}/fastdl_files_to_compress.txt" ]; then
+		echo -e "calculating total file size..."
+		fn_sleep_time
+		totalfiles=$(wc -l < "${tmpdir}/fastdl_files_to_compress.txt")
+		# Calculates total file size.
+		while read -r dufile; do
+			filesize=$(stat -c %s "${dufile}")
+			filesizetotal=$((filesizetotal + filesize))
+			exitcode=$?
+			if [ "${exitcode}" != 0 ]; then
+				fn_print_fail_eol_nl
+				fn_script_log_fatal "Calculating total file size."
+				core_exit.sh
+			fi
+		done < "${tmpdir}/fastdl_files_to_compress.txt"
+	else
+		fn_print_fail_eol_nl "generating file list"
+		fn_script_log_fatal "Generating file list."
+		core_exit.sh
+	fi
+	echo -e "about to compress ${totalfiles} files, total size $(fn_human_readable_file_size ${filesizetotal} 0)"
+	fn_script_log_info "${totalfiles} files, total size $(fn_human_readable_file_size ${filesizetotal} 0)"
+	rm -f "${tmpdir:?}/fastdl_files_to_compress.txt"
+	if ! fn_prompt_yn "Continue?" Y; then
+		fn_script_log "User exited"
+		core_exit.sh
+	fi
+}
+
+# Builds Garry's Mod fastdl directory content.
+fn_fastdl_gmod() {
+	cd "${systemdir}" || exit
+	for allowed_extention in "${allowed_extentions_array[@]}"; do
+		fileswc=0
+		tput sc
+		while read -r fastdlfile; do
+			((fileswc++))
+			tput rc
+			tput el
+			echo -e "copying ${allowed_extention} : ${fileswc}..."
+			cp --parents "${fastdlfile}" "${fastdldir}"
+			exitcode=$?
+			if [ "${exitcode}" != 0 ]; then
+				fn_print_fail_eol_nl
+				fn_script_log_fatal "Copying ${fastdlfile} > ${fastdldir}"
+				core_exit.sh
+			else
+				fn_script_log_pass "Copying ${fastdlfile} > ${fastdldir}"
+			fi
+		done < <(find . -type f -iname "${allowed_extention}")
+		if [ ${fileswc} != 0 ]; then
+			fn_print_ok_eol_nl
+		fi
+	done
+	# Correct addons directory structure for FastDL.
+	if [ -d "${fastdldir}/addons" ]; then
+		echo -en "updating addons file structure..."
+		cp -Rf "${fastdldir}"/addons/*/* "${fastdldir}"
+		exitcode=$?
+		if [ "${exitcode}" != 0 ]; then
+			fn_print_fail_eol_nl
+			fn_script_log_fatal "Updating addons file structure"
+			core_exit.sh
+		else
+			fn_print_ok_eol_nl
+			fn_script_log_pass "Updating addons file structure"
+		fi
+		# Clear addons directory in fastdl.
+		echo -en "clearing addons dir from fastdl dir..."
+		fn_sleep_time
+		rm -rf "${fastdldir:?}/addons"
+		exitcode=$?
+		if [ "${exitcode}" != 0 ]; then
+			fn_print_fail_eol_nl
+			fn_script_log_fatal "Clearing addons dir from fastdl dir"
+			core_exit.sh
+		else
+			fn_print_ok_eol_nl
+			fn_script_log_pass "Clearing addons dir from fastdl dir"
+		fi
+	fi
+	# Correct content that may be into a lua directory by mistake like some darkrpmodification addons.
+	if [ -d "${fastdldir}/lua" ]; then
+		echo -en "correcting DarkRP files..."
+		fn_sleep_time
+		cp -Rf "${fastdldir}/lua/"* "${fastdldir}"
+		exitcode=$?
+		if [ "${exitcode}" != 0 ]; then
+			fn_print_fail_eol_nl
+			fn_script_log_fatal "Correcting DarkRP files"
+			core_exit.sh
+		else
+			fn_print_ok_eol_nl
+			fn_script_log_pass "Correcting DarkRP files"
+		fi
+	fi
+	if [ -f "${tmpdir}/fastdl_files_to_compress.txt" ]; then
+		totalfiles=$(wc -l < "${tmpdir}/fastdl_files_to_compress.txt")
+		# Calculates total file size.
+		while read -r dufile; do
+			filesize=$(du -b "${dufile}" | awk '{ print $1 }')
+			filesizetotal=$((filesizetotal + filesize))
+		done < "${tmpdir}/fastdl_files_to_compress.txt"
+	fi
+}
+
+fn_fastdl_source() {
+	for directory in "${fastdl_directories_array[@]}"; do
+		if [ -d "${systemdir}/${directory}" ]; then
+			if [ "${directory}" == "maps" ]; then
+				local allowed_extentions_array=("*.bsp" "*.ain" "*.nav" "*.jpg" "*.txt")
+			elif [ "${directory}" == "materials" ]; then
+				local allowed_extentions_array=("*.vtf" "*.vmt" "*.vbf" "*.png" "*.svg")
+			elif [ "${directory}" == "models" ]; then
+				local allowed_extentions_array=("*.vtx" "*.vvd" "*.mdl" "*.phy" "*.jpg" "*.png")
+			elif [ "${directory}" == "particles" ]; then
+				local allowed_extentions_array=("*.pcf")
+			elif [ "${directory}" == "sound" ]; then
+				local allowed_extentions_array=("*.wav" "*.mp3" "*.ogg")
+			fi
+			for allowed_extention in "${allowed_extentions_array[@]}"; do
+				fileswc=0
+				tput sc
+				while read -r fastdlfile; do
+					((fileswc++))
+					tput rc
+					tput el
+					echo -e "copying ${directory} ${allowed_extention} : ${fileswc}..."
+					fn_sleep_time
+					# get relative path of file in the dir
+					tmprelfilepath="${fastdlfile#"${systemdir}/"}"
+					copytodir="${tmprelfilepath%/*}"
+					# create relative path for fastdl
+					if [ ! -d "${fastdldir}/${copytodir}" ]; then
+						mkdir -p "${fastdldir}/${copytodir}"
+					fi
+					cp "${fastdlfile}" "${fastdldir}/${copytodir}"
+					exitcode=$?
+					if [ "${exitcode}" != 0 ]; then
+						fn_print_fail_eol_nl
+						fn_script_log_fatal "Copying ${fastdlfile} > ${fastdldir}/${copytodir}"
+						core_exit.sh
+					else
+						fn_script_log_pass "Copying ${fastdlfile} > ${fastdldir}/${copytodir}"
+					fi
+				done < <(find "${systemdir}/${directory}" -type f -iname "${allowed_extention}")
+				if [ ${fileswc} != 0 ]; then
+					fn_print_ok_eol_nl
+				fi
+			done
+		fi
+	done
+}
+
+# Builds the fastdl directory content.
+fn_fastdl_build() {
+	# Copy all needed files for FastDL.
+	echo -e "copying files to ${fastdldir}"
+	fn_script_log_info "Copying files to ${fastdldir}"
+	if [ "${shortname}" == "gmod" ]; then
+		fn_fastdl_gmod
+		fn_fastdl_gmod_dl_enforcer
+	else
+		fn_fastdl_source
+	fi
+}
+
+# Generate lua file that will force download any file into the FastDL directory.
+fn_fastdl_gmod_dl_enforcer() {
+	# Clear old lua file.
+	if [ -f "${luafastdlfullpath}" ]; then
+		echo -en "removing existing download enforcer: ${luafastdlfile}..."
+		rm -f "${luafastdlfullpath:?}"
+		exitcode=$?
+		if [ "${exitcode}" != 0 ]; then
+			fn_print_fail_eol_nl
+			fn_script_log_fatal "Removing existing download enforcer ${luafastdlfullpath}"
+			core_exit.sh
+		else
+			fn_print_ok_eol_nl
+			fn_script_log_pass "Removing existing download enforcer ${luafastdlfullpath}"
+		fi
+	fi
+	# Generate new one if user said yes.
+	if [ "${luaresource}" == "on" ]; then
+		echo -en "creating new download enforcer: ${luafastdlfile}..."
+		touch "${luafastdlfullpath}"
+		# Read all filenames and put them into a lua file at the right path.
+		while read -r line; do
+			echo -e "resource.AddFile( \"${line}\" )" >> "${luafastdlfullpath}"
+		done < <(find "${fastdldir:?}" \( -type f ! -name "*.bz2" \) -printf '%P\n')
+		exitcode=$?
+		if [ "${exitcode}" != 0 ]; then
+			fn_print_fail_eol_nl
+			fn_script_log_fatal "Creating new download enforcer ${luafastdlfullpath}"
+			core_exit.sh
+		else
+			fn_print_ok_eol_nl
+			fn_script_log_pass "Creating new download enforcer ${luafastdlfullpath}"
+		fi
+	fi
+}
+
+# Compresses FastDL files using bzip2.
+fn_fastdl_bzip2() {
+	while read -r filetocompress; do
+		echo -en "\r\033[Kcompressing ${filetocompress}..."
+		bzip2 -f "${filetocompress}"
+		exitcode=$?
+		if [ "${exitcode}" != 0 ]; then
+			fn_print_fail_eol_nl
+			fn_script_log_fatal "Compressing ${filetocompress}"
+			core_exit.sh
+		else
+			fn_script_log_pass "Compressing ${filetocompress}"
+		fi
+	done < <(find "${fastdldir:?}" \( -type f ! -name "*.bz2" \))
+	fn_print_ok_eol_nl
+}
+
+# Run functions.
+fn_fastdl_preview
+fn_clear_old_fastdl
+fn_fastdl_dirs
+fn_fastdl_build
+fn_fastdl_bzip2
+# Finished message.
+echo -e "FastDL files are located in:"
+echo -e "${fastdldir}"
+echo -e "FastDL completed"
+fn_script_log_info "FastDL completed"
+
+core_exit.sh

+ 52 - 0
lgsm/functions/command_install.sh

@@ -0,0 +1,52 @@
+#!/bin/bash
+# LinuxGSM command_install.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Overall function for the installer.
+
+commandname="INSTALL"
+commandaction="Installing"
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+fn_firstcommand_set
+
+check.sh
+if [ "$(whoami)" == "root" ] && [ ! -f /.dockerenv ]; then
+	check_deps.sh
+else
+	install_header.sh
+	install_server_dir.sh
+	install_logs.sh
+	check_deps.sh
+	installflag=1
+	# Download and install.
+	if [ "${shortname}" == "ut2k4" ]; then
+		install_server_files.sh
+		install_ut2k4_key.sh
+	elif [ -z "${appid}" ]; then
+		install_server_files.sh
+	elif [ "${appid}" ]; then
+		install_steamcmd.sh
+		install_server_files.sh
+	fi
+
+	# Configuration.
+	install_config.sh
+	if [ -v gslt ]; then
+		install_gslt.sh
+	elif [ "${shortname}" == "dst" ]; then
+		install_dst_token.sh
+	elif [ "${shortname}" == "squad" ]; then
+		install_squad_license.sh
+	elif [ "${shortname}" == "ts3" ]; then
+		install_ts3db.sh
+	elif [ "${shortname}" == "mta" ]; then
+		command_install_resources_mta.sh
+		fn_firstcommand_reset
+	fi
+
+	fix.sh
+	install_stats.sh
+	install_complete.sh
+fi
+core_exit.sh

+ 32 - 0
lgsm/functions/command_install_resources_mta.sh

@@ -0,0 +1,32 @@
+#!/bin/bash
+# LinuxGSM command_install_resources_mta.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Installs the default resources for Multi Theft Auto.
+
+commandname="DEFAULT-RESOURCES"
+commandaction="Default Resources"
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+fn_firstcommand_set
+
+fn_install_resources() {
+	echo -e ""
+	echo -e "${lightyellow}Installing Default Resources${default}"
+	echo -e "================================="
+	fn_fetch_file "http://mirror.mtasa.com/mtasa/resources/mtasa-resources-latest.zip" "" "" "" "${tmpdir}" "mtasa-resources-latest.zip" "nochmodx" "norun" "noforce" "nohash"
+	fn_dl_extract "${tmpdir}" "mtasa-resources-latest.zip" "${resourcesdir}"
+	echo -e "Default Resources Installed."
+}
+
+fn_print_header
+
+if [ -z "${autoinstall}" ]; then
+	fn_print_warning_nl "Installing the default resources with existing resources may cause issues."
+	if fn_prompt_yn "Do you want to install MTA default resources?" Y; then
+		fn_install_resources
+	fi
+else
+	fn_print_warning_nl "Default resources are not installed when using ./${selfname} auto-install."
+	fn_print_information_nl "To install default resources use ./${selfname} install"
+fi

+ 135 - 0
lgsm/functions/command_mods_install.sh

@@ -0,0 +1,135 @@
+#!/bin/bash
+# LinuxGSM command_mods_install.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: List and installs available mods along with mods_list.sh and mods_core.sh.
+
+commandname="MODS-INSTALL"
+commandaction="Installing mods"
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+fn_firstcommand_set
+
+check.sh
+mods_core.sh
+
+fn_print_header
+
+# Displays a list of installed mods.
+fn_mods_installed_list
+if [ "${installedmodscount}" -gt "0" ]; then
+	echo -e "Installed addons/mods"
+	echo -e "================================="
+	# Go through all available commands, get details and display them to the user.
+	for ((llindex = 0; llindex < ${#installedmodslist[@]}; llindex++)); do
+		# Current mod is the "llindex" value of the array we're going through.
+		currentmod="${installedmodslist[llindex]}"
+		fn_mod_get_info
+		# Display mod info to the user.
+		echo -e " * ${green}${modcommand}${default}${default}"
+	done
+	echo -e ""
+fi
+
+echo -e "Available addons/mods"
+echo -e "================================="
+# Display available mods from mods_list.sh.
+# Set and reset vars
+compatiblemodslistindex=0
+# As long as we're within index values.
+while [ "${compatiblemodslistindex}" -lt "${#compatiblemodslist[@]}" ]; do
+	# Set values for convenience.
+	displayedmodname="${compatiblemodslist[compatiblemodslistindex]}"
+	displayedmodcommand="${compatiblemodslist[compatiblemodslistindex + 1]}"
+	displayedmodsite="${compatiblemodslist[compatiblemodslistindex + 2]}"
+	displayedmoddescription="${compatiblemodslist[compatiblemodslistindex + 3]}"
+	# Output mods to the user.
+	echo -e "${displayedmodname} - ${displayedmoddescription} - ${displayedmodsite}"
+	echo -e " * ${cyan}${displayedmodcommand}${default}"
+	# Increment index from the amount of values we just displayed.
+	let "compatiblemodslistindex+=4"
+	((totalmodsavailable++))
+done
+
+# If no mods are available for a specific game.
+if [ -z "${compatiblemodslist}" ]; then
+	fn_print_fail_nl "No mods are currently available for ${gamename}."
+	fn_script_log_info "No mods are currently available for ${gamename}."
+	core_exit.sh
+fi
+fn_script_log_info "${totalmodsavailable} addons/mods are available for install"
+
+## User selects a mod.
+echo -e ""
+while [[ ! " ${availablemodscommands[@]} " =~ " ${usermodselect} " ]]; do
+	echo -en "Enter an ${cyan}addon/mod${default} to ${green}install${default} (or exit to abort): "
+	read -r usermodselect
+	# Exit if user says exit or abort.
+	if [ "${usermodselect}" == "exit" ] || [ "${usermodselect}" == "abort" ]; then
+		core_exit.sh
+	# Supplementary output upon invalid user input.
+	elif [[ ! " ${availablemodscommands[@]} " =~ " ${usermodselect} " ]]; then
+		fn_print_error2_nl "${usermodselect} is not a valid addon/mod."
+	fi
+done
+# Get mod info.
+currentmod="${usermodselect}"
+fn_mod_get_info
+
+echo -e ""
+echo -e "Installing ${modprettyname}"
+echo -e "================================="
+fn_script_log_info "${modprettyname} selected for install"
+
+# Check if the mod is already installed and warn the user.
+if [ -f "${modsinstalledlistfullpath}" ]; then
+	if [ "$(sed -n "/^${modcommand}$/p" "${modsinstalledlistfullpath}")" ]; then
+		fn_print_warning_nl "${modprettyname} is already installed"
+		fn_script_log_warn "${modprettyname} is already installed"
+		echo -e " * Any configs may be overwritten."
+		if ! fn_prompt_yn "Continue?" Y; then
+			core_exit.sh
+		fi
+		fn_script_log_info "User selected to continue"
+	fi
+fi
+
+## Installation.
+# If amxmodx check if metamod exists first
+if [ "${modcommand}" == "amxmodx" ]; then
+	fn_mod_exist "metamod"
+fi
+
+if [ "${modcommand}" == "amxmodxcs" ] \
+	|| [ "${modcommand}" == "amxmodxdod" ] \
+	|| [ "${modcommand}" == "amxmodxtfc" ] \
+	|| [ "${modcommand}" == "amxmodxns" ] \
+	|| [ "${modcommand}" == "amxmodxts" ]; then
+	fn_mod_exist "amxmodx"
+fi
+
+fn_create_mods_dir
+fn_mods_clear_tmp_dir
+fn_mods_create_tmp_dir
+fn_mod_install_files
+fn_mod_lowercase
+fn_mod_create_filelist
+fn_mod_copy_destination
+fn_mod_add_list
+fn_mod_tidy_files_list
+fn_mods_clear_tmp_dir
+
+# Create/modify existing liblist.gam file for Metamod
+if [ "${modcommand}" == "metamod" ]; then
+	fn_mod_install_liblist_gam_file
+fi
+
+# Create/modify plugins.ini file for Metamod
+if [ "${modcommand}" == "amxmodx" ]; then
+	fn_mod_install_amxmodx_file
+fi
+
+echo -e "${modprettyname} installed"
+fn_script_log_pass "${modprettyname} installed."
+
+core_exit.sh

+ 153 - 0
lgsm/functions/command_mods_remove.sh

@@ -0,0 +1,153 @@
+#!/bin/bash
+# LinuxGSM command_mods_uninstall.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Uninstall mods along with mods_list.sh and mods_core.sh.
+
+commandname="MODS-REMOVE"
+commandaction="Removing mods"
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+fn_firstcommand_set
+
+check.sh
+mods_core.sh
+fn_mods_check_installed
+
+fn_print_header
+echo -e "Remove addons/mods"
+echo -e "================================="
+
+# Displays list of installed mods.
+# Generates list to display to user.
+fn_mods_installed_list
+for ((mlindex = 0; mlindex < ${#installedmodslist[@]}; mlindex++)); do
+	# Current mod is the "mlindex" value of the array we are going through.
+	currentmod="${installedmodslist[mlindex]}"
+	# Get mod info.
+	fn_mod_get_info
+	# Display mod info to the user.
+	echo -e "${red}${modcommand}${default} - ${modprettyname} - ${moddescription}"
+done
+
+echo -e ""
+# Keep prompting as long as the user input doesn't correspond to an available mod.
+while [[ ! " ${installedmodslist[@]} " =~ " ${usermodselect} " ]]; do
+	echo -en "Enter an ${cyan}addon/mod${default} to ${red}remove${default} (or exit to abort): "
+	read -r usermodselect
+	# Exit if user says exit or abort.
+	if [ "${usermodselect}" == "exit" ] || [ "${usermodselect}" == "abort" ]; then
+		core_exit.sh
+	# Supplementary output upon invalid user input.
+	elif [[ ! " ${availablemodscommands[@]} " =~ " ${usermodselect} " ]]; then
+		fn_print_error2_nl "${usermodselect} is not a valid addon/mod."
+	fi
+done
+
+fn_print_warning_nl "You are about to remove ${cyan}${usermodselect}${default}."
+echo -e " * Any custom files/configuration will be removed."
+if ! fn_prompt_yn "Continue?" Y; then
+	core_exit.sh
+fi
+
+currentmod="${usermodselect}"
+fn_mod_get_info
+fn_check_mod_files_list
+
+# Uninstall the mod.
+fn_script_log_info "Removing ${modsfilelistsize} files from ${modprettyname}"
+echo -e "removing ${modprettyname}"
+echo -e "* ${modsfilelistsize} files to be removed"
+echo -e "* location: ${modinstalldir}"
+fn_sleep_time
+# Go through every file and remove it.
+modfileline="1"
+tput sc
+while [ "${modfileline}" -le "${modsfilelistsize}" ]; do
+	# Current line defines current file to remove.
+	currentfileremove=$(sed "${modfileline}q;d" "${modsdir}/${modcommand}-files.txt")
+	# If file or directory exists, then remove it.
+
+	if [ -f "${modinstalldir}/${currentfileremove}" ] || [ -d "${modinstalldir}/${currentfileremove}" ]; then
+		rm -rf "${modinstalldir:?}/${currentfileremove:?}"
+		((exitcode = $?))
+		if [ "${exitcode}" != 0 ]; then
+			fn_script_log_fatal "Removing ${modinstalldir}/${currentfileremove}"
+			break
+		else
+			fn_script_log_pass "Removing ${modinstalldir}/${currentfileremove}"
+		fi
+	fi
+	tput rc
+	tput el
+	echo -e "removing ${modprettyname} ${modfileline} / ${modsfilelistsize} : ${currentfileremove}..."
+	((modfileline++))
+done
+
+# Added logic not to fail since removing game specific mods (amxmodxcs) removes files that will
+# not be found when removing the base (amxmodx) mod
+if [ "${modcommand}" != "amxmodx" ]; then
+	if [ "${exitcode}" != 0 ]; then
+		fn_print_fail_eol_nl
+		core_exit.sh
+	else
+		fn_print_ok_eol_nl
+	fi
+else
+	fn_print_ok_eol_nl
+fi
+
+# Remove file list.
+echo -en "removing ${modcommand}-files.txt..."
+fn_sleep_time
+rm -rf "${modsdir:?}/${modcommand}-files.txt"
+exitcode=$?
+if [ "${exitcode}" != 0 ]; then
+	fn_script_log_fatal "Removing ${modsdir}/${modcommand}-files.txt"
+	fn_print_fail_eol_nl
+	core_exit.sh
+else
+	fn_script_log_pass "Removing ${modsdir}/${modcommand}-files.txt"
+	fn_print_ok_eol_nl
+fi
+
+# Remove mods from installed mods list.
+echo -en "removing ${modcommand} from ${modsinstalledlist}..."
+fn_sleep_time
+
+sed -i "/^${modcommand}$/d" "${modsinstalledlistfullpath}"
+exitcode=$?
+if [ "${exitcode}" != 0 ]; then
+	fn_script_log_fatal "Removing ${modcommand} from ${modsinstalledlist}"
+	fn_print_fail_eol_nl
+	core_exit.sh
+else
+	fn_script_log_pass "Removing ${modcommand} from ${modsinstalledlist}"
+	fn_print_ok_eol_nl
+fi
+
+# Oxide fix
+# Oxide replaces server files, so a validate is required after uninstall.
+if [ "${engine}" == "unity3d" ] && [[ "${modprettyname}" == *"Oxide"* ]]; then
+	fn_print_information_nl "Validating to restore original ${gamename} files replaced by Oxide"
+	fn_script_log "Validating to restore original ${gamename} files replaced by Oxide"
+	exitbypass="1"
+	command_validate.sh
+	fn_firstcommand_reset
+	unset exitbypass
+fi
+
+# Remove/modify existing liblist.gam file for Metamod
+if [ "${modcommand}" == "metamod" ]; then
+	fn_mod_remove_liblist_gam_file
+fi
+
+# Remove/modify plugins.ini file for AMX Mod X
+if [ "${modcommand}" == "amxmodx" ]; then
+	fn_mod_remove_amxmodx_file
+fi
+
+echo -e "${modprettyname} removed"
+fn_script_log "${modprettyname} removed"
+
+core_exit.sh

+ 109 - 0
lgsm/functions/command_mods_update.sh

@@ -0,0 +1,109 @@
+#!/bin/bash
+# LinuxGSM command_mods_update.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Updates installed mods along with mods_list.sh and mods_core.sh.
+
+commandname="MODS-UPDATE"
+commandaction="Updating mods"
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+fn_firstcommand_set
+
+check.sh
+mods_core.sh
+
+# Prevents specific files being overwritten upon update (set by ${modkeepfiles}).
+# For that matter, remove cfg files after extraction before copying them to destination.
+fn_remove_cfg_files() {
+	if [ "${modkeepfiles}" != "OVERWRITE" ] && [ "${modkeepfiles}" != "NOUPDATE" ]; then
+		echo -e "the following files/directories will be preserved:"
+		fn_sleep_time
+		# Count how many files there are to remove.
+		filestopreserve=$(echo -e "${modkeepfiles}" | awk -F ';' '{ print NF }')
+		# Test all subvalues of "modkeepfiles" using the ";" separator.
+		for ((preservefilesindex = 1; preservefilesindex < filestopreserve; preservefilesindex++)); do
+			# Put the current file we are looking for into a variable.
+			filetopreserve=$(echo -e "${modkeepfiles}" | awk -F ';' -v x=${preservefilesindex} '{ print $x }')
+			echo -e "	* serverfiles/${filetopreserve}"
+			# If it matches an existing file that have been extracted delete the file.
+			if [ -f "${extractdest}/${filetopreserve}" ] || [ -d "${extractdest}/${filetopreserve}" ]; then
+				rm -r "${extractdest:?}/${filetopreserve}"
+				# Write the file path in a tmp file, to rebuild a full file list as it is rebuilt upon update.
+				if [ ! -f "${modsdir}/.removedfiles.tmp" ]; then
+					touch "${modsdir}/.removedfiles.tmp"
+				fi
+				echo -e "${filetopreserve}" >> "${modsdir}/.removedfiles.tmp"
+			fi
+		done
+	fi
+}
+
+fn_print_dots "Update addons/mods"
+fn_mods_check_installed
+fn_print_info_nl "Update addons/mods: ${installedmodscount} addons/mods will be updated"
+fn_script_log_info "${installedmodscount} mods or addons will be updated"
+fn_mods_installed_list
+# Go through all available commands, get details and display them to the user.
+for ((ulindex = 0; ulindex < ${#installedmodslist[@]}; ulindex++)); do
+	# Current mod is the "ulindex" value of the array we're going through.
+	currentmod="${installedmodslist[ulindex]}"
+	fn_mod_get_info
+	# Display installed mods and the update policy.
+	if [ -z "${modkeepfiles}" ]; then
+		# If modkeepfiles is not set for some reason, that's a problem.
+		fn_script_log_error "Could not find update policy for ${modprettyname}"
+		fn_print_error_nl "Could not find update policy for ${modprettyname}"
+		exitcode="1"
+		core_exit.sh
+	# If the mod won't get updated.
+	elif [ "${modkeepfiles}" == "NOUPDATE" ]; then
+		echo -e "	* ${red}{modprettyname}${default} (won't be updated)"
+	# If the mode is just overwritten.
+	elif [ "${modkeepfiles}" == "OVERWRITE" ]; then
+		echo -e "	* ${modprettyname} (overwrite)"
+	else
+		echo -e "	* ${yellow}${modprettyname}${default} (retain common custom files)"
+	fi
+done
+
+## Update
+# List all installed mods and apply update.
+# Reset line value.
+installedmodsline="1"
+while [ "${installedmodsline}" -le "${installedmodscount}" ]; do
+	currentmod=$(sed "${installedmodsline}q;d" "${modsinstalledlistfullpath}")
+	if [ "${currentmod}" ]; then
+		fn_mod_get_info
+		# Don not update mod if the policy is set to "NOUPDATE".
+		if [ "${modkeepfiles}" == "NOUPDATE" ]; then
+			fn_print_info "${modprettyname} will not be updated to preserve custom files"
+			fn_script_log_info "${modprettyname} will not be updated to preserve custom files"
+		else
+			echo -e ""
+			echo -e "==> Updating ${modprettyname}"
+			fn_create_mods_dir
+			fn_mods_clear_tmp_dir
+			fn_mods_create_tmp_dir
+			fn_mod_install_files
+			fn_mod_lowercase
+			fn_remove_cfg_files
+			fn_mod_create_filelist
+			fn_mod_copy_destination
+			fn_mod_add_list
+			fn_mod_tidy_files_list
+			fn_mods_clear_tmp_dir
+		fi
+		((installedmodsline++))
+	else
+		fn_print_fail "No mod was selected"
+		fn_script_log_fatal "No mod was selected"
+		exitcode="1"
+		core_exit.sh
+	fi
+done
+echo -e ""
+fn_print_ok_nl "Mods update complete"
+fn_script_log_info "Mods update complete"
+
+core_exit.sh

+ 245 - 0
lgsm/functions/command_monitor.sh

@@ -0,0 +1,245 @@
+#!/bin/bash
+# LinuxGSM command_monitor.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Monitors server by checking for running processes
+# then passes to gamedig and gsquery.
+
+commandname="MONITOR"
+commandaction="Monitoring"
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+fn_firstcommand_set
+
+fn_monitor_check_lockfile() {
+	# Monitor does not run it lockfile is not found.
+	if [ ! -f "${lockdir}/${selfname}.lock" ]; then
+		fn_print_dots "Checking lockfile: "
+		fn_print_checking_eol
+		fn_script_log_info "Checking lockfile: CHECKING"
+		fn_print_error "Checking lockfile: No lockfile found: "
+		fn_print_error_eol_nl
+		fn_script_log_error "Checking lockfile: No lockfile found: ERROR"
+		echo -e "* Start ${selfname} to run monitor."
+		core_exit.sh
+	fi
+
+	# Fix if lockfile is not unix time or contains letters
+	if [ -f "${lockdir}/${selfname}.lock" ] && [[ "$(head -n 1 "${lockdir}/${selfname}.lock")" =~ [A-Za-z] ]]; then
+		date '+%s' > "${lockdir}/${selfname}.lock"
+		echo "${version}" >> "${lockdir}/${selfname}.lock"
+		echo "${port}" >> "${lockdir}/${selfname}.lock"
+	fi
+}
+
+fn_monitor_check_update() {
+	# Monitor will check if update is already running.
+	if [ "$(pgrep "${selfname} update" | wc -l)" != "0" ]; then
+		fn_print_dots "Checking active updates: "
+		fn_print_checking_eol
+		fn_script_log_info "Checking active updates: CHECKING"
+		fn_print_error_nl "Checking active updates: SteamCMD is currently checking for updates: "
+		fn_print_error_eol
+		fn_script_log_error "Checking active updates: SteamCMD is currently checking for updates: ERROR"
+		core_exit.sh
+	fi
+}
+
+fn_monitor_check_session() {
+	fn_print_dots "Checking session: "
+	fn_print_checking_eol
+	fn_script_log_info "Checking session: CHECKING"
+	# uses status var from check_status.sh
+	if [ "${status}" != "0" ]; then
+		fn_print_ok "Checking session: "
+		fn_print_ok_eol_nl
+		fn_script_log_pass "Checking session: OK"
+	else
+		fn_print_error "Checking session: "
+		fn_print_fail_eol_nl
+		fn_script_log_fatal "Checking session: FAIL"
+		alert="restart"
+		alert.sh
+		fn_script_log_info "Checking session: Monitor is restarting ${selfname}"
+		command_restart.sh
+		core_exit.sh
+	fi
+}
+
+fn_monitor_check_queryport() {
+	# Monitor will check queryport is set before continuing.
+	if [ -z "${queryport}" ] || [ "${queryport}" == "0" ]; then
+		fn_print_dots "Checking port: "
+		fn_print_checking_eol
+		fn_script_log_info "Checking port: CHECKING"
+		if [ -n "${rconenabled}" ] && [ "${rconenabled}" != "true" ] && [ "${shortname}" == "av" ]; then
+			fn_print_warn "Checking port: Unable to query, rcon is not enabled"
+			fn_script_log_warn "Checking port: Unable to query, rcon is not enabled"
+		else
+			fn_print_error "Checking port: Unable to query, queryport is not set"
+			fn_script_log_error "Checking port: Unable to query, queryport is not set"
+		fi
+		core_exit.sh
+	fi
+}
+
+fn_query_gsquery() {
+	if [ ! -f "${functionsdir}/query_gsquery.py" ]; then
+		fn_fetch_file_github "lgsm/functions" "query_gsquery.py" "${functionsdir}" "chmodx" "norun" "noforce" "nohash"
+	fi
+	"${functionsdir}"/query_gsquery.py -a "${queryip}" -p "${queryport}" -e "${querytype}" > /dev/null 2>&1
+	querystatus="$?"
+}
+
+fn_query_tcp() {
+	bash -c 'exec 3<> /dev/tcp/'${queryip}'/'${queryport}'' > /dev/null 2>&1
+	querystatus="$?"
+}
+
+fn_monitor_query() {
+	# Will loop and query up to 5 times every 15 seconds.
+	# Query will wait up to 60 seconds to confirm server is down as server can become non-responsive during map changes.
+	totalseconds=0
+	for queryattempt in {1..5}; do
+		for queryip in "${queryips[@]}"; do
+			fn_print_dots "Querying port: ${querymethod}: ${queryip}:${queryport} : ${totalseconds}/${queryattempt}: "
+			fn_print_querying_eol
+			fn_script_log_info "Querying port: ${querymethod}: ${queryip}:${queryport} : ${queryattempt} : QUERYING"
+			# querydelay
+			if [ "$(head -n 1 "${lockdir}/${selfname}.lock")" -gt "$(date "+%s" -d "${querydelay} mins ago")" ]; then
+				fn_print_ok "Querying port: ${querymethod}: ${ip}:${queryport} : ${totalseconds}/${queryattempt}: "
+				fn_print_delay_eol_nl
+				fn_script_log_info "Querying port: ${querymethod}: ${ip}:${queryport} : ${queryattempt} : DELAY"
+				fn_script_log_info "Query bypassed: ${gameservername} started less than ${querydelay} minutes ago"
+				fn_script_log_info "Server started: $(date -d @$(head -n 1 "${lockdir}/${selfname}.lock"))"
+				fn_script_log_info "Current time: $(date)"
+				monitorpass=1
+				core_exit.sh
+			# will use query method selected in fn_monitor_loop
+			# gamedig
+			elif [ "${querymethod}" == "gamedig" ]; then
+				query_gamedig.sh
+			# gsquery
+			elif [ "${querymethod}" == "gsquery" ]; then
+				fn_query_gsquery
+			#tcp query
+			elif [ "${querymethod}" == "tcp" ]; then
+				fn_query_tcp
+			fi
+
+			if [ "${querystatus}" == "0" ]; then
+				# Server query OK.
+				fn_print_ok "Querying port: ${querymethod}: ${queryip}:${queryport} : ${totalseconds}/${queryattempt}: "
+				fn_print_ok_eol_nl
+				fn_script_log_pass "Querying port: ${querymethod}: ${queryip}:${queryport} : ${queryattempt} : OK"
+				monitorpass=1
+				if [ "${querystatus}" == "0" ]; then
+					# Add query data to log.
+					if [ "${gdname}" ]; then
+						fn_script_log_info "Server name: ${gdname}"
+					fi
+					if [ "${gdplayers}" ]; then
+						fn_script_log_info "Players: ${gdplayers}/${gdmaxplayers}"
+					fi
+					if [ "${gdbots}" ]; then
+						fn_script_log_info "Bots: ${gdbots}"
+					fi
+					if [ "${gdmap}" ]; then
+						fn_script_log_info "Map: ${gdmap}"
+					fi
+					if [ "${gdgamemode}" ]; then
+						fn_script_log_info "Game Mode: ${gdgamemode}"
+					fi
+
+					# send LinuxGSM stats if monitor is OK.
+					if [ "${stats}" == "on" ] || [ "${stats}" == "y" ]; then
+						info_stats.sh
+					fi
+				fi
+				core_exit.sh
+			else
+				# Server query FAIL.
+				fn_print_fail "Querying port: ${querymethod}: ${queryip}:${queryport} : ${totalseconds}/${queryattempt}: "
+				fn_print_fail_eol
+				fn_script_log_warn "Querying port: ${querymethod}: ${queryip}:${queryport} : ${queryattempt} : FAIL"
+				# Monitor will try gamedig (if supported) for first 30s then gsquery before restarting.
+				# gsquery will fail if longer than 60s
+				if [ "${totalseconds}" -ge "59" ]; then
+					# Monitor will FAIL if over 60s and trigger gane server reboot.
+					fn_print_fail "Querying port: ${querymethod}: ${queryip}:${queryport} : ${totalseconds}/${queryattempt}: "
+					fn_print_fail_eol_nl
+					fn_script_log_warn "Querying port: ${querymethod}: ${queryip}:${queryport} : ${queryattempt} : FAIL"
+					# Send alert if enabled.
+					alert="restartquery"
+					alert.sh
+					command_restart.sh
+					fn_firstcommand_reset
+					core_exit.sh
+				fi
+			fi
+		done
+		# Second counter will wait for 15s before breaking loop.
+		for seconds in {1..15}; do
+			fn_print_fail "Querying port: ${querymethod}: ${ip}:${queryport} : ${totalseconds}/${queryattempt} : ${cyan}WAIT${default}"
+			sleep 0.5
+			totalseconds=$((totalseconds + 1))
+			if [ "${seconds}" == "15" ]; then
+				break
+			fi
+		done
+	done
+}
+
+fn_monitor_loop() {
+	# loop though query methods selected by querymode.
+	totalseconds=0
+	if [ "${querymode}" == "2" ]; then
+		local query_methods_array=(gamedig gsquery)
+	elif [ "${querymode}" == "3" ]; then
+		local query_methods_array=(gamedig)
+	elif [ "${querymode}" == "4" ]; then
+		local query_methods_array=(gsquery)
+	elif [ "${querymode}" == "5" ]; then
+		local query_methods_array=(tcp)
+	fi
+	for querymethod in "${query_methods_array[@]}"; do
+		# Will check if gamedig is installed and bypass if not.
+		if [ "${querymethod}" == "gamedig" ]; then
+			if [ "$(command -v gamedig 2> /dev/null)" ] && [ "$(command -v jq 2> /dev/null)" ]; then
+				if [ -z "${monitorpass}" ]; then
+					fn_monitor_query
+				fi
+			else
+				fn_script_log_info "gamedig is not installed"
+				fn_script_log_info "https://docs.linuxgsm.com/requirements/gamedig"
+			fi
+		else
+			# will not query if query already passed.
+			if [ -z "${monitorpass}" ]; then
+				fn_monitor_query
+			fi
+		fi
+	done
+}
+
+monitorflag=1
+check.sh
+core_logs.sh
+info_game.sh
+
+# query pre-checks
+fn_monitor_check_lockfile
+fn_monitor_check_update
+fn_monitor_check_session
+# Monitor will not continue if session only check.
+if [ "${querymode}" != "1" ]; then
+	fn_monitor_check_queryport
+
+	# Add a querydelay of 1 min if var missing.
+	if [ -z "${querydelay}" ]; then
+		querydelay="1"
+	fi
+
+	fn_monitor_loop
+fi
+core_exit.sh

+ 78 - 0
lgsm/functions/command_postdetails.sh

@@ -0,0 +1,78 @@
+#!/bin/bash
+# LinuxGSM command_postdetails.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Strips sensitive information out of Details output.
+
+commandname="POST-DETAILS"
+commandaction="Posting details"
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+fn_firstcommand_set
+
+posttarget="https://termbin.com"
+
+# source all of the functions defined in the details command.
+info_messages.sh
+
+fn_bad_postdetailslog() {
+	fn_print_fail_nl "Unable to create temporary file ${postdetailslog}."
+	core_exit.sh
+}
+
+# Remove any existing postdetails.log file.
+if [ -f "${postdetailslog}" ]; then
+	rm -f "${postdetailslog:?}"
+fi
+
+# Rather than a one-pass sed parser, default to using a temporary directory.
+if [ "${exitbypass}" ]; then
+	postdetailslog="${alertlog}"
+else
+	# Run checks and gathers details to display.
+	check.sh
+	info_game.sh
+	info_distro.sh
+	info_messages.sh
+	for queryip in "${queryips[@]}"; do
+		query_gamedig.sh
+		if [ "${querystatus}" == "0" ]; then
+			break
+		fi
+	done
+	touch "${postdetailslog}" || fn_bad_postdetailslog
+	{
+		fn_info_message_distro
+		fn_info_message_server_resource
+		fn_info_message_gameserver_resource
+		fn_info_message_gameserver
+		fn_info_message_script
+		fn_info_message_backup
+		# Some game servers do not have parms.
+		if [ "${shortname}" != "jc2" ] && [ "${shortname}" != "jc3" ] && [ "${shortname}" != "dst" ] && [ "${shortname}" != "pz" ] && [ "${engine}" != "renderware" ]; then
+			fn_info_message_commandlineparms
+		fi
+		fn_info_message_ports_edit
+		fn_info_message_ports
+		fn_info_message_select_engine
+		fn_info_message_statusbottom
+	} | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g" | tee -a "${postdetailslog}" > /dev/null 2>&1
+fi
+
+fn_print_dots "termbin.com"
+link=$(cat "${postdetailslog}" | nc termbin.com 9999 | tr -d '\n\0')
+fn_print_ok_nl "termbin.com for 30D"
+fn_script_log_pass "termbin.com for 30D"
+pdurl="${link}"
+
+if [ "${firstcommandname}" == "POST-DETAILS" ]; then
+	echo -e ""
+	echo -e "Please share the following url for support: "
+	echo -e "${pdurl}"
+fi
+fn_script_log_info "${pdurl}"
+alerturl="${pdurl}"
+
+if [ -z "${exitbypass}" ]; then
+	core_exit.sh
+fi

+ 18 - 0
lgsm/functions/command_restart.sh

@@ -0,0 +1,18 @@
+#!/bin/bash
+# LinuxGSM command_restart.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Restarts the server.
+
+commandname="MODS-INSTALL"
+commandaction="Restarting"
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+fn_firstcommand_set
+
+info_game.sh
+exitbypass=1
+command_stop.sh
+command_start.sh
+fn_firstcommand_reset
+core_exit.sh

+ 41 - 0
lgsm/functions/command_send.sh

@@ -0,0 +1,41 @@
+#!/bin/bash
+# LinuxGSM command_send.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Send command to the server tmux console.
+
+commandname="SEND"
+commandaction="Send"
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+fn_firstcommand_set
+
+check.sh
+if [ -z "${userinput2}" ]; then
+	fn_print_header
+	fn_print_information_nl "Send a command to the console."
+fi
+
+check_status.sh
+if [ "${status}" != "0" ]; then
+	if [ -n "${userinput2}" ]; then
+		commandtosend="${userinput2}"
+	else
+		echo ""
+		commandtosend=$(fn_prompt_message "send: ")
+	fi
+	echo ""
+	fn_print_dots "Sending command to console: \"${commandtosend}\""
+	tmux send-keys -t "${servicename}" "${commandtosend}" ENTER
+	fn_print_ok_nl "Sending command to console: \"${commandtosend}\""
+	fn_script_log_pass "Command \"${commandtosend}\" sent to console"
+else
+	fn_print_error_nl "Server not running"
+	fn_script_log_error "Failed to access: Server not running"
+	if fn_prompt_yn "Do you want to start the server?" Y; then
+		exitbypass=1
+		command_start.sh
+	fi
+fi
+
+core_exit.sh

+ 23 - 0
lgsm/functions/command_skeleton.sh

@@ -0,0 +1,23 @@
+#!/bin/bash
+# LinuxGSM command_skeleton.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Creates an copy of a game servers directorys.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+fn_print_dots "Creating skeleton directory"
+check.sh
+
+# Find all directorys and create them in the skel directory
+find "${rootdir}" -type d -not \( -path ./skel -prune \) | cpio -pdvm skel 2> /dev/null
+exitcode=$?
+if [ "${exitcode}" != 0 ]; then
+	fn_print_fail_nl "Creating skeleton directory"
+	fn_script_log_fatal "Creating skeleton directory"
+else
+	fn_print_ok_nl "Creating skeleton directory: ./skel"
+	fn_script_log_pass "Creating skeleton directory: ./skel"
+fi
+core_exit.sh

+ 224 - 0
lgsm/functions/command_start.sh

@@ -0,0 +1,224 @@
+#!/bin/bash
+# LinuxGSM command_start.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Starts the server.
+
+commandname="START"
+commandaction="Starting"
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+addtimestamp="gawk '{ print strftime(\\\"[$logtimestampformat]\\\"), \\\$0 }'"
+fn_firstcommand_set
+
+fn_start_teamspeak3() {
+	if [ ! -f "${servercfgfullpath}" ]; then
+		fn_print_warn_nl "${servercfgfullpath} is missing"
+		fn_script_log_warn "${servercfgfullpath} is missing"
+		echo "	* Creating blank ${servercfg}"
+		fn_script_log_info "Creating blank ${servercfg}"
+		fn_sleep_time
+		echo "	* ${servercfg} can remain blank by default."
+		fn_script_log_info "${servercfgfullpath} can remain blank by default."
+		fn_sleep_time
+		echo "	* ${servercfg} is located in ${servercfgfullpath}."
+		fn_script_log_info "${servercfg} is located in ${servercfgfullpath}."
+		sleep 5
+		touch "${servercfgfullpath}"
+	fi
+	# Accept license.
+	if [ ! -f "${executabledir}/.ts3server_license_accepted" ]; then
+		install_eula.sh
+	fi
+	fn_start_tmux
+}
+
+# This will allow the Jedi Knight 2 version to be printed in console on start.
+# Used to allow update to detect JK2MV server version.
+fn_start_jk2() {
+	fn_start_tmux
+	tmux send -t "${sessionname}" version ENTER > /dev/null 2>&1
+}
+
+fn_start_tmux() {
+	if [ "${parmsbypass}" ]; then
+		startparameters=""
+	fi
+	# check for tmux size variables.
+	if [[ "${servercfgtmuxwidth}" =~ ^[0-9]+$ ]]; then
+		sessionwidth="${servercfgtmuxwidth}"
+	else
+		sessionwidth="80"
+	fi
+	if [[ "${servercfgtmuxheight}" =~ ^[0-9]+$ ]]; then
+		sessionheight="${servercfgtmuxheight}"
+	else
+		sessionheight="23"
+	fi
+
+	# Log rotation.
+	fn_script_log_info "Rotating log files"
+	if [ "${engine}" == "unreal2" ] && [ -f "${gamelog}" ]; then
+		mv "${gamelog}" "${gamelogdate}"
+	fi
+	if [ -f "${lgsmlog}" ]; then
+		mv "${lgsmlog}" "${lgsmlogdate}"
+	fi
+	if [ -f "${consolelog}" ]; then
+		mv "${consolelog}" "${consolelogdate}"
+	fi
+
+	# Create lockfile
+	date '+%s' > "${lockdir}/${selfname}.lock"
+	echo "${version}" >> "${lockdir}/${selfname}.lock"
+	echo "${port}" >> "${lockdir}/${selfname}.lock"
+	fn_reload_startparameters
+
+	if [ "${shortname}" == "av" ]; then
+		cd "${systemdir}" || exit
+	else
+		cd "${executabledir}" || exit
+	fi
+
+	tmux new-session -d -x "${sessionwidth}" -y "${sessionheight}" -s "${sessionname}" "${preexecutable} ${executable} ${startparameters}" 2> "${lgsmlogdir}/.${selfname}-tmux-error.tmp"
+
+	# Create logfile.
+	touch "${consolelog}"
+
+	# Create last start lock file
+	date +%s > "${lockdir}/${selfname}-laststart.lock"
+
+	# tmux compiled from source will return "master", therefore ignore it.
+	if [ "${tmuxv}" == "master" ]; then
+		fn_script_log "tmux version: master (user compiled)"
+		echo -e "tmux version: master (user compiled)" >> "${consolelog}"
+		if [ "${consolelogging}" == "on" ] || [ -z "${consolelogging}" ]; then
+			if [ "$logtimestamp" == "on" ]; then
+				tmux pipe-pane -o -t "${sessionname}" "exec bash -c \"cat | $addtimestamp\" >> '${consolelog}'"
+			else
+				tmux pipe-pane -o -t "${sessionname}" "exec cat >> '${consolelog}'"
+			fi
+		fi
+
+	elif [ -n "${tmuxv}" ]; then
+		# tmux pipe-pane not supported in tmux versions < 1.6.
+		if [ "${tmuxvdigit}" -lt "16" ]; then
+			echo -e "Console logging disabled: tmux => 1.6 required
+			https://linuxgsm.com/tmux-upgrade
+			Currently installed: $(tmux -V)" > "${consolelog}"
+
+		# Console logging disabled: Bug in tmux 1.8 breaks logging.
+		elif [ "${tmuxvdigit}" -eq "18" ]; then
+			echo -e "Console logging disabled: Bug in tmux 1.8 breaks logging
+			https://linuxgsm.com/tmux-upgrade
+			Currently installed: $(tmux -V)" > "${consolelog}"
+		# Console logging enable or not set.
+		elif [ "${consolelogging}" == "on" ] || [ -z "${consolelogging}" ]; then
+			if [ "$logtimestamp" == "on" ]; then
+				tmux pipe-pane -o -t "${sessionname}" "exec bash -c \"cat | $addtimestamp\" >> '${consolelog}'"
+			else
+				tmux pipe-pane -o -t "${sessionname}" "exec cat >> '${consolelog}'"
+			fi
+		fi
+	else
+		echo -e "Unable to detect tmux version" >> "${consolelog}"
+		fn_script_log_warn "Unable to detect tmux version"
+	fi
+
+	# Console logging disabled.
+	if [ "${consolelogging}" == "off" ]; then
+		echo -e "Console logging disabled by user" >> "${consolelog}"
+		fn_script_log_info "Console logging disabled by user"
+	fi
+	fn_sleep_time
+
+	# If the server fails to start.
+	check_status.sh
+	if [ "${status}" == "0" ]; then
+		fn_print_fail_nl "Unable to start ${servername}"
+		fn_script_log_fatal "Unable to start ${servername}"
+		if [ -s "${lgsmlogdir}/.${selfname}-tmux-error.tmp" ]; then
+			fn_print_fail_nl "Unable to start ${servername}: tmux error:"
+			fn_script_log_fatal "Unable to start ${servername}: tmux error:"
+			echo -e ""
+			echo -e "Command"
+			echo -e "================================="
+			echo -e "tmux new-session -d -s \"${sessionname}\" \"${preexecutable} ${executable} ${startparameters}\"" | tee -a "${lgsmlog}"
+			echo -e ""
+			echo -e "Error"
+			echo -e "================================="
+			tee -a "${lgsmlog}" < "${lgsmlogdir}/.${selfname}-tmux-error.tmp"
+
+			# Detected error https://linuxgsm.com/support
+			if grep -c "Operation not permitted" "${lgsmlogdir}/.${selfname}-tmux-error.tmp"; then
+				echo -e ""
+				echo -e "Fix"
+				echo -e "================================="
+				if ! grep "tty:" /etc/group | grep "$(whoami)"; then
+					echo -e "$(whoami) is not part of the tty group."
+					fn_script_log_info "$(whoami) is not part of the tty group."
+					group=$(grep tty /etc/group)
+					echo -e ""
+					echo -e "	${group}"
+					fn_script_log_info "${group}"
+					echo -e ""
+					echo -e "Run the following command with root privileges."
+					echo -e ""
+					echo -e "	usermod -G tty $(whoami)"
+					echo -e ""
+					echo -e "https://linuxgsm.com/tmux-op-perm"
+					fn_script_log_info "https://linuxgsm.com/tmux-op-perm"
+				else
+					echo -e "No known fix currently. Please log an issue."
+					fn_script_log_info "No known fix currently. Please log an issue."
+					echo -e "https://linuxgsm.com/support"
+					fn_script_log_info "https://linuxgsm.com/support"
+				fi
+			fi
+		fi
+		core_exit.sh
+	else
+		fn_print_ok "${servername}"
+		fn_script_log_pass "Started ${servername}"
+	fi
+	rm -f "${lgsmlogdir:?}/.${selfname}-tmux-error.tmp" 2> /dev/null
+	echo -en "\n"
+}
+
+check.sh
+
+# Is the server already started.
+# $status comes from check_status.sh, which is run by check.sh for this command
+if [ "${status}" != "0" ]; then
+	fn_print_dots "${servername}"
+	fn_print_info_nl "${servername} is already running"
+	fn_script_log_error "${servername} is already running"
+	if [ -z "${exitbypass}" ]; then
+		core_exit.sh
+	fi
+fi
+if [ -z "${fixbypass}" ]; then
+	fix.sh
+fi
+info_game.sh
+core_logs.sh
+
+# Will check for updates is updateonstart is yes.
+if [ "${updateonstart}" == "yes" ] || [ "${updateonstart}" == "1" ] || [ "${updateonstart}" == "on" ]; then
+	exitbypass=1
+	unset updateonstart
+	command_update.sh
+	fn_firstcommand_reset
+fi
+
+fn_print_dots "${servername}"
+
+if [ "${shortname}" == "ts3" ]; then
+	fn_start_teamspeak3
+elif [ "${shortname}" == "jk2" ]; then
+	fn_start_jk2
+else
+	fn_start_tmux
+fi
+
+core_exit.sh

+ 283 - 0
lgsm/functions/command_stop.sh

@@ -0,0 +1,283 @@
+#!/bin/bash
+# LinuxGSM command_stop.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Stops the server.
+
+commandname="STOP"
+commandaction="Stopping"
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+fn_firstcommand_set
+
+# Attempts graceful shutdown by sending 'CTRL+c'.
+fn_stop_graceful_ctrlc() {
+	fn_print_dots "Graceful: CTRL+c"
+	fn_script_log_info "Graceful: CTRL+c"
+	# Sends quit.
+	tmux send-keys -t "${sessionname}" C-c > /dev/null 2>&1
+	# Waits up to 30 seconds giving the server time to shutdown gracefuly.
+	for seconds in {1..30}; do
+		check_status.sh
+		if [ "${status}" == "0" ]; then
+			fn_print_ok "Graceful: CTRL+c: ${seconds}: "
+			fn_print_ok_eol_nl
+			fn_script_log_pass "Graceful: CTRL+c: OK: ${seconds} seconds"
+			break
+		fi
+		sleep 1
+		fn_print_dots "Graceful: CTRL+c: ${seconds}"
+	done
+	check_status.sh
+	if [ "${status}" != "0" ]; then
+		fn_print_error "Graceful: CTRL+c: "
+		fn_print_fail_eol_nl
+		fn_script_log_error "Graceful: CTRL+c: FAIL"
+	fi
+}
+
+# Attempts graceful shutdown by sending a specified command.
+# Usage: fn_stop_graceful_cmd "console_command" "timeout_in_seconds"
+# e.g.: fn_stop_graceful_cmd "quit" "30"
+fn_stop_graceful_cmd() {
+	fn_print_dots "Graceful: sending \"${1}\""
+	fn_script_log_info "Graceful: sending \"${1}\""
+	# Sends specific stop command.
+	tmux send -t "${sessionname}" ENTER "${1}" ENTER > /dev/null 2>&1
+	# Waits up to ${seconds} seconds giving the server time to shutdown gracefully.
+	for ((seconds = 1; seconds <= ${2}; seconds++)); do
+		check_status.sh
+		if [ "${status}" == "0" ]; then
+			fn_print_ok "Graceful: sending \"${1}\": ${seconds}: "
+			fn_print_ok_eol_nl
+			fn_script_log_pass "Graceful: sending \"${1}\": OK: ${seconds} seconds"
+			break
+		fi
+		sleep 1
+		fn_print_dots "Graceful: sending \"${1}\": ${seconds}"
+	done
+	check_status.sh
+	if [ "${status}" != "0" ]; then
+		fn_print_error "Graceful: sending \"${1}\": "
+		fn_print_fail_eol_nl
+		fn_script_log_error "Graceful: sending \"${1}\": FAIL"
+	fi
+}
+
+# Attempts graceful shutdown of goldsrc using rcon 'quit' command.
+# There is only a 3 second delay before a forced a tmux shutdown
+# as GoldSrc servers 'quit' command does a restart rather than shutdown.
+fn_stop_graceful_goldsrc() {
+	fn_print_dots "Graceful: sending \"quit\""
+	fn_script_log_info "Graceful: sending \"quit\""
+	# sends quit
+	tmux send -t "${sessionname}" quit ENTER > /dev/null 2>&1
+	# Waits 3 seconds as goldsrc servers restart with the quit command.
+	for seconds in {1..3}; do
+		sleep 1
+		fn_print_dots "Graceful: sending \"quit\": ${seconds}"
+	done
+	fn_print_ok "Graceful: sending \"quit\": ${seconds}: "
+	fn_print_ok_eol_nl
+	fn_script_log_pass "Graceful: sending \"quit\": OK: ${seconds} seconds"
+}
+
+# telnet command for sdtd graceful shutdown.
+fn_stop_graceful_sdtd_telnet() {
+	if [ -z "${telnetpass}" ] || [ "${telnetpass}" == "NOT SET" ]; then
+		sdtd_telnet_shutdown=$(expect -c '
+		proc abort {} {
+			puts "Timeout or EOF\n"
+			exit 1
+		}
+		spawn telnet '"${telnetip}"' '"${telnetport}"'
+		expect {
+			"session."  { send "shutdown\r" }
+			default         abort
+		}
+		expect { eof }
+		puts "Completed.\n"
+		')
+	else
+		sdtd_telnet_shutdown=$(expect -c '
+		proc abort {} {
+			puts "Timeout or EOF\n"
+			exit 1
+		}
+		spawn telnet '"${telnetip}"' '"${telnetport}"'
+		expect {
+			"password:"     { send "'"${telnetpass}"'\r" }
+			default         abort
+		}
+		expect {
+			"session."  { send "shutdown\r" }
+			default         abort
+		}
+		expect { eof }
+		puts "Completed.\n"
+		')
+	fi
+}
+
+# Attempts graceful shutdown of 7 Days To Die using telnet.
+fn_stop_graceful_sdtd() {
+	fn_print_dots "Graceful: telnet"
+	fn_script_log_info "Graceful: telnet"
+	if [ "${telnetenabled}" == "false" ]; then
+		fn_print_info_nl "Graceful: telnet: DISABLED: Enable in ${servercfg}"
+	elif [ "$(command -v expect 2> /dev/null)" ]; then
+		# Tries to shutdown with both localhost and server IP.
+		for telnetip in 127.0.0.1 ${ip}; do
+			fn_print_dots "Graceful: telnet: ${telnetip}:${telnetport}"
+			fn_script_log_info "Graceful: telnet: ${telnetip}:${telnetport}"
+			fn_stop_graceful_sdtd_telnet
+			completed=$(echo -en "\n ${sdtd_telnet_shutdown}" | grep "Completed.")
+			refused=$(echo -en "\n ${sdtd_telnet_shutdown}" | grep "Timeout or EOF")
+			if [ "${refused}" ]; then
+				fn_print_error "Graceful: telnet: ${telnetip}:${telnetport} : "
+				fn_print_fail_eol_nl
+				fn_script_log_error "Graceful: telnet:  ${telnetip}:${telnetport} : FAIL"
+			elif [ "${completed}" ]; then
+				break
+			fi
+		done
+
+		# If telnet shutdown was successful will use telnet again to check
+		# the connection has closed, confirming that the tmux session can now be killed.
+		if [ "${completed}" ]; then
+			for seconds in {1..30}; do
+				fn_stop_graceful_sdtd_telnet
+				refused=$(echo -en "\n ${sdtd_telnet_shutdown}" | grep "Timeout or EOF")
+				if [ "${refused}" ]; then
+					fn_print_ok "Graceful: telnet: ${telnetip}:${telnetport} : "
+					fn_print_ok_eol_nl
+					fn_script_log_pass "Graceful: telnet: ${telnetip}:${telnetport} : ${seconds} seconds"
+					break
+				fi
+				sleep 1
+				fn_print_dots "Graceful: telnet: ${seconds}"
+			done
+		# If telnet shutdown fails tmux shutdown will be used, this risks loss of world save.
+		else
+			if [ "${refused}" ]; then
+				fn_print_error "Graceful: telnet: "
+				fn_print_fail_eol_nl
+				fn_script_log_error "Graceful: telnet: ${telnetip}:${telnetport} : FAIL"
+			else
+				fn_print_error_nl "Graceful: telnet: Unknown error"
+				fn_script_log_error "Graceful: telnet: Unknown error"
+			fi
+			echo -en "\n" | tee -a "${lgsmlog}"
+			echo -en "Telnet output:" | tee -a "${lgsmlog}"
+			echo -en "\n ${sdtd_telnet_shutdown}" | tee -a "${lgsmlog}"
+			echo -en "\n\n" | tee -a "${lgsmlog}"
+		fi
+	else
+		fn_print_warn "Graceful: telnet: expect not installed: "
+		fn_print_fail_eol_nl
+		fn_script_log_warn "Graceful: telnet: expect not installed: FAIL"
+	fi
+}
+
+# Attempts graceful shutdown by sending /save /stop.
+fn_stop_graceful_avorion() {
+	fn_print_dots "Graceful: /save /stop"
+	fn_script_log_info "Graceful: /save /stop"
+	# Sends /save.
+	tmux send-keys -t "${sessionname}" /save ENTER > /dev/null 2>&1
+	sleep 5
+	# Sends /quit.
+	tmux send-keys -t "${sessionname}" /stop ENTER > /dev/null 2>&1
+	# Waits up to 30 seconds giving the server time to shutdown gracefuly.
+	for seconds in {1..30}; do
+		check_status.sh
+		if [ "${status}" == "0" ]; then
+			fn_print_ok "Graceful: /save /stop: ${seconds}: "
+			fn_print_ok_eol_nl
+			fn_script_log_pass "Graceful: /save /stop: OK: ${seconds} seconds"
+			break
+		fi
+		sleep 1
+		fn_print_dots "Graceful: /save /stop: ${seconds}"
+	done
+	check_status.sh
+	if [ "${status}" != "0" ]; then
+		fn_print_error "Graceful: /save /stop: "
+		fn_print_fail_eol_nl
+		fn_script_log_error "Graceful: /save /stop: FAIL"
+	fi
+}
+
+fn_stop_graceful_select() {
+	if [ "${stopmode}" == "1" ]; then
+		fn_stop_tmux
+	elif [ "${stopmode}" == "2" ]; then
+		fn_stop_graceful_ctrlc
+	elif [ "${stopmode}" == "3" ]; then
+		fn_stop_graceful_cmd "quit" 30
+	elif [ "${stopmode}" == "4" ]; then
+		fn_stop_graceful_cmd "quit" 120
+	elif [ "${stopmode}" == "5" ]; then
+		fn_stop_graceful_cmd "stop" 30
+	elif [ "${stopmode}" == "6" ]; then
+		fn_stop_graceful_cmd "q" 30
+	elif [ "${stopmode}" == "7" ]; then
+		fn_stop_graceful_cmd "exit" 30
+	elif [ "${stopmode}" == "8" ]; then
+		fn_stop_graceful_sdtd
+	elif [ "${stopmode}" == "9" ]; then
+		fn_stop_graceful_goldsrc
+	elif [ "${stopmode}" == "10" ]; then
+		fn_stop_graceful_avorion
+	elif [ "${stopmode}" == "11" ]; then
+		fn_stop_graceful_cmd "end" 30
+	elif [ "${stopmode}" == "12" ]; then
+		fn_stop_graceful_cmd "shutdown" 30
+	fi
+}
+
+fn_stop_tmux() {
+	fn_print_dots "${servername}"
+	fn_script_log_info "tmux kill-session: ${sessionname}: ${servername}"
+	# Kill tmux session.
+	tmux kill-session -t "${sessionname}" > /dev/null 2>&1
+	sleep 0.5
+	check_status.sh
+	if [ "${status}" == "0" ]; then
+		fn_print_ok_nl "${servername}"
+		fn_script_log_pass "Stopped ${servername}"
+	else
+		fn_print_fail_nl "Unable to stop ${servername}"
+		fn_script_log_fatal "Unable to stop ${servername}"
+	fi
+}
+
+# Checks if the server is already stopped.
+fn_stop_pre_check() {
+	if [ "${status}" == "0" ]; then
+		fn_print_info_nl "${servername} is already stopped"
+		fn_script_log_error "${servername} is already stopped"
+	else
+		# Select graceful shutdown.
+		fn_stop_graceful_select
+	fi
+	# Check status again, a kill tmux session if graceful shutdown failed.
+	check_status.sh
+	if [ "${status}" != "0" ]; then
+		fn_stop_tmux
+	fi
+}
+
+check.sh
+fn_print_dots "${servername}"
+
+info_game.sh
+fn_stop_pre_check
+# Remove lockfile.
+if [ -f "${lockdir}/${selfname}.lock" ]; then
+	rm -f "${lockdir:?}/${selfname}.lock"
+fi
+
+if [ -z "${exitbypass}" ]; then
+	core_exit.sh
+fi

+ 19 - 0
lgsm/functions/command_test_alert.sh

@@ -0,0 +1,19 @@
+#!/bin/bash
+# LinuxGSM command_test_alert.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Sends a test alert.
+
+commandname="TEST-ALERT"
+commandaction="Sending Alert"
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+fn_firstcommand_set
+
+fn_print_dots "${servername}"
+check.sh
+info_game.sh
+alert="test"
+alert.sh
+
+core_exit.sh

+ 57 - 0
lgsm/functions/command_ts3_server_pass.sh

@@ -0,0 +1,57 @@
+#!/bin/bash
+# LinuxGSM command_ts3_server_pass.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Changes TS3 serveradmin password.
+
+commandname="CHANGE-PASSWORD"
+commandaction="Changing password"
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+fn_firstcommand_set
+
+fn_serveradmin_password_prompt() {
+	fn_print_header
+	fn_print_information_nl "You are about to change the ${gamename} ServerAdmin password."
+	fn_print_warning_nl "${gamename} will restart during this process."
+	echo -e ""
+	if ! fn_prompt_yn "Continue?" Y; then
+		exitcode=0
+		core_exit.sh
+	fi
+	fn_script_log_info "Initiating ${gamename} ServerAdmin password change"
+	read -rp "Enter new password: " newpassword
+	fn_print_info_nl "Changing password"
+	fn_script_log_info "Changing password"
+}
+
+fn_serveradmin_password_set() {
+	# Start server in "new password mode".
+	ts3serverpass="1"
+	exitbypass="1"
+	command_start.sh
+	fn_firstcommand_reset
+	fn_print_ok_nl "New password applied"
+	fn_script_log_pass "New ServerAdmin password applied"
+}
+
+# Running functions.
+check.sh
+fn_serveradmin_password_prompt
+if [ "${status}" != "0" ]; then
+	# Stop any running server.
+	exitbypass="1"
+	command_stop.sh
+	fn_firstcommand_reset
+	fn_serveradmin_password_set
+	parms="serveradmin_password=\"${newpassword}\" inifile=\"${servercfgfullpath}\" > /dev/null 2>&1"
+	ts3serverpass="0"
+	command_restart.sh
+	fn_firstcommand_reset
+else
+	fn_serveradmin_password_set
+	command_stop.sh
+	fn_firstcommand_reset
+fi
+
+core_exit.sh

+ 40 - 0
lgsm/functions/command_update.sh

@@ -0,0 +1,40 @@
+#!/bin/bash
+# LinuxGSM command_update.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Handles updating of servers.
+
+commandname="UPDATE"
+commandaction="Updating"
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+fn_firstcommand_set
+
+fn_print_dots ""
+check.sh
+core_logs.sh
+check_last_update.sh
+
+if [ "${shortname}" == "ts3" ]; then
+	update_ts3.sh
+elif [ "${shortname}" == "mc" ]; then
+	update_minecraft.sh
+elif [ "${shortname}" == "mcb" ]; then
+	update_minecraft_bedrock.sh
+elif [ "${shortname}" == "pmc" ] || [ "${shortname}" == "vpmc" ] || [ "${shortname}" == "wmc" ]; then
+	update_papermc.sh
+elif [ "${shortname}" == "fctr" ]; then
+	update_factorio.sh
+elif [ "${shortname}" == "mta" ]; then
+	update_mta.sh
+elif [ "${shortname}" == "jk2" ]; then
+	update_jediknight2.sh
+elif [ "${shortname}" == "vints" ]; then
+	update_vintagestory.sh
+elif [ "${shortname}" == "ut99" ]; then
+	update_ut99.sh
+else
+	update_steamcmd.sh
+fi
+
+core_exit.sh

+ 49 - 0
lgsm/functions/command_validate.sh

@@ -0,0 +1,49 @@
+#!/bin/bash
+# LinuxGSM command_validate.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Runs a server validation.
+
+commandname="VALIDATE"
+commandaction="Validating"
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+fn_firstcommand_set
+
+fn_validate() {
+	fn_print_warn "Validate might overwrite some customised files"
+	fn_script_log_warn "${commandaction} server: Validate might overwrite some customised files"
+	totalseconds=3
+	for seconds in {3..1}; do
+		fn_print_warn "Validate might overwrite some customised files: ${totalseconds}"
+		totalseconds=$((totalseconds - 1))
+		sleep 1
+		if [ "${seconds}" == "0" ]; then
+			break
+		fi
+	done
+	fn_print_warn_nl "Validate might overwrite some customised files"
+
+	fn_dl_steamcmd
+}
+
+# The location where the builds are checked and downloaded.
+remotelocation="SteamCMD"
+check.sh
+
+fn_print_dots "${remotelocation}"
+
+if [ "${status}" != "0" ]; then
+	fn_print_restart_warning
+	exitbypass=1
+	command_stop.sh
+	fn_firstcommand_reset
+	fn_validate
+	exitbypass=1
+	command_start.sh
+	fn_firstcommand_reset
+else
+	fn_validate
+fi
+
+core_exit.sh

+ 183 - 0
lgsm/functions/command_wipe.sh

@@ -0,0 +1,183 @@
+#!/bin/bash
+# LinuxGSM command_backup.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Wipes server data, useful after updates for some games like Rust.
+
+commandname="WIPE"
+commandaction="Wiping"
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+fn_firstcommand_set
+
+# Provides an exit code upon error.
+fn_wipe_exit_code() {
+	exitcode=$?
+	if [ "${exitcode}" != 0 ]; then
+		fn_print_fail_eol_nl
+		core_exit.sh
+	else
+		fn_print_ok_eol_nl
+	fi
+}
+
+# Removes files to wipe server.
+fn_wipe_files() {
+	fn_print_start_nl "${wipetype}"
+	fn_script_log_info "${wipetype}"
+
+	# Remove Map files
+	if [ -n "${serverwipe}" ] || [ -n "${mapwipe}" ]; then
+		if [ -n "$(find "${serveridentitydir}" -type f -name "*.map")" ]; then
+			echo -en "removing .map file(s)..."
+			fn_script_log_info "removing *.map file(s)"
+			fn_sleep_time
+			find "${serveridentitydir:?}" -type f -name "*.map" -printf "%f\n" >> "${lgsmlog}"
+			find "${serveridentitydir:?}" -type f -name "*.map" -delete | tee -a "${lgsmlog}"
+			fn_wipe_exit_code
+		else
+			echo -e "no .map file(s) to remove"
+			fn_sleep_time
+			fn_script_log_pass "no .map file(s) to remove"
+		fi
+	fi
+	# Remove Save files.
+	if [ -n "${serverwipe}" ] || [ -n "${mapwipe}" ]; then
+		if [ -n "$(find "${serveridentitydir}" -type f -name "*.sav*")" ]; then
+			echo -en "removing .sav file(s)..."
+			fn_script_log_info "removing .sav file(s)"
+			fn_sleep_time
+			find "${serveridentitydir:?}" -type f -name "*.sav*" -printf "%f\n" >> "${lgsmlog}"
+			find "${serveridentitydir:?}" -type f -name "*.sav*" -delete
+			fn_wipe_exit_code
+		else
+			echo -e "no .sav file(s) to remove"
+			fn_script_log_pass "no .sav file(s) to remove"
+			fn_sleep_time
+		fi
+	fi
+	# Remove db files for full wipe.
+	# Excluding player.tokens.db for Rust+.
+	if [ -n "${serverwipe}" ]; then
+		if [ -n "$(find "${serveridentitydir}" -type f ! -name 'player.tokens.db' -name "*.db")" ]; then
+			echo -en "removing .db file(s)..."
+			fn_script_log_info "removing .db file(s)"
+			fn_sleep_time
+			find "${serveridentitydir:?}" -type f ! -name 'player.tokens.db' -name "*.db" -printf "%f\n" >> "${lgsmlog}"
+			find "${serveridentitydir:?}" -type f ! -name 'player.tokens.db' -name "*.db" -delete
+			fn_wipe_exit_code
+		else
+			echo -e "no .db file(s) to remove"
+			fn_sleep_time
+			fn_script_log_pass "no .db file(s) to remove"
+		fi
+	fi
+}
+
+fn_map_wipe_warning() {
+	fn_print_warn "Map wipe will reset the map data and keep blueprint data"
+	fn_script_log_warn "Map wipe will reset the map data and keep blueprint data"
+	totalseconds=3
+	for seconds in {3..1}; do
+		fn_print_warn "Map wipe will reset the map data and keep blueprint data: ${totalseconds}"
+		totalseconds=$((totalseconds - 1))
+		sleep 1
+		if [ "${seconds}" == "0" ]; then
+			break
+		fi
+	done
+	fn_print_warn_nl "Map wipe will reset the map data and keep blueprint data"
+}
+
+fn_full_wipe_warning() {
+	fn_print_warn "Server wipe will reset the map data and remove blueprint data"
+	fn_script_log_warn "Server wipe will reset the map data and remove blueprint data"
+	totalseconds=3
+	for seconds in {3..1}; do
+		fn_print_warn "Server wipe will reset the map data and remove blueprint data: ${totalseconds}"
+		totalseconds=$((totalseconds - 1))
+		sleep 1
+		if [ "${seconds}" == "0" ]; then
+			break
+		fi
+	done
+	fn_print_warn_nl "Server wipe will reset the map data and remove blueprint data"
+}
+
+# Will change the seed if the seed is not defined by the user.
+fn_wipe_random_seed() {
+	if [ -f "${datadir}/${selfname}-seed.txt" ] && [ -n "${randomseed}" ]; then
+		shuf -i 1-2147483647 -n 1 > "${datadir}/${selfname}-seed.txt"
+		seed=$(cat "${datadir}/${selfname}-seed.txt")
+		randomseed=1
+		echo -en "generating new random seed (${cyan}${seed}${default})..."
+		fn_script_log_pass "Generating new random seed (${cyan}${seed}${default})"
+		fn_sleep_time
+		fn_print_ok_eol_nl
+	fi
+}
+
+# A summary of what wipe is going to do.
+fn_wipe_details() {
+	fn_print_information_nl "Wipe does not remove Rust+ data."
+	echo -en "* Wipe map data: "
+	if [ -n "${serverwipe}" ] || [ -n "${mapwipe}" ]; then
+		fn_print_yes_eol_nl
+	else
+		fn_print_no_eol_nl
+	fi
+
+	echo -en "* Wipe blueprint data: "
+	if [ -n "${serverwipe}" ]; then
+		fn_print_yes_eol_nl
+	else
+		fn_print_no_eol_nl
+	fi
+
+	echo -en "* Change Procedural Map seed: "
+	if [ -n "${randomseed}" ]; then
+		fn_print_yes_eol_nl
+	else
+		fn_print_no_eol_nl
+	fi
+}
+
+fn_print_dots ""
+check.sh
+fix_rust.sh
+
+# Check if there is something to wipe.
+if [ -n "$(find "${serveridentitydir}" -type f -name "*.map")" ] || [ -n "$(find "${serveridentitydir}" -type f -name "*.sav*")" ] && [ -n "$(find "${serveridentitydir}" -type f ! -name 'player.tokens.db' -name "*.db")" ]; then
+	if [ -n "${serverwipe}" ]; then
+		wipetype="Full wipe"
+		fn_full_wipe_warning
+		fn_wipe_details
+	elif [ -n "${mapwipe}" ]; then
+		wipetype="Map wipe"
+		fn_map_wipe_warning
+		fn_wipe_details
+	fi
+	check_status.sh
+	if [ "${status}" != "0" ]; then
+		fn_print_restart_warning
+		exitbypass=1
+		command_stop.sh
+		fn_firstcommand_reset
+		fn_wipe_files
+		fn_wipe_random_seed
+		fn_print_complete_nl "${wipetype}"
+		fn_script_log_pass "${wipetype}"
+		exitbypass=1
+		command_start.sh
+		fn_firstcommand_reset
+	else
+		fn_wipe_files
+		fn_wipe_random_seed
+		fn_print_complete_nl "${wipetype}"
+		fn_script_log_pass "${wipetype}"
+	fi
+else
+	fn_print_ok_nl "Wipe not required"
+	fn_script_log_pass "Wipe not required"
+fi
+core_exit.sh

+ 35 - 0
lgsm/functions/compress_unreal2_maps.sh

@@ -0,0 +1,35 @@
+#!/bin/bash
+# LinuxGSM compress_unreal2_maps.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Compresses unreal maps.
+
+commandname="MAP-COMPRESSOR"
+commandaction="Compressing maps"
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+fn_firstcommand_set
+
+check.sh
+fn_print_header
+echo -e "Will compress all maps in:"
+echo -e ""
+pwd
+echo -e ""
+echo -e "Compressed maps saved to:"
+echo -e ""
+echo -e "${compressedmapsdir}"
+echo -e ""
+if ! fn_prompt_yn "Start compression?" Y; then
+	exitcode=0
+	core_exit.sh
+fi
+mkdir -pv "${compressedmapsdir}" > /dev/null 2>&1
+rm -rfv "${serverfiles:?}/Maps/"*.ut2.uz2
+cd "${systemdir}" || exit
+for map in "${serverfiles}/Maps/"*; do
+	./ucc-bin compress "${map}" --nohomedir
+done
+mv -fv "${serverfiles}/Maps/"*.ut2.uz2 "${compressedmapsdir}"
+
+core_exit.sh

+ 35 - 0
lgsm/functions/compress_ut99_maps.sh

@@ -0,0 +1,35 @@
+#!/bin/bash
+# LinuxGSM compress_ut99_maps.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Compresses unreal maps.
+
+commandname="MAP-COMPRESSOR"
+commandaction="Compressing maps"
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+fn_firstcommand_set
+
+check.sh
+fn_print_header
+echo -e "Will compress all maps in:"
+echo -e ""
+pwd
+echo -e ""
+echo -e "Compressed maps saved to:"
+echo -e ""
+echo -e "${compressedmapsdir}"
+echo -e ""
+if ! fn_prompt_yn "Start compression?" Y; then
+	exitcode=0
+	core_exit.sh
+fi
+mkdir -pv "${compressedmapsdir}" > /dev/null 2>&1
+rm -rfv "${serverfiles:?}/Maps/"*.unr.uz
+cd "${systemdir}" || exit
+for map in "${serverfiles}/Maps/"*; do
+	./ucc-bin compress "${map}" --nohomedir
+done
+mv -fv "${serverfiles}/Maps/"*.unr.uz "${compressedmapsdir}"
+
+core_exit.sh

+ 88 - 0
lgsm/functions/fix.sh

@@ -0,0 +1,88 @@
+#!/bin/bash
+# LinuxGSM fix.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Overall function for managing fixes.
+# Runs functions that will fix an issue.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+# Messages that are displayed for some fixes.
+fn_fix_msg_start() {
+	fn_print_dots "Applying ${fixname} fix: ${gamename}"
+	fn_print_info "Applying ${fixname} fix: ${gamename}"
+	fn_script_log_info "Applying ${fixname} fix: ${gamename}"
+}
+
+fn_fix_msg_start_nl() {
+	fn_print_dots "Applying ${fixname} fix: ${gamename}"
+	fn_print_info_nl "Applying ${fixname} fix: ${gamename}"
+	fn_script_log_info "Applying ${fixname} fix: ${gamename}"
+}
+
+fn_fix_msg_end() {
+	if [ $? != 0 ]; then
+		fn_print_error_nl "Applying ${fixname} fix: ${gamename}"
+		fn_script_log_error "Applying ${fixname} fix: ${gamename}"
+	else
+		fn_print_ok_nl "Applying ${fixname} fix: ${gamename}"
+		fn_script_log_pass "Applying ${fixname} fix: ${gamename}"
+	fi
+}
+
+fn_exists_fix() {
+	local short="${1:?}"
+
+	if [ "$(type -t "fix_${short}.sh")" == 'function' ]; then
+		return 0
+	else
+		return 1
+	fi
+}
+
+fn_apply_fix() {
+	local phase_message="${1:?}"
+	local short="${2:?}"
+
+	if fn_exists_fix "${short}"; then
+		"fix_${short}.sh"
+	else
+		fn_print_error_nl "${shortname} is marked to apply pre start fix but there is no fix registered"
+	fi
+}
+
+apply_pre_start_fix=(arma3 armar ark av bt bo csgo cmw dst hw ins nmrih onset rust rw sdtd sfc sof2 squad st tf2 terraria ts3 mcb mta unt vh wurm zmr)
+apply_post_install_fix=(av kf kf2 lo ro samp ut2k4 ut ut3)
+
+# validate registered fixes for safe development
+for fix in "${apply_pre_start_fix[@]}" "${apply_post_install_fix[@]}"; do
+	if ! fn_exists_fix "${fix}"; then
+		fn_print_fail_nl "fix_${fix}.sh is registered but doesn't exist. Typo or did you miss to modify core_functions.sh?"
+		exitcode 1
+		core_exit.sh
+	fi
+done
+
+# Fixes that are run on start.
+if [ "${commandname}" != "INSTALL" ] && [ -z "${fixbypass}" ]; then
+	if [ "${appid}" ]; then
+		fix_steamcmd.sh
+	fi
+
+	if grep -qEe "(^|\s)${shortname}(\s|$)" <<< "${apply_pre_start_fix[@]}"; then
+		fn_apply_fix "pre start" "${shortname}"
+	fi
+fi
+
+# Fixes that are run on install only.
+if [ "${commandname}" == "INSTALL" ]; then
+	if grep -qEe "(^|\s)${shortname}(\s|$)" <<< "${apply_post_install_fix[@]}"; then
+		echo -e ""
+		echo -e "${lightyellow}Applying Post-Install Fixes${default}"
+		echo -e "================================="
+		fn_sleep_time
+		postinstall=1
+		fn_apply_fix "post install" "${shortname}"
+	fi
+fi

+ 62 - 0
lgsm/functions/fix_ark.sh

@@ -0,0 +1,62 @@
+#!/bin/bash
+# LinuxGSM fix_ark.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Resolves various issues with ARK: Survival Evolved.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+# removes mulitple appworkshop_346110.acf if found.
+steamappsfilewc="$(find "${HOME}" -name appworkshop_346110.acf | wc -l)"
+if [ "${steamappsfilewc}" -gt "1" ]; then
+	fixname="multiple appworkshop acf files"
+	fn_fix_msg_start
+	find "${HOME}" -name appworkshop_346110.acf -exec rm -f {} \;
+	fn_fix_msg_end
+elif [ "${steamappsfilewc}" -eq "1" ]; then
+	# Steam mods directory selecter
+	# This allows LinxuGSM to select either ~/.steam or ~/Steam. depending on what is being used
+	steamappsfile=$(find "${HOME}" -name appworkshop_346110.acf)
+	steamappsdir=$(dirname "${steamappsfile}")
+	steamappspath=$(
+		cd "${steamappsdir}" || return
+		cd ../
+		pwd
+	)
+
+	# removes the symlink if exists.
+	# fixes issue with older versions of LinuxGSM linking to /home/arkserver/steamcmd
+	if [ -L "${serverfiles}/Engine/Binaries/ThirdParty/SteamCMD/Linux" ]; then
+		fixname="broken SteamCMD symlink"
+		fn_fix_msg_start
+		unlink "${serverfiles:?}/Engine/Binaries/ThirdParty/SteamCMD/Linux"
+		fn_fix_msg_end
+		check_steamcmd.sh
+	fi
+
+	# removed ARK steamcmd directory if steamcmd is missing.
+	if [ ! -f "${serverfiles}/Engine/Binaries/ThirdParty/SteamCMD/Linux/steamcmd.sh" ]; then
+		fixname="remove invalid ARK SteamCMD directory"
+		fn_fix_msg_start
+		rm -rf "${serverfiles:?}/Engine/Binaries/ThirdParty/SteamCMD/Linux"
+		fn_fix_msg_end
+		check_steamcmd.sh
+	fi
+
+	# if the steamapps symlink is incorrect unlink it.
+	if [ -d "${serverfiles}/Engine/Binaries/ThirdParty/SteamCMD/Linux" ] && [ -L "${serverfiles}/Engine/Binaries/ThirdParty/SteamCMD/Linux/steamapps" ] && [ "$(readlink "${serverfiles}/Engine/Binaries/ThirdParty/SteamCMD/Linux/steamapps")" != "${steamappspath}" ]; then
+		fixname="incorrect steamapps symlink"
+		fn_fix_msg_start
+		unlink "${serverfiles:?}/Engine/Binaries/ThirdParty/SteamCMD/Linux/steamapps"
+		fn_fix_msg_end
+	fi
+
+	# Put symlink to steamapps directory into the ARK SteamCMD directory to link the downloaded mods to the correct location.
+	if [ ! -L "${serverfiles}/Engine/Binaries/ThirdParty/SteamCMD/Linux/steamapps" ]; then
+		fixname="steamapps symlink"
+		fn_fix_msg_start
+		ln -s "${steamappspath}" "${serverfiles}/Engine/Binaries/ThirdParty/SteamCMD/Linux/steamapps"
+		fn_fix_msg_end
+	fi
+fi

+ 16 - 0
lgsm/functions/fix_arma3.sh

@@ -0,0 +1,16 @@
+#!/bin/bash
+# LinuxGSM fix_arma3.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Resolves an issue with ARMA3.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+# Fixes: 20150 Segmentation fault (core dumped) error.
+if [ ! -d "${XDG_DATA_HOME:="${HOME}/.local/share"}/Arma 3" ] || [ ! -d "${XDG_DATA_HOME:="${HOME}/.local/share"}/Arma 3 - Other Profiles" ]; then
+	fixname="20150 Segmentation fault (core dumped)"
+	fn_fix_msg_start
+	mkdir -p "${XDG_DATA_HOME:="${HOME}/.local/share"}/Arma 3 - Other Profiles"
+	fn_fix_msg_end
+fi

+ 17 - 0
lgsm/functions/fix_armar.sh

@@ -0,0 +1,17 @@
+#!/bin/bash
+# LinuxGSM fix_armar.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Resolves an issue with Arma Reforger.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+# Fixes: Profile directory doesn't exist.
+# Issue Link: https://feedback.bistudio.com/T164845
+if [ ! -d "${serverprofilefullpath}" ]; then
+	fixname="Profile directory doesn't exist"
+	fn_fix_msg_start
+	mkdir -p "${serverprofilefullpath}"
+	fn_fix_msg_end
+fi

+ 19 - 0
lgsm/functions/fix_av.sh

@@ -0,0 +1,19 @@
+#!/bin/bash
+# LinuxGSM fix_av.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Resolves startup issue with Avorion
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${serverfiles}:${serverfiles}/linux64"
+
+# Generates the server config if it doesn't exist.
+if [ ! -f "${servercfgfullpath}" ]; then
+	startparameters="--datapath ${avdatapath} --galaxy-name ${selfname} --init-folders-only"
+	fn_print_information "starting ${gamename} server to generate configs."
+	fn_sleep_time
+	cd "${systemdir}" || exit
+	eval "${executable} ${startparameters}"
+fi

+ 10 - 0
lgsm/functions/fix_bo.sh

@@ -0,0 +1,10 @@
+#!/bin/bash
+# LinuxGSM fix_hw.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Resolves various issues with Ballistic Overkill.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${serverfiles}:${serverfiles}/BODS_Data/Plugins/x86_64"

+ 24 - 0
lgsm/functions/fix_bt.sh

@@ -0,0 +1,24 @@
+#!/bin/bash
+# LinuxGSM fix_bt.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Resolves an issue with Barotrauma.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+# Fixes: Missing user data directory error.
+if [ ! -d "${XDG_DATA_HOME:="${HOME}/.local/share"}/Daedalic Entertainment GmbH/Barotrauma" ]; then
+	fixname="Missing user data directory error."
+	fn_fix_msg_start
+	mkdir -p "${XDG_DATA_HOME:="${HOME}/.local/share"}/Daedalic Entertainment GmbH/Barotrauma"
+	fn_fix_msg_end
+fi
+
+# check if startscript is with windows line endings and reformat it
+if file -b "${serverfiles}${executable:1}" | grep -q CRLF; then
+	fixname="Convert ${executable:2} to unix file format"
+	fn_fix_msg_start
+	dos2unix -q "${serverfiles}${executable:1}"
+	fn_fix_msg_end
+fi

+ 23 - 0
lgsm/functions/fix_cmw.sh

@@ -0,0 +1,23 @@
+#!/bin/bash
+# LinuxGSM fix_cmw.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Resolves the issue of the not starting server on linux
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+if [ ! -f "${executabledir}/steam_appid.txt" ]; then
+	fixname="steam_appid.txt"
+	fn_fix_msg_start
+	echo 219640 > "${executabledir}/steam_appid.txt"
+	fn_fix_msg_end
+fi
+
+if [ ! -f "${servercfgfullpath}" ]; then
+	fn_fix_msg_start
+	fixname="copy config"
+	mkdir "${servercfgdir}"
+	cp "${systemdir}/UDKGame/Config/"*.ini "${servercfgdir}"
+	fn_fix_msg_end
+fi

+ 42 - 0
lgsm/functions/fix_csgo.sh

@@ -0,0 +1,42 @@
+#!/bin/bash
+# LinuxGSM fix_csgo.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Resolves various issues with CS:GO.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+# Fixes: server not always creating steam_appid.txt file.
+if [ ! -f "${serverfiles}/steam_appid.txt" ]; then
+	fixname="730 steam_appid.txt"
+	fn_fix_msg_start
+	echo -n "730" >> "${serverfiles}/steam_appid.txt"
+	fn_fix_msg_end
+fi
+
+# Fixes: Error parsing BotProfile.db - unknown attribute 'Rank'".
+if [ -f "${systemdir}/botprofile.db" ] && grep "^\s*Rank" "${systemdir}/botprofile.db" > /dev/null 2>&1; then
+	fixname="botprofile.db"
+	fn_fix_msg_start
+	sed -i 's/^\s*Rank/\t\/\/Rank/g' "${systemdir}/botprofile.db" > /dev/null 2>&1
+	fn_fix_msg_end
+fi
+
+# Fixes: Unknown command "cl_bobamt_vert" and exec: couldn't exec joystick.cfg.
+if [ -f "${servercfgdir}/valve.rc" ] && grep -E '^\s*exec\s*(default|joystick)\.cfg' "${servercfgdir}/valve.rc" > /dev/null 2>&1; then
+	fixname="valve.rc"
+	fn_fix_msg_start
+	sed -i 's/^\s*exec\s*default.cfg/\/\/exec default.cfg/g' "${servercfgdir}/valve.rc" > /dev/null 2>&1
+	sed -i 's/^\s*exec\s*joystick.cfg/\/\/exec joystick.cfg/g' "${servercfgdir}/valve.rc" > /dev/null 2>&1
+	fn_fix_msg_end
+fi
+
+# Fixes: Detected engine 11 but could not load: /home/csgo/serverfiles/bin/libgcc_s.so.1: version `GCC_7.0.0' not found (required by /lib/i386-linux-gnu/libstdc++.so.6)
+libgccc_so="${serverfiles}/bin/libgcc_s.so.1"
+if [ -f "${libgccc_so}" ]; then
+	fixname="libgcc_s.so.1 move away"
+	fn_fix_msg_start
+	mv -v "${libgccc_so}" "${libgccc_so}.bck"
+	fn_fix_msg_end
+fi

+ 17 - 0
lgsm/functions/fix_dst.sh

@@ -0,0 +1,17 @@
+#!/bin/bash
+# LinuxGSM fix_dst.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Resolves various issues with Don't Starve Together.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+# Fixes: ./dontstarve_dedicated_server_nullrenderer: ./lib32/libcurl-gnutls.so.4: no version information available (required by ./dontstarve_dedicated_server_nullrenderer).
+# Issue only occures on CentOS as libcurl-gnutls.so.4 is called libcurl.so.4 on CentOS.
+if [ -f "/etc/redhat-release" ] && [ ! -f "${serverfiles}/bin/lib32/libcurl-gnutls.so.4" ]; then
+	fixname="libcurl-gnutls.so.4"
+	fn_fix_msg_start
+	ln -s "/usr/lib/libcurl.so.4" "${serverfiles}/bin/lib32/libcurl-gnutls.so.4"
+	fn_fix_msg_end
+fi

+ 10 - 0
lgsm/functions/fix_hw.sh

@@ -0,0 +1,10 @@
+#!/bin/bash
+# LinuxGSM fix_hw.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Resolves various issues with Hurtworld.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${serverfiles}:${serverfiles}/Hurtworld_Data/Plugins/x86_64"

+ 20 - 0
lgsm/functions/fix_ins.sh

@@ -0,0 +1,20 @@
+#!/bin/bash
+# LinuxGSM fix_ins.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Resolves various issues with Insurgency.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+# Fixes: ./srcds_linux: error while loading shared libraries: libtier0.so: cannot open shared object file: No such file or directory.
+
+export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${serverfiles}:${serverfiles}/bin"
+
+# Fixes: issue #529 - gamemode not passed to debug or start.
+
+if [ "${commandname}" == "DEBUG" ]; then
+	defaultmap="\"${defaultmap}\""
+else
+	defaultmap="\\\"${defaultmap}\\\""
+fi

+ 36 - 0
lgsm/functions/fix_kf.sh

@@ -0,0 +1,36 @@
+#!/bin/bash
+# LinuxGSM fix_kf.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Resolves various issues with Killing Floor.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+echo -e "Applying WebAdmin ROOst.css fix."
+echo -e "http://forums.tripwireinteractive.com/showpost.php?p=585435&postcount=13"
+sed -i 's/none}/none;/g' "${serverfiles}/Web/ServerAdmin/ROOst.css"
+sed -i 's/underline}/underline;/g' "${serverfiles}/Web/ServerAdmin/ROOst.css"
+fn_sleep_time
+echo -e "Applying WebAdmin CharSet fix."
+echo -e "http://forums.tripwireinteractive.com/showpost.php?p=442340&postcount=1"
+sed -i 's/CharSet="iso-8859-1"/CharSet="utf-8"/g' "${systemdir}/UWeb.int"
+fn_sleep_time
+echo -e "applying server name fix."
+fn_sleep_time
+echo -e "forcing server restart..."
+fn_sleep_time
+exitbypass=1
+command_start.sh
+fn_firstcommand_reset
+sleep 5
+exitbypass=1
+command_stop.sh
+fn_firstcommand_reset
+exitbypass=1
+command_start.sh
+fn_firstcommand_reset
+sleep 5
+exitbypass=1
+command_stop.sh
+fn_firstcommand_reset

+ 20 - 0
lgsm/functions/fix_kf2.sh

@@ -0,0 +1,20 @@
+#!/bin/bash
+# LinuxGSM fix_kf2.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Resolves various issues with Killing Floor 2.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+startparameters="\"${defaultmap}?Game=KFGameContent.KFGameInfo_VersusSurvival\""
+
+fn_print_information "starting ${gamename} server to generate configs."
+fn_sleep_time
+exitbypass=1
+command_start.sh
+fn_firstcommand_reset
+sleep 10
+exitbypass=1
+command_stop.sh
+fn_firstcommand_reset

+ 17 - 0
lgsm/functions/fix_lo.sh

@@ -0,0 +1,17 @@
+#!/bin/bash
+# LinuxGSM fix_lo.sh function
+# Author: Daniel Gibbs
+# Website: https://linuxgsm.com
+# Description: Resolves installation issue with Last Oasis
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+appidfile=${executabledir}/steam_appid.txt
+if [ ! -f "${appidfile}" ]; then
+	fn_print_information "adding ${appidfile} to ${gamename} server."
+	fn_sleep_time
+	echo "903950" > "${appidfile}"
+else
+	fn_print_information "${appidfile} already exists. No action to be taken."
+	fn_sleep_time
+fi

+ 11 - 0
lgsm/functions/fix_mcb.sh

@@ -0,0 +1,11 @@
+#!/bin/bash
+# LinuxGSM fix_mcb.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Resolves possible startup issue with Minecraft Bedrock.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+# official docs state that the server should be started with: LD_LIBRARY_PATH=. ./bedrock_server
+export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${serverfiles}"

+ 16 - 0
lgsm/functions/fix_mta.sh

@@ -0,0 +1,16 @@
+#!/bin/bash
+# LinuxGSM fix_mta.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Installs the libmysqlclient for database functions on the server.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+if [ ! -f "${lgsmdir}/lib/libmysqlclient.so.16" ]; then
+	fixname="libmysqlclient16"
+	fn_fix_msg_start_nl
+	fn_sleep_time
+	fn_fetch_file "https://nightly.mtasa.com/files/modules/64/libmysqlclient.so.16" "" "" "" "${lgsmdir}/lib" "libmysqlclient.so.16" "chmodx" "norun" "noforce" "6c188e0f8fb5d7a29f4bc413b9fed6c2"
+	fn_fix_msg_end
+fi

+ 17 - 0
lgsm/functions/fix_nmrih.sh

@@ -0,0 +1,17 @@
+#!/bin/bash
+# LinuxGSM fix_nmrih.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Create symlinks for renamed No More Room In Hell serverfiles.
+# Solution from Steam Community post: https://steamcommunity.com/app/224260/discussions/2/1732089092441769414/
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+ln -s "${serverfiles}/bin/vphysics_srv.so" "${serverfiles}/bin/vphysics.so"
+ln -s "${serverfiles}/bin/studiorender_srv.so" "${serverfiles}/bin/studiorender.so"
+ln -s "${serverfiles}/bin/soundemittersystem_srv.so" "${serverfiles}/bin/soundemittersystem.so"
+ln -s "${serverfiles}/bin/shaderapiempty_srv.so" "${serverfiles}/bin/shaderapiempty.so"
+ln -s "${serverfiles}/bin/scenefilecache_srv.so" "${serverfiles}/bin/scenefilecache.so"
+ln -s "${serverfiles}/bin/replay_srv.so" "${serverfiles}/bin/replay.so"
+ln -s "${serverfiles}/bin/materialsystem_srv.so" "${serverfiles}/bin/materialsystem.so"

+ 19 - 0
lgsm/functions/fix_onset.sh

@@ -0,0 +1,19 @@
+#!/bin/bash
+# LinuxGSM fix_onset.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Resolves various issues with Onset.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${serverfiles}"
+
+# Fixes: Failed loading "mariadb": libmariadbclient.so.18: cannot open shared object file: No such file or directory
+# Issue only occures on CentOS as libmariadbclient.so.18 is called libmariadb.so.3 on CentOS.
+if [ -f "/etc/redhat-release" ] && [ ! -f "${serverfiles}/libmariadbclient.so.18" ] && [ -f "/usr/lib64/libmariadb.so.3" ]; then
+	fixname="libmariadbclient.so.18"
+	fn_fix_msg_start
+	ln -s "/usr/lib64/libmariadb.so.3" "${serverfiles}/libmariadbclient.so.18"
+	fn_fix_msg_end
+fi

+ 39 - 0
lgsm/functions/fix_ro.sh

@@ -0,0 +1,39 @@
+#!/bin/bash
+# LinuxGSM fix_ro.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Resolves various issues with Red Orchestra.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+echo -e "Applying WebAdmin ROOst.css fix."
+echo -e "http://forums.tripwireinteractive.com/showpost.php?p=585435&postcount=13"
+sed -i 's/none}/none;/g' "${serverfiles}/Web/ServerAdmin/ROOst.css"
+sed -i 's/underline}/underline;/g' "${serverfiles}/Web/ServerAdmin/ROOst.css"
+fn_sleep_time
+echo -e "Applying WebAdmin CharSet fix."
+echo -e "http://forums.tripwireinteractive.com/showpost.php?p=442340&postcount=1"
+sed -i 's/CharSet="iso-8859-1"/CharSet="utf-8"/g' "${systemdir}/uweb.int"
+fn_sleep_time
+echo -e "Applying Steam AppID fix."
+sed -i 's/1210/1200/g' "${systemdir}/steam_appid.txt"
+fn_sleep_time
+echo -e "applying server name fix."
+fn_sleep_time
+echo -e "forcing server restart..."
+fn_sleep_time
+exitbypass=1
+command_start.sh
+fn_firstcommand_reset
+sleep 5
+exitbypass=1
+command_stop.sh
+fn_firstcommand_reset
+exitbypass=1
+command_start.sh
+fn_firstcommand_reset
+sleep 5
+exitbypass=1
+command_stop.sh
+fn_firstcommand_reset

+ 32 - 0
lgsm/functions/fix_rust.sh

@@ -0,0 +1,32 @@
+#!/bin/bash
+# LinuxGSM fix_rust.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Resolves startup issue with Rust.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+# Fixes: [Raknet] Server Shutting Down (Shutting Down).
+export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${serverfiles}:${serverfiles}/RustDedicated_Data/Plugins/x86_64"
+
+# Part of random seed feature.
+# If seed is not defined by user generate a seed file.
+if [ -z "${seed}" ] || [ "${seed}" == "0" ]; then
+	if [ ! -f "${datadir}/${selfname}-seed.txt" ]; then
+		shuf -i 1-2147483647 -n 1 > "${datadir}/${selfname}-seed.txt"
+		seed="$(cat "${datadir}/${selfname}-seed.txt")"
+		fn_print_info_nl "Generating new random seed (${cyan}${seed}${default})"
+		fn_script_log_pass "Generating new random seed (${cyan}${seed}${default})"
+	fi
+	seed="$(cat "${datadir}/${selfname}-seed.txt")"
+	randomseed=1
+fi
+
+# If Carbon mod is installed, run enviroment.sh
+if [ -f "${serverfiles}/carbon/tools/environment.sh" ]; then
+	fn_print_info_nl "Running Carbon environment.sh"
+	fn_script_log_info "Running Carbon environment.sh"
+	# shellcheck source=/dev/null
+	source "${serverfiles}/carbon/tools/environment.sh"
+fi

+ 10 - 0
lgsm/functions/fix_rw.sh

@@ -0,0 +1,10 @@
+#!/bin/bash
+# LinuxGSM fix_rw.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Resolves various issues with Rising World.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${serverfiles}:${serverfiles}/linux64"

+ 34 - 0
lgsm/functions/fix_samp.sh

@@ -0,0 +1,34 @@
+#!/bin/bash
+# LinuxGSM fix_sfc.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Resolves issue that the default rcon password is not changed
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+if [ -f "${servercfgfullpath}" ]; then
+	# check if default password is set "changeme"
+	currentpass=$(grep -E "^rcon_password" "${servercfgfullpath}" | sed 's/^rcon_password //')
+	defaultpass="changeme"
+	# check if default password is set
+	if [ "${currentpass}" == "${defaultpass}" ]; then
+		fixname="change default rcon password"
+		fn_fix_msg_start
+		fn_script_log_info "changing rcon/admin password."
+		random=$(tr -dc A-Za-z0-9_ < /dev/urandom | head -c 8 | xargs)
+		rconpass="admin${random}"
+		sed -i "s/rcon_password changeme/rcon_password ${rconpass}/g" "${servercfgfullpath}"
+		fn_fix_msg_end
+	fi
+	# check if the hostname is the default name
+	currenthostname=$(grep -E "^hostname" "${servercfgfullpath}" | sed 's/^hostname //')
+	defaulthostname="SA-MP 0.3 Server"
+	if [ "${currenthostname}" == "${defaulthostname}" ]; then
+		fixname="change default hostname"
+		fn_fix_msg_start
+		fn_script_log_info "changing default hostname to LinuxGSM"
+		sed -i "s/hostname ${defaulthostname}/hostname LinuxGSM/g" "${servercfgfullpath}"
+		fn_fix_msg_end
+	fi
+fi

+ 10 - 0
lgsm/functions/fix_sdtd.sh

@@ -0,0 +1,10 @@
+#!/bin/bash
+# LinuxGSM fix_sdtd.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Resolves various issues with 7 Days to Die.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${serverfiles}/7DaysToDieServer_Data/Plugins/x86_64"

+ 48 - 0
lgsm/functions/fix_sfc.sh

@@ -0,0 +1,48 @@
+#!/bin/bash
+# LinuxGSM fix_sfc.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Resolves various issues with Source Forts Classic.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+if [ ! -f "${serverfiles}/bin/datacache.so" ]; then
+	ln -s "${serverfiles}/bin/datacache_srv.so" "${serverfiles}/bin/datacache.so"
+fi
+
+if [ ! -f "${serverfiles}/bin/dedicated.so" ]; then
+	ln -s "${serverfiles}/bin/dedicated_srv.so" "${serverfiles}/bin/dedicated.so"
+fi
+
+if [ ! -f "${serverfiles}/bin/engine.so" ]; then
+	ln -s "${serverfiles}/bin/engine_srv.so" "${serverfiles}/bin/engine.so"
+fi
+
+if [ ! -f "${serverfiles}/bin/materialsystem.so" ]; then
+	ln -s "${serverfiles}/bin/materialsystem_srv.so" "${serverfiles}/bin/materialsystem.so"
+fi
+
+if [ ! -f "${serverfiles}/bin/replay.so" ]; then
+	ln -s "${serverfiles}/bin/replay_srv.so" "${serverfiles}/bin/replay.so"
+fi
+
+if [ ! -f "${serverfiles}/bin/shaderapiempty.so" ]; then
+	ln -s "${serverfiles}/bin/shaderapiempty_srv.so" "${serverfiles}/bin/shaderapiempty.so"
+fi
+
+if [ ! -f "${serverfiles}/bin/soundemittersystem.so" ]; then
+	ln -s "${serverfiles}/bin/soundemittersystem_srv.so" "${serverfiles}/bin/soundemittersystem.so"
+fi
+
+if [ ! -f "${serverfiles}/bin/studiorender.so" ]; then
+	ln -s "${serverfiles}/bin/studiorender_srv.so" "${serverfiles}/bin/studiorender.so"
+fi
+
+if [ ! -f "${serverfiles}/bin/vphysics.so" ]; then
+	ln -s "${serverfiles}/bin/vphysics_srv.so" "${serverfiles}/bin/vphysics.so"
+fi
+
+if [ ! -f "${serverfiles}/bin/scenefilecache.so" ]; then
+	ln -s "${serverfiles}/bin/scenefilecache_srv.so" "${serverfiles}/bin/scenefilecache.so"
+fi

+ 11 - 0
lgsm/functions/fix_sof2.sh

@@ -0,0 +1,11 @@
+#!/bin/bash
+# LinuxGSM fix_rust.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Resolves various issues with Soldier of Fortune 2.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+# Fixes: error while loading shared libraries: libcxa.so.1
+export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${serverfiles}"

+ 26 - 0
lgsm/functions/fix_squad.sh

@@ -0,0 +1,26 @@
+#!/bin/bash
+# LinuxGSM fix_squad.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Resolves various issues with Squad.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+# As the server base dir changed for the game, we need to migrate the default config from the old to the new location
+oldservercfg="${serverfiles}/Squad/ServerConfig/${servercfg}"
+if [ -f "${oldservercfg}" ] && [ -f "${servercfgfullpath}" ]; then
+	# diff old and new config - if it is different move the old config over the new one
+	if [ "$(diff -c "${oldservercfg}" "${servercfgfullpath}" | wc -l)" -gt 0 ]; then
+		fixname="Migrate server config to new Game folder"
+		fn_fix_msg_start
+		mv -v "${oldservercfg}" "${servercfgfullpath}"
+		fn_fix_msg_end
+	else
+		fixname="remove the same config from old configdir"
+		fn_fix_msg_start
+		rm -f "${oldservercfg}"
+		fn_fix_msg_end
+
+	fi
+fi

+ 11 - 0
lgsm/functions/fix_st.sh

@@ -0,0 +1,11 @@
+#!/bin/bash
+# LinuxGSM fix_rust.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Resolves startup issue with Stationeers.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+# Fixes: [Raknet] Server Shutting Down (Shutting Down).
+export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${serverfiles}:${serverfiles}/rocketstation_DedicatedServer_Data/Plugins/x86_64"

+ 141 - 0
lgsm/functions/fix_steamcmd.sh

@@ -0,0 +1,141 @@
+#!/bin/bash
+# LinuxGSM fix_steamcmd.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Resolves various issues related to SteamCMD.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+# function to simplify the steamclient.so fix
+# example
+# fn_fix_steamclient_so 32|64 (bit) "${serverfiles}/linux32/"
+fn_fix_steamclient_so() {
+	# $1 type of fix 32 or 64 as possible values
+	# $2 as destination where the lib will be copied to
+	if [ "$1" == "32" ]; then
+		# steamclient.so x86 fix.
+		if [ ! -f "${2}/steamclient.so" ]; then
+			fixname="steamclient.so x86"
+			fn_fix_msg_start
+			if [ ! -d "${2}" ]; then
+				mkdir -p "${2}"
+			fi
+			if [ -f "${HOME}/.steam/steamcmd/linux32/steamclient.so" ]; then
+				cp "${HOME}/.steam/steamcmd/linux32/steamclient.so" "${2}/steamclient.so"
+			elif [ -f "${steamcmddir}/linux32/steamclient.so" ]; then
+				cp "${steamcmddir}/linux32/steamclient.so" "${2}/steamclient.so"
+			elif [ -f "${HOME}/.local/share/Steam/steamcmd/linux32/steamclient.so" ]; then
+				cp "${HOME}/.local/share/Steam/steamcmd/linux32/steamclient.so" "${2}/steamclient.so"
+			fi
+			fn_fix_msg_end
+		fi
+	elif [ "$1" == "64" ]; then
+		# steamclient.so x86_64 fix.
+		if [ ! -f "${2}/steamclient.so" ]; then
+			fixname="steamclient.so x86_64"
+			fn_fix_msg_start
+			if [ ! -d "${2}" ]; then
+				mkdir -p "${2}"
+			fi
+			if [ -f "${HOME}/.steam/steamcmd/linux64/steamclient.so" ]; then
+				cp "${HOME}/.steam/steamcmd/linux64/steamclient.so" "${2}/steamclient.so"
+			elif [ -f "${steamcmddir}/linux64/steamclient.so" ]; then
+				cp "${steamcmddir}/linux64/steamclient.so" "${2}/steamclient.so"
+			elif [ -f "${HOME}/.local/share/Steam/steamcmd/linux64/steamclient.so" ]; then
+				cp "${HOME}/.local/share/Steam/steamcmd/linux64/steamclient.so" "${2}/steamclient.so"
+			fi
+			fn_fix_msg_end
+		fi
+	fi
+}
+
+# Helps fix: [S_API FAIL] SteamAPI_Init() failed; unable to locate a running instance of Steam,or a local steamclient.so.
+steamsdk64="${HOME}/.steam/sdk64"
+steamclientsdk64="${steamsdk64}/steamclient.so"
+# remove any old unlinked versions of steamclient.so
+if [ -f "${steamclientsdk64}" ]; then
+	if [ "$(stat -c '%h' "${steamclientsdk64}")" -eq 1 ]; then
+		fixname="steamclient.so sdk64 - remove old file"
+		fn_fix_msg_start
+		rm -f "${steamclientsdk64}"
+		fn_fix_msg_end
+	fi
+fi
+
+# place new hardlink for the file to the disk
+if [ ! -f "${steamclientsdk64}" ]; then
+	fixname="steamclient.so sdk64 hardlink"
+	fn_fix_msg_start
+	if [ ! -d "${steamsdk64}" ]; then
+		mkdir -p "${steamsdk64}"
+	fi
+	if [ -f "${HOME}/.steam/steamcmd/linux64/steamclient.so" ]; then
+		ln "${HOME}/.steam/steamcmd/linux64/steamclient.so" "${steamclientsdk64}"
+	elif [ -f "${steamcmddir}/linux64/steamclient.so" ]; then
+		ln "${steamcmddir}/linux64/steamclient.so" "${steamclientsdk64}"
+	elif [ -f "${HOME}/.local/share/Steam/steamcmd/linux64/steamclient.so" ]; then
+		ln "${HOME}/.local/share/Steam/steamcmd/linux64/steamclient.so" "${steamclientsdk64}"
+	else
+		fn_print_fail_nl "Could not copy any steamclient.so 64bit for the gameserver"
+	fi
+	fn_fix_msg_end
+fi
+
+# Helps fix: [S_API FAIL] SteamAPI_Init() failed; unable to locate a running instance of Steam,or a local steamclient.so.
+steamsdk32="${HOME}/.steam/sdk32"
+steamclientsdk32="${HOME}/.steam/sdk32/steamclient.so"
+if [ -f "${steamclientsdk32}" ]; then
+	if [ " $(stat -c '%h' "${steamclientsdk32}")" -eq 1 ]; then
+		fixname="steamclient.so sdk32 - remove old file"
+		fn_fix_msg_start
+		rm -f "${steamclientsdk32}"
+		fn_fix_msg_end
+	fi
+fi
+
+# place new hardlink for the file to the disk
+if [ ! -f "${steamclientsdk32}" ]; then
+	fixname="steamclient.so sdk32 link"
+	fn_fix_msg_start
+	if [ ! -d "${steamsdk32}" ]; then
+		mkdir -p "${steamsdk32}"
+	fi
+	if [ -f "${HOME}/.steam/steamcmd/linux32/steamclient.so" ]; then
+		ln "${HOME}/.steam/steamcmd/linux32/steamclient.so" "${steamclientsdk32}"
+	elif [ -f "${steamcmddir}/linux32/steamclient.so" ]; then
+		ln "${steamcmddir}/linux32/steamclient.so" "${steamclientsdk32}"
+	elif [ -f "${HOME}/.local/share/Steam/steamcmd/linux32/steamclient.so" ]; then
+		ln "${HOME}/.local/share/Steam/steamcmd/linux32/steamclient.so" "${steamclientsdk32}"
+	else
+		fn_print_fail_nl "Could not copy any steamclient.so 32bit for the gameserver"
+	fi
+	fn_fix_msg_end
+fi
+
+# steamclient.so fixes
+if [ "${shortname}" == "bo" ]; then
+	fn_fix_steamclient_so "32" "${serverfiles}/BODS_Data/Plugins/x86"
+	fn_fix_steamclient_so "64" "${serverfiles}/BODS_Data/Plugins/x86_64"
+elif [ "${shortname}" == "cmw" ]; then
+	fn_fix_steamclient_so "32" "${executabledir}/lib"
+elif [ "${shortname}" == "cs" ]; then
+	fn_fix_steamclient_so "32" "${serverfiles}"
+elif [ "${shortname}" == "col" ]; then
+	fn_fix_steamclient_so "64" "${serverfiles}"
+elif [ "${shortname}" == "ins" ]; then
+	fn_fix_steamclient_so "32" "${serverfiles}/bin"
+elif [ "${shortname}" == "pz" ]; then
+	fn_fix_steamclient_so "32" "${serverfiles}/linux32"
+	fn_fix_steamclient_so "64" "${serverfiles}/linux64"
+elif [ "${shortname}" == "pvr" ]; then
+	fn_fix_steamclient_so "64" "${executabledir}"
+elif [ "${shortname}" == "ss3" ]; then
+	fn_fix_steamclient_so "32" "${serverfiles}/Bin"
+elif [ "${shortname}" == "tu" ]; then
+	fn_fix_steamclient_so "64" "${executabledir}"
+elif [ "${shortname}" == "unt" ]; then
+	fn_fix_steamclient_so "64" "${serverfiles}"
+elif [ "${shortname}" == "wurm" ]; then
+	fn_fix_steamclient_so "64" "${serverfiles}/nativelibs"
+fi

+ 10 - 0
lgsm/functions/fix_terraria.sh

@@ -0,0 +1,10 @@
+#!/bin/bash
+# LinuxGSM fix_terraria.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Resolves an issue with Terraria.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+export TERM=xterm

+ 16 - 0
lgsm/functions/fix_tf2.sh

@@ -0,0 +1,16 @@
+#!/bin/bash
+# LinuxGSM fix_tf2.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Resolves various issues with Team Fortress 2.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+# Fixes: Team Fortress 2 Segmentation fault for Red-Hat Distros #2062.
+if [ -f "/etc/redhat-release" ] && [ ! -f "${serverfiles}/bin/libcurl-gnutls.so.4" ]; then
+	fixname="libcurl-gnutls.so.4"
+	fn_fix_msg_start
+	ln -s "/usr/lib/libcurl.so.4" "${serverfiles}/bin/libcurl-gnutls.so.4"
+	fn_fix_msg_end
+fi

+ 33 - 0
lgsm/functions/fix_ts3.sh

@@ -0,0 +1,33 @@
+#!/bin/bash
+# LinuxGSM fix_ts3.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Resolves various issues with Teamspeak 3.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+# Fixes: makes libmariadb2 available #1924.
+if [ ! -f "${serverfiles}/libmariadb.so.2" ]; then
+	fixname="libmariadb.so.2"
+	fn_fix_msg_start
+	cp "${serverfiles}/redist/libmariadb.so.2" "${serverfiles}/libmariadb.so.2"
+	fn_fix_msg_end
+fi
+
+# Fixes: failed to register local accounting service: No such file or directory.
+accountingfile="/dev/shm/7gbhujb54g8z9hu43jre8"
+if [ -f "${accountingfile}" ] && [ "${status}" == "0" ]; then
+	# Check permissions for the file if the current user owns it, if not exit.
+	if [ "$(stat -c %U ${accountingfile})" == "$(whoami)" ]; then
+		fixname="Delete file ${accountingfile}"
+		fn_fix_msg_start
+		rm -f "${accountingfile}"
+		fn_fix_msg_end
+	# file is not owned by the current user and needs to be deleted manually.
+	else
+		fn_print_error_nl "File ${accountingfile} is not owned by $(whoami) and needs to be deleted manually"
+		fn_script_log_fatal "File ${accountingfile} is not owned by $(whoami) and needs to be deleted manually"
+		core_exit.sh
+	fi
+fi

+ 10 - 0
lgsm/functions/fix_unt.sh

@@ -0,0 +1,10 @@
+#!/bin/bash
+# LinuxGSM fix_rust.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Resolves startup issue with Unturned.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${serverfiles}:${serverfiles}/Unturned_Headless_Data/Plugins/x86_64"

+ 13 - 0
lgsm/functions/fix_ut.sh

@@ -0,0 +1,13 @@
+#!/bin/bash
+# LinuxGSM fix_ut.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Resolves various issues with Unreal Tournament.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+#Set Binary Executable
+echo -e "chmod +x ${executabledir}/${executable}"
+chmod +x "${executabledir}/${executable}"
+fn_sleep_time

+ 36 - 0
lgsm/functions/fix_ut2k4.sh

@@ -0,0 +1,36 @@
+#!/bin/bash
+# LinuxGSM fix_ut2k4.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Resolves various issues with Unreal Tournament 2004.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+echo -e "applying WebAdmin ut2003.css fix."
+echo -e "http://forums.tripwireinteractive.com/showpost.php?p=585435&postcount=13"
+sed -i 's/none}/none;/g' "${serverfiles}/Web/ServerAdmin/ut2003.css"
+sed -i 's/underline}/underline;/g' "${serverfiles}/Web/ServerAdmin/ut2003.css"
+fn_sleep_time
+echo -e "applying WebAdmin CharSet fix."
+echo -e "http://forums.tripwireinteractive.com/showpost.php?p=442340&postcount=1"
+sed -i 's/CharSet="iso-8859-1"/CharSet="utf-8"/g' "${systemdir}/UWeb.int"
+fn_sleep_time
+echo -e "applying server name fix."
+fn_sleep_time
+echo -e "forcing server restart."
+fn_sleep_time
+exitbypass=1
+command_start.sh
+fn_firstcommand_reset
+sleep 5
+exitbypass=1
+command_stop.sh
+fn_firstcommand_reset
+exitbypass=1
+command_start.sh
+fn_firstcommand_reset
+sleep 5
+exitbypass=1
+command_stop.sh
+fn_firstcommand_reset

+ 20 - 0
lgsm/functions/fix_ut3.sh

@@ -0,0 +1,20 @@
+#!/bin/bash
+# LinuxGSM fix_ut2.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Resolves various issues with Unreal Tournament 3.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+startparameters="server VCTF-Suspense?Game=UTGameContent.UTVehicleCTFGame_Content?bIsDedicated=true?bIsLanMatch=false?bUsesStats=false?bShouldAdvertise=false?PureServer=1?bAllowJoinInProgress=true?ConfigSubDir=${selfname} -port=${port} -queryport=${queryport} -multihome=${ip} -nohomedir -unattended -log=${gamelog}"
+
+fn_print_information "starting ${gamename} server to generate configs."
+fn_sleep_time
+exitbypass=1
+command_start.sh
+fn_firstcommand_reset
+sleep 10
+exitbypass=1
+command_stop.sh
+fn_firstcommand_reset

+ 35 - 0
lgsm/functions/fix_vh.sh

@@ -0,0 +1,35 @@
+#!/bin/bash
+# LinuxGSM fix_rust.sh function
+# Author: Alasdair Haig
+# Website: https://linuxgsm.com
+# Description: Resolves startup issue with Valheim
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+export LD_LIBRARY_PATH=./linux64:$LD_LIBRARY_PATH
+
+modsdir="${lgsmdir}/mods"
+modsinstalledlistfullpath="${modsdir}/installed-mods.txt"
+if [ -f "${modsinstalledlistfullpath}" ]; then
+	# special check if Valheim Plus is installed
+	if grep -qE "^valheimplus" "${modsinstalledlistfullpath}"; then
+		if ! grep -qE "^executable=\"./start_server_bepinex.sh\"" "${configdirserver}/${selfname}.cfg"; then
+			echo 'executable="./start_server_bepinex.sh"' >> "${configdirserver}/${selfname}.cfg"
+			executable="./start_server_bepinex.sh"
+		fi
+	fi
+	# special exports for BepInEx if installed
+	if grep -qE "^bepinexvh" "${modsinstalledlistfullpath}"; then
+		fn_print_info_nl "BepInEx install detected, applying start exports"
+		fn_script_log_info "BepInEx install detected, applying start exports"
+		# exports for BepInEx framework from script start_server_bepinex.sh
+		export DOORSTOP_ENABLE=TRUE
+		export DOORSTOP_INVOKE_DLL_PATH=./BepInEx/core/BepInEx.Preloader.dll
+		export DOORSTOP_CORLIB_OVERRIDE_PATH=./unstripped_corlib
+
+		export LD_LIBRARY_PATH="./doorstop_libs:${LD_LIBRARY_PATH}"
+		export LD_PRELOAD="libdoorstop_x64.so:${LD_PRELOAD}"
+
+		export SteamAppId=892970
+	fi
+fi

+ 23 - 0
lgsm/functions/fix_wurm.sh

@@ -0,0 +1,23 @@
+#!/bin/bash
+# LinuxGSM fix_wurm.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Resolves various issues with Wurm Unlimited.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+# First run requires start with no parms.
+# After first run new dirs are created.
+if [ ! -d "${serverfiles}/Creative" ]; then
+	parmsbypass=1
+	fixbypass=1
+	exitbypass=1
+	command_start.sh
+	fn_firstcommand_reset
+	sleep 10
+	exitbypass=1
+	command_stop.sh
+	fn_firstcommand_reset
+	unset parmsbypass
+fi

+ 48 - 0
lgsm/functions/fix_zmr.sh

@@ -0,0 +1,48 @@
+#!/bin/bash
+# LinuxGSM fix_sfc.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Resolves various issues with Zombie Master: Reborn.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+if [ ! -f "${serverfiles}/bin/datacache.so" ]; then
+	ln -s "${serverfiles}/bin/datacache_srv.so" "${serverfiles}/bin/datacache.so"
+fi
+
+if [ ! -f "${serverfiles}/bin/dedicated.so" ]; then
+	ln -s "${serverfiles}/bin/dedicated_srv.so" "${serverfiles}/bin/dedicated.so"
+fi
+
+if [ ! -f "${serverfiles}/bin/engine.so" ]; then
+	ln -s "${serverfiles}/bin/engine_srv.so" "${serverfiles}/bin/engine.so"
+fi
+
+if [ ! -f "${serverfiles}/bin/materialsystem.so" ]; then
+	ln -s "${serverfiles}/bin/materialsystem_srv.so" "${serverfiles}/bin/materialsystem.so"
+fi
+
+if [ ! -f "${serverfiles}/bin/replay.so" ]; then
+	ln -s "${serverfiles}/bin/replay_srv.so" "${serverfiles}/bin/replay.so"
+fi
+
+if [ ! -f "${serverfiles}/bin/shaderapiempty.so" ]; then
+	ln -s "${serverfiles}/bin/shaderapiempty_srv.so" "${serverfiles}/bin/shaderapiempty.so"
+fi
+
+if [ ! -f "${serverfiles}/bin/soundemittersystem.so" ]; then
+	ln -s "${serverfiles}/bin/soundemittersystem_srv.so" "${serverfiles}/bin/soundemittersystem.so"
+fi
+
+if [ ! -f "${serverfiles}/bin/studiorender.so" ]; then
+	ln -s "${serverfiles}/bin/studiorender_srv.so" "${serverfiles}/bin/studiorender.so"
+fi
+
+if [ ! -f "${serverfiles}/bin/vphysics.so" ]; then
+	ln -s "${serverfiles}/bin/vphysics_srv.so" "${serverfiles}/bin/vphysics.so"
+fi
+
+if [ ! -f "${serverfiles}/bin/scenefilecache.so" ]; then
+	ln -s "${serverfiles}/bin/scenefilecache_srv.so" "${serverfiles}/bin/scenefilecache.so"
+fi

+ 281 - 0
lgsm/functions/info_distro.sh

@@ -0,0 +1,281 @@
+#!/bin/bash
+# LinuxGSM info_distro.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Variables providing useful info on the Operating System such as disk and performace info.
+# Used for command_details.sh, command_debug.sh and alert.sh.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+### Game Server pid
+if [ "${status}" == "1" ]; then
+	gameserverpid="$(tmux list-sessions -F "#{session_name} #{pane_pid}" | grep "^${sessionname} " | awk '{print $NF}')"
+	if [ "${engine}" == "source" ]; then
+		srcdslinuxpid="$(ps -ef | grep -v grep | grep "${gameserverpid}" | grep srcds_linux | awk '{print $2}')"
+	elif [ "${engine}" == "goldsrc" ]; then
+		hldslinuxpid="$(ps -ef | grep -v grep | grep "${gameserverpid}" | grep hlds_linux | awk '{print $2}')"
+	fi
+fi
+### Distro information
+
+## Distro
+# Returns architecture, kernel and distro/os.
+arch="$(uname -m)"
+kernel="$(uname -r)"
+
+# Distro Name - Ubuntu 16.04 LTS
+# Distro Version - 16.04
+# Distro ID - ubuntu
+# Distro Codename - xenial
+
+# Gathers distro info from various sources filling in missing gaps.
+distro_info_array=(os-release lsb_release hostnamectl debian_version redhat-release)
+for distro_info in "${distro_info_array[@]}"; do
+	if [ -f "/etc/os-release" ] && [ "${distro_info}" == "os-release" ]; then
+		distroname="$(grep "PRETTY_NAME" /etc/os-release | awk -F\= '{gsub(/"/,"",$2);print $2}')"
+		distroversion="$(grep "VERSION_ID" /etc/os-release | awk -F\= '{gsub(/"/,"",$2);print $2}')"
+		# Special var for rhel like distros to removed point in number e.g 8.4 to just 8.
+		distroversionrh="$(sed -nr 's/^VERSION_ID="([0-9]*).+?"/\1/p' /etc/os-release)"
+		distroid="$(grep "ID=" /etc/os-release | grep -v _ID | awk -F\= '{gsub(/"/,"",$2);print $2}')"
+		distroidlike="$(grep "ID_LIKE=" /etc/os-release | grep -v _ID | awk -F\= '{gsub(/"/,"",$2);print $2}')"
+		distrocodename="$(grep "VERSION_CODENAME" /etc/os-release | awk -F\= '{gsub(/"/,"",$2);print $2}')"
+	elif [ "$(command -v lsb_release 2> /dev/null)" ] && [ "${distro_info}" == "lsb_release" ]; then
+		if [ -z "${distroname}" ]; then
+			distroname="$(lsb_release -sd)"
+		elif [ -z "${distroversion}" ]; then
+			distroversion="$(lsb_release -sr)"
+		elif [ -z "${distroid}" ]; then
+			distroid="$(lsb_release -si)"
+		elif [ -z "${distrocodename}" ]; then
+			distrocodename="$(lsb_release -sc)"
+		fi
+	elif [ "$(command -v hostnamectl 2> /dev/null)" ] && [ "${distro_info}" == "hostnamectl" ]; then
+		if [ -z "${distroname}" ]; then
+			distroname="$(hostnamectl | grep "Operating System" | sed 's/Operating System: //g')"
+		fi
+	elif [ -f "/etc/debian_version" ] && [ "${distro_info}" == "debian_version" ]; then
+		if [ -z "${distroname}" ]; then
+			distroname="Debian $(cat /etc/debian_version)"
+		elif [ -z "${distroversion}" ]; then
+			distroversion="$(cat /etc/debian_version)"
+		elif [ -z "${distroid}" ]; then
+			distroid="debian"
+		fi
+	elif [ -f "/etc/redhat-release" ] && [ "${distro_info}" == "redhat-release" ]; then
+		if [ -z "${distroname}" ]; then
+			distroname="$(cat /etc/redhat-release)"
+		elif [ -z "${distroversion}" ]; then
+			distroversion="$(rpm -qa \*-release | grep -Ei "oracle|redhat|centos|fedora" | cut -d"-" -f3)"
+		elif [ -z "${distroid}" ]; then
+			distroid="$(awk '{print $1}' /etc/redhat-release)"
+		fi
+	fi
+done
+
+# some RHEL based distros use 8.4 instead of just 8.
+if [[ "${distroidlike}" == *"rhel"* ]] || [ "${distroid}" == "rhel" ]; then
+	distroversioncsv="${distroversionrh}"
+else
+	distroversioncsv="${distroversion}"
+fi
+
+# Check if distro supported by distro vendor.
+if [ "$(command -v distro-info 2> /dev/null)" ]; then
+	distrosunsupported="$(distro-info --unsupported)"
+	distrosunsupported_array=("${distrosunsupported}")
+	for distrounsupported in "${distrosunsupported_array[@]}"; do
+		if [ "${distrounsupported}" == "${distrocodename}" ]; then
+			distrosupport=unsupported
+			break
+		else
+			distrosupport=supported
+		fi
+	done
+else
+	distrosupport=unknown
+fi
+
+## Glibc version
+# e.g: 1.17
+glibcversion="$(ldd --version | sed -n '1s/.* //p')"
+
+## tmux version
+# e.g: tmux 1.6
+if [ ! "$(command -V tmux 2> /dev/null)" ]; then
+	tmuxv="${red}NOT INSTALLED!${default}"
+	tmuxvdigit="0"
+else
+	tmuxvdigit="$(tmux -V | sed "s/tmux //" | sed -n '1 p' | tr -cd '[:digit:]')"
+	if [ "${tmuxvdigit}" -lt "16" ]; then
+		tmuxv="$(tmux -V) (>= 1.6 required for console log)"
+	else
+		tmuxv="$(tmux -V)"
+	fi
+fi
+
+if [ "$(command -V java 2> /dev/null)" ]; then
+	javaversion="$(java -version 2>&1 | grep "version")"
+fi
+
+if [ "$(command -v mono 2> /dev/null)" ]; then
+	monoversion="$(mono --version 2>&1 | grep -Po '(?<=version )\d')"
+fi
+
+## Uptime
+uptime="$(< /proc/uptime)"
+uptime=${uptime/[. ]*/}
+minutes="$((uptime / 60 % 60))"
+hours="$((uptime / 60 / 60 % 24))"
+days="$((uptime / 60 / 60 / 24))"
+
+### Performance information
+
+## Average server load
+load="$(uptime | awk -F 'load average: ' '{ print $2 }')"
+
+## CPU information
+cpumodel="$(awk -F: '/model name/ {name=$2} END {print name}' /proc/cpuinfo | sed 's/^[ \t]*//;s/[ \t]*$//')"
+cpucores="$(awk -F: '/model name/ {core++} END {print core}' /proc/cpuinfo)"
+cpufreqency="$(awk -F: '/cpu MHz/ {freq=$2} END {print freq}' /proc/cpuinfo | sed 's/^[ \t]*//;s/[ \t]*$//')"
+# CPU usage of the game server pid
+if [ -n "${gameserverpid}" ]; then
+	cpuused="$(ps --forest -o pcpu -g "${gameserverpid}" | awk '{s+=$1} END {print s}')"
+	cpuusedmhz="$(echo "${cpufreqency} * ${cpuused} / 100" | bc)"
+fi
+
+## Memory information
+# Available RAM and swap.
+
+# Newer distros can use numfmt to give more accurate results.
+if [ "$(command -v numfmt 2> /dev/null)" ]; then
+	# Issue #2005 - Kernel 3.14+ contains MemAvailable which should be used. All others will be calculated.
+
+	# get the raw KB values of these fields.
+	physmemtotalkb="$(grep MemTotal /proc/meminfo | awk '{print $2}')"
+	physmemfreekb="$(grep ^MemFree /proc/meminfo | awk '{print $2}')"
+	physmembufferskb="$(grep ^Buffers /proc/meminfo | awk '{print $2}')"
+	physmemcachedkb="$(grep ^Cached /proc/meminfo | awk '{print $2}')"
+	physmemreclaimablekb="$(grep ^SReclaimable /proc/meminfo | awk '{print $2}')"
+
+	# check if MemAvailable Exists.
+	if grep -q ^MemAvailable /proc/meminfo; then
+		physmemactualfreekb="$(grep ^MemAvailable /proc/meminfo | awk '{print $2}')"
+	else
+		physmemactualfreekb="$((physmemfreekb + physmembufferskb + physmemcachedkb))"
+	fi
+
+	# Available RAM and swap.
+	physmemtotalmb="$((physmemtotalkb / 1024))"
+	physmemtotal="$(numfmt --to=iec --from=iec --suffix=B "${physmemtotalkb}K")"
+	physmemfree="$(numfmt --to=iec --from=iec --suffix=B "${physmemactualfreekb}K")"
+	physmemused="$(numfmt --to=iec --from=iec --suffix=B "$((physmemtotalkb - physmemfreekb - physmembufferskb - physmemcachedkb - physmemreclaimablekb))K")"
+	physmemavailable="$(numfmt --to=iec --from=iec --suffix=B "${physmemactualfreekb}K")"
+	physmemcached="$(numfmt --to=iec --from=iec --suffix=B "$((physmemcachedkb + physmemreclaimablekb))K")"
+
+	swaptotal="$(numfmt --to=iec --from=iec --suffix=B "$(grep ^SwapTotal /proc/meminfo | awk '{print $2}')K")"
+	swapfree="$(numfmt --to=iec --from=iec --suffix=B "$(grep ^SwapFree /proc/meminfo | awk '{print $2}')K")"
+	swapused="$(numfmt --to=iec --from=iec --suffix=B "$(($(grep ^SwapTotal /proc/meminfo | awk '{print $2}') - $(grep ^SwapFree /proc/meminfo | awk '{print $2}')))K")"
+	# RAM usage of the game server pid
+	# MB
+	if [ "${gameserverpid}" ]; then
+		memused="$(ps --forest -o rss -g "${gameserverpid}" | awk '{s+=$1} END {print s}' | awk '{$1/=1024;printf "%.0f",$1}{print $2}')"
+		# %
+		pmemused="$(ps --forest -o %mem -g "${gameserverpid}" | awk '{s+=$1} END {print s}')"
+	fi
+else
+	# Older distros will need to use free.
+	# Older versions of free do not support -h option.
+	if [ "$(
+		free -h > /dev/null 2>&1
+		echo $?
+	)" -ne "0" ]; then
+		humanreadable="-m"
+	else
+		humanreadable="-h"
+	fi
+	physmemtotalmb="$(free -m | awk '/Mem:/ {print $2}')"
+	physmemtotal="$(free ${humanreadable} | awk '/Mem:/ {print $2}')"
+	physmemfree="$(free ${humanreadable} | awk '/Mem:/ {print $4}')"
+	physmemused="$(free ${humanreadable} | awk '/Mem:/ {print $3}')"
+
+	oldfree="$(free ${humanreadable} | awk '/cache:/')"
+	if [ "${oldfree}" ]; then
+		physmemavailable="n/a"
+		physmemcached="n/a"
+	else
+		physmemavailable="$(free ${humanreadable} | awk '/Mem:/ {print $7}')"
+		physmemcached="$(free ${humanreadable} | awk '/Mem:/ {print $6}')"
+	fi
+
+	swaptotal="$(free ${humanreadable} | awk '/Swap:/ {print $2}')"
+	swapused="$(free ${humanreadable} | awk '/Swap:/ {print $3}')"
+	swapfree="$(free ${humanreadable} | awk '/Swap:/ {print $4}')"
+fi
+
+### Disk information
+
+## Available disk space on the partition.
+filesystem="$(LC_ALL=C df -hP "${rootdir}" | tail -n 1 | awk '{print $1}')"
+totalspace="$(LC_ALL=C df -hP "${rootdir}" | tail -n 1 | awk '{print $2}')"
+usedspace="$(LC_ALL=C df -hP "${rootdir}" | tail -n 1 | awk '{print $3}')"
+availspace="$(LC_ALL=C df -hP "${rootdir}" | tail -n 1 | awk '{print $4}')"
+
+## LinuxGSM used space total.
+rootdirdu="$(du -sh "${rootdir}" 2> /dev/null | awk '{print $1}')"
+if [ -z "${rootdirdu}" ]; then
+	rootdirdu="0M"
+fi
+
+## LinuxGSM used space in serverfiles dir.
+serverfilesdu="$(du -sh "${serverfiles}" 2> /dev/null | awk '{print $1}')"
+if [ -z "${serverfilesdu}" ]; then
+	serverfilesdu="0M"
+fi
+
+## LinuxGSM used space total minus backup dir.
+rootdirduexbackup="$(du -sh --exclude="${backupdir}" "${serverfiles}" 2> /dev/null | awk '{print $1}')"
+if [ -z "${rootdirduexbackup}" ]; then
+	rootdirduexbackup="0M"
+fi
+
+## Backup info
+if [ -d "${backupdir}" ]; then
+	# Used space in backups dir.
+	backupdirdu="$(du -sh "${backupdir}" | awk '{print $1}')"
+	# If no backup dir, size is 0M.
+	if [ -z "${backupdirdu}" ]; then
+		backupdirdu="0M"
+	fi
+
+	# number of backups set to 0 by default.
+	backupcount=0
+
+	# If there are backups in backup dir.
+	if [ "$(find "${backupdir}" -name "*.tar.gz" | wc -l)" -ne "0" ]; then
+		# number of backups.
+		backupcount="$(find "${backupdir}"/*.tar.gz | wc -l)"
+		# most recent backup.
+		lastbackup="$(ls -1t "${backupdir}"/*.tar.gz | head -1)"
+		# date of most recent backup.
+		lastbackupdate="$(date -r "${lastbackup}")"
+		# no of days since last backup.
+		lastbackupdaysago="$((($(date +'%s') - $(date -r "${lastbackup}" +'%s')) / 60 / 60 / 24))"
+		# size of most recent backup.
+		lastbackupsize="$(du -h "${lastbackup}" | awk '{print $1}')"
+	fi
+fi
+
+# Network Interface name
+netint=$(${ipcommand} -o addr | grep "${ip}" | awk '{print $2}')
+netlink=$(${ethtoolcommand} "${netint}" 2> /dev/null | grep Speed | awk '{print $2}')
+
+# Sets the SteamCMD glibc requirement if the game server requirement is less or not required.
+if [ "${appid}" ]; then
+	if [ "${glibc}" = "null" ] || [ -z "${glibc}" ] || [ "$(printf '%s\n'${glibc}'\n' "2.14" | sort -V | head -n 1)" != "2.14" ]; then
+		glibc="2.14"
+	fi
+fi
+
+# Gather Port Info using ss
+ssinfo="$(ss -tuplwn)"

+ 2600 - 0
lgsm/functions/info_game.sh

@@ -0,0 +1,2600 @@
+#!/bin/bash
+# LinuxGSM info_game.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Gathers various game server information.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+## Examples of filtering to get info from config files.
+# sed 's/foo//g' - remove foo
+# tr -cd '[:digit:]' leave only digits
+# tr -d '=\"; ' remove selected characters =\";
+# grep -v "foo" filter out lines that contain foo
+# cut -f1 -d "/" remove everything after /
+
+fn_info_game_ac() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		adminpassword="${unavailable}"
+		httpport="${zero}"
+		port="${zero}"
+		queryport="${zero}"
+		servername="${unavailable}"
+	else
+		adminpassword=$(grep "ADMIN_PASSWORD" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/ADMIN_PASSWORD//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		httpport=$(grep "HTTP_PORT" "${servercfgfullpath}" | tr -cd '[:digit:]')
+		port=$(grep "TCP_PORT" "${servercfgfullpath}" | tr -cd '[:digit:]')
+		queryport="${httpport}"
+		servername=$(grep "NAME" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/NAME//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//' | head -n 1)
+
+		# Not set
+		adminpassword=${adminpassword:-"NOT SET"}
+		httpport=${httpport:-"0"}
+		port=${port:-"0"}
+		queryport=${queryport:-"0"}
+		servername=${servername:-"NOT SET"}
+
+	fi
+}
+
+fn_info_game_ark() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		adminpassword="${unavailable}"
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+	else
+		adminpassword=$(sed -nr 's/^ServerAdminPassword=(.*)/\1/p' "${servercfgfullpath}")
+		servername=$(sed -nr 's/^SessionName=(.*)/\1/p' "${servercfgfullpath}")
+		serverpassword=$(sed -nr 's/^ServerPassword=(.*)/\1/p' "${servercfgfullpath}")
+
+		# Not set
+		adminpassword=${adminpassword:-"NOT SET"}
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+	fi
+
+	# Parameters
+	maxplayers=${maxplayers:-"0"}
+	port=${port:-"0"}
+	queryport=${queryport:-"0"}
+	rawport=$((port + 1))
+	rconport=${rconport:-"0"}
+}
+
+fn_info_game_arma3() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		adminpassword="${unavailable}"
+		maxplayers="${zero}"
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+	else
+		adminpassword=$(sed -nr 's/^passwordAdmin\s*=\s*"(.*)"\s*;/\1/p' "${servercfgfullpath}")
+		maxplayers=$(sed -nr 's/^maxPlayers\s*=\s*([0-9]+)\s*;/\1/p' "${servercfgfullpath}")
+		servername=$(sed -nr 's/^hostname\s*=\s*"(.*)"\s*;/\1/p' "${servercfgfullpath}")
+		serverpassword=$(sed -nr 's/^password\s*=\s*"(.*)"\s*;/\1/p' "${servercfgfullpath}")
+
+		# Not set
+		adminpassword=${adminpassword:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+	fi
+
+	# Parameters
+	battleeyeport=$((port + 4))
+	port=${port:-"2302"}
+	queryport=$((port + 1))
+	steammasterport=$((port + 2))
+	voiceport=${port:-"2302"}
+	voiceunusedport=$((port + 3))
+}
+
+fn_info_game_armar() {
+	if [ ! -f "${servercfgfullpath}" ]; then
+		adminpassword="${unavailable}"
+		maxplayers="${zero}"
+		port=${port:-"0"}
+		queryport=
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+	else
+		adminpassword=$(jq -r '.adminPassword' "${servercfgfullpath}")
+		battleeyeport=1376
+		configip=$(jq -r '.gameHostBindAddress' "${servercfgfullpath}")
+		maxplayers=$(jq -r '.game.playerCountLimit' "${servercfgfullpath}")
+		port=$(jq -r '.gameHostBindPort' "${servercfgfullpath}")
+		queryport=$(jq -r '.steamQueryPort' "${servercfgfullpath}")
+		servername=$(jq -r '.game.name' "${servercfgfullpath}")
+		serverpassword=$(jq -r '.game.password' "${servercfgfullpath}")
+
+		# Not set
+		adminpassword=${adminpassword:-"NOT SET"}
+		configip=${configip:-"0.0.0.0"}
+		maxplayers=${maxplayers:-"0"}
+		port=${port:-"0"}
+		queryport=${queryport:-"0"}
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+	fi
+}
+
+fn_info_game_av() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		maxplayers="${unavailable}"
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+		port=${zero}
+		queryport=${zero}
+		steamqueryport=${zero}
+		steammasterport=${zero}
+		rconport=${zero}
+		rconenabled="${unavailable}"
+		rconpassword="${unavailable}"
+	else
+		maxplayers=$(grep "maxPlayers=" "${servercfgfullpath}" | sed 's/maxPlayers=//')
+		servername=$(grep "name=" "${servercfgfullpath}" | sed 's/name=//')
+		serverpassword=$(grep "password=" "${servercfgfullpath}" | sed 's/password=//')
+		port=$(grep "port=" "${servercfgfullpath}" | sed 's/port=//')
+		queryport=$((port + 3))
+		steamqueryport=$((port + 20))
+		steammasterport=$((port + 21))
+		rconport=$(grep "rconPort=" "${servercfgfullpath}" | sed 's/rconPort=//')
+
+		rconpassword=$(grep "rconPassword=" "${servercfgfullpath}" | sed 's/rconPassword=//')
+		if [ -n "${rconpassword}" ]; then
+			rconenabled="true"
+		fi
+
+		# Not set
+		maxplayers=${maxplayers:-"0"}
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		port=${port:-"0"}
+		queryport=${queryport:-"0"}
+		steamqueryport=${steamqueryport:-"0"}
+		steammasterport=${steammasterport:-"0"}
+		rconport=${rconport:-"0"}
+		rconenabled=${rconenabled:-"NOT SET"}
+		rconpassword=${rconpassword:-"NOT SET"}
+	fi
+}
+
+fn_info_game_bf1942() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+		maxplayers="${zero}"
+		port="${zero}"
+		queryport="${zero}"
+	else
+		servername=$(grep -E "^game.serverName " "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^--/d' -e 's/game.serverName //g' | tr -d '=\";,:' | xargs)
+		serverpassword=$(grep "game.serverPassword" "${servercfgfullpath}" | sed -e 's/^ *//g' -e '/^--/d' -e 's/game.serverPassword//g' | tr -d '=\";,:' | xargs)
+		maxplayers=$(grep "game.serverMaxPlayers" "${servercfgfullpath}" | grep -v "\--" | tr -cd '[:digit:]')
+		port=$(grep "game.serverPort" "${servercfgfullpath}" | grep -v "\--" | tr -cd '[:digit:]')
+		queryport="22000"
+		configip=$(grep "game.serverIP" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^--/d' -e 's/game.serverIP//g' | tr -d '=\";,:' | xargs)
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+		port=${port:-"0"}
+		queryport=${queryport:-"0"}
+		configip=${configip:-"0.0.0.0"}
+	fi
+}
+
+fn_info_game_bfv() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+		maxplayers="${zero}"
+		port="${zero}"
+		queryport="${zero}"
+	else
+		servername=$(grep "game.serverName" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^--/d' -e 's/game.serverName//g' | tr -d '=\";,:' | xargs)
+		serverpassword=$(grep "game.serverPassword" "${servercfgfullpath}" | sed -e 's/^ *//g' -e '/^--/d' -e 's/game.serverPassword//g' | tr -d '=\";,:' | xargs)
+		maxplayers=$(grep "game.serverMaxPlayers" "${servercfgfullpath}" | grep -v "\--" | tr -cd '[:digit:]')
+		port=$(grep "game.serverPort" "${servercfgfullpath}" | grep -v "\--" | tr -cd '[:digit:]')
+		queryport="23000"
+		configip=$(grep "game.serverIP" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^--/d' -e 's/game.serverIP//g' | tr -d '=\";,:' | xargs)
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+		port=${port:-"0"}
+		queryport=${queryport:-"0"}
+		configip=${configip:-"0.0.0.0"}
+	fi
+}
+
+fn_info_game_bo() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+		port="${zero}"
+		queryport="${zero}"
+		maxplayers="${unavailable}"
+	else
+		servername=$(grep "ServerName=" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/ServerName//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		serverpassword=$(grep "Password=" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/Password//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		port=$(grep "ServerPort=" "${servercfgfullpath}" | tr -cd '[:digit:]')
+		queryport=$((port + 1))
+		maxplayers=$(grep "MaxPlayers=" "${servercfgfullpath}" | tr -cd '[:digit:]')
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		port=${port:-"0"}
+		queryport=${queryport:-"0"}
+		maxplayers=${maxplayers:-"0"}
+	fi
+}
+
+fn_info_game_bt() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+		port="${zero}"
+		queryport="${zero}"
+		maxplayers="${unavailable}"
+	else
+		servername=$(grep -Po 'name="\K.*(?=")' "${servercfgfullpath}")         # Assuming GNU grep is used
+		serverpassword=$(grep -Po 'password="\K.*(?=")' "${servercfgfullpath}") # Assuming GNU grep is used
+		port=$(grep " port=" "${servercfgfullpath}" | tr -cd '[:digit:]')
+		queryport=$(grep "queryport=" "${servercfgfullpath}" | tr -cd '[:digit:]')
+		maxplayers=$(grep "maxplayers=" "${servercfgfullpath}" | tr -cd '[:digit:]')
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		port=${port:-"0"}
+		queryport=${queryport:-"0"}
+		maxplayers=${maxplayers:-"0"}
+	fi
+}
+
+fn_info_game_btl() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+		gamemode="${unavailable}"
+	else
+		servername=$(grep -m2 "ServerName" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^--/d' -e 's/ServerName//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		serverpassword=$(grep "Password" "${servercfgfullpath}" | grep -v "RCONPassword" | sed -e 's/^[ \t]*//g' -e '/^--/d' -e 's/Password//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		gamemode=$(grep -m2 "PlayMode" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^--/d' -e 's/PlayMode//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		gamemode=${gamemode:-"NOT SET"}
+	fi
+
+	# Parameters
+	port=${port:-"0"}
+	queryport=${queryport:-"0"}
+	rconport=$((port + 2))
+}
+
+fn_info_game_cd() {
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		port="${zero}"
+		rconenabled="false"
+		rconport="${zero}"
+		rconpassword="${unavailable}"
+		steamport="${zero}"
+		maxplayers="${zero}"
+	else
+		servername=$(jq -r '.game_title' "${servercfgfullpath}")
+		port=$(jq -r '.game_port' "${servercfgfullpath}")
+		steamport=$(jq -r '.steam_port_messages' "${servercfgfullpath}")
+		rconenabled=$(jq -r '.rcon' "${servercfgfullpath}")
+		rconport=$(jq -r '.rcon_port' "${servercfgfullpath}")
+		rconpassword=$(jq -r '.rcon_password' "${servercfgfullpath}")
+		maxplayers=$(jq -r '.player_count' "${servercfgfullpath}")
+	fi
+}
+
+fn_info_game_ck() {
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		maxplayers="${zero}"
+	else
+		servername=$(jq -r '.worldName' "${servercfgfullpath}")
+		maxplayers=$(jq -r '.maxNumberPlayers' "${servercfgfullpath}")
+	fi
+	queryport=$((port + 1))
+}
+
+fn_info_game_cmw() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		adminpassword="${unavailable}"
+		rconport=${zero}
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+
+	else
+		adminpassword=$(grep -E "^adminpassword=" "${servercfgfullpath}" | tr -cd '[:digit:]')
+		rconport=$(grep -E "^RConPort=" "${servercfgdir}/DefaultGame.ini" | tr -cd '[:digit:]')
+		servername=$(grep -E "^ServerName" "${servercfgfullpath}" | sed 's/^ServerName=//')
+		serverpassword=$(grep -E "^GamePassword" "${servercfgfullpath}" | sed 's/^ServerName=//')
+
+		# Not set
+		adminpassword=${adminpassword:-"NOT SET"}
+		rconport=${rconport:-"0"}
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+	fi
+
+	# Parameters
+	defaultmap=${defaultmap:-"NOT SET"}
+	port=${port:-"0"}
+	queryport=${queryport:-"0"}
+}
+
+fn_info_game_cod() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		rconpassword="${unavailable}"
+	else
+		servername=$(grep "sv_hostname " "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/set sv_hostname //g' | tr -d '=\";,:' | xargs)
+		rconpassword=$(grep "rconpassword" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/set rconpassword //g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		rconpassword=${rconpassword=:-"NOT SET"}
+	fi
+
+	# Parameters
+	defaultmap=${defaultmap:-"NOT SET"}
+	maxplayers=${maxplayers:-"0"}
+	port=${port:-"0"}
+}
+
+fn_info_game_coduo() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		rconpassword="${unavailable}"
+	else
+		servername=$(grep "sv_hostname " "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/set sv_hostname //g' | tr -d '=\";,:' | xargs)
+		rconpassword=$(grep "rconpassword" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/set rconpassword //g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		rconpassword=${rconpassword=:-"NOT SET"}
+	fi
+
+	# Parameters
+	defaultmap=${defaultmap:-"NOT SET"}
+	maxplayers=${maxplayers:-"0"}
+	port=${port:-"0"}
+	queryport=${port:-"28960"}
+}
+
+fn_info_game_cod2() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		rconpassword="${unavailable}"
+	else
+		servername=$(grep "sv_hostname " "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/set sv_hostname //g' | tr -d '=\";,:' | xargs)
+		rconpassword=$(grep "rconpassword" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/set rconpassword //g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		rconpassword=${rconpassword=:-"NOT SET"}
+	fi
+
+	# Parameters
+	defaultmap=${defaultmap:-"NOT SET"}
+	maxplayers=${maxplayers:-"0"}
+	port=${port:-"0"}
+	queryport=${port:-"28960"}
+}
+
+fn_info_game_cod4() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		rconpassword="${unavailable}"
+	else
+		servername=$(sed -nr 's/^set\s*sv_hostname\s*"(.*)".*/\1/p' "${servercfgfullpath}")
+		rconpassword=$(sed -nr 's/^set\s*rcon_password\s*"(.*)"\s*\/.*/\1/p' "${servercfgfullpath}")
+		queryport=${port:-"28960"}
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		rconpassword=${rconpassword=:-"NOT SET"}
+		queryport=${queryport:-"28960"}
+	fi
+
+	# Parameters
+	defaultmap=${defaultmap:-"NOT SET"}
+	maxplayers=${maxplayers:-"0"}
+	port=${port:-"0"}
+	queryport=${port:-"28960"}
+}
+
+fn_info_game_codwaw() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		rconpassword="${unavailable}"
+	else
+		servername=$(grep "sv_hostname " "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/set sv_hostname //g' | tr -d '=\";,:' | xargs)
+		rconpassword=$(grep "rconpassword" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/set rconpassword //g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		rconpassword=${rconpassword=:-"NOT SET"}
+	fi
+
+	# Parameters
+	defaultmap=${defaultmap:-"NOT SET"}
+	maxplayers=${maxplayers:-"0"}
+	port=${port:-"0"}
+	queryport=${port:-"28960"}
+}
+
+fn_info_game_col() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+		maxplayers="${unavailable}"
+		port="${zero}"
+		queryport="${zero}"
+		steamport="${zero}"
+		rconpassword="${unavailable}"
+	else
+		servername=$(jq -r '.ServerSettings.ServerName' "${servercfgfullpath}")
+		serverpassword=$(jq -r '.ServerSettings.ServerPassword' "${servercfgfullpath}")
+		maxplayers=$(jq -r '.ServerSettings.MaxPlayerCount' "${servercfgfullpath}")
+		port=$(jq -r '.ServerSettings.ServerGamePort' "${servercfgfullpath}")
+		queryport=${port:-"0"}
+		steamport=$(jq -r '.ServerSettings.ServerSteamPort' "${servercfgfullpath}")
+		rconpassword=$(jq -r '.ServerSettings.RCONPassword' "${servercfgfullpath}")
+		configip=$(jq -r '.ServerSettings.ServerIP' "${servercfgfullpath}")
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+		port=${port:-"27004"}
+		queryport=${queryport:-"0"}
+		steamport=${steamport:-"27005"}
+		rconpassword=${rconpassword:-"NOT SET"}
+		configip=${configip:-"0.0.0.0"}
+	fi
+}
+
+fn_info_game_dodr() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		maxplayers="${zero}"
+	else
+		maxplayers=$(sed -nr 's/^iServerMaxPlayers=(.*)$/\1/p' "${servercfgfullpath}")
+
+		# Not set
+		maxplayers=${maxplayers:-"0"}
+	fi
+
+	# Parameters
+	servername=${servername:-"NOT SET"}
+	port=${port:-"7777"}
+	queryport=${queryport:-"27015"}
+}
+
+fn_info_game_dayz() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		adminpassword="${unavailable}"
+		serverpassword="${unavailable}"
+		maxplayers="${zero}"
+	else
+		servername=$(sed -nr 's/^hostname\s*=\s*"(.*)"\s*;/\1/p' "${servercfgfullpath}")
+		adminpassword=$(sed -nr 's/^passwordAdmin\s*=\s*"(.*)"\s*;/\1/p' "${servercfgfullpath}")
+		serverpassword=$(sed -nr 's/^password\s*=\s*"(.*)"\s*;/\1/p' "${servercfgfullpath}")
+		maxplayers=$(sed -nr 's/^maxPlayers\s*=\s*([0-9]+)\s*;/\1/p' "${servercfgfullpath}")
+		queryport=$(sed -nr 's/^steamQueryPort\s*=\s*([0-9]+)\s*;/\1/p' "${servercfgfullpath}")
+
+		# Not Set
+		servername=${servername:-"NOT SET"}
+		adminpassword=${adminpassword:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+		queryport=${queryport:-"27016"}
+	fi
+
+	# Parameters
+	port=${port:-"2302"}
+	steammasterport=$((port + 2))
+	battleeyeport=$((port + 4))
+}
+
+fn_info_game_dst() {
+	# Config
+	if [ ! -f "${clustercfgfullpath}" ]; then
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+		maxplayers="${zero}"
+		gamemode="${unavailable}"
+		tickrate="${zero}"
+		masterport="${zero}"
+	else
+		servername=$(grep "cluster_name" "${clustercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/cluster_name//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		serverpassword=$(grep "cluster_password" "${clustercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/cluster_password//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		maxplayers=$(grep "max_players" "${clustercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+		gamemode=$(grep "game_mode" "${clustercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/game_mode//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		tickrate=$(grep "tick_rate" "${clustercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+		masterport=$(grep "master_port" "${clustercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+		configip=$(grep "bind_ip" "${clustercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/bind_ip//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+		gamemode=${gamemode:-"NOT SET"}
+		tickrate=${tickrate:-"0"}
+		masterport=${masterport:-"0"}
+		configip=${configip:-"0.0.0.0"}
+	fi
+
+	if [ ! -f "${servercfgfullpath}" ]; then
+		port="${zero}"
+		steamauthport="${zero}"
+		steammasterport="${zero}"
+	else
+		port=$(grep "server_port" "${servercfgfullpath}" | grep "^server_port" | grep -v "#" | tr -cd '[:digit:]')
+		steamauthport=$(grep "authentication_port" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+		steammasterport=$(grep "master_server_port" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+
+		# Not set
+		port=${port:-"0"}
+		steamauthport=${steamauthport:-"0"}
+		steammasterport=${steammasterport:-"0"}
+	fi
+
+	# Parameters
+	sharding=${sharding:-"NOT SET"}
+	master=${master:-"NOT SET"}
+	shard=${shard:-"NOT SET"}
+	cluster=${cluster:-"NOT SET"}
+	cave=${cave:-"NOT SET"}
+}
+
+fn_info_game_eco() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+		maxplayers="${zero}"
+		tickrate="${zero}"
+		port="${zero}"
+		webadminport="${zero}"
+	else
+		configip=$(jq -r '.IPAddress' "${servercfgfullpath}")
+		servername=$(jq -r '.Description' "${servercfgfullpath}")
+		serverpassword=$(jq -r '.Password' "${servercfgfullpath}")
+		maxplayers=$(jq -r '.MaxConnections' "${servercfgfullpath}")
+		tickrate=$(jq -r '.Rate' "${servercfgfullpath}")
+		port=$(jq -r '.GameServerPort' "${servercfgfullpath}")
+		webadminport=$(jq -r '.WebServerPort' "${servercfgfullpath}")
+
+		# Not set
+		configip=${configip:-"0.0.0.0"}
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		maxplayers=${maxplayers=:-"0"}
+		tickrate=${tickrate=:-"0"}
+		port=${port=:-"0"}
+		webadminport=${webadminport=:-"0"}
+	fi
+}
+
+fn_info_game_etl() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		rconpassword="${unavailable}"
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+		maxplayers="${zero}"
+		port="${zero}"
+		queryport="${zero}"
+	else
+		port=$(grep "set net_port" "${servercfgfullpath}" | grep -v "//" | tr -cd '[:digit:]')
+		queryport="${port}"
+		rconpassword=$(grep "set rconpassword" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/set rconpassword //g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//g' -e '/^\//d' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		servername=$(grep "set sv_hostname" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/set sv_hostname //g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		serverpassword=$(grep "set g_password" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/set g_password //g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		maxplayers=$(grep "set sv_maxclients" "${servercfgfullpath}" | grep -v "//" | tr -cd '[:digit:]')
+		configip=$(grep "set net_ip" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/set net_ip//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+
+		# Not set
+		rconpassword=${rconpassword:-"NOT SET"}
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+		port=${port:-"27960"}
+		queryport=${queryport:-"27960"}
+		configip=${configip:-"0.0.0.0"}
+	fi
+}
+
+fn_info_game_fctr() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="Factorio Server"
+		serverpassword="${unavailable}"
+		maxplayers="${zero}"
+		authtoken="${unavailable}"
+		savegameinterval="${unavailable}"
+		versioncount="${unavailable}"
+	else
+		servername=$(jq -r '.name' "${servercfgfullpath}")
+		serverpassword=$(jq -r '.game_password' "${servercfgfullpath}")
+		maxplayers=$(jq -r '.max_players' "${servercfgfullpath}")
+		authtoken=$(jq -r '.token' "${servercfgfullpath}")
+		savegameinterval=$(jq -r '.autosave_interval' "${servercfgfullpath}")
+		versioncount=$(jq -r '.autosave_slots' "${servercfgfullpath}")
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+		authtoken=${authtoken:-"NOT SET"}
+		savegameinterval=${savegameinterval:-"0"}
+		versioncount=${versioncount:-"0"}
+	fi
+
+	# Parameters
+	port=${port:-"0"}
+	rconport=${rconport:-"0"}
+	rconpassword=${rconpassword:-"NOT SET"}
+
+	# get server version if installed
+	local factoriobin="${executabledir}${executable:1}"
+	if [ -f "${factoriobin}" ]; then
+		serverversion=$(${factoriobin} --version | grep "Version:" | awk '{print $2}')
+	fi
+}
+
+fn_info_game_jc2() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		serverdescription="${unavailable}"
+		serverpassword="${unavailable}"
+		maxplayers="${zero}"
+		port="${zero}"
+		queryport="${zero}"
+	else
+		servername=$(grep "Name" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^--/d' -e 's/Name//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		serverdescription=$(grep "Description" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^--/d' -e 's/Description//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		serverpassword=$(grep "Password" "${servercfgfullpath}" | sed -e 's/^ *//g' -e '/^--/d' -e 's/Password//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		maxplayers=$(grep "MaxPlayers" "${servercfgfullpath}" | grep -v "\--" | tr -cd '[:digit:]')
+		port=$(grep "BindPort" "${servercfgfullpath}" | grep -v "\--" | tr -cd '[:digit:]')
+		queryport="${port}"
+		configip=$(grep "BindIP" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^--/d' -e 's/BindIP//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		serverdescription=${serverdescription:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+		port=${port:-"0"}
+		queryport=${queryport:-"0"}
+		configip=${configip:-"0.0.0.0"}
+	fi
+}
+
+fn_info_game_hw() {
+	# Parameters
+	servername=${servername:-"NOT SET"}
+	port=${port:-"0"}
+	queryport=${queryport:-"0"}
+	maxplayers=${maxplayers:-"0"}
+	defaultmap=${defaultmap:-"NOT SET"}
+	creativemode=${creativemode:-"NOT SET"}
+}
+
+fn_info_game_inss() {
+	# Parameters
+	port=${port:-"0"}
+	queryport=${queryport:-"0"}
+	rconport=${rconport:-"0"}
+	servername=${servername:-"NOT SET"}
+	serverpassword=${serverpassword:-"NOT SET"}
+	defaultmap=${defaultmap:-"NOT SET"}
+	defaultscenario=${defaultscenario:-"NOT SET"}
+	maxplayers=${maxplayers:-"0"}
+}
+
+fn_info_game_jc3() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		serverdescription="${unavailable}"
+		serverpassword="${unavailable}"
+		maxplayers="${zero}"
+		port="${zero}"
+		queryPort="${zero}"
+		steamport="${zero}"
+		httpport="${zero}"
+		tickrate="${zero}"
+	else
+		servername=$(grep "name" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/name//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		serverdescription=$(grep "description" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/description//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		serverpassword=$(grep "password" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/password//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		maxplayers=$(grep "\"maxPlayers\"" "${servercfgfullpath}" | tr -cd '[:digit:]')
+		port=$(grep "\"port\"" "${servercfgfullpath}" | tr -cd '[:digit:]')
+		queryport=$(grep "\"queryPort\"" "${servercfgfullpath}" | tr -cd '[:digit:]')
+		steamport=$(grep "\"steamPort\"" "${servercfgfullpath}" | tr -cd '[:digit:]')
+		httpport=$(grep "\"httpPort\"" "${servercfgfullpath}" | tr -cd '[:digit:]')
+		tickrate=$(grep "\"maxTickRate\"" "${servercfgfullpath}" | tr -cd '[:digit:]')
+		configip=$(grep "host" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/host//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		serverdescription=${serverdescription:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		maxplayers=${maxplayers=:-"0"}
+		port=${port=:-"0"}
+		queryport=${queryport=:-"0"}
+		steamport=${steamport=:-"0"}
+		httpport=${httpport=:-"0"}
+		tickrate=${tickrate=:-"0"}
+		configip=${configip:-"0.0.0.0"}
+	fi
+}
+
+fn_info_game_jk2() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		rconpassword="${unavailable}"
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+		maxplayers="${zero}"
+		serverversion="${unavailable}"
+	else
+		rconpassword=$(grep "seta rconpassword" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/seta rconpassword//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		servername=$(grep "seta sv_hostname" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/seta sv_hostname//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		serverpassword=$(grep "seta g_password" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/seta g_password//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		maxplayers=$(grep "seta sv_maxclients" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+		serverversion=$(grep "seta mv_serverversion" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/seta mv_serverversion//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+
+		# Not set
+		rconpassword=${rconpassword:-"NOT SET"}
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+		serverversion=${serverversion:-"NOT SET"}
+	fi
+
+	# Parameters
+	port=${port:-"0"}
+	queryport=${port}
+	defaultmap=${defaultmap:-"NOT SET"}
+}
+
+fn_info_game_kf() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+		adminpassword="${unavailable}"
+		port="${zero}"
+		queryport="${zero}"
+		queryportgs="${zero}"
+		steamport="${zero}"
+		steammasterport="${zero}"
+		lanport="${zero}"
+		httpport="${zero}"
+		webadminenabled="${unavailable}"
+		webadminuser="${unavailable}"
+		webadminpass="${unavailable}"
+	else
+		servername=$(sed -nr 's/^ServerName=(.*)$/\1/p' "${servercfgfullpath}" | tr -d '=\";,:' | sed 's/\r$//')
+		serverpassword=$(sed -nr 's/^GamePassword=(.*)$/\1/p' "${servercfgfullpath}" | tr -d '=\";,:' | sed 's/\r$//')
+		adminpassword=$(sed -nr 's/^AdminPassword=(.*)$/\1/p' "${servercfgfullpath}" | tr -d '=\";,:' | sed 's/\r$//')
+		port=$(sed -nr 's/^Port=(.*)$/\1/p' "${servercfgfullpath}" | tr -cd '[:digit:]')
+		queryport=$((port + 1))
+		queryportgs=$(sed -nr 's/^OldQueryPortNumber=(.*)$/\1/p' "${servercfgfullpath}" | tr -cd '[:digit:]')
+		steamport="20560"
+		steammasterport="28852"
+		lanport=$(grep "LANServerPort=" "${servercfgfullpath}" | tr -cd '[:digit:]')
+		httpport=$(sed -nr 's/^ListenPort=(.*)$/\1/p' "${servercfgfullpath}" | tr -cd '[:digit:]')
+		webadminenabled=$(sed -nr 's/^bEnabled=(.*)$/\1/p' "${servercfgfullpath}" | tr -d '=\";,:' | sed 's/\r$//')
+		webadminuser=$(sed -nr 's/^AdminName=(.*)$/\1/p' "${servercfgfullpath}" | tr -d '=\";,:' | sed 's/\r$//')
+		webadminpass="${adminpassword}"
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		adminpassword=${adminpassword:-"NOT SET"}
+		port=${port:-"0"}
+		queryport=${queryport:-"0"}
+		queryportgs=${queryportgs:-"0"}
+		steamport=${steamport:-"0"}
+		steammasterport=${steammasterport:-"0"}
+		lanport=${lanport:-"0"}
+		httpport=${httpport:-"0"}
+		webadminenabled=${webadminenabled:-"NOT SET"}
+		webadminuser=${webadminuser:-"NOT SET"}
+		webadminpass=${webadminpass:-"NOT SET"}
+	fi
+
+	# Parameters
+	defaultmap=${defaultmap:-"NOT SET"}
+}
+
+fn_info_game_kf2() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+		adminpassword="${unavailable}"
+		port=${zero}
+		queryport=${zero}
+		webadminenabled="${unavailable}"
+		httpport="${zero}"
+		webadminuser="${unavailable}"
+		webadminpass="${unavailable}"
+	else
+		servername=$(grep "ServerName" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/ServerName//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		serverpassword=$(grep "GamePassword" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/GamePassword//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		adminpassword=$(grep "AdminPassword" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/AdminPassword//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		port=$(grep "Port" "${servercfgdir}/LinuxServer-KFEngine.ini" | sed -e 's/^[ \t]*//g' | grep "^Port" | grep -v "#" | tr -cd '[:digit:]')
+		webadminenabled=$(grep "bEnabled" "${servercfgdir}/KFWeb.ini" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/bEnabled//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		httpport=$(grep "ListenPort" "${servercfgdir}/KFWeb.ini" | grep -v "#" | tr -cd '[:digit:]')
+		webadminuser="Admin"
+		webadminpass=$(grep "AdminPassword" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/AdminPassword//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		adminpassword=${adminpassword:-"NOT SET"}
+		port=${port:-"0"}
+		webadminenabled=${webadminenabled:-"NOT SET"}
+		httpport=${webadminport:-"0"}
+		webadminuser=${webadminuser:-"NOT SET"}
+		webadminpass=${webadminpass:-"NOT SET"}
+	fi
+
+	# Parameters
+	queryport=${queryport:-"0"}
+	defaultmap=${defaultmap:-"NOT SET"}
+}
+
+fn_info_game_lo() {
+	# Parameters
+	servername=${servername:-"NOT SET"}
+	port=${port:-"0"}
+	queryport=${queryport:-"0"}
+	maxplayers=${slots:-"0"}
+}
+
+fn_info_game_mc() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		rconpassword="${unavailable}"
+		rconport="${zero}"
+		maxplayers="${zero}"
+		port="${zero}"
+		queryport="${zero}"
+		queryenabled="${unavailable}"
+		gamemode="${unavailable}"
+		gameworld="${unavailable}"
+	else
+		servername=$(grep "motd" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/motd//g' | tr -d '=\";,:' | sed 's/\\u00A70//g;s/\\u00A71//g;s/\\u00A72//g;s/\\u00A73//g;s/\\u00A74//g;s/\\u00A75//g;s/\\u00A76//g;s/\\u00A77//g;s/\\u00A78//g;s/\\u00A79//g;s/\\u00A7a//g;s/\\u00A7b//g;s/\\u00A7c//g;s/\\u00A7d//g;s/\\u00A7e//g;s/\\u00A7f//g;s/\\u00A7l//g;s/\\u00A7o//g;s/\\u00A7n//g;s/\\u00A7m//g;s/\\u00A7k//g' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		rconpassword=$(grep "rcon.password" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/rcon.password//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		rconport=$(grep "rcon.port" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+		maxplayers=$(grep "max-players" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+		port=$(grep "server-port" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+		queryport=$(grep "query.port" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+		if [ -z "${queryport}" ]; then
+			queryport=${port}
+		fi
+		queryenabled=$(grep "enable-query" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/enable-query//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		gamemode=$(grep "gamemode" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+		gameworld=$(grep "level-name" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/level-name//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		configip=$(grep "server-ip" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^--/d' -e 's/server-ip//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		rconpassword=${rconpassword:-"NOT SET"}
+		rconport=${rconport:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+		port=${port:-"NOT SET"}
+		queryport=${queryport:-"NOT SET"}
+		queryenabled="${queryenabled:-"NOT SET"}"
+		gamemode=${gamemode:-"NOT SET"}
+		gameworld=${gameworld:-"NOT SET"}
+		configip=${configip:-"0.0.0.0"}
+	fi
+}
+
+fn_info_game_mcb() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		maxplayers="${zero}"
+		port="${zero}"
+		portipv6="${zero}"
+		queryport="${zero}"
+		gamemode="${unavailable}"
+		gameworld="${unavailable}"
+	else
+		servername=$(grep "server-name" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/server-name//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		maxplayers=$(grep "max-players" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+		port=$(grep "server-port\b" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+		portipv6=$(grep "server-portv6\b" "${servercfgfullpath}" | sed 's/v6//g' | grep -v "#" | tr -cd '[:digit:]')
+		queryport=${port}
+		gamemode=$(grep "gamemode" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/gamemode//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		gameworld=$(grep "level-name" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/level-name//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+		port=${port:-"NOT SET"}
+		portipv6=${portipv6:-"NOT SET"}
+		queryport=${queryport:-"NOT SET"}
+		gamemode=${gamemode:-"NOT SET"}
+		gameworld=${gameworld:-"NOT SET"}
+	fi
+}
+
+fn_info_game_mh() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+		rconpassword="${unavailable}"
+		maxplayers="${unavailable}"
+	else
+		servername=$(grep "ServerName" "${servercfgfullpath}" | awk -F '=' '{print $2}')
+		serverpassword=$(grep "ServerPassword" "${servercfgfullpath}" | awk -F '=' '{print $2}')
+		rconpassword=$(grep "AdminPassword" "${servercfgfullpath}" | awk -F '=' '{print $2}')
+		maxplayers=$(grep "MaxSlots" "${servercfgfullpath}" | awk -F '=' '{print $2}')
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		rconpassword=${rconpassword:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+	fi
+
+	# Parameters
+	port=${port:-"0"}
+	queryport=${queryport:-"0"}
+	beaconport=${beaconport:-"0"}
+}
+
+fn_info_game_mohaa() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		rconpassword="${unavailable}"
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+		maxplayers="${zero}"
+	else
+		rconpassword=$(grep "rconpassword" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/seta rconpassword//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		servername=$(grep "sv_hostname" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/seta sv_hostname//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		serverpassword=$(grep "g_password" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/seta g_password//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		maxplayers=$(grep "sv_maxclients" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+
+		# Not set
+		rconpassword=${rconpassword:-"NOT SET"}
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+	fi
+
+	# Parameters
+	port=${port:-"0"}
+	queryport=${port:-"0"}
+	defaultmap=${defaultmap:-"NOT SET"}
+}
+
+fn_info_game_mom() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+		maxplayers="${zero}"
+		defaultmap="${unavailable}"
+	else
+		servername=$(grep "ServerName" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^--/d' -e 's/ServerName//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		serverpassword=$(grep "ServerPassword" "${servercfgfullpath}" | sed -e 's/^ *//g' -e '/^--/d' -e 's/ServerPassword//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		maxplayers=$(grep "MaxPlayers" "${servercfgfullpath}" | sed -e 's/^ *//g' -e '/^--/d' -e 's/MaxPlayers//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		defaultmap=$(grep "MapName" "${servercfgfullpath}" | sed -e 's/^ *//g' -e '/^--/d' -e 's/MapName//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+		defaultmap=${defaultmap:-"NOT SET"}
+	fi
+
+	# Parameters
+	port=${port:-"7777"}
+	beaconport=${queryport:-"15000"}
+}
+
+fn_info_game_mta() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		port=${zero}
+		queryport=${zero}
+		httpport=${zero}
+		ase="${unavailable}"
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+		maxplayers="${zero}"
+	else
+		port=$(grep -m 1 "serverport" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/<serverport>//g' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//' | cut -f1 -d "<" | tr -cd '[:digit:]')
+		queryport=$((port + 123))
+		httpport=$(grep -m 1 "httpport" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/<httpport>//g' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//' | cut -f1 -d "<" | tr -cd '[:digit:]')
+		servername=$(grep -m 1 "servername" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/<servername>//g' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//' | cut -f1 -d "<")
+		serverpassword=$(grep -m 1 "password" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/<password>//g' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//' | cut -f1 -d "<")
+		maxplayers=$(grep -m 1 "maxplayers" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/<maxplayers>//g' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//' | cut -f1 -d "<" | tr -cd '[:digit:]')
+		ase=$(grep -m 1 "ase" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/<ase>//g' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//' | cut -f1 -d "<" | tr -cd '[:digit:]')
+		if [ "${ase}" == "1" ]; then
+			ase="Enabled"
+		else
+			ase="Disabled"
+		fi
+
+		# Not set
+		port=${port:-"22003"}
+		queryport=${queryport:-"2326"}
+		httpport=${httpport:-"22005"}
+		ase=${ase:-"Disabled"}
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+	fi
+
+}
+
+fn_info_game_nec() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		maxplayers=${maxplayers:-"0"}
+		port=${port:-"0"}
+		servername="Necesse"
+		serverpassword="${unavailable}"
+	else
+		maxplayers=$(grep "slots" "${servercfgfullpath}" | cut -f1 -d "/" | tr -cd '[:digit:]')
+		port=$(grep "port" "${servercfgfullpath}" | cut -f1 -d "/" | tr -cd '[:digit:]')
+		serverpassword=$(grep "password" "${servercfgfullpath}" | cut -f1 -d "/" | tr -cd '[:digit:]')
+
+		# Not set
+		maxplayers=${maxplayers:-"0"}
+		port=${port:-"0"}
+		servername="Necesse Port ${port}"
+		serverpassword=${serverpassword:-"NOT SET"}
+	fi
+}
+
+fn_info_game_onset() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		maxplayers="${zero}"
+		port="${zero}"
+		httpport="${zero}"
+		queryport="${zero}"
+	else
+		servername=$(grep -v "servername_short" "${servercfgfullpath}" | grep "servername" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/servername//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		maxplayers=$(grep "maxplayers" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+		port=$(grep "port" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+		httpport=$((port - 2))
+		queryport=$((port - 1))
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+		port=${port:-"NOT SET"}
+		httpport=${httpport:-"NOT SET"}
+		queryport=${queryport:-"NOT SET"}
+	fi
+}
+
+fn_info_game_pc() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+		maxplayers="${zero}"
+		port="${zero}"
+		queryport="${zero}"
+		steamport="${zero}"
+	else
+		servername=$(grep "name" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/name//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		serverpassword=$(grep "password " "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/password//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		maxplayers=$(grep "MaxPlayers" "${servercfgfullpath}" | grep -v "//" | tr -cd '[:digit:]')
+		port=$(grep "hostPort" "${servercfgfullpath}" | grep -v "//" | tr -cd '[:digit:]')
+		queryport=$(grep "queryPort" "${servercfgfullpath}" | grep -v "//" | tr -cd '[:digit:]')
+		steamport=$(grep "steamPort" "${servercfgfullpath}" | grep -v "//" | tr -cd '[:digit:]')
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+		port=${port:-"NOT SET"}
+		queryport=${queryport:-"NOT SET"}
+		steamport=${steamport:-"NOT SET"}
+	fi
+}
+
+fn_info_game_pc2() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+		maxplayers="${zero}"
+		port="${zero}"
+		queryport="${zero}"
+		steamport="${zero}"
+	else
+		servername=$(grep "name" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/name//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		serverpassword=$(grep "password " "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/password//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		maxplayers=$(grep "MaxPlayers" "${servercfgfullpath}" | grep -v "//" | tr -cd '[:digit:]')
+		port=$(grep "hostPort" "${servercfgfullpath}" | grep -v "//" | tr -cd '[:digit:]')
+		queryport=$(grep "queryPort" "${servercfgfullpath}" | grep -v "//" | tr -cd '[:digit:]')
+		steamport=$(grep "steamPort" "${servercfgfullpath}" | grep -v "//" | tr -cd '[:digit:]')
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+		port=${port:-"NOT SET"}
+		queryport=${queryport:-"NOT SET"}
+		steamport=${steamport:-"NOT SET"}
+	fi
+}
+
+fn_info_game_pstbs() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		maxplayers="${unavailable}"
+		reservedslots="${unavailable}"
+	else
+		servername=$(grep "ServerName=" "${servercfgfullpath}" | sed -e 's/^[ \t]//g' -e '/^#/d' -e 's/ServerName//g' | tr -d '=";,:' | sed -e 's/^[ \t]//' -e 's/[ \t]*$//')
+		maxplayers=$(grep "MaxPlayers=" "${servercfgfullpath}" | tr -cd '[:digit:]')
+		reservedslots=$(grep "NumReservedSlots=" "${servercfgfullpath}" | tr -cd '[:digit:]')
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+		reservedslots=${reservedslots:-"0"}
+	fi
+
+	if [ ! -f "${servercfgdir}/Rcon.cfg" ]; then
+		rconport=${unavailable}
+		rconpassword=${unavailable}
+	else
+		rconport=$(grep "Port=" "${servercfgdir}/Rcon.cfg" | tr -cd '[:digit:]')
+		rconpassword=$(grep "Password=" "${servercfgdir}/Rcon.cfg" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/Password//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+
+		# Not set
+		rconport=${rconport:-"0"}
+		if [ -z "${rconpassword}" ] || [ ${#rconpassword} == 1 ]; then
+			rconpassword="NOT SET"
+		fi
+	fi
+
+	# Parameters
+	port=${port:-"0"}
+	if [ -z "${queryport}" ]; then
+		queryport=${port:-"0"}
+	fi
+	rconport=${rconport:-"0"}
+	randommap=${randommap:-"NOT SET"}
+	maxplayers=${maxplayers:-"0"}
+	reservedslots=${reservedslots:-"0"}
+}
+
+fn_info_game_pvr() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		maxplayers="${unavailable}"
+	else
+		servername=$(grep "ServerName" "${servercfgfullpath}" | awk -F '=' '{print $2}')
+		maxplayers=$(grep "MaxPlayers" "${servercfgfullpath}" | awk -F '=' '{print $2}')
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+	fi
+
+	# Parameters
+	port=${port:-"0"}
+	port401=$((port + 400))
+	queryport=${port:-"0"}
+}
+
+fn_info_game_prism3d() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		maxplayers="${unavailable}"
+		port="${zero}"
+		queryport="${zero}"
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+	else
+		maxplayers=$(sed -nr 's/^\s*max_players\s*:\s*([0-9]+)/\1/p' "${servercfgfullpath}")
+		port=$(sed -nr 's/^\s*connection_dedicated_port\s*:\s*([0-9]+)/\1/p' "${servercfgfullpath}")
+		queryport=$(sed -nr 's/^\s*query_dedicated_port\s*:\s*([0-9]+)/\1/p' "${servercfgfullpath}")
+		servername=$(sed -nr 's/^\s*lobby_name\s*:\s*"?([^"\r\n]+)"?/\1/p' "${servercfgfullpath}")
+		serverpassword=$(sed -nr 's/^\s*password\s*:\s*"(.*)"/\1/p' "${servercfgfullpath}")
+
+		# Not set
+		maxplayers=${maxplayers:-"0"}
+		port=${port:-"27015"}
+		queryport=${queryport:-"27016"}
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+	fi
+}
+
+fn_info_game_pz() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+		rconpassword="${unavailable}"
+		maxplayers="${zero}"
+		port="${zero}"
+		queryport="${zero}"
+		gameworld="${unavailable}"
+	else
+		servername=$(grep "PublicName" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/PublicName//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		serverpassword=$(grep "Password" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' | grep "^Password" | sed -e '/^#/d' -e 's/Password//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		rconpassword=$(grep "RCONPassword" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/RCONPassword//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		maxplayers=$(grep "MaxPlayers" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+		port=$(grep "DefaultPort" "${servercfgfullpath}" | tr -cd '[:digit:]')
+		queryport=${port}
+		gameworld=$(grep "Map" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' | grep "^Map" | sed -e '/^#/d' -e 's/Map//g' | tr -d '=\";' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		rconpassword=${rconpassword:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+		port=${port:-"0"}
+		queryport=${queryport:-"0"}
+		gameworld=${gameworld:-"NOT SET"}
+	fi
+
+	# Parameters
+	adminpassword=${adminpassword:-"NOT SET"}
+
+}
+
+fn_info_game_q2() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		rconpassword="${unavailable}"
+		servername="${unavailable}"
+		maxplayers="${zero}"
+	else
+		rconpassword=$(grep "rcon_password" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/set rcon_password//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		servername=$(grep "hostname" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/set hostname//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		maxplayers=$(grep "maxclients" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+
+		# Not set
+		rconpassword=${rconpassword:-"NOT SET"}
+		servername=${servername:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+	fi
+
+	# Parameters
+	port=${port:-"0"}
+	queryport=${port}
+	defaultmap=${defaultmap:-"NOT SET"}
+}
+
+fn_info_game_q3() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		rconpassword="${unavailable}"
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+		maxplayers="${zero}"
+	else
+		rconpassword=$(grep "zmq_rcon_password" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/set zmq_rcon_password//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		servername=$(grep "sv_hostname" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/set sv_hostname//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		serverpassword=$(grep "rconpassword" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/set rconpassword//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		maxplayers=$(grep "sv_maxclients" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+
+		# Not set
+		rconpassword=${rconpassword:-"NOT SET"}
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+	fi
+
+	# Parameters
+	port=${port:-"0"}
+	queryport=${port}
+	defaultmap=${defaultmap:-"NOT SET"}
+}
+
+fn_info_game_ql() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		rconpassword="${unavailable}"
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+		maxplayers="${zero}"
+		port="${zero}"
+		queryport="${zero}"
+		rconport="${zero}"
+		statsport="${zero}"
+	else
+		rconpassword=$(grep "zmq_rcon_password" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/set zmq_rcon_password//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		servername=$(grep "sv_hostname" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/set sv_hostname//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		serverpassword=$(grep "g_password" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/set g_password//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		maxplayers=$(grep "sv_maxClients" "${servercfgfullpath}" | grep -v "//" | tr -cd '[:digit:]')
+		port=$(grep "net_port" "${servercfgfullpath}" | grep -v "//" | tr -cd '[:digit:]')
+		queryport=${port}
+		rconport=$(grep "zmq_rcon_port" "${servercfgfullpath}" | grep -v "//" | tr -cd '[:digit:]')
+		statsport=$(grep "zmq_stats_port" "${servercfgfullpath}" | grep -v "//" | tr -cd '[:digit:]')
+		configip=$(grep "set net_ip" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/set net_ip//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+
+		# Not set
+		rconpassword=${rconpassword:-"NOT SET"}
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+		port=${port:-"0"}
+		queryport=${queryport:-"0"}
+		rconport=${rconport:-"0"}
+		statsport=${statsport:-"0"}
+		configip=${configip:-"0.0.0.0"}
+	fi
+}
+
+fn_info_game_qw() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		rconpassword="${unavailable}"
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+		maxplayers="${zero}"
+		port="${zero}"
+	else
+		rconpassword=$(grep "rcon_password" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/set rcon_password//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//' | cut -f1 -d "/")
+		servername=$(grep "hostname" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/set hostname//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//' | cut -f1 -d "/")
+		maxplayers=$(grep "maxclients" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+
+		# Not set
+		rconpassword=${rconpassword:-"NOT SET"}
+		servername=${servername:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+	fi
+
+	# Parameters
+	port=${port:-"0"}
+	queryport=${port}
+}
+
+fn_info_game_ro() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+		adminpassword="${unavailable}"
+		port="${zero}"
+		queryport="${zero}"
+		steamport="${zero}"
+		steammasterport="${zero}"
+		lanport="${zero}"
+		httpport="${zero}"
+		webadminenabled="${unavailable}"
+		webadminuser="${unavailable}"
+		webadminpass="${unavailable}"
+	else
+		servername=$(sed -nr 's/^ServerName=(.*)$/\1/p' "${servercfgfullpath}" | tr -d '=\";,:' | sed 's/\r$//')
+		serverpassword=$(sed -nr 's/^GamePassword=(.*)$/\1/p' "${servercfgfullpath}" | tr -d '=\";,:' | sed 's/\r$//')
+		adminpassword=$(sed -nr 's/^AdminPassword=(.*)$/\1/p' "${servercfgfullpath}" | tr -d '=\";,:' | sed 's/\r$//')
+		port=$(sed -nr 's/^Port=(.*)$/\1/p' "${servercfgfullpath}" | tr -cd '[:digit:]')
+		queryport=$((port + 1))
+		steamport="20610"
+		steammasterport="28902"
+		lanport=$(grep "LANServerPort=" "${servercfgfullpath}" | tr -cd '[:digit:]')
+		httpport=$(sed -nr 's/^ListenPort=(.*)$/\1/p' "${servercfgfullpath}" | tr -cd '[:digit:]')
+		webadminenabled=$(sed -nr 's/^bEnabled=(.*)$/\1/p' "${servercfgfullpath}" | tr -d '=\";,:' | sed 's/\r$//')
+		webadminuser=$(sed -nr 's/^AdminName=(.*)$/\1/p' "${servercfgfullpath}" | tr -d '=\";,:' | sed 's/\r$//')
+		webadminpass="${adminpassword}"
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		adminpassword=${adminpassword:-"NOT SET"}
+		port=${port:-"0"}
+		queryport=${queryport:-"0"}
+		steamport=${steamport:-"0"}
+		steammasterport=${steammasterport:-"0"}
+		lanport=${lanport:-"0"}
+		httpport=${httpport:-"0"}
+		webadminenabled=${webadminenabled:-"NOT SET"}
+		webadminuser=${webadminuser:-"NOT SET"}
+		webadminpass=${webadminpass:-"NOT SET"}
+	fi
+
+	# Parameters
+	defaultmap=${defaultmap:-"NOT SET"}
+}
+
+fn_info_game_rtcw() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		rconpassword="${unavailable}"
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+		maxplayers="${zero}"
+	else
+		rconpassword=$(grep "rconpassword" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/set rconpassword//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		servername=$(grep "sv_hostname" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/set sv_hostname//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		serverpassword=$(grep "g_password" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/set g_password//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		maxplayers=$(grep "sv_maxclients" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+
+		# Not set
+		rconpassword=${rconpassword:-"NOT SET"}
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+	fi
+
+	# Parameters
+	port=${port:-"0"}
+	queryport=${port:-"0"}
+	defaultmap=${defaultmap:-"NOT SET"}
+}
+
+fn_info_game_rust() {
+	# Parameters
+	servername=${servername:-"NOT SET"}
+	port=${port:-"0"}
+	queryport=${queryport:-"0"}
+	appport=${appport:-"0"}
+	rconport=${rconport:-"0"}
+	gamemode=${gamemode:-"NOT SET"}
+	maxplayers=${maxplayers:-"0"}
+	rconpassword=${rconpassword:-"NOT SET"}
+	rconweb=${rconweb:-"NOT SET"}
+	tickrate=${tickrate:-"0"}
+	saveinterval=${saveinterval:-"0"}
+	serverlevel=${serverlevel:-"NOT SET"}
+	customlevelurl=${customlevelurl:-"NOT SET"}
+	worldsize=${worldsize:-"0"}
+	if [ -n "${seed}" ]; then
+		seed=${seed:-"0"}
+	elif [ -f "${datadir}/${selfname}-seed.txt" ]; then
+		seed=$(cat "${datadir}/${selfname}-seed.txt")
+	else
+		seed="0"
+	fi
+	salt=${salt:-"0"}
+}
+
+fn_info_game_rw() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+		rconpassword="${unavailable}"
+		rconport="${zero}"
+		maxplayers="${zero}"
+		port="${zero}"
+		port2="${zero}"
+		port3="${zero}"
+		port4="${zero}"
+		queryport="${zero}"
+		gamemode="${unavailable}"
+		gameworld="${unavailable}"
+	else
+		servername=$(grep "server_name" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/server_name//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		serverpassword=$(grep "server_password" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/server_password//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		rconpassword=$(grep "rcon_password" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/rcon_password//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		rconport=$(grep "rcon_port" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+		maxplayers=$(grep "settings_max_players" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+		port=$(grep "server_port" "${servercfgfullpath}" | grep -v "database_mysql_server_port" | grep -v "#" | tr -cd '[:digit:]')
+		port2=$((port + 1))
+		port3=$((port + 2))
+		port4=$((port + 3))
+		queryport="${port}"
+		httpqueryport=$((port - 1))
+		gamemode=$(grep "settings_default_gamemode=" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/settings_default_gamemode//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		gameworld=$(grep "server_world_name" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/server_world_name//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		configip=$(grep "server_ip" "${servercfgfullpath}" | grep -v "database_mysql_server_ip" | sed -e 's/^[ \t]*//g' -e '/^--/d' -e 's/server_ip//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		rconpassword=${rconpassword:-"NOT SET"}
+		rconport=${rconport:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+		port=${port:-"0"}
+		port2=${port2:-"0"}
+		port3=${port3:-"0"}
+		port4=${port4:-"0"}
+		queryport=${queryport:-"0"}
+		httpqueryport=${httpport:-"0"}
+		gamemode=${gamemode:-"NOT SET"}
+		gameworld=${gameworld:-"NOT SET"}
+		configip=${configip:-"0.0.0.0"}
+	fi
+}
+
+fn_info_game_samp() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="unnamed server"
+		rconpassword="${unavailable}"
+		port="7777"
+		rconport="${port}"
+		maxplayers="50"
+	else
+		servername=$(grep "hostname" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/hostname//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		rconpassword=$(grep "rcon_password" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/^rcon_password//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		port=$(grep "port" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+		queryport=${port}
+		rconport=${port}
+		maxplayers=$(grep "maxplayers" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		rconpassword=${rconpassword:-"NOT SET"}
+		port=${port:-"7777"}
+		queryport=${port:-"7777"}
+		rconport=${rconport:-"7777"}
+		maxplayers=${maxplayers:-"12"}
+	fi
+}
+
+fn_info_game_sb() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		queryenabled="${unavailable}"
+		rconenabled="${unavailable}"
+		rconpassword="${unavailable}"
+		port="21025"
+		queryport="21025"
+		rconport="21026"
+		maxplayers="8"
+	else
+		servername=$(grep "serverName" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e 's/serverName//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		queryenabled=$(grep "runQueryServer" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e 's/runQueryServer//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		rconenabled=$(grep "runRconServer" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e 's/runRconServer//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		rconpassword=$(grep "rconServerPassword" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e 's/rconServerPassword//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		port=$(grep "gameServerPort" "${servercfgfullpath}" | tr -cd '[:digit:]')
+		queryport=$(grep "queryServerPort" "${servercfgfullpath}" | tr -cd '[:digit:]')
+		rconport=$(grep "rconServerPort" "${servercfgfullpath}" | tr -cd '[:digit:]')
+		maxplayers=$(grep "maxPlayers" "${servercfgfullpath}" | tr -cd '[:digit:]')
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		queryenabled=${queryenabled:-"NOT SET"}
+		rconenabled=${rconenabled:-"NOT SET"}
+		rconpassword=${rconpassword:-"NOT SET"}
+		port=${port:-"21025"}
+		queryport=${queryport:-"21025"}
+		rconport=${rconport:-"21026"}
+		maxplayers=${maxplayers:-"8"}
+	fi
+}
+
+fn_info_game_sbots() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		maxplayers="${unavailable}"
+	else
+		servername=$(grep "ServerName=" "${servercfgfullpath}" | sed -e 's/^[ \t]//g' -e '/^#/d' -e 's/ServerName//g' | tr -d '=";,:' | sed -e 's/^[ \t]//' -e 's/[ \t]*$//')
+		maxplayers=$(grep "MaxPlayers=" "${servercfgfullpath}" | tr -cd '[:digit:]')
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+	fi
+
+	# Parameters
+	port=${port:-"0"}
+	queryport=${queryport:-"0"}
+	servername=${servername:-"NOT SET"}
+	serverpassword=${serverpassword:-"NOT SET"}
+	defaultmap=${defaultmap:-"NOT SET"}
+	maxplayers=${maxplayers:-"0"}
+}
+
+fn_info_game_scpsl() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername=${servername:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+		configip=${configip:-"0.0.0.0"}
+		tickrate=${tickrate:-"NOT SET"}
+		adminpassword=${adminpassword:-"NOT SET"}
+	else
+		servername=$(sed -nr 's/^server_name: (.*)$/\1/p' "${servercfgfullpath}")
+		maxplayers=$(sed -nr 's/^max_players: (.*)$/\1/p' "${servercfgfullpath}")
+		configip=$(sed -nr 's/^ipv4_bind_ip: (.*)$/\1/p' "${servercfgfullpath}")
+		tickrate=$(sed -nr 's/^server_tickrate: (.*)$/\1/p' "${servercfgfullpath}")
+		adminpassword=$(sed -nr 's/^administrator_query_password: (.*)$/\1/p' "${servercfgfullpath}")
+	fi
+
+	# Parameters
+	port=${port:-"0"}
+	queryport=${port}
+}
+
+fn_info_game_sdtd() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+		port="${zero}"
+		port3="${zero}"
+		queryport="${zero}"
+		webadminenabled="${unavailable}"
+		webadminport="${zero}"
+		webadminpass="${unavailable}"
+		telnetenabled="${unavailable}"
+		telnetport="${zero}"
+		telnetpass="${unavailable}"
+		telnetip="${unavailable}"
+		maxplayers="${unavailable}"
+		gamemode="${unavailable}"
+		gameworld="${unavailable}"
+	else
+		servername=$(grep "ServerName" "${servercfgfullpath}" | sed 's/^.*value="//' | cut -f1 -d"\"")
+		serverpassword=$(grep "ServerPassword" "${servercfgfullpath}" | sed 's/^.*value="//' | cut -f1 -d"\"")
+		port=$(grep "ServerPort" "${servercfgfullpath}" | grep -Eo 'value="[0-9]+"' | tr -cd '[:digit:]')
+		port3=$((port + 2))
+		queryport=${port:-"0"}
+		webadminenabled=$(grep "ControlPanelEnabled" "${servercfgfullpath}" | sed 's/^.*value="//' | cut -f1 -d"\"")
+		webadminport=$(grep "ControlPanelPort" "${servercfgfullpath}" | tr -cd '[:digit:]')
+		webadminpass=$(grep "ControlPanelPassword" "${servercfgfullpath}" | sed 's/^.*value="//' | cut -f1 -d"\"")
+		telnetenabled=$(grep "TelnetEnabled" "${servercfgfullpath}" | sed 's/^.*value="//' | cut -f1 -d"\"")
+		telnetport=$(grep "TelnetPort" "${servercfgfullpath}" | tr -cd '[:digit:]')
+		telnetpass=$(grep "TelnetPassword" "${servercfgfullpath}" | sed 's/^.*value="//' | cut -f1 -d"\"")
+		# Telnet IP will be localhost if no password is set
+		# check_ip will set the IP first. This will overwrite it.
+		if [ -z "${telnetpass}" ]; then
+			telnetip="127.0.0.1"
+		fi
+		maxplayers=$(grep "ServerMaxPlayerCount" "${servercfgfullpath}" | tr -cd '[:digit:]')
+		gamemode=$(grep "GameMode" "${servercfgfullpath}" | sed 's/^.*value="//' | cut -f1 -d"\"")
+		gameworld=$(grep "GameWorld" "${servercfgfullpath}" | sed 's/^.*value="//' | cut -f1 -d"\"")
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		port=${port:-"0"}
+		queryport=${queryport:-"0"}
+		webadminenabled=${webadminenabled:-"NOT SET"}
+		webadminport=${webadminport:-"0"}
+		webadminpass=${webadminpass:-"NOT SET"}
+		telnetenabled=${telnetenabled:-"NOT SET"}
+		telnetport=${telnetport:-"0"}
+		telnetpass=${telnetpass:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+		gamemode=${gamemode:-"NOT SET"}
+		gameworld=${gameworld:-"NOT SET"}
+	fi
+}
+
+fn_info_game_sf() {
+	# Parameters
+	servername=${selfname:-"NOT SET"}
+	port=${port:-"0"}
+	queryport=${queryport:-"0"}
+	beaconport=${beaconport:-"0"}
+}
+
+fn_info_game_sof2() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		rconpassword="${unavailable}"
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+		maxplayers="${zero}"
+	else
+		rconpassword=$(grep "rconpassword" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/set rconpassword//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		servername=$(grep "sv_hostname" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/set sv_hostname//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		serverpassword=$(grep "g_password" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/set g_password//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		maxplayers=$(grep "sv_maxclients" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+
+		# Not set
+		rconpassword=${rconpassword:-"NOT SET"}
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+	fi
+
+	# Parameters
+	port=${port:-"0"}
+	queryport=${port}
+	defaultmap=${defaultmap:-"NOT SET"}
+}
+
+fn_info_game_sol() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		adminpassword="${unavailable}"
+		maxplayers="${unavailable}"
+		port="${zero}"
+		queryport="${zero}"
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+	else
+		adminpassword=$(grep "Admin_Password=" "${servercfgfullpath}" | awk -F '=' '{print $2}')
+		maxplayers=$(grep "Max_Players=" "${servercfgfullpath}" | tr -cd '[:digit:]')
+		port=$(grep "Port=" "${servercfgfullpath}" | tr -cd '[:digit:]')
+		queryport="${port}"
+		filesport=$((port + 10))
+		servername=$(grep "Server_Name=" "${servercfgfullpath}" | awk -F '=' '{print $2}')
+		serverpassword=$(grep "Game_Password=" "${servercfgfullpath}" | awk -F '=' '{print $2}')
+
+		# Not set
+		adminpassword=${adminpassword:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+		port=${port:-"23073"}
+		queryport=${queryport:-"23083"}
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+	fi
+}
+
+fn_info_game_source() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+		rconpassword="${unavailable}"
+	else
+		servername=$(grep "hostname" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/hostname//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		serverpassword=$(grep "sv_password" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/sv_password//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		rconpassword=$(grep "rcon_password" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/rcon_password//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		rconpassword=${rconpassword:-"NOT SET"}
+	fi
+
+	# Parameters
+	defaultmap=${defaultmap:-"NOT SET"}
+	maxplayers=${maxplayers:-"0"}
+	port=${port:-"0"}
+	rconport=${port:-"0"}
+	queryport=${port:-"0"}
+	clientport=${clientport:-"0"}
+	# Steamport can be between 26901-26910 and is normaly automatically set.
+	# Some servers might support -steamport parameter to set
+	if [ "${steamport}" == "0" ] || [ -v "${steamport}" ]; then
+		steamport="$(echo "${ssinfo}" | grep "${srcdslinuxpid}" | awk '{print $5}' | grep ":269" | cut -d ":" -f2)"
+	fi
+}
+
+fn_info_game_spark() {
+	defaultmap=${defaultmap:-"NOT SET"}
+	maxplayers=${maxplayers:-"0"}
+	port=${port:-"0"}
+	queryport=$((port + 1))
+	servername=${servername:-"NOT SET"}
+	serverpassword=${serverpassword:-"NOT SET"}
+	webadminuser=${webadminuser:-"NOT SET"}
+	webadminpass=${webadminpass:-"NOT SET"}
+	webadminport=${webadminport:-"0"}
+	# Commented out as displaying not set in details parameters
+	#mods=${mods:-"NOT SET"}
+}
+
+fn_info_game_squad() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		maxplayers="${unavailable}"
+	else
+		servername=$(grep "ServerName=" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/ServerName//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		maxplayers=$(grep "MaxPlayers=" "${servercfgfullpath}" | tr -cd '[:digit:]')
+
+		servername=${servername:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+	fi
+
+	if [ ! -f "${servercfgdir}/Rcon.cfg" ]; then
+		rconport=${unavailable}
+		rconpassword=${unavailable}
+	else
+		rconport=$(grep "Port=" "${servercfgdir}/Rcon.cfg" | tr -cd '[:digit:]')
+		rconpassword=$(grep "Password=" "${servercfgdir}/Rcon.cfg" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/Password//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+
+		rconport=${rconport:-"0"}
+		if [ -z "${rconpassword}" ] || [ ${#rconpassword} == 1 ]; then
+			rconpassword="NOT SET"
+		fi
+
+	fi
+
+	# Parameters
+	port=${port:-"0"}
+	queryport=${queryport:-"0"}
+}
+
+fn_info_game_st() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+		rconpassword="${unavailable}"
+		maxplayers="${unavailable}"
+	else
+		servername=$(grep "SERVERNAME" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/SERVERNAME//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		serverpassword=$(grep "PASSWORD" "${servercfgfullpath}" | grep "^PASSWORD" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/PASSWORD//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		rconpassword=$(grep "RCONPASSWORD" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/RCONPASSWORD//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		maxplayers=$(grep "MAXPLAYER" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/MAXPLAYER//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		rconpassword=${rconpassword:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+	fi
+
+	# Parameters
+	port=${port:-"0"}
+	queryport=${queryport:-"0"}
+	httpport=${port:-"0"}
+	worldtype=${worldtype:-"NOT SET"}
+	autosaveinterval=${autosaveinterval:-"0"}
+	clearinterval=${clearinterval:-"0"}
+	worldname=${worldname:-"NOT SET"}
+}
+
+fn_info_game_terraria() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		port="${zero}"
+		gameworld="${unavailable}"
+		maxplayers="${zero}"
+		queryport="${zero}"
+	else
+		servername=$(grep "worldname" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/worldname//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		port=$(grep "port" "${servercfgfullpath}" | tr -cd '[:digit:]')
+		queryport=${port:-"0"}
+		gameworld=$(grep "world=" "${servercfgfullpath}" | grep -v "//" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/world=//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		maxplayers=$(grep "maxplayers" "${servercfgfullpath}" | grep -v "//" | tr -cd '[:digit:]')
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		port=${port:-"0"}
+		queryport=${queryport:-"0"}
+		gameworld=${gameworld:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+	fi
+}
+
+fn_info_game_stn() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		configip=${configip:-"0.0.0.0"}
+		port="${zero}"
+		queryport="${zero}"
+		serverpassword=${serverpassword:-"NOT SET"}
+	else
+		servername=$(sed -nr 's/^ServerName="(.*)"/\1/p' "${servercfgfullpath}")
+		configip=$(sed -nr 's/^ServerIP=([0-9]+)/\1/p' "${servercfgfullpath}")
+		port=$(sed -nr 's/^ServerPort=([0-9]+)/\1/p' "${servercfgfullpath}")
+		serverpassword=$(sed -nr 's/^ServerPassword=(.*)$/\1/p' "${servercfgfullpath}")
+		queryport=$((port + 1))
+
+		# Not set
+		serverpassword=${serverpassword:-"NOT SET"}
+		port=${port:-"0"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		queryport=${queryport:-"0"}
+	fi
+}
+
+fn_info_game_ti() {
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		maxplayers="${zero}"
+	else
+		servername=$(sed -nr 's/^ServerName="(.*)"/\1/p' "${servercfgfullpath}")
+		maxplayers=$(sed -nr 's/^MaxPlayerCount=([0-9]+)/\1/' "${servercfgfullpath}")
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+	fi
+
+}
+
+fn_info_game_ts3() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		dbplugin="${unavailable}"
+		port="9987"
+		queryport="10011"
+		querysshport="10022"
+		queryhttpport="10080"
+		queryhttpsport="10443"
+		fileport="30033"
+		telnetport="10011"
+	else
+		dbplugin=$(grep "dbplugin=" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/dbplugin=//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		port=$(grep "default_voice_port" "${servercfgfullpath}" | tr -cd '[:digit:]')
+		queryport=$(grep "query_port" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+		querysshport=$(grep "query_ssh_port" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+		queryhttpport=$(grep "query_http_port" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+		queryhttpsport=$(grep "query_https_port" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+		fileport=$(grep "filetransfer_port" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+		telnetport="${queryport}"
+		configip=$(grep "voice_ip" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/voice_ip//g' | sed 's/,.*//' | tr -d '=\";,' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+
+		# Not set
+		dbplugin=${dbplugin:-"NOT SET"}
+		port=${port:-"9987"}
+		queryport=${queryport:-"10011"}
+		querysshport=${querysshport:-"10022"}
+		queryhttpport=${queryhttpport:-"10080"}
+		queryhttpsport=${queryhttpsport:-"10443"}
+		fileport=${fileport:-"30033"}
+		telnetport=${telnetport:-"10011"}
+		configip=${configip:-"0.0.0.0"}
+	fi
+}
+
+fn_info_game_tu() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		maxplayers="${zero}"
+	else
+		servername=$(grep "ServerTitle" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^--/d' -e 's/ServerTitle//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		maxplayers=$(grep "MaxPlayers" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+	fi
+
+	# Parameters
+	port=${port:-"0"}
+	steamport=$((port + 1))
+	queryport=${queryport:-"0"}
+}
+
+fn_info_game_tw() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="unnamed server"
+		serverpassword="${unavailable}"
+		rconpassword="${unavailable}"
+		port="8303"
+		queryport="8303"
+		maxplayers="12"
+	else
+		servername=$(grep "sv_name" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/^sv_name//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		serverpassword=$(grep "password" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' | grep "^password" | sed -e '/^#/d' -e 's/^password//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		rconpassword=$(grep "sv_rcon_password" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/^sv_rcon_password//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		port=$(grep "sv_port" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+		queryport="${port}"
+		maxplayers=$(grep "sv_max_clients" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		rconpassword=${rconpassword:-"NOT SET"}
+		port=${port:-"8303"}
+		queryport=${port:-"8303"}
+		maxplayers=${maxplayers:-"12"}
+	fi
+}
+
+fn_info_game_ut99() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+		adminpassword="${unavailable}"
+		port="${zero}"
+		queryport="${zero}"
+		queryportgs="${zero}"
+		webadminenabled="${unavailable}"
+		webadminport="${zero}"
+		webadminuser="${unavailable}"
+		webadminpass="${unavailable}"
+	else
+		servername=$(grep "ServerName" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/ServerName//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//' | sed 's/\r$//')
+		serverpassword=$(grep "GamePassword" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/GamePassword//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//' | sed 's/\r$//')
+		adminpassword=$(grep "AdminPassword" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/AdminPassword//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//' | sed 's/\r$//')
+		port=$(grep "Port" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' | grep "^Port" | grep -v "#" | tr -cd '[:digit:]')
+		queryport=$((port + 1))
+		queryportgs=$(grep "OldQueryPortNumber" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+		beaconport=$(grep "ServerBeaconPort" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+		webadminenabled=$(grep "bEnabled" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/bEnabled//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//' | sed 's/\r$//')
+		httpport=$(grep "ListenPort" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+		webadminuser=$(grep "AdminUsername" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/AdminUsername//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//' | sed 's/\r$//')
+		webadminpass=$(grep "UTServerAdmin.UTServerAdmin" "${servercfgfullpath}" -A 4 | grep "AdminPassword" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/AdminPassword//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//' | sed 's/\r$//')
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		adminpassword=${adminpassword:-"NOT SET"}
+		port=${port:-"0"}
+		queryport=${queryport:-"0"}
+		beaconport=${beaconport:-"8777"}
+		queryportgs=${queryportgs:-"0"}
+		webadminenabled=${webadminenabled:-"NOT SET"}
+		webadminport=${webadminport:-"0"}
+		webadminuser=${webadminuser:-"NOT SET"}
+		webadminpass=${webadminpass:-"NOT SET"}
+	fi
+
+	# Parameters
+	defaultmap=${defaultmap:-"NOT SET"}
+}
+
+fn_info_game_unreal2() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+		adminpassword="${unavailable}"
+		port="${zero}"
+		queryport="${zero}"
+		queryportgs="${zero}"
+		webadminenabled="${unavailable}"
+		webadminport="${zero}"
+		webadminuser="${unavailable}"
+		webadminpass="${unavailable}"
+	else
+		servername=$(sed -nr 's/^ServerName=(.*)$/\1/p' "${servercfgfullpath}" | tr -d '=\";,:' | sed 's/\r$//')
+		serverpassword=$(sed -nr 's/^GamePassword=(.*)$/\1/p' "${servercfgfullpath}" | tr -d '=\";,:' | sed 's/\r$//')
+		adminpassword=$(sed -nr 's/^AdminPassword=(.*)$/\1/p' "${servercfgfullpath}" | tr -d '=\";,:' | sed 's/\r$//')
+		port=$(sed -nr 's/^Port=(.*)$/\1/p' "${servercfgfullpath}" | tr -cd '[:digit:]')
+		queryport=$((port + 1))
+		queryportgs=$(sed -nr 's/^OldQueryPortNumber=(.*)$/\1/p' "${servercfgfullpath}" | tr -cd '[:digit:]')
+		webadminenabled=$(sed -nr 's/^bEnabled=(.*)$/\1/p' "${servercfgfullpath}" | tr -d '=\";,:' | sed 's/\r$//')
+		webadminport=$(sed -nr 's/^ListenPort=(.*)$/\1/p' "${servercfgfullpath}" | tr -cd '[:digit:]')
+		webadminuser=$(sed -nr 's/^AdminName=(.*)$/\1/p' "${servercfgfullpath}" | tr -d '=\";,:' | sed 's/\r$//')
+		webadminpass="${adminpassword}"
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		adminpassword=${adminpassword:-"NOT SET"}
+		port=${port:-"0"}
+		queryport=${queryport:-"0"}
+		queryportgs=${queryportgs:-"0"}
+		webadminenabled=${webadminenabled:-"NOT SET"}
+		webadminport=${webadminport:-"0"}
+		webadminuser=${webadminuser:-"NOT SET"}
+		webadminpass=${webadminpass:-"NOT SET"}
+	fi
+
+	# Parameters
+	defaultmap=${defaultmap:-"NOT SET"}
+}
+
+fn_info_game_unt() {
+	# Parameters
+	servername=${selfname:-"NOT SET"}
+	port=${port:-"0"}
+	queryport=${port}
+	steamport=$((port + 1))
+}
+
+fn_info_game_ut() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+	else
+		servername=$(grep "ServerName" "${servercfgfullpath}" | awk -F '=' '{print $2}')
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+	fi
+
+	# Parameters
+	port=${port:-"0"}
+	queryport=$((port + 1))
+}
+
+fn_info_game_unreal2k4() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+		adminpassword="${unavailable}"
+		port="${zero}"
+		queryport="${zero}"
+		queryportgs="${zero}"
+		lanport="${zero}"
+		webadminenabled="${unavailable}"
+		httpport="${zero}"
+		webadminuser="${unavailable}"
+		webadminpass="${unavailable}"
+	else
+		servername=$(sed -nr 's/^ServerName=(.*)$/\1/p' "${servercfgfullpath}" | tr -d '=\";,:' | sed 's/\r$//')
+		serverpassword=$(sed -nr 's/^GamePassword=(.*)$/\1/p' "${servercfgfullpath}" | tr -d '=\";,:' | sed 's/\r$//')
+		adminpassword=$(sed -nr 's/^AdminPassword=(.*)$/\1/p' "${servercfgfullpath}" | tr -d '=\";,:' | sed 's/\r$//')
+		port=$(sed -nr 's/^Port=(.*)$/\1/p' "${servercfgfullpath}" | tr -cd '[:digit:]')
+		queryport=$((port + 1))
+		queryportgs=$(sed -nr 's/^OldQueryPortNumber=(.*)$/\1/p' "${servercfgfullpath}" | tr -cd '[:digit:]')
+		lanport=$(grep "LANServerPort=" "${servercfgfullpath}" | tr -cd '[:digit:]')
+		webadminenabled=$(sed -nr 's/^bEnabled=(.*)$/\1/p' "${servercfgfullpath}" | tr -d '=\";,:' | sed 's/\r$//')
+		httpport=$(sed -nr 's/^ListenPort=(.*)$/\1/p' "${servercfgfullpath}" | tr -cd '[:digit:]')
+		webadminuser=$(sed -nr 's/^AdminName=(.*)$/\1/p' "${servercfgfullpath}" | tr -d '=\";,:' | sed 's/\r$//')
+		webadminpass="${adminpassword}"
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		adminpassword=${adminpassword:-"NOT SET"}
+		port=${port:-"0"}
+		queryport=${queryport:-"0"}
+		queryportgs=${queryportgs:-"0"}
+		lanport=${lanport:-"0"}
+		webadminenabled=${webadminenabled:-"NOT SET"}
+		httpport=${httpport:-"0"}
+		webadminuser=${webadminuser:-"NOT SET"}
+		webadminpass=${webadminpass:-"NOT SET"}
+	fi
+}
+
+fn_info_game_ut3() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+		adminpassword="${unavailable}"
+		maxplayers="${unavailable}"
+		webadminenabled="${unavailable}"
+		webadminport="${zero}"
+		webadminuser="${unavailable}"
+		webadminpass="${unavailable}"
+	else
+		servername=$(grep "ServerName" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/ServerName//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		serverpassword=$(grep "GamePassword" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/GamePassword//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		adminpassword=$(grep "AdminPassword" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/AdminPassword//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		maxplayers=$(grep "MaxPlayers" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+		webadminenabled=$(grep "bEnabled" "${servercfgdir}/UTWeb.ini" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/bEnabled//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		webadminport=$(grep "ListenPort" "${servercfgdir}/UTWeb.ini" | grep -v "#" | tr -cd '[:digit:]')
+		webadminuser="Admin"
+		webadminpass=$(grep "AdminPassword" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/AdminPassword//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		adminpassword=${adminpassword:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+		webadminenabled=${webadminenabled:-"NOT SET"}
+		webadminport=${webadminport:-"0"}
+		webadminuser=${webadminuser:-"NOT SET"}
+		webadminpass=${webadminpass:-"NOT SET"}
+	fi
+
+	# Parameters
+	port=${port:-"0"}
+	queryport=${queryport:-"0"}
+	defaultmap=${defaultmap:-"NOT SET"}
+}
+
+fn_info_game_vh() {
+	# Parameters
+	port=${port:-"0"}
+	# Query port only enabled if public server
+	if [ "${public}" != "0" ]; then
+		queryport=$((port + 1))
+	else
+		querymode="1"
+	fi
+	gameworld=${gameworld:-"NOT SET"}
+	serverpassword=${serverpassword:-"NOT SET"}
+	servername=${servername:-"NOT SET"}
+}
+
+fn_info_game_vints() {
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		maxplayers="${unavailable}"
+		serverpassword="${unavailable}"
+		port="${port:-"0"}"
+	else
+		servername=$(jq -r '.ServerName' "${servercfgfullpath}")
+		maxplayers=$(jq -r '.MaxClients' "${servercfgfullpath}")
+		serverpassword=$(jq -r 'select(.Password != null) | .Password' "${servercfgfullpath}")
+		port=$(jq -r '.Port' "${servercfgfullpath}")
+		configip=$(jq -r 'select(.Ip != null) | .Ip' "${servercfgfullpath}")
+	fi
+	queryport=${port:-"0"}
+	serverpassword=${serverpassword:-"NOT SET"}
+	configip=${configip:-"0.0.0.0"}
+}
+
+fn_info_game_vpmc() {
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		configip="0.0.0.0"
+		port="25577"
+	else
+		servername=$(sed -nr 's/^motd\s*=\s*"(.*)"/\1/p' "${servercfgfullpath}")
+		bindaddress=$(sed -nr 's/^bind\s*=\s*"([0-9.:]+)"/\1/p' "${servercfgfullpath}")
+		configip=$(echo "${bindaddress}" | cut -d ':' -f 1)
+		port=$(echo "${bindaddress}" | cut -d ':' -f 2)
+
+		servername=${servername:-"NOT SET"}
+	fi
+	queryport=${port:-"25577"}
+}
+
+fn_info_game_wet() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		rconpassword="${unavailable}"
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+		maxplayers="${zero}"
+		port="${zero}"
+		queryport="${zero}"
+	else
+		port=$(grep "set net_port" "${servercfgfullpath}" | grep -v "//" | tr -cd '[:digit:]')
+		queryport="${port}"
+		rconpassword=$(grep "set zmq_rcon_password" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/set zmq_rcon_password //g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//g' -e '/^\//d' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		servername=$(grep "set sv_hostname" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/set sv_hostname //g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		serverpassword=$(grep "set g_password" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/set g_password //g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		maxplayers=$(grep "set sv_maxclients" "${servercfgfullpath}" | grep -v "//" | tr -cd '[:digit:]')
+		configip=$(grep "set net_ip" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/set net_ip//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+
+		# Not set
+		rconpassword=${rconpassword:-"NOT SET"}
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+		port=${port:-"27960"}
+		queryport=${queryport:-"27960"}
+		configip=${configip:-"0.0.0.0"}
+	fi
+}
+
+fn_info_game_wf() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		rconpassword="${unavailable}"
+		servername="${unavailable}"
+		maxplayers="${zero}"
+	else
+		rconpassword=$(grep "rcon_password" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/set rcon_password//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		servername=$(grep "sv_hostname" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/set sv_hostname//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		maxplayers=$(grep "sv_maxclients" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+
+		# Not set
+		rconpassword=${rconpassword:-"NOT SET"}
+		servername=${servername:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+	fi
+
+	# Parameters
+	port=${port:-"0"}
+	queryport="${port:-"0"}"
+	webadminport=${webadminport:-"0"}
+}
+
+fn_info_game_wmc() {
+	if [ ! -f "${servercfgfullpath}" ]; then
+		servername="${unavailable}"
+		maxplayers="${zero}"
+		port="${zero}"
+		queryport="${zero}"
+		queryenabled="${unavailable}"
+	else
+		servername=$(sed -e '/^listeners:/,/^[a-z]/!d' "${servercfgfullpath}" | sed -nr 's/^[ ]+motd: (.*)$/\1/p' | tr -d "'" | sed 's/&1//')
+		queryport=$(sed -nr 's/^[ -]+query_port: ([0-9]+)/\1/p' "${servercfgfullpath}")
+		queryenabled=$(sed -nr 's/^[ ]+query_enabled: (.*)$/\1/p' "${servercfgfullpath}")
+		port=$(sed -nr 's/^[ ]+host: [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:([0-9]+)/\1/p' "${servercfgfullpath}")
+		# the normal max_players does only show in on the client side and has no effect how many players can connect.
+		maxplayers=$(sed -nr 's/^player_limit: ([-]*[0-9])/\1/p' "${servercfgfullpath}")
+		configip=$(sed -nr 's/^[ ]+host: ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+):[0-9]+/\1/p' "${servercfgfullpath}")
+
+		if [ "${maxplayers}" == "-1" ] || [ "${maxplayers}" == "0" ]; then
+			maxplayers="UNLIMITED"
+		fi
+
+		# Not set
+		servername=${servername:-"NOT SET"}
+		queryport=${queryport:-"25577"}
+		maxplayers=${maxplayers:-"0"}
+		configip=${configip:-"0.0.0.0"}
+	fi
+}
+
+fn_info_game_wurm() {
+	# Config
+	if [ ! -f "${servercfgfullpath}" ]; then
+		port="${zero}"
+		queryport="${zero}"
+		rconpassword="${unavailable}"
+		servername="${unavailable}"
+		serverpassword="${unavailable}"
+		adminpassword="${unavailable}"
+		maxplayers="${zero}"
+	else
+		port=$(grep "EXTERNALPORT=" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+		queryport=$(grep "QUERYPORT=" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+		servername=$(grep "SERVERNAME=" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/SERVERNAME//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		serverpassword=$(grep "SERVERPASSWORD=" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/SERVERPASSWORD//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		adminpassword=$(grep "ADMINPWD=" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/ADMINPWD//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+		maxplayers=$(grep "MAXPLAYERS=" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+		configip=$(grep "IP" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/IP//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
+
+		# Not set
+		port=${port:-"3724"}
+		queryport=${queryport:-"27017"}
+		servername=${servername:-"NOT SET"}
+		serverpassword=${serverpassword:-"NOT SET"}
+		adminpassword=${adminpassword:-"NOT SET"}
+		maxplayers=${maxplayers:-"0"}
+	fi
+}
+
+unavailable="${red}UNAVAILABLE${default}"
+zero="${red}0${default}"
+
+if [ "${shortname}" == "ac" ]; then
+	fn_info_game_ac
+elif [ "${shortname}" == "ark" ]; then
+	fn_info_game_ark
+elif [ "${shortname}" == "arma3" ]; then
+	fn_info_game_arma3
+elif [ "${shortname}" == "armar" ]; then
+	fn_info_game_armar
+elif [ "${shortname}" == "av" ]; then
+	fn_info_game_av
+elif [ "${shortname}" == "bf1942" ]; then
+	fn_info_game_bf1942
+elif [ "${shortname}" == "bfv" ]; then
+	fn_info_game_bfv
+elif [ "${shortname}" == "bo" ]; then
+	fn_info_game_bo
+elif [ "${shortname}" == "bt" ]; then
+	fn_info_game_bt
+elif [ "${shortname}" == "btl" ]; then
+	fn_info_game_btl
+elif [ "${shortname}" == "cd" ]; then
+	fn_info_game_cd
+elif [ "${shortname}" == "ck" ]; then
+	fn_info_game_ck
+elif [ "${shortname}" == "cmw" ]; then
+	fn_info_game_cmw
+elif [ "${shortname}" == "cod" ]; then
+	fn_info_game_cod
+elif [ "${shortname}" == "coduo" ]; then
+	fn_info_game_cod
+elif [ "${shortname}" == "cod2" ]; then
+	fn_info_game_cod2
+elif [ "${shortname}" == "cod4" ]; then
+	fn_info_game_cod4
+elif [ "${shortname}" == "codwaw" ]; then
+	fn_info_game_codwaw
+elif [ "${shortname}" == "col" ]; then
+	fn_info_game_col
+elif [ "${shortname}" == "dayz" ]; then
+	fn_info_game_dayz
+elif [ "${shortname}" == "dodr" ]; then
+	fn_info_game_dodr
+elif [ "${shortname}" == "dst" ]; then
+	fn_info_game_dst
+elif [ "${shortname}" == "eco" ]; then
+	fn_info_game_eco
+elif [ "${shortname}" == "etl" ]; then
+	fn_info_game_etl
+elif [ "${shortname}" == "fctr" ]; then
+	fn_info_game_fctr
+elif [ "${shortname}" == "hw" ]; then
+	fn_info_game_hw
+elif [ "${shortname}" == "inss" ]; then
+	fn_info_game_inss
+elif [ "${shortname}" == "jc2" ]; then
+	fn_info_game_jc2
+elif [ "${shortname}" == "jc3" ]; then
+	fn_info_game_jc3
+elif [ "${shortname}" == "jk2" ]; then
+	fn_info_game_jk2
+elif [ "${shortname}" == "kf" ]; then
+	fn_info_game_kf
+elif [ "${shortname}" == "kf2" ]; then
+	fn_info_game_kf2
+elif [ "${shortname}" == "lo" ]; then
+	fn_info_game_lo
+elif [ "${shortname}" == "mc" ] || [ "${shortname}" == "pmc" ]; then
+	fn_info_game_mc
+elif [ "${shortname}" == "mcb" ]; then
+	fn_info_game_mcb
+elif [ "${shortname}" == "mh" ]; then
+	fn_info_game_mh
+elif [ "${shortname}" == "mohaa" ]; then
+	fn_info_game_mohaa
+elif [ "${shortname}" == "mom" ]; then
+	fn_info_game_mom
+elif [ "${shortname}" == "mta" ]; then
+	fn_info_game_mta
+elif [ "${shortname}" == "nec" ]; then
+	fn_info_game_nec
+elif [ "${shortname}" == "onset" ]; then
+	fn_info_game_onset
+elif [ "${shortname}" == "pc" ]; then
+	fn_info_game_pc
+elif [ "${shortname}" == "pc2" ]; then
+	fn_info_game_pc2
+elif [ "${shortname}" == "pstbs" ]; then
+	fn_info_game_pstbs
+elif [ "${shortname}" == "pvr" ]; then
+	fn_info_game_pvr
+elif [ "${shortname}" == "pz" ]; then
+	fn_info_game_pz
+elif [ "${shortname}" == "q2" ]; then
+	fn_info_game_q2
+elif [ "${shortname}" == "q3" ]; then
+	fn_info_game_q3
+elif [ "${shortname}" == "ql" ]; then
+	fn_info_game_ql
+elif [ "${shortname}" == "qw" ]; then
+	fn_info_game_qw
+elif [ "${shortname}" == "ro" ]; then
+	fn_info_game_ro
+elif [ "${shortname}" == "rtcw" ]; then
+	fn_info_game_rtcw
+elif [ "${shortname}" == "rust" ]; then
+	fn_info_game_rust
+elif [ "${shortname}" == "rw" ]; then
+	fn_info_game_rw
+elif [ "${shortname}" == "samp" ]; then
+	fn_info_game_samp
+elif [ "${shortname}" == "sb" ]; then
+	fn_info_game_sb
+elif [ "${shortname}" == "sbots" ]; then
+	fn_info_game_sbots
+elif [ "${shortname}" == "scpsl" ] || [ "${shortname}" == "scpslsm" ]; then
+	fn_info_game_scpsl
+elif [ "${shortname}" == "sdtd" ]; then
+	fn_info_game_sdtd
+elif [ "${shortname}" == "sf" ]; then
+	fn_info_game_sf
+elif [ "${shortname}" == "sof2" ]; then
+	fn_info_game_sof2
+elif [ "${shortname}" == "sol" ]; then
+	fn_info_game_sol
+elif [ "${engine}" == "spark" ]; then
+	fn_info_game_spark
+elif [ "${shortname}" == "squad" ]; then
+	fn_info_game_squad
+elif [ "${shortname}" == "st" ]; then
+	fn_info_game_st
+elif [ "${shortname}" == "stn" ]; then
+	fn_info_game_stn
+elif [ "${shortname}" == "terraria" ]; then
+	fn_info_game_terraria
+elif [ "${shortname}" == "ti" ]; then
+	fn_info_game_ti
+elif [ "${shortname}" == "ts3" ]; then
+	fn_info_game_ts3
+elif [ "${shortname}" == "tu" ]; then
+	fn_info_game_tu
+elif [ "${shortname}" == "tw" ]; then
+	fn_info_game_tw
+elif [ "${shortname}" == "unt" ]; then
+	fn_info_game_unt
+elif [ "${shortname}" == "ut" ]; then
+	fn_info_game_ut
+elif [ "${shortname}" == "ut2k4" ]; then
+	fn_info_game_unreal2k4
+elif [ "${shortname}" == "ut3" ]; then
+	fn_info_game_ut3
+elif [ "${shortname}" == "ut99" ]; then
+	fn_info_game_ut99
+elif [ "${shortname}" == "vh" ]; then
+	fn_info_game_vh
+elif [ "${shortname}" == "vints" ]; then
+	fn_info_game_vints
+elif [ "${shortname}" == "vpmc" ]; then
+	fn_info_game_vpmc
+elif [ "${shortname}" == "wet" ]; then
+	fn_info_game_wet
+elif [ "${shortname}" == "wf" ]; then
+	fn_info_game_wf
+elif [ "${shortname}" == "wmc" ]; then
+	fn_info_game_wmc
+elif [ "${shortname}" == "wurm" ]; then
+	fn_info_game_wurm
+elif [ "${engine}" == "prism3d" ]; then
+	fn_info_game_prism3d
+elif [ "${engine}" == "source" ] || [ "${engine}" == "goldsrc" ]; then
+	fn_info_game_source
+elif [ "${engine}" == "unreal2" ]; then
+	fn_info_game_unreal2
+fi
+
+# External IP address
+# Cache external IP address for 24 hours
+if [ -f "${tmpdir}/extip.txt" ]; then
+	if [ "$(find "${tmpdir}/extip.txt" -mmin +1440)" ]; then
+		rm -f "${tmpdir:?}/extip.txt"
+	fi
+fi
+
+if [ ! -f "${tmpdir}/extip.txt" ]; then
+	extip="$(curl --connect-timeout 10 -s https://api.ipify.org 2> /dev/null)"
+	exitcode=$?
+	# if curl passes add extip to externalip.txt
+	if [ "${exitcode}" != "0" ]; then
+		echo "Unable to get external IP address"
+	else
+		echo "${extip}" > "${tmpdir}/extip.txt"
+	fi
+else
+	extip="$(cat "${tmpdir}/extip.txt")"
+fi
+
+# Alert IP address
+if [ "${displayip}" ]; then
+	alertip="${displayip}"
+elif [ "${extip}" ]; then
+	alertip="${extip}"
+else
+	alertip="${ip}"
+fi
+
+# Steam Master Server - checks if detected by master server.
+# Checked after config init, as the queryport is needed
+if [ -z "${displaymasterserver}" ]; then
+	if [ "$(command -v jq 2> /dev/null)" ]; then
+		if [ "${ip}" ] && [ "${port}" ]; then
+			if [ "${steammaster}" == "true" ] || [ "${commandname}" == "DEV-QUERY-RAW" ]; then
+				# Will query server IP addresses first.
+				for queryip in "${queryips[@]}"; do
+					masterserver="$(curl --connect-timeout 10 -m 3 -s "https://api.steampowered.com/ISteamApps/GetServersAtAddress/v0001?addr=${queryip}&format=json" | jq --arg port "${port}" --arg queryport "${queryport}" '.response.servers[] | select((.gameport == ($port|tonumber) or (.gameport == ($queryport|tonumber)))) | .addr' | wc -l 2> /dev/null)"
+				done
+				# Should that not work it will try the external IP.
+				if [ "${masterserver}" == "0" ]; then
+					masterserver="$(curl --connect-timeout 10 -m 3 -s "https://api.steampowered.com/ISteamApps/GetServersAtAddress/v0001?addr=${extip}&format=json" | jq --arg port "${port}" --arg queryport "${queryport}" '.response.servers[] | select((.gameport == ($port|tonumber) or (.gameport == ($queryport|tonumber)))) | .addr' | wc -l 2> /dev/null)"
+				fi
+				if [ "${masterserver}" == "0" ]; then
+					displaymasterserver="false"
+				else
+					displaymasterserver="true"
+				fi
+			fi
+		fi
+	fi
+fi

+ 1847 - 0
lgsm/functions/info_messages.sh

@@ -0,0 +1,1847 @@
+#!/bin/bash
+# LinuxGSM info_messages.sh module
+# Author: Daniel Gibbs
+# Contributors: http://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Defines server info messages for details and alerts.
+
+functionselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+# Separator is different for details.
+fn_messages_separator() {
+	if [ "${commandname}" == "DETAILS" ]; then
+		printf '%*s\n' "${COLUMNS:-$(tput cols)}" '' | tr ' ' =
+	else
+		echo -e "================================="
+	fi
+}
+
+# Removes the passwords form all but details.
+fn_info_message_password_strip() {
+	if [ "${commandname}" != "DETAILS" ]; then
+		if [ "${serverpassword}" ]; then
+			serverpassword="********"
+		fi
+
+		if [ "${rconpassword}" ]; then
+			rconpassword="********"
+		fi
+
+		if [ "${adminpassword}" ]; then
+			adminpassword="********"
+		fi
+
+		if [ "${statspassword}" ]; then
+			statspassword="********"
+		fi
+
+		if [ "${webadminpass}" ]; then
+			webadminpass="********"
+		fi
+
+		if [ "${telnetpass}" ]; then
+			telnetpass="********"
+		fi
+
+		if [ "${wsapikey}" ]; then
+			wsapikey="********"
+		fi
+
+		if [ "${gslt}" ]; then
+			gslt="********"
+		fi
+	fi
+}
+
+# Alert Summary
+# used with alertlog
+fn_info_message_head() {
+	echo -e ""
+	echo -e "${lightyellow}Alert Summary${default}"
+	fn_messages_separator
+	echo -e "Message"
+	echo -e "${alertbody}"
+	echo -e ""
+	echo -e "Game"
+	echo -e "${gamename}"
+	echo -e ""
+	echo -e "Server name"
+	echo -e "${servername}"
+	echo -e ""
+	echo -e "Hostname"
+	echo -e "${HOSTNAME}"
+	echo -e ""
+	echo -e "Server IP"
+	echo -e "${ip}:${port}"
+}
+
+fn_info_message_distro() {
+	#
+	# Distro Details
+	# =================================
+	# Date:      Sun 21 Feb 2021 09:22:53 AM UTC
+	# Distro:    Ubuntu 20.04.2 LTS
+	# Arch:      x86_64
+	# Kernel:    5.4.0-65-generic
+	# Hostname:  server
+	# Uptime:    16d, 5h, 18m
+	# tmux:      tmux 3.0a
+	# glibc:     2.31
+
+	echo -e ""
+	echo -e "${lightyellow}Distro Details${default}"
+	fn_messages_separator
+	{
+		echo -e "${lightblue}Date:\t${default}$(date)"
+		echo -e "${lightblue}Distro:\t${default}${distroname}"
+		echo -e "${lightblue}Arch:\t${default}${arch}"
+		echo -e "${lightblue}Kernel:\t${default}${kernel}"
+		echo -e "${lightblue}Hostname:\t${default}${HOSTNAME}"
+		echo -e "${lightblue}Uptime:\t${default}${days}d, ${hours}h, ${minutes}m"
+		echo -e "${lightblue}tmux:\t${default}${tmuxv}"
+		echo -e "${lightblue}glibc:\t${default}${glibcversion}"
+		if [ -n "${javaram}" ]; then
+			echo -e "${lightblue}Java:\t${default}${javaversion}"
+		fi
+	} | column -s $'\t' -t
+}
+
+fn_info_message_server_resource() {
+	#
+	# Server Resource
+	# =================================
+	# CPU
+	# Model:      AMD EPYC 7601 32-Core Processor
+	# Cores:      2
+	# Frequency:  2199.994MHz
+	# Avg Load:   0.01, 0.05, 0.18
+	#
+	# Memory
+	# Mem:       total  used   free   cached  available
+	# Physical:  3.9GB  350MB  3.3GB  3.2GB   3.3GB
+	# Swap:      512MB  55MB   458MB
+	#
+	# Storage
+	# Filesystem:  /dev/sda
+	# Total:       79G
+	# Used:        73G
+	# Available:   1.4G
+	#
+	# Network
+	# IP:           0.0.0.0
+	# Internet IP:  176.58.124.96
+
+	echo -e ""
+	echo -e "${lightyellow}Server Resource${default}"
+	fn_messages_separator
+	{
+		echo -e "${lightyellow}CPU\t${default}"
+		echo -e "${lightblue}Model:\t${default}${cpumodel}"
+		echo -e "${lightblue}Cores:\t${default}${cpucores}"
+		echo -e "${lightblue}Frequency:\t${default}${cpufreqency}MHz"
+		echo -e "${lightblue}Avg Load:\t${default}${load}"
+	} | column -s $'\t' -t
+	echo -e ""
+	{
+		echo -e "${lightyellow}Memory\t${default}"
+		echo -e "${lightblue}Mem:\t${lightblue}total\tused\tfree\tcached\tavailable${default}"
+		echo -e "${lightblue}Physical:\t${default}${physmemtotal}\t${physmemused}\t${physmemfree}\t${physmemcached}\t${physmemavailable}${default}"
+		echo -e "${lightblue}Swap:\t${default}${swaptotal}\t${swapused}\t${swapfree}${default}"
+	} | column -s $'\t' -t
+	echo -e ""
+	{
+		echo -e "${lightyellow}Storage${default}"
+		echo -e "${lightblue}Filesystem:\t${default}${filesystem}"
+		echo -e "${lightblue}Total:\t\t${default}${totalspace}"
+		echo -e "${lightblue}Used:\t\t${default}${usedspace}"
+		echo -e "${lightblue}Available:\t${default}${availspace}"
+	} | column -s $'\t' -t
+	echo -e ""
+	{
+		echo -e "${lightyellow}Network${default}"
+		if [ -n "${netint}" ]; then
+			echo -e "${lightblue}Interface:\t${default}${netint}"
+		fi
+		if [ -n "${netlink}" ]; then
+			echo -e "${lightblue}Link Speed:\t${default}${netlink}"
+		fi
+		echo -e "${lightblue}IP:\t${default}${ip}"
+		if [ "${ip}" != "${extip}" ]; then
+			echo -e "${lightblue}Internet IP:\t${default}${extip}"
+		fi
+	} | column -s $'\t' -t
+}
+
+fn_info_message_gameserver_resource() {
+	#
+	# Game Server Resource Usage
+	# =================================
+	# CPU Used:  1.1%
+	# Mem Used:  4.8%  189MB
+	#
+	# Storage
+	# Total:        241M
+	# Serverfiles:  240M
+	# Backups:      24K
+
+	echo -e ""
+	echo -e "${lightyellow}Game Server Resource Usage${default}"
+	fn_messages_separator
+	{
+		if [ "${status}" != "0" ] && [ -v status ]; then
+			if [ -n "${cpuused}" ]; then
+				echo -e "${lightblue}CPU Used:\t${default}${cpuused}%${default}"
+			else
+				echo -e "${lightblue}CPU Used:\t${red}unknown${default}"
+			fi
+			if [ -n "${memused}" ]; then
+				echo -e "${lightblue}Mem Used:\t${default}${pmemused}%\t${memused}MB${default}"
+			else
+				echo -e "${lightblue}Mem Used:\t${default}${pmemused}\t${red}unknown${default}"
+			fi
+		else
+			echo -e "${lightblue}CPU Used:\t${default}0%${default}"
+			echo -e "${lightblue}Mem Used:\t${default}0%\t0MB${default}"
+		fi
+	} | column -s $'\t' -t
+	echo -e ""
+	{
+		echo -e "${lightyellow}Storage${default}"
+		echo -e "${lightblue}Total:\t${default}${rootdirdu}"
+		echo -e "${lightblue}Serverfiles:\t${default}${serverfilesdu}"
+		if [ -d "${backupdir}" ]; then
+			echo -e "${lightblue}Backups:\t${default}${backupdirdu}"
+		fi
+	} | column -s $'\t' -t
+}
+
+fn_info_message_gameserver() {
+	#
+	# Counter-Strike: Global Offensive Server Details
+	# =================================
+	# Server name:      LinuxGSM
+	# Server IP:        0.0.0.0:27015
+	# Internet IP:      176.48.124.96:34197
+	# Server password:  NOT SET
+	# RCON password:    adminF54CC0VR
+	# Players:          0/16
+	# Current map:      de_mirage
+	# Default map:      de_mirage
+	# Game type:        0
+	# Game mode:        0
+	# Tick rate:        64
+	# Master Server:    listed
+	# Status:           STARTED
+
+	echo -e ""
+	echo -e "${lightgreen}${gamename} Server Details${default}"
+	fn_info_message_password_strip
+	fn_messages_separator
+	{
+		# Server name
+		if [ -n "${gdname}" ]; then
+			echo -e "${lightblue}Server name:\t${default}${gdname}"
+		elif [ -n "${servername}" ]; then
+			echo -e "${lightblue}Server name:\t${default}${servername}"
+		fi
+
+		# Server description
+		if [ -n "${serverdescription}" ]; then
+			echo -e "${lightblue}Server Description:\t${default}${serverdescription}"
+		fi
+
+		# Appid
+		if [ -n "${appid}" ]; then
+			echo -e "${lightblue}App ID:\t${default}${appid}"
+		fi
+
+		# Branch
+		if [ -n "${branch}" ]; then
+			echo -e "${lightblue}Branch:\t${default}${branch}"
+		fi
+
+		# Beta Password
+		if [ -n "${betapassword}" ]; then
+			echo -e "${lightblue}Beta Password:\t${default}${betapassword}"
+		fi
+
+		# Server Version
+		if [ -n "${gdversion}" ]; then
+			echo -e "${lightblue}Server Version:\t${default}${gdversion}"
+		fi
+
+		# Server ip
+		echo -e "${lightblue}Server IP:\t${default}${ip}:${port}"
+
+		# Internet ip
+		if [ -n "${extip}" ]; then
+			if [ "${ip}" != "${extip}" ]; then
+				echo -e "${lightblue}Internet IP:\t${default}${extip}:${port}"
+			fi
+		fi
+
+		# Display ip
+		if [ -n "${displayip}" ]; then
+			echo -e "${lightblue}Display IP:\t${default}${displayip}:${port}"
+		fi
+
+		# Server password
+		if [ -n "${serverpassword}" ]; then
+			echo -e "${lightblue}Server password:\t${default}${serverpassword}"
+		fi
+
+		# Query enabled (Starbound)
+		if [ -n "${queryenabled}" ]; then
+			echo -e "${lightblue}Query enabled:\t${default}${queryenabled}"
+		fi
+
+		# RCON enabled (Starbound)
+		if [ -n "${rconenabled}" ]; then
+			echo -e "${lightblue}RCON enabled:\t${default}${rconenabled}"
+		fi
+
+		# RCON password
+		if [ -n "${rconpassword}" ]; then
+			echo -e "${lightblue}RCON password:\t${default}${rconpassword}"
+		fi
+
+		# RCON web (Rust)
+		if [ -n "${rconweb}" ]; then
+			echo -e "${lightblue}RCON web:\t${default}${rconweb}"
+		fi
+
+		# Admin password
+		if [ -n "${adminpassword}" ]; then
+			echo -e "${lightblue}Admin password:\t${default}${adminpassword}"
+		fi
+
+		# Stats password (Quake Live)
+		if [ -n "${statspassword}" ]; then
+			echo -e "${lightblue}Stats password:\t${default}${statspassword}"
+		fi
+
+		# Players
+		if [ "${querystatus}" != "0" ]; then
+			if [ -n "${maxplayers}" ]; then
+				echo -e "${lightblue}Maxplayers:\t${default}${maxplayers}"
+			fi
+		else
+			if [ -n "${gdplayers}" ] && [ -n "${gdmaxplayers}" ]; then
+				echo -e "${lightblue}Players:\t${default}${gdplayers}/${gdmaxplayers}"
+			elif [ -n "${gdplayers}" ] && [ -n "${maxplayers}" ]; then
+				echo -e "${lightblue}Players:\t${default}${gdplayers}/${maxplayers}"
+			elif [ -z "${gdplayers}" ] && [ -n "${gdmaxplayers}" ]; then
+				echo -e "${lightblue}Players:\t${default}0/${gdmaxplayers}"
+			elif [ -n "${gdplayers}" ] && [ -z "${gdmaxplayers}" ]; then
+				echo -e "${lightblue}Players:\t${default}${gdplayers}/∞"
+			elif [ -z "${gdplayers}" ] && [ -z "${gdmaxplayers}" ] && [ -n "${maxplayers}" ]; then
+				echo -e "${lightblue}Maxplayers:\t${default}${maxplayers}"
+			fi
+		fi
+
+		# Reverved Slots
+		if [ -n "${statspassword}" ]; then
+			echo -e "${lightblue}Reserved Slots:\t${default}${reservedslots}"
+		fi
+
+		# Bots
+		if [ -n "${gdbots}" ]; then
+			echo -e "${lightblue}Bots:\t${default}${gdbots}"
+		fi
+
+		# Current map
+		if [ -n "${gdmap}" ]; then
+			echo -e "${lightblue}Current map:\t${default}${gdmap}"
+		fi
+
+		# Default map
+		if [ -n "${defaultmap}" ]; then
+			echo -e "${lightblue}Default map:\t${default}${defaultmap}"
+		fi
+
+		if [ -n "${defaultscenario}" ]; then
+			# Current scenario (Insurgency: Sandstorm)
+			if [ -n "${gdgamemode}" ]; then
+				echo -e "${lightblue}Current scenario:\t${default}${gdgamemode}"
+			fi
+		else
+			# Current game mode
+			if [ -n "${gdgamemode}" ]; then
+				echo -e "${lightblue}Current game mode:\t${default}${gdgamemode}"
+			fi
+		fi
+
+		# Default scenario
+		if [ -n "${defaultscenario}" ]; then
+			echo -e "${lightblue}Default scenario:\t${default}${defaultscenario}"
+		fi
+
+		# Game type
+		if [ -n "${gametype}" ]; then
+			echo -e "${lightblue}Game type:\t${default}${gametype}"
+		fi
+
+		# Game mode
+		if [ -n "${gamemode}" ]; then
+			echo -e "${lightblue}Game mode:\t${default}${gamemode}"
+		fi
+
+		# Game world
+		if [ -n "${gameworld}" ]; then
+			echo -e "${lightblue}Game world:\t${default}${gameworld}"
+		fi
+
+		# Tick rate
+		if [ -n "${tickrate}" ]; then
+			echo -e "${lightblue}Tick rate:\t${default}${tickrate}"
+		fi
+
+		# Sharding (Don't Starve Together)
+		if [ -n "${sharding}" ]; then
+			echo -e "${lightblue}Sharding:\t${default}${sharding}"
+		fi
+
+		# Master (Don't Starve Together)
+		if [ -n "${master}" ]; then
+			echo -e "${lightblue}Master:\t${default}${master}"
+		fi
+
+		# Shard (Don't Starve Together)
+		if [ -n "${shard}" ]; then
+			echo -e "${lightblue}Shard:\t${default}${shard}"
+		fi
+
+		# Cluster (Don't Starve Together)
+		if [ -n "${cluster}" ]; then
+			echo -e "${lightblue}Cluster:\t${default}${cluster}"
+		fi
+
+		# Cave (Don't Starve Together)
+		if [ -n "${cave}" ]; then
+			echo -e "${lightblue}Cave:\t${default}${cave}"
+		fi
+
+		# Creativemode (Hurtworld)
+		if [ -n "${creativemode}" ]; then
+			echo -e "${lightblue}Creativemode:\t${default}${creativemode}"
+		fi
+
+		# TeamSpeak dbplugin
+		if [ -n "${dbplugin}" ]; then
+			echo -e "${lightblue}dbplugin:\t${default}${dbplugin}"
+		fi
+
+		# ASE (Multi Theft Auto)
+		if [ -n "${ase}" ]; then
+			echo -e "${lightblue}ASE:\t${default}${ase}"
+		fi
+
+		# Save interval (Rust)
+		if [ -n "${saveinterval}" ]; then
+			echo -e "${lightblue}Save interval:\t${default}${saveinterval}s"
+		fi
+
+		# Seed (Rust)
+		if [ -n "${seed}" ]; then
+			echo -e "${lightblue}Seed:\t${default}${seed}"
+		fi
+
+		# Salt (Rust)
+		if [ -n "${salt}" ]; then
+			echo -e "${lightblue}Salt:\t${default}${salt}"
+		fi
+
+		# World Size (Rust)
+		if [ -n "${worldsize}" ]; then
+			echo -e "${lightblue}World size:\t${default}${worldsize}m"
+		fi
+
+		# Random map rotation mode (Squad and Post Scriptum)
+		if [ -n "${randommap}" ]; then
+			echo -e "${lightblue}Map rotation:\t${default}${randommap}"
+		fi
+
+		# Server Version (Jedi Knight II: Jedi Outcast)
+		if [ -n "${serverversion}" ]; then
+			echo -e "${lightblue}Server Version:\t${default}${serverversion}"
+		fi
+
+		# authentication token (Factorio)
+		if [ -n "${authtoken}" ]; then
+			echo -e "${lightblue}Auth Token:\t${default}${authtoken}"
+		fi
+
+		# savegameinterval (Factorio)
+		if [ -n "${savegameinterval}" ]; then
+			echo -e "${lightblue}Savegame Interval:\t${default}${savegameinterval}"
+		fi
+
+		# versioncount (Factorio)
+		if [ -n "${versioncount}" ]; then
+			echo -e "${lightblue}Version Count:\t${default}${versioncount}"
+		fi
+
+		# Listed on Master server
+		if [ -n "${displaymasterserver}" ]; then
+			if [ "${displaymasterserver}" == "true" ]; then
+				echo -e "${lightblue}Master server:\t${green}listed${default}"
+			else
+				echo -e "${lightblue}Master server:\t${red}not listed${default}"
+			fi
+		fi
+
+		# Game server status
+		if [ "${status}" == "0" ]; then
+			echo -e "${lightblue}Status:\t${red}STOPPED${default}"
+		else
+			echo -e "${lightblue}Status:\t${green}STARTED${default}"
+		fi
+	} | column -s $'\t' -t
+	echo -e ""
+}
+
+fn_info_message_script() {
+	# csgoserver Script Details
+	# =================================
+	# Script name:            csgoserver
+	# LinuxGSM version:       v21.1.3
+	# glibc required:         2.18
+	# Discord alert:          off
+	# Email alert:            off
+	# Gotify alert:           off
+	# IFTTT alert:            off
+	# Mailgun (email) alert:  off
+	# Pushbullet alert:       off
+	# Pushover alert:         off
+	# Rocketchat alert:       off
+	# Slack alert:            off
+	# Telegram alert:         off
+	# Update on start:        off
+	# User:                   lgsm
+	# Location:               /home/lgsm/csgoserver
+	# Config file:            /home/lgsm/csgoserver/serverfiles/csgo/cfg/csgoserver.cfg
+
+	echo -e "${lightgreen}${selfname} Script Details${default}"
+	fn_messages_separator
+	{
+		# Script name
+		echo -e "${lightblue}Script name:\t${default}${selfname}"
+
+		# LinuxGSM version
+		if [ -n "${version}" ]; then
+			echo -e "${lightblue}LinuxGSM version:\t${default}${version}"
+		fi
+
+		# glibc required
+		if [ -n "${glibc}" ]; then
+			if [ "${glibc}" == "null" ]; then
+				# Glibc is not required.
+				:
+			elif [ -z "${glibc}" ]; then
+				echo -e "${lightblue}glibc required:\t${red}UNKNOWN${default}"
+			elif [ "$(printf '%s\n'${glibc}'\n' ${glibcversion} | sort -V | head -n 1)" != "${glibc}" ]; then
+				echo -e "${lightblue}glibc required:\t${red}${glibc} ${default}(${red}distro glibc ${glibcversion} too old${default})"
+			else
+				echo -e "${lightblue}glibc required:\t${green}${glibc}${default}"
+			fi
+		fi
+
+		# Discord alert
+		echo -e "${lightblue}Discord alert:\t${default}${discordalert}"
+		# Email alert
+		echo -e "${lightblue}Email alert:\t${default}${emailalert}"
+		# Gotify alert
+		echo -e "${lightblue}Gotify alert:\t${default}${gotifyalert}"
+		# IFTTT alert
+		echo -e "${lightblue}IFTTT alert:\t${default}${iftttalert}"
+		# Mailgun alert
+		echo -e "${lightblue}Mailgun (email) alert:\t${default}${mailgunalert}"
+		# Pushbullet alert
+		echo -e "${lightblue}Pushbullet alert:\t${default}${pushbulletalert}"
+		# Pushover alert
+		echo -e "${lightblue}Pushover alert:\t${default}${pushoveralert}"
+		# Rocketchat alert
+		echo -e "${lightblue}Rocketchat alert:\t${default}${rocketchatalert}"
+		# Slack alert
+		echo -e "${lightblue}Slack alert:\t${default}${slackalert}"
+		# Telegram alert
+		echo -e "${lightblue}Telegram alert:\t${default}${telegramalert}"
+
+		# Update on start
+		if [ -n "${updateonstart}" ]; then
+			echo -e "${lightblue}Update on start:\t${default}${updateonstart}"
+		fi
+
+		# User
+		echo -e "${lightblue}User:\t${default}$(whoami)"
+
+		# Script location
+		echo -e "${lightblue}Location:\t${default}${rootdir}"
+
+		# Config file location
+		if [ -n "${servercfgfullpath}" ]; then
+			if [ -f "${servercfgfullpath}" ]; then
+				echo -e "${lightblue}Config file:\t${default}${servercfgfullpath}"
+			elif [ -d "${servercfgfullpath}" ]; then
+				echo -e "${lightblue}Config dir:\t${default}${servercfgfullpath}"
+			else
+				echo -e "${lightblue}Config file:\t${default}${red}${servercfgfullpath}${default} (${red}FILE MISSING${default})"
+			fi
+		fi
+
+		# Network config file location (ARMA 3)
+		if [ -n "${networkcfgfullpath}" ]; then
+			echo -e "${lightblue}Network config file:\t${default}${networkcfgfullpath}"
+		fi
+	} | column -s $'\t' -t
+}
+
+fn_info_message_backup() {
+	#
+	# Backups
+	# =================================
+	# No. of backups:    1
+	# Latest backup:
+	#     date:          Fri May  6 18:34:19 UTC 2016
+	#     file:          /home/lgsm/qlserver/backups/ql-server-2016-05-06-183239.tar.gz
+	#     size:          945M
+
+	echo -e ""
+	echo -e "${lightgreen}Backups${default}"
+	fn_messages_separator
+	if [ ! -d "${backupdir}" ] || [ "${backupcount}" == "0" ]; then
+		echo -e "No Backups created"
+	else
+		{
+			echo -e "${lightblue}No. of backups:\t${default}${backupcount}"
+			echo -e "${lightblue}Latest backup:${default}"
+			if [ "${lastbackupdaysago}" == "0" ]; then
+				echo -e "${lightblue}    date:\t${default}${lastbackupdate} (less than 1 day ago)"
+			elif [ "${lastbackupdaysago}" == "1" ]; then
+				echo -e "${lightblue}    date:\t${default}${lastbackupdate} (1 day ago)"
+			else
+				echo -e "${lightblue}    date:\t${default}${lastbackupdate} (${lastbackupdaysago} days ago)"
+			fi
+			echo -e "${lightblue}    file:\t${default}${lastbackup}"
+			echo -e "${lightblue}    size:\t${default}${lastbackupsize}"
+		} | column -s $'\t' -t
+	fi
+}
+
+fn_info_message_commandlineparms() {
+	#
+	# Command-line Parameters
+	# =================================
+	# ./run_server_x86.sh +set net_strict 1
+
+	echo -e ""
+	echo -e "${lightgreen}Command-line Parameters${default}"
+	fn_info_message_password_strip
+	fn_messages_separator
+	if [ "${serverpassword}" == "NOT SET" ]; then
+		unset serverpassword
+	fi
+	fn_reload_startparameters
+	echo -e "${preexecutable} ${executable} ${startparameters}"
+}
+
+fn_info_message_ports_edit() {
+	#
+	# Ports
+	# =================================
+	# Change ports by editing the parameters in:
+	# /home/lgsm/qlserver/serverfiles/baseq3/ql-server.cfg
+	echo -e ""
+	echo -e "${lightgreen}Ports${default}"
+	fn_messages_separator
+	echo -e "${lightblue}Change ports by editing the parameters in:${default}"
+
+	startparameterslocation="${red}UNKNOWN${default}"
+	# engines/games that require editing in the config file.
+	local ports_edit_array=("ac" "arma3" "armar" "bo" "bt" "cd" "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")
+	for port_edit in "${ports_edit_array[@]}"; do
+		if [ "${shortname}" == "ut3" ]; then
+			startparameterslocation="${servercfgdir}/UTWeb.ini"
+		elif [ "${shortname}" == "kf2" ]; then
+			startparameterslocation="${servercfgdir}/LinuxServer-KFEngine.ini\n${servercfgdir}/KFWeb.ini"
+		elif [ "${engine}" == "${port_edit}" ] || [ "${gamename}" == "${port_edit}" ] || [ "${shortname}" == "${port_edit}" ]; then
+			startparameterslocation="${servercfgfullpath}"
+		fi
+	done
+	# engines/games that require editing the start parameters.
+	local ports_edit_array=("av" "ck" "col" "fctr" "goldsrc" "hw" "iw3.0" "ioquake3" "qfusion" "rust" "scpsl" "scpslsm" "sol" "spark" "source" "unreal4" "arma3" "dayz" "unt" "vh")
+	for port_edit in "${ports_edit_array[@]}"; do
+		if [ "${engine}" == "${port_edit}" ] || [ "${gamename}" == "${port_edit}" ] || [ "${shortname}" == "${port_edit}" ]; then
+			startparameterslocation="${configdirserver}"
+		fi
+	done
+	echo -e "${startparameterslocation}"
+	echo -e ""
+}
+
+fn_info_message_ports() {
+	echo -e "${lightblue}Useful port diagnostic command:${default}"
+	if [ "${shortname}" == "armar" ]; then
+		echo -e "ss -tuplwn | grep enfMain"
+	elif [ "${shortname}" == "av" ]; then
+		echo -e "ss -tuplwn | grep AvorionServer"
+	elif [ "${shortname}" == "bf1942" ]; then
+		echo -e "ss -tuplwn | grep bf1942_lnxded"
+	elif [ "${shortname}" == "mc" ] || [ "${shortname}" == "nec" ] || [ "${shortname}" == "pmc" ] || [ "${shortname}" == "rw" ] || [ "${shortname}" == "vpmc" ] || [ "${shortname}" == "wmc" ]; then
+		echo -e "ss -tuplwn | grep java"
+	elif [ "${shortname}" == "terraria" ]; then
+		echo -e "ss -tuplwn | grep Main"
+	elif [ "${engine}" == "source" ]; then
+		echo -e "ss -tuplwn | grep srcds_linux"
+	elif [ "${engine}" == "goldsrc" ]; then
+		echo -e "ss -tuplwn | grep hlds_linux"
+	else
+		executableshort="$(basename "${executable}" | cut -c -15)"
+		echo -e "ss -tuplwn | grep ${executableshort}"
+	fi
+	echo -e ""
+}
+
+fn_info_message_statusbottom() {
+	echo -e ""
+	if [ "${status}" == "0" ]; then
+		echo -e "${lightblue}Status:\t${red}STOPPED${default}"
+	else
+		echo -e "${lightblue}Status:\t${green}STARTED${default}"
+	fi
+	echo -e ""
+}
+
+fn_info_logs() {
+	echo -e ""
+	echo -e "${selfname} Logs"
+	echo -e "================================="
+
+	if [ -n "${lgsmlog}" ]; then
+		echo -e "\nScript log\n==================="
+		if [ ! "$(ls -A "${lgsmlogdir}")" ]; then
+			echo -e "${lgsmlogdir} (NO LOG FILES)"
+		elif [ ! -s "${lgsmlog}" ]; then
+			echo -e "${lgsmlog} (LOG FILE IS EMPTY)"
+		else
+			echo -e "${lgsmlog}"
+			tail -25 "${lgsmlog}"
+		fi
+		echo -e ""
+	fi
+
+	if [ -n "${consolelog}" ]; then
+		echo -e "\nConsole log\n===================="
+		if [ ! "$(ls -A "${consolelogdir}")" ]; then
+			echo -e "${consolelogdir} (NO LOG FILES)"
+		elif [ ! -s "${consolelog}" ]; then
+			echo -e "${consolelog} (LOG FILE IS EMPTY)"
+		else
+			echo -e "${consolelog}"
+			tail -25 "${consolelog}" | awk '{ sub("\r$", ""); print }'
+		fi
+		echo -e ""
+	fi
+
+	if [ -n "${gamelogdir}" ]; then
+		echo -e "\nServer log\n==================="
+		if [ ! "$(ls -A "${gamelogdir}")" ]; then
+			echo -e "${gamelogdir} (NO LOG FILES)"
+		else
+			echo -e "${gamelogdir}"
+			# dos2unix sed 's/\r//'
+			tail "${gamelogdir}"/* 2> /dev/null | grep -v "==>" | sed '/^$/d' | sed 's/\r//' | tail -25
+		fi
+		echo -e ""
+	fi
+}
+
+# Engine/Game Specific details
+
+# Function used to generate port info. by passing info to function. (Reduces repeating code)
+# example output
+# DESCRIPTION     PORT   PROTOCOL  LISTEN
+# Game            7777   udp       1
+# RAW UDP Socket  7778   udp       1
+# Query           27015  udp       1
+# RCON            27020  tcp       1
+
+fn_port() {
+	if [ "${1}" == "header" ]; then
+		echo -e "${lightblue}DESCRIPTION\tPORT\tPROTOCOL\tLISTEN${default}"
+	else
+		portname="${1}"
+		porttype="${2}"
+		portprotocol="${3}"
+		echo -e "${portname}\t${!porttype}\t${portprotocol}\t$(echo "${ssinfo}" | grep "${portprotocol}" | grep -c "${!porttype}")"
+	fi
+}
+
+fn_info_message_ac() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Game" port tcp
+		fn_port "Query" queryport udp
+		fn_port "HTTP" httpport tcp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_ark() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "RAW UDP Socket" rawport udp
+		fn_port "Query" queryport udp
+		fn_port "RCON" rconport tcp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_arma3() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Voice" voiceport udp
+		fn_port "Query" queryport udp
+		fn_port "Steam Master" steammasterport udp
+		fn_port "Voice (unused)" voiceunusedport udp
+		fn_port "BattleEye" battleeyeport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_armar() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Steam Query" queryport udp
+		fn_port "BattleEye" battleeyeport tcp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_av() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+		fn_port "Steam Master" steammasterport udp
+		fn_port "Steam Query" steamqueryport udp
+		fn_port "RCON" rconport tcp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_bf1942() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_bfv() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_bo() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_bt() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_btl() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+		fn_port "RCON" rconport tcp
+	} | column -s $'\t' -t
+}
+
+fn_info_messages_cd() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Steam" steamport udp
+		fn_port "RCON" rconport tcp
+	} | column -s $'\t' -t
+}
+
+fn_info_messages_ck() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_cmw() {
+	fn_info_message_password_strip
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+		fn_port "RCON" rconport tcp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_cod() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_coduo() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_cod2() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_cod4() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_codwaw() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_col() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport tcp
+		fn_port "Steam" steamport tcp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_csgo() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport tcp
+		fn_port "RCON" rconport tcp
+		fn_port "SourceTV" sourcetvport udp
+		fn_port "Client" clientport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_dayz() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query Steam" queryport udp
+		fn_port "Steam Master" steammasterport udp
+		fn_port "BattleEye" battleeyeport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_dodr() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_dst() {
+	{
+		fn_port "header"
+		fn_port "Game: Server" port udp
+		fn_port "Game: Master" masterport udp
+		fn_port "Steam: Auth" steamauthport udp
+		fn_port "Steam: Master" steammasterport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_eco() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Web Admin" webadminport tcp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_etl() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_fctr() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "RCON" rconport tcp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_goldsrc() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Client" clientport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_hw() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_ins() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport tcp
+		fn_port "RCON" rconport tcp
+		fn_port "SourceTV" sourcetvport udp
+		fn_port "Client" clientport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_inss() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+		fn_port "RCON" rconport tcp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_jc2() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_jc3() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+		fn_port "Steam" steamport udp
+		fn_port "HTTP" httpport tcp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_jk2() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_kf() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+		fn_port "Query (GameSpy)" queryportgs udp
+		fn_port "Web Admin" webadminport tcp
+		fn_port "LAN" lanport udp
+		fn_port "Steam" steamport udp
+		fn_port "Steam Master" steammasterport udp
+	} | column -s $'\t' -t
+	echo -e ""
+	echo -e "${lightgreen}${servername} Web Admin${default}"
+	fn_messages_separator
+	{
+		echo -e "${lightblue}Web Admin enabled:\t${default}${webadminenabled}"
+		echo -e "${lightblue}Web Admin url:\t${default}http://${webadminip}:${webadminport}"
+		echo -e "${lightblue}Web Admin username:\t${default}${webadminuser}"
+		echo -e "${lightblue}Web Admin password:\t${default}${webadminpass}"
+	} | column -s $'\t' -t
+}
+
+fn_info_message_kf2() {
+	fn_info_message_password_strip
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+		fn_port "Web Admin" webadminport tcp
+	} | column -s $'\t' -t
+	echo -e ""
+	echo -e "${lightgreen}${servername} Web Admin${default}"
+	fn_messages_separator
+	{
+		echo -e "${lightblue}Web Admin enabled:\t${default}${webadminenabled}"
+		echo -e "${lightblue}Web Admin url:\t${default}http://${webadminip}:${webadminport}"
+		echo -e "${lightblue}Web Admin username:\t${default}${webadminuser}"
+		echo -e "${lightblue}Web Admin password:\t${default}${webadminpass}"
+	} | column -s $'\t' -t
+}
+
+fn_info_message_lo() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_mc() {
+	{
+		fn_port "header"
+		fn_port "Game" port tcp
+		fn_port "Query" queryport udp
+		fn_port "RCON" rconport tcp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_mcb() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Game" portipv6 udp6
+	} | column -s $'\t' -t
+}
+
+fn_info_message_mh() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+		fn_port "Beacon" beaconport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_mohaa() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_mom() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Beacon" beaconport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_mta() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		if [ "${ase}" == "Enabled" ]; then
+			fn_port "Query" queryport udp
+		fi
+		fn_port "HTTP" httpport tcp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_nec() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_onset() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+		fn_port "HTTP" httpport tcp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_pc() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+		fn_port "Steam" steamport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_pc2() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+		fn_port "Steam" steamport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_pstbs() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+		fn_port "RCON" rconport tcp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_pvr() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Game" port tcp
+		fn_port "Game+400" port401 udp
+		fn_port "Query" queryport tcp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_pz() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_qw() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_q2() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_q3() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_ql() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+		fn_port "RCON" rconport tcp
+		fn_port "Stats" statsport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_ro() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+		fn_port "Web Admin" webadminport tcp
+		fn_port "LAN" lanport udp
+		fn_port "Steam" steamport udp
+		fn_port "Steam Master" steammasterport udp
+	} | column -s $'\t' -t
+	echo -e ""
+	echo -e "${lightgreen}${servername} Web Admin${default}"
+	fn_messages_separator
+	{
+		echo -e "${lightblue}Web Admin enabled:\t${default}${webadminenabled}"
+		echo -e "${lightblue}Web Admin url:\t${default}http://${webadminip}:${webadminport}"
+		echo -e "${lightblue}Web Admin username:\t${default}${webadminuser}"
+		echo -e "${lightblue}Web Admin password:\t${default}${webadminpass}"
+	} | column -s $'\t' -t
+}
+
+fn_info_message_rtcw() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_rust() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+		fn_port "RCON" rconport tcp
+		fn_port "App" appport tcp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_rw() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Game+1" port2 udp
+		fn_port "Game+2" port3 udp
+		fn_port "Game+3" port4 udp
+		fn_port "Game+1" port2 tcp
+		fn_port "Game+2" port3 tcp
+		fn_port "Game+3" port4 tcp
+		fn_port "Query" queryport tcp
+		fn_port "Query HTTP" httpqueryport tcp
+		fn_port "RCON" rconport tcp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_samp() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "RCON" rconport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_sb() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport tcp
+		fn_port "RCON" rconport tcp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_sbots() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_scpsl() {
+	{
+		fn_port "header"
+		fn_port "Game" port tcp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_sdtd() {
+	fn_info_message_password_strip
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Game+2" port3 udp
+		fn_port "Query" queryport tcp
+		fn_port "Web Admin" webadminport tcp
+		fn_port "Telnet" telnetport tcp
+	} | column -s $'\t' -t
+	echo -e ""
+	echo -e "${lightgreen}${gamename} Web Admin${default}"
+	fn_messages_separator
+	{
+		echo -e "${lightblue}Web Admin enabled:\t${default}${webadminenabled}"
+		echo -e "${lightblue}Web Admin url:\t${default}http://${webadminip}:${webadminport}/index.html"
+		echo -e "${lightblue}Web Admin username:\t${default}${webadminuser}"
+		echo -e "${lightblue}Web Admin password:\t${default}${webadminpass}"
+	} | column -s $'\t' -t
+	echo -e ""
+	echo -e "${lightgreen}${gamename} Telnet${default}"
+	fn_messages_separator
+	{
+		echo -e "${lightblue}Telnet enabled:\t${default}${telnetenabled}"
+		echo -e "${lightblue}Telnet address:\t${default}${telnetip} ${telnetport}"
+		echo -e "${lightblue}Telnet password:\t${default}${telnetpass}"
+	} | column -s $'\t' -t
+}
+
+fn_info_message_sf() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+		fn_port "Beacon" beaconport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_sof2() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_sol() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+		fn_port "Files" filesport tcp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_prism3d() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_source() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport tcp
+		fn_port "RCON" rconport tcp
+		fn_port "SourceTV" sourcetvport udp
+		# Will not show if unaviable
+		if [ "${steamport}" == "0" ] || [ -v "${steamport}" ]; then
+			fn_port "Steam" steamport udp
+		fi
+		fn_port "Client" clientport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_spark() {
+	fn_info_message_password_strip
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+		fn_port "Web Admin" webadminport tcp
+	} | column -s $'\t' -t
+	echo -e ""
+	echo -e "${lightgreen}${gamename} Web Admin${default}"
+	fn_messages_separator
+	{
+		echo -e "${lightblue}Web Admin url:\t${default}http://${webadminip}:${webadminport}/index.html"
+		echo -e "${lightblue}Web Admin username:\t${default}${webadminuser}"
+		echo -e "${lightblue}Web Admin password:\t${default}${webadminpass}"
+	} | column -s $'\t' -t
+}
+
+fn_info_message_squad() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+		fn_port "RCON" rconport tcp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_st() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+		fn_port "Web Admin" webadminport tcp
+	} | column -s $'\t' -t
+	echo -e ""
+	echo -e "${lightgreen}${gamename} Web Admin${default}"
+	fn_messages_separator
+	{
+		echo -e "${lightblue}Web Admin url:\t${default}http://${webadminip}:${webadminport}"
+	} | column -s $'\t' -t
+}
+
+fn_info_message_ti() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_ts3() {
+	{
+		fn_port "header"
+		fn_port "Voice" port udp
+		fn_port "Query" queryport tcp
+		fn_port "Query (SSH)" querysshport tcp
+		fn_port "Query (http)" queryhttpport tcp
+		fn_port "Query (https)" queryhttpsport tcp
+		fn_port "File Transfer" fileport tcp
+		fn_port "Telnet" telnetport tcp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_tw() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_terraria() {
+	{
+		fn_port "header"
+		fn_port "Game" port tcp
+		fn_port "Query" queryport tcp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_tu() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+		fn_port "Steam" steamport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_unreal() {
+	fn_info_message_password_strip
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+		fn_port "LAN Beacon" beaconport udp
+		fn_port "Web Admin" webadminport tcp
+	} | column -s $'\t' -t
+	echo -e ""
+	echo -e "${lightgreen}${servername} Web Admin${default}"
+	fn_messages_separator
+	{
+		echo -e "${lightblue}Web Admin enabled:\t${default}${webadminenabled}"
+		echo -e "${lightblue}Web Admin url:\t${default}http://${webadminip}:${webadminport}"
+		echo -e "${lightblue}Web Admin username:\t${default}${webadminuser}"
+		echo -e "${lightblue}Web Admin password:\t${default}${webadminpass}"
+	} | column -s $'\t' -t
+}
+
+fn_info_message_ut2k4() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+		fn_port "Query (GameSpy)" queryportgs udp
+		fn_port "Web Admin" webadminport tcp
+		fn_port "LAN" lanport udp
+	} | column -s $'\t' -t
+	echo -e ""
+	echo -e "${lightgreen}${servername} Web Admin${default}"
+	fn_messages_separator
+	{
+		echo -e "${lightblue}Web Admin enabled:\t${default}${webadminenabled}"
+		echo -e "${lightblue}Web Admin url:\t${default}http://${webadminip}:${webadminport}"
+		echo -e "${lightblue}Web Admin username:\t${default}${webadminuser}"
+		echo -e "${lightblue}Web Admin password:\t${default}${webadminpass}"
+	} | column -s $'\t' -t
+}
+
+fn_info_message_unreal() {
+	fn_info_message_password_strip
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+		fn_port "LAN Beacon" beaconport udp
+		fn_port "Web Admin" webadminport tcp
+	} | column -s $'\t' -t
+	echo -e ""
+	echo -e "${lightgreen}${servername} Web Admin${default}"
+	fn_messages_separator
+	{
+		echo -e "${lightblue}Web Admin enabled:\t${default}${webadminenabled}"
+		echo -e "${lightblue}Web Admin url:\t${default}http://${webadminip}:${webadminport}"
+		echo -e "${lightblue}Web Admin username:\t${default}${webadminuser}"
+		echo -e "${lightblue}Web Admin password:\t${default}${webadminpass}"
+	} | column -s $'\t' -t
+}
+
+fn_info_message_unt() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+		fn_port "Steam" steamport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_ut() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_ut3() {
+	fn_info_message_password_strip
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+		fn_port "Web Admin" webadminport tcp
+	} | column -s $'\t' -t
+	echo -e ""
+	echo -e "${lightgreen}${servername} Web Admin${default}"
+	fn_messages_separator
+	{
+		echo -e "${lightblue}Web Admin enabled:\t${default}${webadminenabled}"
+		echo -e "${lightblue}Web Admin url:\t${default}http://${webadminip}:${webadminport}"
+		echo -e "${lightblue}Web Admin username:\t${default}${webadminuser}"
+		echo -e "${lightblue}Web Admin password:\t${default}${webadminpass}"
+	} | column -s $'\t' -t
+}
+
+fn_info_message_vh() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_vints() {
+	{
+		fn_port "header"
+		fn_port "Game" port tcp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_vpmc() {
+	{
+		fn_port "header"
+		fn_port "Game" port tcp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_wet() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_wf() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "HTTP" httpport tcp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_wurm() {
+	{
+		fn_port "header"
+		fn_port "Game" port tcp
+		fn_port "Query" queryport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_stn() {
+	{
+		fn_port "header"
+		fn_port "Game" port udp
+		fn_port "Query" queryport udp
+	} | column -s $'\t' -t
+}
+
+fn_info_message_select_engine() {
+	# Display details depending on game or engine.
+	if [ "${shortname}" == "ac" ]; then
+		fn_info_message_ac
+	elif [ "${shortname}" == "ark" ]; then
+		fn_info_message_ark
+	elif [ "${shortname}" == "arma3" ]; then
+		fn_info_message_arma3
+	elif [ "${shortname}" == "armar" ]; then
+		fn_info_message_armar
+	elif [ "${shortname}" == "av" ]; then
+		fn_info_message_av
+	elif [ "${shortname}" == "bf1942" ]; then
+		fn_info_message_bf1942
+	elif [ "${shortname}" == "bfv" ]; then
+		fn_info_message_bfv
+	elif [ "${shortname}" == "bo" ]; then
+		fn_info_message_bo
+	elif [ "${shortname}" == "bt" ]; then
+		fn_info_message_bt
+	elif [ "${shortname}" == "btl" ]; then
+		fn_info_message_btl
+	elif [ "${shortname}" == "cd" ]; then
+		fn_info_messages_cd
+	elif [ "${shortname}" == "ck" ]; then
+		fn_info_messages_ck
+	elif [ "${shortname}" == "csgo" ]; then
+		fn_info_message_csgo
+	elif [ "${shortname}" == "cmw" ]; then
+		fn_info_message_cmw
+	elif [ "${shortname}" == "cod" ]; then
+		fn_info_message_cod
+	elif [ "${shortname}" == "coduo" ]; then
+		fn_info_message_coduo
+	elif [ "${shortname}" == "cod2" ]; then
+		fn_info_message_cod2
+	elif [ "${shortname}" == "cod4" ]; then
+		fn_info_message_cod4
+	elif [ "${shortname}" == "codwaw" ]; then
+		fn_info_message_codwaw
+	elif [ "${shortname}" == "col" ]; then
+		fn_info_message_col
+	elif [ "${shortname}" == "dayz" ]; then
+		fn_info_message_dayz
+	elif [ "${shortname}" == "dodr" ]; then
+		fn_info_message_dodr
+	elif [ "${shortname}" == "dst" ]; then
+		fn_info_message_dst
+	elif [ "${shortname}" == "eco" ]; then
+		fn_info_message_eco
+	elif [ "${shortname}" == "etl" ]; then
+		fn_info_message_etl
+	elif [ "${shortname}" == "fctr" ]; then
+		fn_info_message_fctr
+	elif [ "${shortname}" == "hw" ]; then
+		fn_info_message_hw
+	elif [ "${shortname}" == "ins" ]; then
+		fn_info_message_ins
+	elif [ "${shortname}" == "inss" ]; then
+		fn_info_message_inss
+	elif [ "${shortname}" == "jc2" ]; then
+		fn_info_message_jc2
+	elif [ "${shortname}" == "jc3" ]; then
+		fn_info_message_jc3
+	elif [ "${shortname}" == "jk2" ]; then
+		fn_info_message_jk2
+	elif [ "${shortname}" == "kf" ]; then
+		fn_info_message_kf
+	elif [ "${shortname}" == "kf2" ]; then
+		fn_info_message_kf2
+	elif [ "${shortname}" == "lo" ]; then
+		fn_info_message_lo
+	elif [ "${shortname}" == "mc" ] || [ "${shortname}" == "pmc" ] || [ "${shortname}" == "wmc" ]; then
+		fn_info_message_mc
+	elif [ "${shortname}" == "mcb" ]; then
+		fn_info_message_mcb
+	elif [ "${shortname}" == "mh" ]; then
+		fn_info_message_mh
+	elif [ "${shortname}" == "mohaa" ]; then
+		fn_info_message_mohaa
+	elif [ "${shortname}" == "mom" ]; then
+		fn_info_message_mom
+	elif [ "${shortname}" == "mta" ]; then
+		fn_info_message_mta
+	elif [ "${shortname}" == "nec" ]; then
+		fn_info_message_nec
+	elif [ "${shortname}" == "onset" ]; then
+		fn_info_message_onset
+	elif [ "${shortname}" == "pc" ]; then
+		fn_info_message_pc
+	elif [ "${shortname}" == "pc2" ]; then
+		fn_info_message_pc2
+	elif [ "${shortname}" == "pstbs" ]; then
+		fn_info_message_pstbs
+	elif [ "${shortname}" == "pvr" ]; then
+		fn_info_message_pvr
+	elif [ "${shortname}" == "pz" ]; then
+		fn_info_message_pz
+	elif [ "${shortname}" == "q2" ]; then
+		fn_info_message_q2
+	elif [ "${shortname}" == "q3" ]; then
+		fn_info_message_q3
+	elif [ "${shortname}" == "ql" ]; then
+		fn_info_message_ql
+	elif [ "${shortname}" == "qw" ]; then
+		fn_info_message_qw
+	elif [ "${shortname}" == "ro" ]; then
+		fn_info_message_ro
+	elif [ "${shortname}" == "rtcw" ]; then
+		fn_info_message_rtcw
+	elif [ "${shortname}" == "samp" ]; then
+		fn_info_message_samp
+	elif [ "${shortname}" == "sb" ]; then
+		fn_info_message_sb
+	elif [ "${shortname}" == "sbots" ]; then
+		fn_info_message_sbots
+	elif [ "${shortname}" == "scpsl" ] || [ "${shortname}" == "scpslsm" ]; then
+		fn_info_message_scpsl
+	elif [ "${shortname}" == "sdtd" ]; then
+		fn_info_message_sdtd
+	elif [ "${shortname}" == "sf" ]; then
+		fn_info_message_sf
+	elif [ "${shortname}" == "sof2" ]; then
+		fn_info_message_sof2
+	elif [ "${shortname}" == "sol" ]; then
+		fn_info_message_sol
+	elif [ "${shortname}" == "squad" ]; then
+		fn_info_message_squad
+	elif [ "${shortname}" == "st" ]; then
+		fn_info_message_st
+	elif [ "${shortname}" == "stn" ]; then
+		fn_info_message_stn
+	elif [ "${shortname}" == "terraria" ]; then
+		fn_info_message_terraria
+	elif [ "${shortname}" == "ti" ]; then
+		fn_info_message_ti
+	elif [ "${shortname}" == "ts3" ]; then
+		fn_info_message_ts3
+	elif [ "${shortname}" == "tu" ]; then
+		fn_info_message_tu
+	elif [ "${shortname}" == "tw" ]; then
+		fn_info_message_tw
+	elif [ "${shortname}" == "unt" ]; then
+		fn_info_message_unt
+	elif [ "${shortname}" == "vh" ]; then
+		fn_info_message_vh
+	elif [ "${shortname}" == "vints" ]; then
+		fn_info_message_vints
+	elif [ "${shortname}" == "rust" ]; then
+		fn_info_message_rust
+	elif [ "${shortname}" == "rw" ]; then
+		fn_info_message_rw
+	elif [ "${shortname}" == "ut" ]; then
+		fn_info_message_ut
+	elif [ "${shortname}" == "ut2k4" ]; then
+		fn_info_message_ut2k4
+	elif [ "${shortname}" == "ut3" ]; then
+		fn_info_message_ut3
+	elif [ "${shortname}" == "vpmc" ]; then
+		fn_info_message_vpmc
+	elif [ "${shortname}" == "wet" ]; then
+		fn_info_message_wet
+	elif [ "${shortname}" == "wf" ]; then
+		fn_info_message_wf
+	elif [ "${shortname}" == "wurm" ]; then
+		fn_info_message_wurm
+	elif [ "${engine}" == "goldsrc" ]; then
+		fn_info_message_goldsrc
+	elif [ "${engine}" == "prism3d" ]; then
+		fn_info_message_prism3d
+	elif [ "${engine}" == "source" ]; then
+		fn_info_message_source
+	elif [ "${engine}" == "spark" ]; then
+		fn_info_message_spark
+	elif [ "${engine}" == "unreal" ]; then
+		fn_info_message_unreal
+	else
+		fn_print_error_nl "Unable to detect game server."
+	fi
+}

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio