mirror of
https://ceregatti.org/git/daniel/dayzdockerserver.git
synced 2025-05-06 22:31:18 +00:00

Login. Still WIP, but works. Install server files. Once logged in, this works. Add support for experimental in both web and scripts. WIP. Continuous streaming text web interface for long-running server processes. Large refactor of the config file. Use arrays instead of delimited strings. Large refactor of the express server to support the above. Add Steam API key to env for when we do mod searches. Customize the bash shell of the web container should we exec into it. Ignore the env file.
387 lines
10 KiB
Bash
Executable file
387 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}
|
|
}
|
|
|
|
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
|