dayzdockerserver/files/dayzserver
Daniel Ceregatti f6153d02b5 Refactor to allow cleanup to happen in an exit trap.
Remove loop to allow docker to handle restarts on crashes. Hacks exist for now to force exit 0 because the server always crashes on exit.
Rework how the config is loaded. It need not always happen.
2022-07-10 11:26:16 -07:00

430 lines
12 KiB
Bash
Executable file

#!/usr/bin/env bash
# Colors
default="\e[0m"
red="\e[31m"
green="\e[32m"
yellow="\e[93m"
lightblue="\e[94m"
blue="\e[34m"
magenta="\e[35m"
cyan="\e[36m"
# Make sure to report and clean up on exit, as these files remain in the container's volume
function report() {
echo
echo -e "${yellow}========================================== error.log =========================================="
find "${HOME}" -name error.log -exec head {} \; -exec rm -f {} \;
echo
echo -e "${yellow}========================================== script*.log ========================================"
find "${HOME}" -name "script*.log" -exec head {} \; -exec rm -f {} \;
echo
echo -e "${yellow}========================================== *.RPT =============================================="
find "${HOME}" -name "*.RPT" -exec ls -la {} \; -exec rm -f {} \;
echo
echo -e "${yellow}========================================== End crash log ======================================"
}
# Make sure we don't start collecting core files
# FIXME Set this in Docker somewhere
ulimit -c 0
# DayZ Experimental SteamID, because there is no release version of the Linux server yet
appid=1042420
# DayZ Release SteamID. This is for mods, as only the release has them.
dayz_id=221100
# Command line argument variables
# Server config file
config=serverDZ.cfg
# Server port
port=2302
rcon_port=2303
# Server profile directory
profile="-profiles=${HOME}/serverprofile/"
# To log or not to log.
# All possible logging on:
#logs="-dologs -adminlog -netlog"
# No logs? This should read "-somelogs"
logs="-nologs"
# Mods. WIP. This will have to come from another file, as the plan is to manage this with
# this very script.
# mods="@CF;@community-Online-Tools;@Banov;@SimpleAutorun"
mods=""
# Put them all together
dayzparameter=" -config=${config} -port=${port} -mod='${mods}' -freezecheck -fps=60 -BEpath=${HOME}/serverfiles/battleye ${profile} ${logs}"
# Base directories
CFG_SRC_FILES="/files"
SERVER_FILES="${HOME}/serverfiles"
SERVER_PROFILE="${HOME}/serverprofile"
# Server configuration file
SERVER_CFG_FILE="serverDZ.cfg"
SERVER_CFG_DST="${SERVER_FILES}/${SERVER_CFG_FILE}"
SERVER_CFG_SRC="${CFG_SRC_FILES}/${SERVER_CFG_FILE}"
# Used to check if dayZ is installed
SERVER_INSTALL_FILE="${SERVER_FILES}/DayZServer"
# Steam files
STEAM_LOGIN="${HOME}/steamlogin"
STEAMCMD=steamcmd
# Workshop
WORKSHOP_CFG="${HOME}/workshop.cfg"
# Other stuff
YES="${green}yes${default}"
NO="${red}no${default}"
fn_usage(){
echo -e "
${red}Bad option or arguments! ${yellow}${*}${default}
Usage: ${green}$(basename $0)${yellow} option [ arg1 [ arg2 ] ]
Options and arguments:
install - Install the DayZ server files
login - Login to Steam
rcon - Connect to the server using a python RCON client
status - Shows the server's status as well as the state of the server files and mods (Needs install, update, etc.)
stop - Stop the server
update - Update the DayZ server files
workshop - Workshop Features
activate id [id...] - Activate one or many installed DayZ Workshop item by id
add id [id...] - Add one or many DayZ Workshop items by id
deactivate id [id...] - Deactivate one or many installed DayZ Workshop items by id - Keeps the addon files but excludes from -mod=
list - List Workshop items and their states (Active vs. Non)
remove id [id...] - Remove one or many Workshop items by id
${default}"
exit 1
}
fn_prompt_yn(){
echo -n "${1} (y|N) " >&2
read -s -n 1 a
a=$(echo ${a} | tr A-Z a-z)
if [[ "${a}" = "y" ]]
then
echo
return 0
else
echo
return 1
fi
}
fn_loadconfig_dayz(){
if [ ! -f "${SERVER_INSTALL_FILE}" ]
then
echo
echo -e "The DayZ server files are not installed. Run '${green}docker-compose run --rm main dayzserver install${default}'"
echo
exit 1
fi
# Handle the initial server configuration file
if [ ! -f ${SERVER_CFG_DST} ]
then
echo "Creating initial server configuration file"
cp "${SERVER_CFG_SRC}" "${SERVER_CFG_DST}"
fi
# battleye config and rconpassword setup
# The server creates a new file from this file, which it then uses.
# Let's make sure to delete it first
BE_SERVER_FILE="${HOME}/serverfiles/battleye/beserver_x64.cfg"
ALT_BE_SERVER_FILE=$(find ${HOME}/serverfiles/battleye -name "beserver_x64_active*")
if [ ! -f "${BE_SERVER_FILE}" ] && [ ! -f "${ALT_BE_SERVER_FILE}" ]
then
passwd=$(openssl rand -base64 8 | tr -dc 'A-Za-z0-9')
if [ "${passwd}" == "" ]; then
passwd=$(< /dev/urandom tr -dc 'A-Za-z0-9' | head -c10)
fi
if [ "${passwd}" == "" ]; then
printf "[ ${red}FAIL${default} ] Could not generate a passwort for RCON!\nOpen the Battleye config with 'dayzserver rcon'."
exit 1
else
cat > "${BE_SERVER_FILE}" <<EOF
RConPassword ${passwd}
RestrictRCon 0
RConPort ${rcon_port}
EOF
fi
printf "[ ${cyan}INFO${default} ] New RCON password: ${yellow}${passwd}${default}\n"
else
if [ -f "${BE_SERVER_FILE}" ]
then
FILE="${BE_SERVER_FILE}"
elif [ -f "${ALT_BE_SERVER_FILE}" ]
then
FILE="${ALT_BE_SERVER_FILE}"
fi
passwd=$(grep RConPassword ${FILE} | awk '{print $2}')
# printf "[ ${cyan}INFO${default} ] Using existing RCON password: ${yellow}${passwd}${default}\n"
fi
cp /usr/local/py3rcon/configexample.json ~/py3rcon.config.json
jq --arg port 2303 --arg rcon_password b0fNIBVfkM \
'.logfile="py3rcon.log" | .loglevel=0 | .server.port=$port | .server.rcon_password=$rcon_password | del(.repeatMessage)' \
/usr/local/py3rcon/configexample.json \
> ~/py3rcon.config.json
}
fn_start_dayz(){
# Check for interactive shell
if [ -t 0 ]
then
echo
echo -e "Not starting the server in an interactive shell. Start the server using '${green}docker-compose up -d${default}'"
echo
exit
fi
# Do the report on exit. Set here so that it only happens once we're starting the server, and not for other actions.
trap '
report
' EXIT
fn_workshop_mods
cd ${SERVER_FILES}
# Run the server. Allow docker to restart the container if the script exits with a code other than 0. This is so we can
# safely shut the container down without killing the server within.
printf "[ ${green}DayZ${default} ] Server starting...\n"
./DayZServer $dayzparameter
EXIT_CODE=$?
if [[ ${EXIT_CODE} = "139" ]]
then
EXIT_CODE=0
fi
printf "[ ${yellow}DayZ${default} ] Server stopped. Exit code: ${EXIT_CODE}\n"
exit ${EXIT_CODE}
}
fn_stop_dayz(){
echo "Stopping DayZ server...(this is not implemented)"
}
fn_steam_login(){
if [ -f "${STEAM_LOGIN}" ]
then
if fn_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
}
fn_steamlogin_dayz(){
if [ -f "${STEAM_LOGIN}" ]
then
source "${STEAM_LOGIN}"
else
echo "No cached Steam credentials. Please configure this now: "
fn_steam_login
fi
}
fn_runvalidate_dayz(){
# fn_loadconfig_dayz
${STEAMCMD} +force_install_dir ${SERVER_FILES} +login "${steamlogin}" +app_update "${appid}" validate +quit
}
fn_install_dayz(){
if [ ! -f "${SERVER_INSTALL_FILE}" ]; then
mkdir -p "${SERVER_FILES}"
mkdir -p "${SERVER_PROFILE}"
printf "[ ${yellow}DayZ${default} ] Downloading DayZ Server-Files!\n"
fn_steamlogin_dayz
fn_runvalidate_dayz
else
printf "[ ${lightblue}DayZ${default} ] The Server is already installed.\n"
fi
}
fn_runupdate_dayz(){
if ! diff -q "${SERVER_CFG_SRC}" "${SERVER_CFG_DST}"
then
echo "========================================================================="
diff -Nau --color "${SERVER_CFG_SRC}" "${SERVER_CFG_DST}" || echo ""
echo "========================================================================="
if fn_prompt_yn "The new server configuration file differs from what's installed. Use it?"
then
echo "Updating the server configuration file"
cp "${SERVER_CFG_SRC}" "${SERVER_CFG_DST}"
else
echo "NOT updating the server configuration file"
fi
fi
${STEAMCMD} +force_install_dir ${SERVER_FILES} +login "${steamlogin}" +app_update "${appid}" +quit
}
fn_update_dayz(){
fn_steamlogin_dayz
appmanifestfile=${SERVER_FILES}/steamapps/appmanifest_"${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 "${appid}" +app_info_print \
"${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}" ]; 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/${appid}/\n"
printf "\nApplying update"
# run update
fn_runupdate_dayz
fn_workshop_mods
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/${appid}/\n\n"
fi
}
fn_workshop_mods(){
fn_steamlogin_dayz
declare -a workshopID
workshopfolder="${SERVER_FILES}/steamapps/workshop/content/${dayz_id}"
workshoplist=""
if [ ! -f "${WORKSHOP_CFG}" ]
then
echo "No workshop mods..."
return
else
echo "Syncing workshop mods..."
fi
mapfile -t workshopID < "${WORKSHOP_CFG}"
# gather mods
for i in "${workshopID[@]}"
do
if [[ $i =~ ^[0-9] ]] && [ $(expr length $i) -gt 7 ]; then
workshoplist+=" +workshop_download_item "${dayz_id}" "$i""
fi
done
# download mods
${STEAMCMD} +force_install_dir ${SERVER_FILES} +login "${steamlogin}" ${workshoplist} +quit
# link mods
for i in "${workshopID[@]}"
do
if [[ $i =~ ^[0-9] ]] && [ $(expr length $i) -gt 7 ] && [ -d "${workshopfolder}/$i" ]; then
modname=$(cut -d '"' -f 2 <<< $(grep name ${workshopfolder}/$i/meta.cpp))
if [ ! -d "${SERVER_FILES}/@${modname}" ]; then
ln -s ${workshopfolder}/$i "${SERVER_FILES}/@${modname}" &> /dev/null
fi
find "${workshopfolder}/$i" -depth -exec rename -f 's/(.*)\/([^\/]*)/$1\/\L$2/' {} \;
fi
done
if ls ${SERVER_FILES}/@* 1> /dev/null 2>&1; then
printf "\n[ ${green}DayZ${default} ] Copy Key Files from Mods...\n"
cp -vu ${SERVER_FILES}/@*/keys/* "${SERVER_FILES}/keys/" > /dev/null 2>&1
fi
}
fn_rcon(){
exec /usr/local/py3rcon/py3rcon.py --gui ~/py3rcon.config.json
}
fn_status(){
INSTALLED="${NO}"
LOGGED_IN="${NO}"
RUNNING="${NO}"
MOD_INSTALLED="${yellow}0${default}"
MOD_LIST=""
# DayZ Server files installation
if [ -f "${SERVER_INSTALL_FILE}" ]
then
INSTALLED="${YES}"
fi
# Logged into Steam
if [ -f "${STEAM_LOGIN}" ]
then
LOGGED_IN="${YES}"
if grep -q anonymous "${STEAM_LOGIN}"
then
ANONYMOUS="(as anonymous)"
fi
fi
# Running or not
if pidof DayZServer > /dev/null
then
RUNNING="${YES}"
fi
# Number of mods plus the list denoting on or off
echo -e "
Status:
Logged in to Steam: ${LOGGED_IN} ${ANONYMOUS}
Server files installed: ${INSTALLED}
Mods installed: ${MOD_INSTALLED}${MOD_LIST}
Server running: ${RUNNING}
"
}
case "${1}" in
install)
fn_loadconfig_dayz
fn_install_dayz
;;
login)
fn_loadconfig_dayz
fn_steam_login
;;
rcon)
fn_rcon "${2}"
;;
start)
fn_loadconfig_dayz
fn_start_dayz
;;
status)
fn_status
;;
stop)
fn_stop_dayz
;;
update)
fn_loadconfig_dayz
fn_update_dayz
;;
workshop)
fn_loadconfig_dayz
fn_workshop_mods "${2}"
;;
**)
fn_usage "$*"
;;
esac