Kaynağa Gözat

feat(orarserver): add OpenRA Red Alert dedicated server (#5102)

Adds orarserver for OpenRA: Red Alert using the official stable Linux x86_64 AppImage. LinuxGSM extracts the package so installation does not require FUSE, manages start/stop, checks the TCP game port and updates from the upstream stable GitHub release.

Updates stage and validate the new package before replacing the old one, restoring the previous engine if the swap fails. Instance data is stored outside the replaceable engine directory. orar is added to the shared Update Check matrix.

Related to #2233.
Daniel Gibbs 4 gün önce
ebeveyn
işleme
1e403746f6

+ 1 - 1
.github/workflows/action-update-check.yml

@@ -22,7 +22,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        shortname: [css, fctr, jk2, mc, mcb, mta, pmc, ts3, ut99, vints, xnt]
+        shortname: [css, fctr, jk2, mc, mcb, mta, orar, pmc, ts3, ut99, vints, xnt]
 
     steps:
       - name: Download linuxgsm.sh

+ 208 - 0
lgsm/config-default/config-lgsm/orarserver/_default.cfg

@@ -0,0 +1,208 @@
+##################################
+######## Default Settings ########
+##################################
+# DO NOT EDIT, ANY CHANGES WILL BE OVERWRITTEN!
+# Copy settings from here and use them in either:
+# common.cfg - applies settings to every instance.
+# [instance].cfg - applies settings to a specific instance.
+
+#### Game Server Settings ####
+
+servername="LinuxGSM"
+serverpassword=""
+port="1234"
+advertiseonline="True"
+advertiseonlocalnetwork="True"
+enablesingleplayer="True"
+recordreplays="False"
+
+## Server Parameters | https://docs.linuxgsm.com/configuration/start-parameters#additional-parameters
+startparameters="--server Server.Name=\"${servername}\" Server.Password=\"${serverpassword}\" Server.ListenPort=${port} Server.AdvertiseOnline=${advertiseonline} Server.AdvertiseOnLocalNetwork=${advertiseonlocalnetwork} Server.EnableSingleplayer=${enablesingleplayer} Server.RecordReplays=${recordreplays} Engine.SupportDir=\"${servercfgdir}\""
+
+## Extra Parameters | https://docs.linuxgsm.com/configuration/start-parameters#extra-parameters
+# Added to the end of startparameters.
+extraparams=""
+
+#### LinuxGSM Settings ####
+
+## LinuxGSM Stats
+# Send useful stats to LinuxGSM developers.
+# https://docs.linuxgsm.com/configuration/linuxgsm-stats
+# (on|off)
+stats="off"
+
+## Notification Alerts
+# (on|off)
+
+# Display IP | https://docs.linuxgsm.com/alerts#display-ip
+displayip=""
+
+# More info | https://docs.linuxgsm.com/alerts#more-info
+postalert="off"
+
+# Alert on Start/Stop/Restart
+statusalert="off"
+
+# Discord Alerts | https://docs.linuxgsm.com/alerts/discord
+discordalert="off"
+discordwebhook="webhook"
+
+# Email Alerts | https://docs.linuxgsm.com/alerts/email
+emailalert="off"
+email="email@example.com"
+emailfrom=""
+
+# Gotify Alerts | https://docs.linuxgsm.com/alerts/gotify
+gotifyalert="off"
+gotifytoken="token"
+gotifywebhook="webhook"
+
+# IFTTT Alerts | https://docs.linuxgsm.com/alerts/ifttt
+iftttalert="off"
+ifttttoken="accesstoken"
+iftttevent="linuxgsm_alert"
+
+# ntfy Alerts | https://docs.linuxgsm.com/alerts/ntfy
+ntfyalert="off"
+ntfytopic="LinuxGSM"
+ntfyserver="https://ntfy.sh"
+ntfytoken=""
+ntfyusername=""
+ntfypassword=""
+ntfypriority=""
+ntfytags=""
+
+# Matrix Alerts | https://docs.linuxgsm.com/alerts/matrix
+matrixalert="off"
+matrixhomeserver="matrix.org"
+matrixtoken="accesstoken"
+matrixroom=""
+
+# Pushbullet Alerts | https://docs.linuxgsm.com/alerts/pushbullet
+pushbulletalert="off"
+pushbullettoken="accesstoken"
+channeltag=""
+
+# Pushover Alerts | https://docs.linuxgsm.com/alerts/pushover
+pushoveralert="off"
+pushovertoken="accesstoken"
+pushoveruserkey="userkey"
+
+# Rocket.Chat Alerts | https://docs.linuxgsm.com/alerts/rocket.chat
+rocketchatalert="off"
+rocketchatwebhook="webhook"
+
+# Slack Alerts | https://docs.linuxgsm.com/alerts/slack
+slackalert="off"
+slackwebhook="webhook"
+
+# Telegram Alerts | https://docs.linuxgsm.com/alerts/telegram
+# You can add a custom cURL string eg proxy (useful in Russia) in "curlcustomstring".
+# For example "--socks5 ipaddr:port" for socks5 proxy see more in "curl --help all".
+telegramapi="api.telegram.org"
+telegramalert="off"
+telegramtoken="accesstoken"
+telegramchatid=""
+telegramthreadid=""
+telegramsilentnotification="false"
+curlcustomstring=""
+
+## Stop | https://docs.linuxgsm.com/commands/start-stop-restart
+stoponlyifnoplayers="off"
+stoponlyifnoplayersallcommands="on"
+
+## Player Warnings | https://docs.linuxgsm.com/features/player-warnings
+# Minutes to warn players in-game before a stop, restart or update. 0 to disable.
+# e.g. 15 warns at 15, 10, 5, 3 and 1 minutes.
+warnplayerstimer="0"
+
+## Updating | https://docs.linuxgsm.com/commands/update
+updateonstart="off"
+
+## Backup | https://docs.linuxgsm.com/commands/backup
+maxbackups="4"
+maxbackupdays="30"
+stoponbackup="on"
+
+## Logging | https://docs.linuxgsm.com/features/logging
+consolelogging="on"
+logdays="7"
+
+## Monitor | https://docs.linuxgsm.com/commands/monitor
+# Query delay time
+querydelay="1"
+
+## ANSI Colors | https://docs.linuxgsm.com/features/ansi-colors
+ansi="on"
+
+#### Advanced Settings ####
+
+## Message Display Time | https://docs.linuxgsm.com/features/message-display-time
+sleeptime="0.5"
+
+## Stop Mode | https://docs.linuxgsm.com/features/stop-mode
+# 1: tmux kill
+# 2: CTRL+c
+# 3: quit
+# 4: quit 120s
+# 5: stop
+# 6: q
+# 7: exit
+# 8: 7 Days to Die
+# 9: GoldSrc
+# 10: Avorion
+# 11: end
+stopmode="2"
+
+## Query mode
+# 1: session only
+# 2: gamedig (gsquery fallback)
+# 3: gamedig
+# 4: gsquery
+# 5: tcp
+querymode="5"
+querytype=""
+
+## Console type
+consoleverbose="yes"
+consoleinteract="no"
+
+## Game Server Details
+# Do not edit
+gamename="OpenRA: Red Alert"
+engine="openra"
+glibc="2.17"
+
+#### Directories ####
+# Edit with care
+
+## Game Server Directories
+systemdir="${serverfiles}"
+executabledir="${serverfiles}/openra"
+preexecutable=""
+executable="./AppRun"
+servercfgdir="${serverfiles}/data/${selfname}"
+servercfg=""
+servercfgdefault=""
+servercfgfullpath=""
+
+## Backup Directory
+backupdir="${lgsmdir}/backup"
+
+## Logging Directories
+[ -n "${LGSM_LOGDIR}" ] && logdir="${LGSM_LOGDIR}" || logdir="${rootdir}/log"
+gamelogdir="${servercfgdir}/Logs"
+lgsmlogdir="${logdir}/script"
+consolelogdir="${logdir}/console"
+lgsmlog="${lgsmlogdir}/${selfname}-script.log"
+consolelog="${consolelogdir}/${selfname}-console.log"
+alertlog="${lgsmlogdir}/${selfname}-alert.log"
+postdetailslog="${lgsmlogdir}/${selfname}-postdetails.log"
+
+## Logs Naming
+lgsmlogdate="${lgsmlogdir}/${selfname}-script-$(date '+%Y-%m-%d-%H:%M:%S').log"
+consolelogdate="${consolelogdir}/${selfname}-console-$(date '+%Y-%m-%d-%H:%M:%S').log"
+
+## Log Parameters
+logtimestamp="off"
+logtimestampformat="%Y-%m-%d %H:%M:%S"

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

@@ -85,6 +85,7 @@ ns2,speex,tbb
 ns2c,speex.i686,tbb.i686
 ohd
 onset,mariadb-connector-c
+orar,libicu,openssl-libs,zlib
 opfor
 pc
 pc2

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

@@ -85,6 +85,7 @@ ns2,speex,tbb
 ns2c,speex.i686,tbb.i686
 ohd
 onset,mariadb-connector-c
+orar,libicu,openssl-libs,zlib
 opfor
 pc
 pc2

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

@@ -85,6 +85,7 @@ ns2,speex,tbb
 ns2c,speex.i686,tbb.i686
 ohd
 onset,mariadb-connector-c
+orar
 opfor
 pc
 pc2

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

@@ -85,6 +85,7 @@ ns2,speex,tbb
 ns2c,speex.i686,tbb.i686
 ohd
 onset,mariadb-connector-c
+orar
 opfor
 pc
 pc2

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

@@ -85,6 +85,7 @@ ns2,speex,tbb
 ns2c,speex.i686,tbb.i686
 ohd
 onset,mariadb-connector-c
+orar
 opfor
 pc
 pc2

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

@@ -84,6 +84,7 @@ ns2,speex,libtbb2
 ns2c,speex:i386,libtbb2
 ohd
 onset,libmariadb-dev
+orar
 opfor
 pc
 pc2

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

@@ -85,6 +85,7 @@ ns2,speex,libtbb2
 ns2c,speex:i386,libtbb2
 ohd
 onset,libmariadb-dev
+orar,libicu67,libssl1.1,zlib1g
 opfor
 pc
 pc2

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

@@ -85,6 +85,7 @@ ns2,speex,libtbb12
 ns2c,speex:i386,libtbb12
 ohd
 onset,libmariadb-dev
+orar,libicu72,libssl3,zlib1g
 opfor
 pc
 pc2

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

@@ -85,6 +85,7 @@ ns2,speex,libtbb12
 ns2c,speex:i386,libtbb12
 ohd
 onset,libmariadb-dev
+orar,libicu76,libssl3t64,zlib1g
 opfor
 pc
 pc2

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

@@ -85,6 +85,7 @@ ns2,speex,libtbb2
 ns2c,speex:i386,libtbb2
 ohd
 onset,libmariadb-dev
+orar
 opfor
 pc
 pc2

BIN
lgsm/data/gameicons/orar-icon.png


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

@@ -86,6 +86,7 @@ ns2,speex,tbb
 ns2c,speex.i686,tbb.i686
 ohd
 onset,mariadb-connector-c
+orar
 opfor
 pc
 pc2

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

@@ -85,6 +85,7 @@ ns2,speex,tbb
 ns2c,speex.i686,tbb.i686
 ohd
 onset,mariadb-connector-c
+orar,libicu,openssl-libs,zlib
 opfor
 pc
 pc2

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

@@ -85,6 +85,7 @@ ns2,speex,tbb
 ns2c,speex.i686,tbb.i686
 ohd
 onset,mariadb-connector-c
+orar,libicu,openssl-libs,zlib
 opfor
 pc
 pc2

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

@@ -85,6 +85,7 @@ ns2,speex,tbb
 ns2c,speex.i686,tbb.i686
 ohd
 onset,mariadb-connector-c
+orar,libicu,openssl-libs,zlib
 opfor
 pc
 pc2

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

@@ -85,6 +85,7 @@ ns2,speex,tbb
 ns2c,speex.i686,tbb.i686
 ohd
 onset,mariadb-connector-c
+orar,libicu,openssl-libs,zlib
 opfor
 pc
 pc2

+ 1 - 0
lgsm/data/serverlist.csv

@@ -84,6 +84,7 @@ ns2,ns2server,Natural Selection 2,ubuntu-26.04
 ns2c,ns2cserver,NS2: Combat,ubuntu-26.04
 ohd,ohdserver,Operation: Harsh Doorstop,ubuntu-26.04
 onset,onsetserver,Onset,ubuntu-20.04
+orar,orarserver,OpenRA: Red Alert,ubuntu-24.04
 opfor,opforserver,Opposing Force,ubuntu-26.04
 pc,pcserver,Project Cars,ubuntu-26.04
 pc2,pc2server,Project Cars 2,ubuntu-26.04

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

@@ -86,6 +86,7 @@ ns2,speex,libtbb2
 ns2c,speex:i386,libtbb2
 ohd
 onset,libmariadb-dev
+orar
 opfor
 pc
 pc2

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

@@ -86,6 +86,7 @@ ns2,speex,libtbb2
 ns2c,speex:i386,libtbb2
 ohd
 onset,libmariadb-dev
+orar
 opfor
 pc
 pc2

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

@@ -85,6 +85,7 @@ ns2,speex,libtbb2
 ns2c,speex:i386,libtbb2
 ohd
 onset,libmariadb-dev
+orar
 opfor
 pc
 pc2

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

@@ -85,6 +85,7 @@ ns2,speex,libtbb2
 ns2c,speex:i386,libtbb2
 ohd
 onset,libmariadb-dev
+orar,libicu70,libssl3,zlib1g
 opfor
 pc
 pc2

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

@@ -85,6 +85,7 @@ ns2,speex,libtbb2
 ns2c,speex:i386,libtbb2
 ohd
 onset,libmariadb-dev
+orar
 opfor
 pc
 pc2

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

@@ -85,6 +85,7 @@ ns2,speex,libtbb2
 ns2c,speex:i386,libtbb2
 ohd
 onset,libmariadb-dev
+orar
 opfor
 pc
 pc2

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

@@ -85,6 +85,7 @@ ns2,speex
 ns2c,speex:i386
 ohd
 onset,libmariadb-dev
+orar,libicu74,libssl3t64,zlib1g
 opfor
 pc
 pc2

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

@@ -85,6 +85,7 @@ ns2,speex
 ns2c,speex:i386
 ohd
 onset,libmariadb-dev
+orar,libicu78,libssl3t64,zlib1g
 opfor
 pc
 pc2

+ 4 - 0
lgsm/modules/check_config.sh

@@ -7,6 +7,10 @@
 
 moduleselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
 
+if [ "${shortname}" == "orar" ] && [ ! -d "${servercfgdir}" ]; then
+	install_config.sh
+fi
+
 if [ -n "${servercfgfullpath}" ] && [ ! -f "${servercfgfullpath}" ]; then
 	fn_print_warn_nl "Configuration file missing!"
 	echo -e "${servercfgfullpath}"

+ 2 - 0
lgsm/modules/command_check_update.sh

@@ -32,6 +32,8 @@ elif [ "${shortname}" == "jk2" ]; then
 	update_jk2.sh
 elif [ "${shortname}" == "jk3" ]; then
 	update_jk3.sh
+elif [ "${shortname}" == "orar" ]; then
+	update_openra.sh
 elif [ "${shortname}" == "vints" ]; then
 	update_vints.sh
 elif [ "${shortname}" == "bb" ]; then

+ 2 - 0
lgsm/modules/command_update.sh

@@ -73,6 +73,8 @@ elif [ "${shortname}" == "jk2" ]; then
 	update_jk2.sh
 elif [ "${shortname}" == "jk3" ]; then
 	update_jk3.sh
+elif [ "${shortname}" == "orar" ]; then
+	update_openra.sh
 elif [ "${shortname}" == "vints" ]; then
 	update_vints.sh
 elif [ "${shortname}" == "bb" ]; then

+ 5 - 0
lgsm/modules/core_modules.sh

@@ -762,6 +762,11 @@ update_mta.sh() {
 	fn_fetch_module
 }
 
+update_openra.sh() {
+	modulefile="${FUNCNAME[0]}"
+	fn_fetch_module
+}
+
 update_pmc.sh() {
 	modulefile="${FUNCNAME[0]}"
 	fn_fetch_module

+ 9 - 0
lgsm/modules/info_game.sh

@@ -2292,6 +2292,13 @@ fn_info_game_vh() {
 	servername="${servername:-"NOT SET"}"
 }
 
+# Parameters: true
+fn_info_game_openra() {
+	port="${port:-"1234"}"
+	queryport="${port}"
+	servername="${servername:-"NOT SET"}"
+}
+
 # Config Type: json
 # Parameters: false
 # Comment: // or /* */
@@ -2609,6 +2616,8 @@ elif [ "${shortname}" == "ut99" ]; then
 	fn_info_game_ut99
 elif [ "${shortname}" == "vh" ]; then
 	fn_info_game_vh
+elif [ "${shortname}" == "orar" ]; then
+	fn_info_game_openra
 elif [ "${shortname}" == "vints" ]; then
 	fn_info_game_vints
 elif [ "${shortname}" == "vpmc" ]; then

+ 10 - 1
lgsm/modules/info_messages.sh

@@ -747,7 +747,7 @@ fn_info_messages_ports_edit() {
 		fi
 	done
 	# engines/games that require editing the start parameters.
-	local ports_edit_array=("av" "ce" "ck" "col" "cs2" "fctr" "goldsrc" "hcu" "hw" "hyt" "iw3.0" "ioquake3" "pw" "qfusion" "rsdw" "rust" "scpsl" "sf" "sol" "spark" "source" "unreal4" "arma3" "dayz" "unt" "vh")
+	local ports_edit_array=("av" "ce" "ck" "col" "cs2" "fctr" "goldsrc" "hcu" "hw" "hyt" "iw3.0" "ioquake3" "openra" "pw" "qfusion" "rsdw" "rust" "scpsl" "sf" "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}"
@@ -1828,6 +1828,13 @@ fn_info_messages_vh() {
 	} | column -s $'\t' -t
 }
 
+fn_info_messages_openra() {
+	{
+		fn_port "header"
+		fn_port "Game" port tcp
+	} | column -s $'\t' -t
+}
+
 fn_info_messages_vints() {
 	{
 		fn_port "header"
@@ -2044,6 +2051,8 @@ fn_info_messages_select_engine() {
 		fn_info_messages_unt
 	elif [ "${shortname}" == "vh" ]; then
 		fn_info_messages_vh
+	elif [ "${shortname}" == "orar" ]; then
+		fn_info_messages_openra
 	elif [ "${shortname}" == "vints" ]; then
 		fn_info_messages_vints
 	elif [ "${shortname}" == "rust" ]; then

+ 4 - 1
lgsm/modules/install_config.sh

@@ -256,7 +256,10 @@ fn_list_config_locations() {
 	echo -e "Config documentation: ${italic}https://docs.linuxgsm.com/configuration${default}"
 }
 
-if [ "${shortname}" == "sdtd" ]; then
+if [ "${shortname}" == "orar" ]; then
+	fn_check_cfgdir
+	fn_list_config_locations
+elif [ "${shortname}" == "sdtd" ]; then
 	fn_default_config_local
 	fn_list_config_locations
 elif [ "${shortname}" == "ac" ]; then

+ 2 - 0
lgsm/modules/install_server_files.sh

@@ -279,6 +279,8 @@ elif [ "${shortname}" == "jk2" ]; then
 	update_jk2.sh
 elif [ "${shortname}" == "jk3" ]; then
 	update_jk3.sh
+elif [ "${shortname}" == "orar" ]; then
+	update_openra.sh
 elif [ "${shortname}" == "vints" ]; then
 	update_vints.sh
 elif [ "${shortname}" == "bb" ]; then

+ 155 - 0
lgsm/modules/update_openra.sh

@@ -0,0 +1,155 @@
+#!/bin/bash
+# LinuxGSM update_openra.sh module
+# Author: Daniel Gibbs
+# Contributors: https://linuxgsm.com/contrib
+# Website: https://linuxgsm.com
+# Description: Handles updating of OpenRA servers.
+
+moduleselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
+
+fn_update_dl() {
+	fn_fetch_file "${remotebuildurl}" "" "" "" "${tmpdir}" "${remotebuildfilename}" "chmodx" "norun" "force" "nohash"
+	local extractdir
+	extractdir=$(mktemp -d "${tmpdir}/openra.XXXXXX") || exit 1
+	if ! (cd "${extractdir}" && "${tmpdir}/${remotebuildfilename}" --appimage-extract > /dev/null) || [ ! -x "${extractdir}/squashfs-root/usr/lib/openra/OpenRA.Server" ] || [ ! -x "${extractdir}/squashfs-root/AppRun" ]; then
+		fn_print_fail_nl "Extracting OpenRA server files"
+		fn_script_log_fail "Extracting OpenRA server files"
+		exitcode=1
+		core_exit.sh
+	fi
+	printf '%s\n' "${remotebuild}" > "${extractdir}/squashfs-root/build.txt" || exit 1
+	if [ -d "${serverfiles}/openra" ]; then
+		mv "${serverfiles}/openra" "${extractdir}/previous" || exit 1
+	fi
+	if ! mv "${extractdir}/squashfs-root" "${serverfiles}/openra"; then
+		if [ -d "${extractdir}/previous" ]; then
+			mv "${extractdir}/previous" "${serverfiles}/openra"
+		fi
+		exitcode=1
+		core_exit.sh
+	fi
+	fn_clear_tmp
+}
+
+fn_update_localbuild() {
+	fn_print_dots "Checking local build: ${remotelocation}"
+	localbuild="0"
+	if [ -x "${serverfiles}/openra/AppRun" ] && [ -x "${serverfiles}/openra/usr/lib/openra/OpenRA.Server" ]; then
+		localbuild=$(head -n 1 "${serverfiles}/openra/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"
+		localbuild="0"
+	else
+		fn_print_ok "Checking local build: ${remotelocation}"
+		fn_script_log_pass "Checking local build"
+	fi
+}
+
+fn_update_remotebuild() {
+	if [ "$(uname -m)" != "x86_64" ]; then
+		fn_print_fail_nl "OpenRA requires an x86_64 Linux server"
+		fn_script_log_fail "OpenRA requires an x86_64 Linux server"
+		exitcode=1
+		core_exit.sh
+	fi
+	apiurl="https://api.github.com/repos/OpenRA/OpenRA/releases/latest"
+	if ! remotebuildresponse=$(curl --fail --location --silent --show-error --connect-timeout 10 --max-time 60 "${apiurl}"); then
+		fn_print_fail_nl "Checking remote build: ${remotelocation}"
+		fn_script_log_fail "Checking remote build: ${apiurl}"
+		exitcode=1
+		core_exit.sh
+	fi
+	remotebuildfilename="OpenRA-Red-Alert-x86_64.AppImage"
+	remotebuild=$(echo "${remotebuildresponse}" | jq -er '.tag_name | select(test("^release-[0-9]+$"))')
+	remotebuildurl=$(echo "${remotebuildresponse}" | jq -er --arg filename "${remotebuildfilename}" '[.assets[] | select(.name == $filename)] | select(length == 1) | .[0].browser_download_url')
+	if [ -z "${remotebuild}" ] || [[ "${remotebuildurl}" != https://github.com/OpenRA/OpenRA/releases/download/* ]]; then
+		fn_print_fail_nl "Unable to get OpenRA release asset"
+		fn_script_log_fail "Unable to get OpenRA release asset"
+		exitcode=1
+		core_exit.sh
+	fi
+	fn_print_ok "Checking remote build: ${remotelocation}"
+	fn_script_log_pass "Checking remote build: ${remotebuild}"
+}
+
+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
+			unset updateonstart
+			fn_update_apply fn_update_dl
+			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/OpenRA/OpenRA"
+
+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