#!/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 # 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" # Put them all together dayzparameter=" -config=${config} -port=${port} -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" 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}" 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. Added items are become active by default. 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}" < ~/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_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 ${dayzparameter} ${mod_command_line} EXIT_CODE=$? printf "[ ${yellow}DayZ${default} ] Server exited. Exit code: ${EXIT_CODE}\n" exit ${EXIT_CODE} } fn_stop_dayz(){ echo "Stopping DayZ server..." kill -TERM $(pidof DayZServer) } 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_update_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_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 } fn_update_mods(){ fn_get_mods ${STEAMCMD} +force_install_dir ${SERVER_FILES} +login "${steamlogin}" ${workshoplist} +quit echo } fn_link_mod(){ MODNAME=$(cut -d '"' -f 2 <<< $(grep name ${workshopfolder}/${1}/meta.cpp)) if [ ! -L "${SERVER_FILES}/@${MODNAME}" ] then ln -s ${workshopfolder}/${1} "${SERVER_FILES}/@${MODNAME}" echo "Created symlink for mod id ${1}" else echo "Symlink for mod id ${1} already exists" fi # find "${workshopfolder}/${1}" -depth -print -exec rename -f 's/(.*)\/([^\/]*)/$1\/\L$2/' {} \; if ls ${SERVER_FILES}/@* 1> /dev/null 2>&1 then echo "Copying key files..." cp -vu "${workshopfolder}/*/Keys/*" "${SERVER_FILES}/keys/" 2> /dev/null # cp -vu "${SERVER_FILES}/@*/{K,k}eys/*" "${SERVER_FILES}/keys/" fi } fn_add_mod(){ 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)) else echo -e "${green}The mod directory ${workshopfolder}/${1} doesn't exist...${default}" fi if [ -L "${SERVER_FILES}/@${MODNAME}" ] then echo -e "${yellow}Warning: The mod symlink ${SERVER_FILES}/@${MODNAME} already exists!${default}" else echo -e "${green}The mod symlink ${SERVER_FILES}/@${MODNAME} doesn't exist...${default}" fi if grep -qP "\b${1}\b" "${WORKSHOP_CFG}" then echo "The mod with id ${1} is already installed" return fi echo "Adding mod id ${1}" echo "${1}:MODNAME:1" >> ${WORKSHOP_CFG} fn_update_mods fn_link_mod ${1} if [ -d "${workshopfolder}/${1}" ] then sed -i "${WORKSHOP_CFG}" -e "s/${1}:MODNAME/${1}:${MODNAME}/" echo "Mod id ${1} - ${MODNAME} - added" else echo "Installation failed! See above (You probably need to use a real Steam login)" head -n-1 "${WORKSHOP_CFG}" > /tmp/workshop.cfg.tmp mv /tmp/workshop.cfg.tmp "${WORKSHOP_CFG}" fi } fn_remove_mod(){ 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" } fn_activate_mod(){ fn_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 } fn_deactivate_mod(){ fn_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 } fn_list_mods(){ fn_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 } fn_mod_cmd(){ fn_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}'" } fn_workshop_mods(){ # Check usage if [[ ${1} = "" ]] then fn_usage fi if [[ ${1} != "list" ]] && [[ ${2} = "" ]] then fn_usage fi # Most mods require a valid Steam login. Not all will work with the anonymous user. fn_steamlogin_dayz # Options case "${1}" in activate) fn_activate_mod ${2} ;; add) fn_add_mod ${2} ;; deactivate) fn_deactivate_mod ${2} ;; list) fn_list_mods ;; remove) fn_remove_mod ${2} ;; update) echo "Updating mods..." fn_update_mods ;; **) fn_usage ;; esac } fn_rcon(){ exec /usr/local/py3rcon/py3rcon.py --gui ~/py3rcon.config.json } fn_status(){ INSTALLED="${NO}" LOGGED_IN="${NO}" RUNNING="${NO}" fn_get_mods MOD_INSTALLED="${green}${#workshopID[@]}${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)" else ANONYMOUS="(not anonymous)" fi fi # Running or not if pidof DayZServer > /dev/null then RUNNING="${YES}" fi fn_mod_cmd # 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} Server command line:${dayzparameter} Mod command line: ${mod_command_line} " } case "${1}" in cmd) fn_mod_cmd ;; 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} ${3} ${4} ;; **) fn_usage "$*" ;; esac