dayzdockerserver/web/bin/dz
2025-03-10 21:20:07 -07:00

388 lines
10 KiB
Bash
Executable file

#!/usr/bin/env bash
source dz-common
# An array to store Workshop items. Each element contains the mod's ID, name, and state (active or not).
WORKSHOP_DIR="/mods/${release_client_appid}"
if [ ! -d ${WORKSHOP_DIR} ]
then
mkdir -p ${WORKSHOP_DIR}
fi
workshoplist=""
# Functions
# Usage
usage(){
echo -e "
${red}Bad option or arguments! ${yellow}${*}${default}
Usage: ${green}$(basename $0)${yellow} option [ arg1 [ arg2 ] ]
Options and arguments:
a|add id - Add a DayZ Workshop item by id. Added items become active by default
i|install - Install the DayZ server files
g|login - Login to Steam.
m|modupdate - Update the mod files
p|map id - Install a mod's mpmissions files by id. (Presumes template exists)
r|remove id - Remove all files and directories of a Workshop item by id
l|s|status - Shows Steam login status, if base files are installed, installed mods
u|update - Update the server files
x|xml id - Get and normalize XML files from a mod's template by id (Presumes template exists)
${default}"
exit 1
}
# Manage the mod symlink
symlink(){
W=${1}
ID=${2}
NAME=${3}
if [ ! -L "${SERVER_FILES}/@${NAME}" ] && [[ ${W} = 1 ]]
then
ln -sv ${WORKSHOP_DIR}/${ID} "${SERVER_FILES}/@${NAME}"
elif [[ "${W}" = "0" ]]
then
rm -vf "${SERVER_FILES}/@${NAME}"
fi
}
installxml(){
ID=${1}
# Going to have to maintain a matrix of file names -> root node -> child node permutations
for i in "CFGEVENTGROUPS:eventgroupdef:group" "CFGEVENTSPAWNS:eventposdef:event" "CFGSPAWNABLETYPES:spawnabletypes:type" "EVENTS:events:event" "TYPES:types:type"
do
var=$(echo ${i} | cut -d: -f1)
CHECK=$(echo ${i} | cut -d: -f2)
if [ -f "${WORKSHOP_DIR}/${ID}/${var,,}.xml" ]
then
echo "Normalizing ${WORKSHOP_DIR}/${ID}/${var,,}.xml..."
cp ${WORKSHOP_DIR}/${ID}/${var,,}.xml /tmp/x
# Quirks
# Some cfgeventspanws.xml files have <events> instead of <eventposdef>. Let's just try to fix that first.
if [[ ${var} = "CFGEVENTSPAWNS" ]]
then
if grep -q '<events>' /tmp/x
then
echo " - (Quirk) has <events> instead of <eventposdef>. fixing..."
xmlstarlet ed -L -r "events" -v "eventposdef" /tmp/x
fi
fi
# Some mod authors ship invalid XML files. This fixes comments before the <?xml> declaration.
xmlstarlet ed -d '//comment()' /tmp/x > /tmp/y && mv /tmp/y /tmp/x
# Check for the required root node and add it if it's missing
if ! grep -q '<'${CHECK}'>' /tmp/x
then
echo " - has no root node <${CHECK}>. fixing..."
echo '<'${CHECK}'>' > /tmp/y
cat /tmp/x >> /tmp/y
echo '</'${CHECK}'>' >> /tmp/y
xmlstarlet fo /tmp/y > /tmp/x
fi
if ! grep -q '<?xml' /tmp/x
then
echo " - has no XML node, fixing..."
xmlstarlet fo /tmp/x > /tmp/y
mv /tmp/y /tmp/x
fi
xmllint --noout /tmp/x && (
# Keep the normalized version in the /mods directory
cp /tmp/x ${WORKSHOP_DIR}/${ID}/${var,,}.xml
echo -e "${green}${WORKSHOP_DIR}/${ID}/${var,,}.xml passes XML lint test!${default}"
) || (
echo -e "${yellow}The final ${WORKSHOP_DIR}/${ID}/${var,,}.xml does not pass XML lint test! IT WAS NOT COPIED!${default}"
)
fi
done
exit 0
}
# Add a mod
add(){
if [ -d "${WORKSHOP_DIR}/${1}" ]
then
echo -e "${yellow}Warning: The mod directory ${WORKSHOP_DIR}/${1} already exists!${default}"
MODNAME=$(get_mod_name ${1})
fi
if [ -L "${SERVER_FILES}/@${MODNAME}" ]
then
echo -e "${yellow}Warning: The mod symlink ${SERVER_FILES}/@${MODNAME} already exists!${default}"
fi
echo "Adding mod id ${1}"
dologin
${STEAMCMD} +force_install_dir ${SERVER_FILES} +login "${steamlogin}" +workshop_download_item "${release_client_appid}" "${1}" +quit
# Make sure the install succeeded
if [ ! -d "${WORKSHOP_DIR}/${1}" ]
then
echo -e "${red}Mod installation failed: The mod directory ${WORKSHOP_DIR}/${1} was not created!${default}"
echo "Installation failed! See above (You probably need to use a real Steam login)"
return
fi
# Get the name of the newly added mod
MODNAME=$(get_mod_name ${1})
symlink 1 ${1} "${MODNAME}"
echo -e "Mod id ${1} - ${green}${MODNAME}${default} - added"
xml ${ID}
map ${ID}
}
# Remove a mod
remove(){
DIR="${WORKSHOP_DIR}/${1:?}"
if [ -d "${DIR}" ]
then
MODNAME=$(get_mod_name ${1})
echo "Removing directory ${DIR}"
rm -rf "${DIR}"
else
echo "Directory ${DIR} doesn't exist?"
fi
if [ -L "${SERVER_FILES}/@${MODNAME}" ]
then
echo "Removing symlink ${SERVER_FILES}/@${MODNAME}"
rm -f "${SERVER_FILES}/@${MODNAME}"
else
echo "Symlink ${SERVER_FILES}/@${MODNAME} doesn't exist?"
fi
echo -e "Mod id ${1} - ${red}${MODNAME}${default} - removed"
}
# Handle the Steam login information.
login(){
if [ -f "${STEAM_LOGIN}" ]
then
if prompt_yn "The steam login is already set. Reset it?"
then
rm -f "${STEAM_LOGIN}"
else
echo "Not reset."
exit 0
fi
fi
if [ ! -f "${STEAM_LOGIN}" ]
then
echo "Setting up Steam credentials"
echo -n "Steam Username (anonymous): "
read steamlogin
if [[ "${steamlogin}" = "" ]]
then
echo "Steam login set to 'anonymous'"
steamlogin="anonymous"
fi
echo "steamlogin=${steamlogin}" > "${STEAM_LOGIN}"
${STEAMCMD} +force_install_dir ${SERVER_FILES} +login "${steamlogin}" +quit
fi
}
# "Perform" the Steam login. This just sources the file with the Steam login name.
dologin(){
if [ -f "${STEAM_LOGIN}" ]
then
echo "Logging in to Steam"
steamlogin=$(cat ${STEAM_LOGIN})
else
echo "No cached Steam credentials. Please configure this now: "
login
fi
}
# Perform the installation of the server files.
install(){
WHICH=${1}
if [[ ${WHICH} = "experimental" ]]
then
SERVER_FILES=${SERVER_FILES_EXPERIMENTAL}
SERVER_INSTALL_FILE="${SERVER_FILES}/DayZServer"
release_server_appid=${experimental_server_appid}
fi
if [ ! -f "${SERVER_INSTALL_FILE}" ] || [[ ${2} = "force" ]]
then
printf "[ ${yellow}DayZ${default} ] Downloading ${WHICH} DayZ Server-Files!\n"
dologin
${STEAMCMD} +force_install_dir ${SERVER_FILES} +login "${steamlogin}" +app_update "${release_server_appid}" validate +quit
# This installs the mpmissions for charnarusplus and enoch (AKA Livonia) from github. The game once allowed the full server
# to be downloaded, but now you get the server without any mpmissions. This is a workaround.
echo "Installing mpmissions for ChernarusPlus and Livonia from github..."
map default
else
printf "[ ${lightblue}DayZ${default} ] The ${WHICH} server is already installed.\n"
fi
}
# Update the server files.
update(){
dologin
appmanifestfile=${SERVER_FILES}/steamapps/appmanifest_"${release_server_appid}".acf
printf "[ ... ] Checking for update:"
# gets currentbuild
currentbuild=$(grep buildid "${appmanifestfile}" | tr '[:blank:]"' ' ' | tr -s ' ' | cut -d \ -f3)
# Removes appinfo.vdf as a fix for not always getting up to date version info from SteamCMD
if [ -f "${HOME}/Steam/appcache/appinfo.vdf" ]
then
rm -f "${HOME}/Steam/appcache/appinfo.vdf"
fi
# check for new build
availablebuild=$(${STEAMCMD} +login "${steamlogin}" +app_info_update 1 +app_info_print "${release_server_appid}" +quit | \
sed -n '/branch/,$p' | grep -m 1 buildid | tr -cd '[:digit:]')
if [ -z "${availablebuild}" ]
then
printf "\r[ ${red}FAIL${default} ] Checking for update:\n"
printf "\r[ ${red}FAIL${default} ] Checking for update:: Not returning version info\n"
exit
else
printf "\r[ ${green}OK${default} ] Checking for update:"
fi
# compare builds
if [ "${currentbuild}" != "${availablebuild}" ] || [[ ${1} = "force" ]]
then
printf "\r[ ${green}OK${default} ] Checking for update:: Update available\n"
printf "Update available:\n"
printf "\tCurrent build: ${red}${currentbuild}${default}\n"
printf "\tAvailable build: ${green}${availablebuild}${default}\n"
printf "\thttps://steamdb.info/app/${release_server_appid}/\n"
printf "\nApplying update"
# run update
dologin
${STEAMCMD} +force_install_dir ${SERVER_FILES} +login "${steamlogin}" +app_update "${release_server_appid}" validate +quit
modupdate
else
printf "\r[ ${green}OK${default} ] Checking for update:: No update available\n"
printf "\nNo update available:\n"
printf "\tCurrent version: ${green}${currentbuild}${default}\n"
printf "\tAvailable version: ${green}${availablebuild}${default}\n"
printf "\thttps://steamdb.info/app/${release_server_appid}/\n\n"
fi
}
# Update mods
modupdate(){
echo "Updating mods..."
dologin
get_mods
${STEAMCMD} +force_install_dir ${SERVER_FILES} +login "${steamlogin}" ${workshoplist} +quit
# Updated files come in with mixed cases. Fix that.
echo "done"
echo
}
# Display the status of everything
status(){
INSTALLED="${NO}"
LOGGED_IN="${NO}"
# DayZ Server files installation
if [ -f "${SERVER_INSTALL_FILE}" ]
then
INSTALLED="${YES}"
if [[ ${release_client_appid} = "221100" ]]
then
RELEASE="Stable"
else
RELEASE="Experimental"
fi
VERSION="$(strings /serverfiles/DayZServer | grep -P "DayZ \d\.\d+\.\d+" | cut -c6-) - ${RELEASE}"
fi
# Logged into Steam
if [ -f "${STEAM_LOGIN}" ]
then
LOGGED_IN="${YES}"
if grep -q anonymous "${STEAM_LOGIN}"
then
ANONYMOUS="${yellow}(as anonymous)${default}"
else
ANONYMOUS="${green}(not anonymous)${default}"
fi
fi
echo -ne "
Logged in to Steam: ${LOGGED_IN} ${ANONYMOUS}
Server files installed: ${INSTALLED}"
if [[ "${INSTALLED}" = "${NO}" ]]
then
echo
echo
exit 0
fi
# Version of DayZ Server files
echo -ne "
Version: ${VERSION}"
# Mods
echo -ne "
Mods: "
MODS=$(list)
if [[ ${MODS} == "" ]]
then
echo -ne "${red}none${default}"
fi
echo -e "${MODS}"
}
map(){
# Install map mpmissions for mods that have them. Presumes a map.env was created for the mod, with the required metadata (git URL, etc.)
TERM="map"
if [[ "${1}" =~ ^[0-9]+$ ]]
then
TERM="mod id"
fi
if [ -f "${FILES}/mods/${1}/map.env" ]
then
echo "Installing mpmissions files for ${TERM} ${1}..."
source ${FILES}/mods/${1}/map.env
${FILES}/bin/map.sh ${1} install
fi
}
mod_install(){
if [ -f ${FILES}/mods/${1}/${2}.sh ]
then
echo "An ${2}.sh was found for mod id ${1}. Running..."
${FILES}/mods/${1}/${2}.sh
fi
# A generic map install script. Presumes a git repo as the source
}
# "Manage" XML files.
xml(){
/files/bin/xml.sh ${1}
installxml ${1}
}
C=${1}
shift || {
usage
}
case "${C}" in
a|add)
add "${@}"
;;
i|install)
install "${@}"
;;
g|login)
login "${@}"
;;
m|modupdate)
modupdate "${@}"
;;
r|remove)
remove "${@}"
;;
l|s|status)
status "${@}"
;;
p|map)
map "${@}"
;;
u|update)
update "${@}"
;;
x|xml)
xml "${@}"
;;
*)
usage "$*"
;;
esac