4
0
Эх сурвалжийг харах

feat(etl): add ET: Legacy downloader and update module (#4896)

* feat(etl): add downloader and update support for ET: Legacy

Refactors ET: Legacy to use a dedicated update module instead of hardcoded installation files. The new update_etl.sh module leverages the GitHub API to check for, download, and apply the latest builds from GameServerManagers/etlserver-build.

* feat(etl): add update_etl module to core modules

Registers the update_etl.sh module within the core modules to enable the fetching and execution of ET: Legacy updates.

* feat(etl): add update commands to core_getopt

Ensures that the update and check-update commands are available for ET: Legacy by including the etl shortname in the getopt command registration logic.

* feat(etl): improve local build detection

Updates the game log directory to the "legacy" folder and enhances local build detection by parsing etconsole.log for version information, falling back to build.txt if necessary.

* feat(etl): update glibc requirement and log path detection

Updates the minimum glibc requirement to 2.17 and switches the local build detection to use the gamelogdir variable instead of a hardcoded path.

* feat(etl): improve MD5 hash extraction

Updates the MD5 hash parsing to use a specific regex for 32-character hexadecimal strings, providing a more robust extraction from the release body than relying on the last field of the line.

* feat(xnt): add SHA512 hash extraction for remote builds

Updates the Xonotic update module to fetch the SHA512 hash from the remote download server, allowing for build verification and identification.

* fix(xnt): derive sha512 URL from remotebuildfilename not remotebuildtag

Tag format is xonotic-v0.8.6 but the sha512 file is named xonotic-0.8.6.sha512
(without the v). Using remotebuildtag directly produced a 404. Deriving from
remotebuildfilename (which already has the v stripped by tr -d v) gives the
correct URL.

* fix(core_dl): fix zip extraction with extractsrc across devices and non-empty dirs

Using mv to move extracted directories fails in two cases:
- Cross-device moves (e.g. tmp and serverfiles on different Docker volumes)
- Target directory already exists and is non-empty (update scenario)

Replace find+mv with cp -a which handles both cases by copying recursively
and merging into the destination. Also replace the hardcoded 'Xonotic'
temp_extractdir with ${extractsrc} to be generic.

* fix(core_dl): remove duplicate ellipsis in hash verification message
Daniel Gibbs 1 сар өмнө
parent
commit
cba689ab4b

+ 2 - 2
lgsm/config-default/config-lgsm/etlserver/_default.cfg

@@ -141,7 +141,7 @@ consoleinteract="yes"
 # Do not edit
 gamename="ET: Legacy"
 engine="idtech3"
-glibc="2.7"
+glibc="2.17"
 
 #### Directories ####
 # Edit with care
@@ -160,7 +160,7 @@ backupdir="${lgsmdir}/backup"
 
 ## Logging Directories
 [ -n "${LGSM_LOGDIR}" ] && logdir="${LGSM_LOGDIR}" || logdir="${rootdir}/log"
-gamelogdir="${serverfiles}/Logs"
+gamelogdir="${serverfiles}/legacy"
 lgsmlogdir="${logdir}/script"
 consolelogdir="${logdir}/console"
 lgsmlog="${lgsmlogdir}/${selfname}-script.log"

+ 2 - 0
lgsm/modules/command_check_update.sh

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

+ 2 - 0
lgsm/modules/command_update.sh

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

+ 3 - 3
lgsm/modules/core_dl.sh

@@ -204,7 +204,7 @@ fn_dl_hash() {
 			fn_print_error_nl "hash length not known for hash type"
 			core_exit.sh
 		fi
-		echo -en "verifying ${local_filename} with ${hashtype}..."
+		echo -en "verifying ${local_filename} with ${hashtype}"
 		fn_sleep_time
 		hashsumcmd=$(${hashbin} "${local_filedir}/${local_filename}" | awk '{print $1}')
 		if [ "${hashsumcmd}" != "${hash}" ]; then
@@ -267,9 +267,9 @@ fn_dl_extract() {
 		fi
 	elif [ "${mime}" == "application/zip" ]; then
 		if [ -n "${extractsrc}" ]; then
-			temp_extractdir="${tmpdir}/Xonotic"
+			temp_extractdir="${tmpdir}/${extractsrc}"
 			extractcmd=$(unzip -qo "${local_filedir}/${local_filename}" "${extractsrc}/*" -d "${temp_extractdir}")
-			find "${temp_extractdir}/${extractsrc}" -mindepth 1 -maxdepth 1 -exec mv -t "${extractdest}" {} +
+			cp -a "${temp_extractdir}/${extractsrc}/." "${extractdest}/"
 			rm -rf "${temp_extractdir}"
 		else
 			extractcmd=$(unzip -qo -d "${extractdest}" "${local_filedir}/${local_filename}")

+ 1 - 1
lgsm/modules/core_getopt.sh

@@ -66,7 +66,7 @@ currentopt=("${cmd_start[@]}" "${cmd_stop[@]}" "${cmd_restart[@]}" "${cmd_monito
 currentopt+=("${cmd_update_linuxgsm[@]}")
 
 # Exclude noupdate games here.
-if [ "${shortname}" == "jk2" ] || [ "${engine}" != "idtech3" ]; then
+if [ "${shortname}" == "jk2" ] || [ "${shortname}" == "etl" ] || [ "${engine}" != "idtech3" ]; then
 	if [ "${shortname}" != "bf1942" ] && [ "${shortname}" != "bfv" ] && [ "${engine}" != "idtech2" ] && [ "${engine}" != "iw2.0" ] && [ "${engine}" != "iw3.0" ] && [ "${engine}" != "quake" ] && [ "${shortname}" != "samp" ] && [ "${shortname}" != "ut2k4" ]; then
 		currentopt+=("${cmd_update[@]}" "${cmd_check_update[@]}")
 		# force update for SteamCMD or Multi Theft Auto only.

+ 5 - 0
lgsm/modules/core_modules.sh

@@ -660,6 +660,11 @@ fn_update_modules.sh() {
 	fn_fetch_module
 }
 
+update_etl.sh() {
+	modulefile="${FUNCNAME[0]}"
+	fn_fetch_module
+}
+
 update_fctr.sh() {
 	modulefile="${FUNCNAME[0]}"
 	fn_fetch_module

+ 2 - 8
lgsm/modules/install_server_files.sh

@@ -80,14 +80,6 @@ fn_install_server_files() {
 		run="norun"
 		force="noforce"
 		md5="2c6be1bb66ea631b9b2e7ae6216c6680"
-	elif [ "${shortname}" == "etl" ]; then
-		remote_fileurl="http://linuxgsm.download/WolfensteinEnemyTerritory/etlegacy-v2.78.1-i386-et-260b.tar.xz"
-		local_filedir="${tmpdir}"
-		local_filename="etlegacy-v2.78.1-i386-et-260b.tar.xz"
-		chmodx="nochmodx"
-		run="norun"
-		force="noforce"
-		md5="7c08b52cb09b30eadb98ea05ef780fc7"
 	elif [ "${shortname}" == "mohaa" ]; then
 		remote_fileurl="http://linuxgsm.download/MedalofHonorAlliedAssault/moh_revival_v1.12_RC3.5.1.tar.xz"
 		local_filedir="${tmpdir}"
@@ -280,6 +272,8 @@ elif [ "${shortname}" == "ut99" ]; then
 	update_ut99.sh
 elif [ "${shortname}" == "xnt" ]; then
 	update_xnt.sh
+elif [ "${shortname}" == "etl" ]; then
+	update_etl.sh
 elif [ -z "${appid}" ] || [ "${shortname}" == "ahl" ] || [ "${shortname}" == "bb" ] || [ "${shortname}" == "q4" ] || [ "${shortname}" == "ns" ] || [ "${shortname}" == "sfc" ] || [ "${shortname}" == "ts" ] || [ "${shortname}" == "vs" ] || [ "${shortname}" == "zmr" ]; then
 	if [ "${shortname}" == "ut" ]; then
 		install_eula.sh

+ 173 - 0
lgsm/modules/update_etl.sh

@@ -0,0 +1,173 @@
+#!/bin/bash
+# LinuxGSM update_etl.sh module
+# Author: Daniel Gibbs
+# Contributors: https://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Handles updating of ET: Legacy servers.
+
+moduleselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+fn_update_dl() {
+	# Download and extract files to serverfiles.
+	fn_fetch_file "${remotebuildurl}" "" "" "" "${tmpdir}" "${remotebuildfilename}" "nochmodx" "norun" "force" "${remotebuildhash}"
+	fn_dl_extract "${tmpdir}" "${remotebuildfilename}" "${serverfiles}"
+	echo "${remotebuild}" > "${serverfiles}/build.txt"
+	fn_clear_tmp
+}
+
+fn_update_localbuild() {
+	# Gets local build info.
+	fn_print_dots "Checking local build: ${remotelocation}"
+	# Try to get build version from etconsole.log.
+	if [ -f "${gamelogdir}/etconsole.log" ]; then
+		localbuild=$(grep "Initializing legacy game" "${gamelogdir}/etconsole.log" | sed -n 's/.*\^2\(v[0-9]\+\.[0-9]\+\.[0-9]\+\).*/\1/p' | tail -1)
+	fi
+	# Fall back to build.txt if log parse failed or log does not exist.
+	if [ -z "${localbuild}" ]; then
+		localbuild=$(head -n 1 "${serverfiles}/build.txt" 2> /dev/null)
+	fi
+	if [ -z "${localbuild}" ]; then
+		fn_print_error "Checking local build: ${remotelocation}: missing local build info"
+		fn_script_log_error "Missing local build info"
+		fn_script_log_error "Set localbuild to 0"
+		localbuild="0"
+	else
+		fn_print_ok "Checking local build: ${remotelocation}"
+		fn_script_log_pass "Checking local build"
+	fi
+}
+
+fn_update_remotebuild() {
+	# Gets remote build info.
+	apiurl="https://api.github.com/repos/GameServerManagers/etlserver-build/releases/latest"
+	remotebuildresponse=$(curl -s "${apiurl}")
+	remotebuildfilename=$(echo "${remotebuildresponse}" | jq -r '.assets[] | select(.browser_download_url | contains("i386-et-260b")) | .name')
+	remotebuildurl=$(echo "${remotebuildresponse}" | jq -r '.assets[] | select(.browser_download_url | contains("i386-et-260b")) | .browser_download_url')
+	remotebuild=$(echo "${remotebuildresponse}" | jq -r '.tag_name')
+	remotebuildhash=$(echo "${remotebuildresponse}" | jq -r '.body' | grep 'MD5' | grep -oE '[a-f0-9]{32}')
+
+	if [ "${firstcommandname}" != "INSTALL" ]; then
+		fn_print_dots "Checking remote build: ${remotelocation}"
+		# Checks if remotebuild variable has been set.
+		if [ -z "${remotebuild}" ] || [ "${remotebuild}" == "null" ]; then
+			fn_print_fail "Checking remote build: ${remotelocation}"
+			fn_script_log_fail "Checking remote build"
+			core_exit.sh
+		else
+			fn_print_ok "Checking remote build: ${remotelocation}"
+			fn_script_log_pass "Checking remote build"
+		fi
+	else
+		# Checks if remotebuild variable has been set.
+		if [ -z "${remotebuild}" ] || [ "${remotebuild}" == "null" ]; then
+			fn_print_failure "Unable to get remote build"
+			fn_script_log_fail "Unable to get remote build"
+			core_exit.sh
+		fi
+	fi
+}
+
+fn_update_compare() {
+	fn_print_dots "Checking for update: ${remotelocation}"
+	# Update has been found or force update.
+	if [ "${localbuild}" != "${remotebuild}" ] || [ "${forceupdate}" == "1" ]; then
+		# Create update lockfile.
+		date '+%s' > "${lockdir:?}/update.lock"
+		fn_print_ok_nl "Checking for update: ${remotelocation}"
+		fn_print "\n"
+		fn_print_nl "${bold}${underline}Update${default} available"
+		fn_print_nl "* Local build: ${red}${localbuild}${default}"
+		fn_print_nl "* Remote build: ${green}${remotebuild}${default}"
+		if [ -n "${branch}" ]; then
+			fn_print_nl "* Branch: ${branch}"
+		fi
+		if [ -f "${rootdir}/.dev-debug" ]; then
+			fn_print_nl "Remote build info"
+			fn_print_nl "* apiurl: ${apiurl}"
+			fn_print_nl "* remotebuildfilename: ${remotebuildfilename}"
+			fn_print_nl "* remotebuildurl: ${remotebuildurl}"
+			fn_print_nl "* remotebuild: ${remotebuild}"
+		fi
+		fn_print "\n"
+		fn_script_log_info "Update available"
+		fn_script_log_info "Local build: ${localbuild}"
+		fn_script_log_info "Remote build: ${remotebuild}"
+		if [ -n "${branch}" ]; then
+			fn_script_log_info "Branch: ${branch}"
+		fi
+		fn_script_log_info "${localbuild} > ${remotebuild}"
+
+		if [ "${commandname}" == "UPDATE" ]; then
+			date +%s > "${lockdir:?}/last-updated.lock"
+			unset updateonstart
+			check_status.sh
+			# If server stopped.
+			if [ "${status}" == "0" ]; then
+				fn_update_dl
+				if [ "${localbuild}" == "0" ]; then
+					exitbypass=1
+					command_start.sh
+					fn_firstcommand_reset
+					exitbypass=1
+					fn_sleep_time_5
+					command_stop.sh
+					fn_firstcommand_reset
+				fi
+			# If server started.
+			else
+				fn_print_restart_warning
+				exitbypass=1
+				command_stop.sh
+				fn_firstcommand_reset
+				exitbypass=1
+				fn_update_dl
+				exitbypass=1
+				command_start.sh
+				fn_firstcommand_reset
+			fi
+			unset exitbypass
+			alert="update"
+		elif [ "${commandname}" == "CHECK-UPDATE" ]; then
+			alert="check-update"
+		fi
+		alert.sh
+	else
+		fn_print_ok_nl "Checking for update: ${remotelocation}"
+		fn_print "\n"
+		fn_print_nl "${bold}${underline}No update${default} available"
+		fn_print_nl "* Local build: ${green}${localbuild}${default}"
+		fn_print_nl "* Remote build: ${green}${remotebuild}${default}"
+		if [ -n "${branch}" ]; then
+			fn_print_nl "* Branch: ${branch}"
+		fi
+		fn_print "\n"
+		fn_script_log_info "No update available"
+		fn_script_log_info "Local build: ${localbuild}"
+		fn_script_log_info "Remote build: ${remotebuild}"
+		if [ -n "${branch}" ]; then
+			fn_script_log_info "Branch: ${branch}"
+		fi
+		if [ -f "${rootdir}/.dev-debug" ]; then
+			fn_print_nl "Remote build info"
+			fn_print_nl "* apiurl: ${apiurl}"
+			fn_print_nl "* remotebuildfilename: ${remotebuildfilename}"
+			fn_print_nl "* remotebuildurl: ${remotebuildurl}"
+			fn_print_nl "* remotebuild: ${remotebuild}"
+		fi
+	fi
+}
+
+# The location where the builds are checked and downloaded.
+remotelocation="github.com"
+
+if [ "${firstcommandname}" == "INSTALL" ]; then
+	fn_update_remotebuild
+	fn_update_dl
+else
+	fn_print_dots "Checking for update"
+	fn_print_dots "Checking for update: ${remotelocation}"
+	fn_script_log_info "Checking for update: ${remotelocation}"
+	fn_update_localbuild
+	fn_update_remotebuild
+	fn_update_compare
+fi

+ 1 - 0
lgsm/modules/update_xnt.sh

@@ -55,6 +55,7 @@ fn_update_remotebuild() {
 	remotebuildfilename="${remotebuildfilename}.zip"
 	remotebuildurl="https://dl.xonotic.org/${remotebuildfilename}"
 	remotebuild="${remotebuildtag}"
+	remotebuildhash=$(curl -s "https://dl.xonotic.org/${remotebuildfilename%.zip}.sha512" | grep "${remotebuildfilename}$" | grep -oE '[a-f0-9]{128}')
 
 	if [ "${firstcommandname}" != "INSTALL" ]; then
 		fn_print_dots "Checking remote build: ${remotelocation}"