#!/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 tail {} \; -exec rm -f {} \; echo echo -e "${yellow}========================================== script*.log ========================================" find "${HOME}" -name "script*.log" -exec head {} \; -exec tail {} \; -exec rm -f {} \; echo echo -e "${yellow}========================================== *.RPT ==============================================" find "${HOME}" -name "*.RPT" -exec ls -la {} \; -exec tail {} \; -exec rm -f {} \; echo echo -e "${yellow}========================================== End crash log ======================================" } # 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 # Base directories CFG_SRC_FILES="/files" SERVER_FILES="${HOME}/serverfiles" SERVER_PROFILE="${HOME}/profiles" # Command line argument variables # Server config file config=serverDZ.cfg # Server port port=2302 rcon_port=2303 # Server profile argument profile="-profiles=${SERVER_PROFILE}" # To log or not to log. # All possible logging on: #logs="-dologs -adminlog -netlog" # No logs? This should read "-somelogs" logs="-nologs" # Put them all together parameters=" -config=${config} -port=${port} -freezecheck -fps=60 -BEpath=${SERVER_FILES}/battleye ${profile} ${logs}" # 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" if [ ! -f "${WORKSHOP_CFG}" ] then touch "${WORKSHOP_CFG}" fi declare -a workshopID workshoplist="" workshopfolder="${SERVER_FILES}/steamapps/workshop/content/${dayz_id}" mod_command_line="-mod=" # Other stuff YES="${green}yes${default}" NO="${red}no${default}" usage(){ echo -e " ${red}Bad option or arguments! ${yellow}${*}${default} Usage: ${green}$(basename $0)${yellow} option [ arg1 [ arg2 ] ] Options and arguments: activate id [id...] - Activate one or many installed DayZ Workshop items by id add id [id...] - Add one or many DayZ Workshop items by id. Added items become active by default config - Update the internal serverDZ.cfg file from files/serverDZ.cfg on the host. Presents a unified diff if the internal file doesn't match the host file. deactivate id [id...] - Deactivate one or many installed DayZ Workshop items by id - Keeps the mod files but excludes from -mod= install - Install the DayZ server files list - List Workshop items and if they are active or not login - Login to Steam rcon - Connect to the server using a python RCON client remove id [id...] - Remove all vestiges of one or many Workshop items by id. 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 server files ${default}" exit 1 } prompt_yn(){ echo -n "${1} (y|N) " >&2 read -s -n 1 a a=$(echo ${a} | tr A-Z a-z) echo if [[ "${a}" = "y" ]] then return 0 else return 1 fi } loadconfig(){ 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}" < ~/py3rcon.config.json } start(){ # 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 mod_cmd 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 "${mod_command_line}" ${parameters} EXIT_CODE=$? if [ -f ${SERVER_FILES}/restart ] then rm -f ${SERVER_FILES}/restart EXIT_CODE=42 fi printf "[ ${yellow}DayZ${default} ] Server exited. Exit code: ${EXIT_CODE}\n" exit ${EXIT_CODE} } restart(){ touch "${SERVER_FILES}/restart" echo "Restarting DayZ server..." kill -TERM $(pidof DayZServer) } stop(){ echo "Stopping DayZ server..." kill -TERM $(pidof DayZServer) } login(){ loadconfig 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 } dologin(){ loadconfig if [ -f "${STEAM_LOGIN}" ] then source "${STEAM_LOGIN}" else echo "No cached Steam credentials. Please configure this now: " login fi } install(){ loadconfig if [ ! -f "${SERVER_INSTALL_FILE}" ] || [[ ${1} = "force" ]] then mkdir -p "${SERVER_FILES}" mkdir -p "${SERVER_PROFILE}" printf "[ ${yellow}DayZ${default} ] Downloading DayZ Server-Files!\n" dologin ${STEAMCMD} +force_install_dir ${SERVER_FILES} +login "${steamlogin}" +app_update "${appid}" validate +quit else printf "[ ${lightblue}DayZ${default} ] The Server is already installed.\n" fi } config(){ if ! diff -q "${SERVER_CFG_SRC}" "${SERVER_CFG_DST}" then echo "=========================================================================" diff -Nau --color "${SERVER_CFG_SRC}" "${SERVER_CFG_DST}" || echo "" echo "=========================================================================" if 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 } update(){ dologin 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 dologin ${STEAMCMD} +force_install_dir ${SERVER_FILES} +login "${steamlogin}" +app_update "${appid}" +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/${appid}/\n\n" fi } get_mods(){ mapfile -t workshopID < "${WORKSHOP_CFG}" for i in "${workshopID[@]}" do if [[ $i =~ ^[0-9] ]] && [ $(expr length $i) -gt 7 ] then workshoplist+=" +workshop_download_item "${dayz_id}" "$i"" fi done } modupdate(){ get_mods echo "Updating mods..." dologin ${STEAMCMD} +force_install_dir ${SERVER_FILES} +login "${steamlogin}" ${workshoplist} +quit echo } add(){ if [ -d "${workshopfolder}/${1}" ] then echo -e "${yellow}Warning: The mod directory ${workshopfolder}/${1} already exists!${default}" MODNAME=$(cut -d '"' -f 2 <<< $(grep name ${workshopfolder}/${1}/meta.cpp)) fi if [ -L "${SERVER_FILES}/@${MODNAME}" ] then echo -e "${yellow}Warning: The mod symlink ${SERVER_FILES}/@${MODNAME} already exists!${default}" fi if grep -qP "\b${1}\b" "${WORKSHOP_CFG}" then echo "The mod with id ${1} is already in the workshop configuration." return fi echo "Adding mod id ${1}" echo "${1}:MODNAME:1" >> ${WORKSHOP_CFG} modupdate # Make sure the install succeeded if [ ! -d "${workshopfolder}/${1}" ] then echo -e "${red}Mod installation failed: The mod directory ${workshopfolder}/${1} was not created!${default}" echo "Installation failed! See above (You probably need to use a real Steam login)" # The mod is added temporarily into the workshop config. Since the installation failed, reemove it instead of updating it. head -n-1 "${WORKSHOP_CFG}" > /tmp/workshop.cfg.tmp mv /tmp/workshop.cfg.tmp "${WORKSHOP_CFG}" return fi # Get the name of the newly added mod MODNAME=$(cut -d '"' -f 2 <<< $(grep name ${workshopfolder}/${1}/meta.cpp)) # Symlink it if [ ! -L "${SERVER_FILES}/@${MODNAME}" ] then ln -s ${workshopfolder}/${1} "${SERVER_FILES}/@${MODNAME}" echo "Created symlink ${workshopfolder}/${1} ${SERVER_FILES}/@${MODNAME}" fi # Lower case all the files in mod directories. find "${workshopfolder}/${1}" -depth -exec rename -f 's/(.*)\/([^\/]*)/$1\/\L$2/' {} \; # Copy the key files echo "Copying key files..." cp -v ${workshopfolder}/${1}/keys/* "${SERVER_FILES}/keys/" # Set the mod name in the workshop config file, as we don't know this at the start. sed -i "${WORKSHOP_CFG}" -e "s/${1}:MODNAME/${1}:${MODNAME}/" echo -e "Mod id ${1} - ${green}${MODNAME}${default} - added" } remove(){ if [ -d "${workshopfolder}/${1}" ] then MODNAME=$(cut -d '"' -f 2 <<< $(grep name ${workshopfolder}/${1}/meta.cpp)) echo "Removing directory ${workshopfolder}/${1}" rm -rf "${workshopfolder}/${1}" fi if [ -L "${SERVER_FILES}/@${MODNAME}" ] then echo "Removing symlink ${SERVER_FILES}/@${MODNAME}" rm -f "${SERVER_FILES}/@${MODNAME}" fi if grep -q ${1} "${WORKSHOP_CFG}" then echo "Removing workshop file entry" sed -i "${WORKSHOP_CFG}" -e "/${1}:/d" fi echo "Mod id ${1} - ${MODNAME} - removed" } activate(){ get_mods for i in "${workshopID[@]}" do ID=$(echo ${i} | cut -d: -f1) NAME=$(echo ${i} | cut -d: -f2) ACTIVE=$(echo ${i} | cut -d: -f3) if [[ ${ID} = ${1} ]] then if [[ ${ACTIVE} = "0" ]] then sed -i "${WORKSHOP_CFG}" -e 's/'${1}':\(.*\):0/'${1}':\1:1/' echo "Activcated mod id ${1}" else echo "Mod id ${1} was already active" fi fi done } deactivate(){ get_mods for i in "${workshopID[@]}" do ID=$(echo ${i} | cut -d: -f1) NAME=$(echo ${i} | cut -d: -f2) ACTIVE=$(echo ${i} | cut -d: -f3) if [[ ${ID} = ${1} ]] then if [[ ${ACTIVE} = "1" ]] then sed -i "${WORKSHOP_CFG}" -e 's/'${1}':\(.*\):1/'${1}':\1:0/' echo "Dectivcated mod id ${1}" else echo "Mod id ${1} was already inactive" fi fi done } list(){ get_mods spaces=" " echo -e "ID\t\tName\t\t\tActive\tURL" for i in "${workshopID[@]}" do ID=$(echo ${i} | cut -d: -f1) NAME=$(echo ${i} | cut -d: -f2) ACTIVE=$(echo ${i} | cut -d: -f3) printf "%s\t%.23s%s\t%s\thttps://steamcommunity.com/sharedfiles/filedetails/?id=%s\n" ${ID} "${NAME}" "${spaces:${#NAME}+1}" ${ACTIVE} ${ID} done } mod_cmd(){ get_mods for i in "${workshopID[@]}" do ID=$(echo ${i} | cut -d: -f1) NAME=$(echo ${i} | cut -d: -f2) ACTIVE=$(echo ${i} | cut -d: -f3) if [[ ${ACTIVE} = "1" ]] then modname=$(cut -d '"' -f 2 <<< $(grep name ${workshopfolder}/${ID}/meta.cpp)) mod_command_line="${mod_command_line}@${modname};" fi done mod_command_line="${mod_command_line::-1}" } rcon(){ exec /usr/local/py3rcon/py3rcon.py --gui ~/py3rcon.config.json } status(){ INSTALLED="${NO}" LOGGED_IN="${NO}" RUNNING="${NO}" get_mods MODS_INSTALLED="${green}${#workshopID[@]}${default}" # 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="${yellow}(as anonymous)${default}" else ANONYMOUS="${green}(not anonymous)${default}" fi fi # Running or not if pidof DayZServer > /dev/null then RUNNING="${YES}" fi mod_cmd # Uptime UPTIME=$(date -d@$(($(date +%s) - $(date +%s -r ${SERVER_PROFILE}/server_console.log))) -u +%H:%M:%S) # Number of mods plus the list denoting on or off echo -e " Status: Uptime: ${green}${UPTIME}${default} Logged in to Steam: ${LOGGED_IN} ${ANONYMOUS} Server files installed: ${INSTALLED} Mods installed: ${MODS_INSTALLED} Server running: ${RUNNING} Default parameters:${parameters} Mod parameter: ${mod_command_line} " } C=${1} shift case "${C}" in activate) activate "${@}" ;; add) add "${@}" ;; cmd) mod_cmd "${@}" ;; config) config "${@}" ;; deactivate) deactivate "${@}" ;; install) install "${@}" ;; list) list "${@}" ;; login) login "${@}" ;; modupdate) modupdate "${@}" ;; rcon) rcon "${@}" ;; remove) remove "${@}" ;; restart) restart "${@}" ;; start) start "${@}" ;; status) status "${@}" ;; stop) stop "${@}" ;; update) update "${@}" ;; **) usage "$*" ;; esac