The server doesn't like read only directories where it finds its files. Removing read-only flags from mounts for now.

Rework directories so there are fewer volumes within volumes.
Keep splitting up the code between the two scripts, removing unused variables, etc..
Add XML file merging integration. WIP.
Fix lack of comma in cfg file that _might_ have been causing issues...
This commit is contained in:
Daniel Ceregatti 2023-05-18 17:29:27 -07:00
parent 64f162001d
commit 2fc31fea37
9 changed files with 211 additions and 190 deletions

View file

@ -22,7 +22,7 @@ services:
- homedir_main:/home/user
- serverfiles:/serverfiles
- mods:/serverfiles/steamapps/workshop/content
- mpmissions:/serverfiles/mpmissions
- mods:/mods
- ./files:/files
- ./web/bin/dz:/usr/local/bin/dz
- ./web:/web
@ -34,9 +34,9 @@ services:
build: server
volumes:
- homedir_server:/home/user
- serverfiles:/serverfiles:ro
- mods:/serverfiles/steamapps/workshop/content:ro
- mpmissions:/serverfiles/mpmissions:ro
- serverfiles:/serverfiles
- mods:/mods
- mpmissions:/serverfiles/mpmissions
- profiles:/profiles
- ./files:/files
- ./server/bin/dz:/usr/local/bin/dz

View file

@ -81,9 +81,9 @@ check_mod_install(){
fi
}
get_mod_id_by_index(){
get_mod_id_by_index2(){
# If we were passed a valid mod id, just return it
if [[ -d "${WORKSHOP_DIR}/${1}" ]]
if [ -d "${WORKSHOP_DIR}/${1}" ]
then
echo -n ${1}
return
@ -104,7 +104,7 @@ get_mod_id_by_index(){
# Get mod name by ID or index
get_mod_name(){
ID=$(get_mod_id_by_index ${1})
ID=$(get_mod_id_by_index2 ${1})
if ! [ -d "${WORKSHOP_DIR}/${ID}" ]
then
echo "Mod ID ${1} doesn't exist" >&2
@ -116,19 +116,19 @@ get_mod_name(){
# List mods
list(){
workshoplist=""
X=1
C="${green}"
spaces=" "
echo -e "\n ID Name URL Size"
echo "Installed mods:"
echo -e " ID Name URL Size"
echo "------------------------------------------------------------------------------------------------------------------------"
for dir in $(ls -tr ${WORKSHOP_DIR})
do
ID=${dir}
NAME=$(grep name "${WORKSHOP_DIR}/${dir}/meta.cpp" | cut -d '"' -f2 | sed -r 's/\s+//g')
SIZE=$(du -sh "${WORKSHOP_DIR}/${dir}" | awk '{print $1}')
workshoplist+=" +workshop_download_item "${release_client_appid}" "${ID}
printf "${C}%.3d %s %.23s %s https://steamcommunity.com/sharedfiles/filedetails/?id=%s %s${default}\n" ${X} ${ID} "${NAME}" "${spaces:${#NAME}+1}" ${ID} ${SIZE}
X=$((X+1))
done
echo
}

View file

@ -1,8 +1,9 @@
#!/usr/bin/env bash
CFGSPAWNABLETYPES=https://raw.githubusercontent.com/RedFalconKen/RedFalconFlightSystem-Heliz/main/Config%20Files/Event%20Spawn%20Config/RFFSHelis_cfgspawnabletypes.xml
CFGEVENTSPAWNS=https://raw.githubusercontent.com/RedFalconKen/RedFalconFlightSystem-Heliz/main/Config%20Files/Event%20Spawn%20Config/Chernarus/RFFSHelis_cfgeventspawns.xml
CFGSPAWNABLETYPES=https://raw.githubusercontent.com/RedFalconKen/RedFalconFlightSystem-Heliz/main/Config%20Files/Event%20Spawn%20Config/RFFSHelis_cfgspawnabletypes.xml
#CFGEVENTSPAWNS=https://raw.githubusercontent.com/RedFalconKen/RedFalconFlightSystem-Heliz/main/Config%20Files/Event%20Spawn%20Config/Banov/RFFSHelis_cfgeventspawns.xml
CFGEVENTSPAWNS=https://raw.githubusercontent.com/RedFalconKen/RedFalconFlightSystem-Heliz/main/Config%20Files/Event%20Spawn%20Config/Chernarus/RFFSHelis_cfgeventspawns.xml
#CFGEVENTSPAWNS=https://raw.githubusercontent.com/RedFalconKen/RedFalconFlightSystem-Heliz/main/Config%20Files/Event%20Spawn%20Config/DeerIsle/RFFSHelis_cfgeventspawns.xml
#CFGEVENTSPAWNS=https://raw.githubusercontent.com/RedFalconKen/RedFalconFlightSystem-Heliz/main/Config%20Files/Event%20Spawn%20Config/Namalsk/RFFSHelis_cfgeventspawns.xml
EVENTS=https://raw.githubusercontent.com/RedFalconKen/RedFalconFlightSystem-Heliz/main/Config%20Files/Event%20Spawn%20Config/RFFSHelis_events.xml
EVENTS=https://raw.githubusercontent.com/RedFalconKen/RedFalconFlightSystem-Heliz/main/Config%20Files/Event%20Spawn%20Config/RFFSHelis_events.xml
TYPES=https://raw.githubusercontent.com/RedFalconKen/RedFalconFlightSystem-Heliz/main/Config%20Files/Types.XML/RFFSHelis_Types.xml

View file

@ -1,68 +0,0 @@
#!/usr/bin/env bash
# A generic script to manage a mod's types.xml against all installed missions
set -eE
ID=${1}
MODE=${2}
TYPES_FILE="${WORKSHOP_DIR}/${ID}/extras/types.xml"
if [[ ${3} != "" ]]
then
TYPES_FILE="${3}"
fi
for file in $(find ${SERVER_FILES}/mpmissions -name types.xml -print -prune)
do
if [[ ${MODE} = "uninstall" ]]
then
# Remove the lines that were added by the mod's extras/types.xml from
# every db/types.xml in all mission directories
# Chop the top tag from the source file
tail -n+2 ${TYPES_FILE} > /tmp/types-tmp.xml
# Chop the bottom tag from the source file
head -n-1 /tmp/types-tmp.xml > /tmp/types-src.xml
# Remove that content from the original file
grep -qvxFf /tmp/types-src.xml ${file}
else
# Add the contents of extras/types.xml to every db/types.xml in all
# mission directories
xmllint --noout ${TYPES_FILE} 2> /dev/null && {
echo -e "${green}${TYPES_FILE} passes XML lint test!"
echo -e "Merging to $file...${default}"
# Chop the bottom tag from the destination file
head -n-1 ${file} > /tmp/types-dst.xml
# Chop the top 2 tags, xml and types, from the source file
tail -n+2 ${TYPES_FILE} > /tmp/types-src.xml
# Concatenate the two files back into the source file
cat /tmp/types-dst.xml /tmp/types-src.xml > /tmp/types.xml
xmllint --noout /tmp/types.xml 2> /dev/null && {
cp -v /tmp/types.xml ${file}
} || {
# Try again, but chop the top 3 tags, hopefully xml and types, from the source file...
echo "First merge attempt failed, trying again..."
tail -n+3 ${TYPES_FILE} > /tmp/types-src.xml
# Concatenate the two files back into the source file
cat /tmp/types-dst.xml /tmp/types-src.xml > /tmp/types.xml
# And lint again. This should probably be a recursive function...
xmllint --noout /tmp/types.xml && {
cp -v /tmp/types.xml ${file}
} || {
echo "XML lint check after merge failed! No files changed!"
}
}
} || {
echo -e "${red}${TYPES_FILE} fails XML lint test!"
echo -e "This will have to be merged by hand!${default}"
}
fi
done

25
files/mods/xml.sh Executable file
View file

@ -0,0 +1,25 @@
#!/usr/bin/env bash
# A generic script to manage merging mod XML files to mpmissions XML files
set -eE
ID=${1}
source ${FILES}/mods/${ID}/xml.env
# Iterate over the file names we can handle
for var in CFGEVENTSPAWNS CFGSPAWNABLETYPES EVENTS TYPES
do
if echo ${!var} | grep -q http
then
OUT="${WORKSHOP_DIR}/${ID}/${var,,}.xml"
echo "${var} is a URL, downloading to ${OUT}"
curl -so ${OUT} ${!var}
xmllint --noout ${OUT} 2> /dev/null || {
echo -e "${red}${var,,}.xml does not pass XML lint test!${default}"
} && {
echo -e "${green}${var,,}.xml passes XML lint test!${default}"
}
fi
done

View file

@ -85,8 +85,8 @@ class Missions
{
template="dayzOffline.chernarusplus"; // Chernarus
// template="dayzOffline.enoch"; // Livonia
// template="empty.banov" // Banov
// template="empty.deerisle" // Deer Isle
// template="serverMission.Pripyat" // Pripyat
// template="empty.banov"; // Banov
// template="empty.deerisle"; // Deer Isle
// template="serverMission.Pripyat"; // Pripyat
};
};

View file

@ -23,6 +23,7 @@ RUN apt-get update && apt-get -y upgrade && apt-get -y install --no-install-reco
nano \
procps \
python3-pip \
strace \
wget
RUN update-alternatives --install /usr/bin/python python /usr/bin/python3.9 1
@ -43,8 +44,8 @@ RUN cd /usr/local && git clone https://github.com/indepth666/py3rcon.git
# Setup a non-privileged user
RUN groupadd user && \
useradd -l -g user user && \
mkdir -p /home/user /serverfiles/mpmissions /serverfiles/steamapps/workshop/content /profiles /mods && \
chown -R user:user /home/user /serverfiles /profiles /mods
mkdir -p /home/user /serverfiles/mpmissions /mods /mpmissions /profiles && \
chown -R user:user /home/user /serverfiles /mods /mpmissions /profiles
# Use our non-privileged user
USER user

View file

@ -18,18 +18,10 @@ parameters="-config=${SERVER_CFG_DST} -port=${port} -freezecheck -BEpath=${SERVE
# Used to check if dayZ is installed
SERVER_INSTALL_FILE="${SERVER_FILES}/DayZServer"
# Workshop. This file will store metadata about what mods are in use in this server instance
WORKSHOP_CFG="${SERVER_PROFILE}/workshop.cfg"
if [ ! -f "${WORKSHOP_CFG}" ]
then
touch "${WORKSHOP_CFG}"
fi
INSTALLED_MODS="${SERVER_FILES}/workshop.cfg"
# An array to store Workshop items. Each element contains the mod's ID, name, and state (active or not).
declare -a workshopID
WORKSHOP_DIR="${SERVER_FILES}/steamapps/workshop/content/${release_client_appid}"
#WORKSHOP_DIR="${SERVER_FILES}/steamapps/workshop/content/${release_client_appid}"
WORKSHOP_DIR="/mods/${release_client_appid}"
mod_command_line=""
# Backups
BACKUP_DIR="${HOME}/backup"
@ -56,7 +48,7 @@ Options and arguments:
f|force - Forcibly kill the server. Use only as a last resort if the server won't shut down
l|list - List Workshop items and their details
n|rcon - Connect to the server using a python RCON client
restart - Restart the server without restarting the container
r|restart - Restart the server without restarting the container
s|status - Shows the server's status: Running, uptime, mods, parameters, mod parameter, etc.
stop - Stop the server
${default}"
@ -119,17 +111,19 @@ EOF
> ~/py3rcon.config.json
}
# Manage the mod symlink
symlink(){
W=${1}
ID=${2}
NAME=${3}
if [ ! -L "${SERVER_FILES}/@${NAME}" ] && [[ ${W} = 1 ]]
get_mods(){
workshoplist=""
mod_command_line=""
for link in $(ls -tdr ${SERVER_PROFILE}/@* 2> /dev/null)
do
ID=$(readlink ${link} | awk -F/ '{print $NF}')
MODNAME=$(get_mod_name ${ID})
workshoplist+=" +workshop_download_item "${release_client_appid}" "${ID}
mod_command_line+="@${MODNAME};"
done
if [[ ${mod_command_line} != "" ]]
then
ln -sv ${WORKSHOP_DIR}/${ID} "${SERVER_FILES}/@${NAME}"
elif [[ "${W}" = "0" ]]
then
rm -vf "${SERVER_FILES}/@${NAME}"
mod_command_line="-mod=${mod_command_line::-1}"
fi
}
@ -155,7 +149,7 @@ start(){
trap '
report
' EXIT
mod_cmd
get_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.
@ -164,7 +158,7 @@ start(){
# actual status with those
echo ${mod_command_line} > /tmp/mod_command_line
echo ${parameters} > /tmp/parameters
./DayZServer ${mod_command_line} ${parameters}
./DayZServer "${mod_command_line}" ${parameters}
EXIT_CODE=$?
if [ -f ${SERVER_FILES}/restart ]
then
@ -213,42 +207,15 @@ config(){
fi
}
# Assemble the workshop variables
get_installed_mods(){
mapfile -t installedModsID < "${INSTALLED_MODS}"
workshoplist=""
for i in "${installedModsID[@]}"
do
ID=$(echo ${i} | cut -d: -f1)
workshoplist+=" +workshop_download_item "${release_client_appid}" "${ID}
done
}
get_mods(){
mapfile -t workshopID < "${WORKSHOP_CFG}"
workshoplist=""
for i in "${workshopID[@]}"
do
ID=$(echo ${i} | cut -d: -f1)
workshoplist+=" +workshop_download_item "${release_client_appid}" "${ID}
done
}
get_mod_id_by_index(){
# If we were passed a valid mod id, just return it
if [[ -d "${workshopdir}/${1}" ]]
then
echo ${1}
return
fi
X=1
# Loop over mod list
for i in "${workshopID[@]}"
# Loop over mods
for link in $(ls -tdr ${SERVER_PROFILE}/@* 2> /dev/null)
do
ID=$(echo ${i} | cut -d: -f1)
ID=$(readlink ${link} | awk -F/ '{print $NF}')
if [[ ${X} = ${1} ]]
then
echo ${ID}
echo -n ${ID}
return
fi
X=$((X+1))
@ -258,19 +225,13 @@ get_mod_id_by_index(){
# Get mod name by ID or index
get_mod_name(){
# Check for an ID
if [ -d "${WORKSHOP_DIR}/${1}" ]
then
ID=${1}
else
ID=$(get_mod_id_by_index ${1})
fi
if ! [ -d "${WORKSHOP_DIR}/${ID}" ]
then
echo "Mod ID ${1} doesn't exist" >&2
exit 1
fi
NAME=$(grep name ${WORKSHOP_DIR}/${ID}/meta.cpp | cut -d '"' -f2 | sed -r 's/\s+//g')
echo ${NAME}
echo -n ${NAME}
}
# Activate / Deactivate a mod
@ -285,35 +246,20 @@ activate(){
UU="un"
COLOR="${red}"
fi
ID=$(get_mod_id_by_index ${1})
MODNAME=$(get_mod_name ${1})
ID=$(get_mod_id_by_index2 ${1})
MODNAME=$(get_mod_name ${ID})
# Toggle state or report nothing burger
if [[ "${ACTIVE}" != "${W}" ]]
pushd "${SERVER_PROFILE}" > /dev/null
if [ -L "${SERVER_PROFILE}/@${MODNAME}" ]
then
sed -i "${WORKSHOP_CFG}" -e "s/${ID}:${MODNAME}:[0-1]/${ID}:${MODNAME}:${W}/"
echo -e "Mod id ${ID} - ${COLOR}${MODNAME}${default} ${WW}activated"
rm -vf "${SERVER_PROFILE}/@${MODNAME}"
else
echo -e "Mod id ${ID} - ${COLOR}${MODNAME}${default} - is already ${WW}active"
fi
list
}
# Assemble the mod command line
mod_cmd(){
mod_command_line=""
for i in "${workshopID[@]}"
do
NAME=$(echo ${i} | cut -d: -f2)
ACTIVE=$(echo ${i} | cut -d: -f3)
if [[ ${ACTIVE} = "1" ]]
then
mod_command_line="${mod_command_line}@${NAME};"
fi
done
if [[ ${mod_command_line} != "" ]]
then
mod_command_line='-mod='${mod_command_line::-1}
ln -s "${WORKSHOP_DIR}/${ID}" "${SERVER_PROFILE}/@${MODNAME}"
# echo -e "Mod id ${ID} - ${COLOR}${MODNAME}${default} - is already ${WW}active"
fi
echo -e "Mod id ${ID} - ${COLOR}${MODNAME}${default} ${WW}activated"
popd > /dev/null
status
}
checkXML(){
@ -359,6 +305,29 @@ rcon(){
exec /usr/local/py3rcon/py3rcon.py --gui ~/py3rcon.config.json
}
# List mods
activelist(){
X=1
C="${green}"
spaces=" "
have=no
for link in $(ls -tdr ${SERVER_PROFILE}/@* 2> /dev/null)
do
if [[ ${have} = "no" ]]
then
have="yes"
echo -e "\n ID Name URL Size"
echo "------------------------------------------------------------------------------------------------------------------------"
fi
ID=$(readlink ${link} | awk -F/ '{print $NF}')
MODNAME=$(get_mod_name ${ID})
SIZE=$(du -sh "${WORKSHOP_DIR}/${ID}" | awk '{print $1}')
printf "${C}%.3d %s %.23s %s https://steamcommunity.com/sharedfiles/filedetails/?id=%s %s${default}\n" ${X} ${ID} "${MODNAME}" "${spaces:${#MODNAME}+1}" ${ID} ${SIZE}
X=$((X+1))
done
echo
}
# Display the status of everything
status(){
loadconfig
@ -385,8 +354,6 @@ status(){
RUNNING="${RUNNING}\nRunning Parameters: $(cat /tmp/parameters)\nRunning mod parameter: $(cat /tmp/mod_command_line)"
fi
mod_cmd
MAP="none"
# Map name
if [[ -f ${SERVER_CFG_DST} ]]
@ -402,9 +369,10 @@ Server files installed: ${INSTALLED}"
echo
exit 0
fi
get_mods
echo -ne "
Mods: "
MODS=$(list)
Active mods: "
activelist
if [[ ${MODS} == "" ]]
then
echo -n "none"
@ -418,6 +386,7 @@ Working mod parameter: ${mod_command_line}"
MAP=$(grep template ${SERVER_CFG_DST} | grep -v "^//" | cut -d= -f2 | cut -d\; -f1)
echo "Map: ${MAP}"
fi
echo
}
backup(){
@ -438,8 +407,6 @@ shift || {
usage
}
get_mods
case "${C}" in
a|activate)
activate 1 "${@}"
@ -477,7 +444,7 @@ case "${C}" in
r|remove)
remove "${@}"
;;
restart)
r|restart)
restart "${@}"
;;
start)

View file

@ -3,7 +3,7 @@
source /files/dz-common
# An array to store Workshop items. Each element contains the mod's ID, name, and state (active or not).
WORKSHOP_DIR="${SERVER_FILES}/steamapps/workshop/content/${release_client_appid}"
WORKSHOP_DIR="/mods/${release_client_appid}"
workshoplist=""
@ -18,18 +18,96 @@ Usage: ${green}$(basename $0)${yellow} option [ arg1 [ arg2 ] ]
Options and arguments:
add id - Add a DayZ Workshop item by id. Added items become active by default
a|add id - Add a DayZ Workshop item by id. Added items become active by default
i|install - Install the DayZ server files
l|list - List Workshop items and their details
g|login - Login to Steam.
m|modupdate - Update the mod files
r|remove id - Remove all files and directories of a Workshop item by id
s|status - Shows the server's status: Running, uptime, mods, parameters, mod parameter, etc.
s|status - Shows Steam login status, if base files are installed, installed mods
u|update - Update the server files
${default}"
exit 1
}
# "Manage" XML files.
xml(){
/files/mods/xml.sh ${1}
}
# Copy mod keys
copy_keys(){
if [[ ${1} = 1 ]]
then
echo "Copying key files..."
find ${WORKSHOP_DIR}/${2} -name "*.bikey" -exec cp -v {} "${SERVER_FILES}/keys/" \;
fi
}
# 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
}
# 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}"
# Lower case all the files in mod directories.
find "${WORKSHOP_DIR}/${1}" -depth -exec rename -f 's/(.*)\/([^\/]*)/$1\/\L$2/' {} \;
# Copy the key files
copy_keys 1 ${1}
echo -e "Mod id ${1} - ${green}${MODNAME}${default} - added"
# checkTypesXML ${1} install
# checkInstall ${1} install
}
# Remove a mod
remove(){
# checkTypesXML ${1} uninstall
# checkInstall ${1} uninstall
if [ -d "${WORKSHOP_DIR}/${1}" ]
then
MODNAME=$(get_mod_name ${1})
echo "Removing directory ${WORKSHOP_DIR}/${1}"
rm -rf "${WORKSHOP_DIR}/${1}"
fi
if [ -L "${SERVER_FILES}/@${MODNAME}" ]
then
echo "Removing symlink ${SERVER_FILES}/@${MODNAME}"
rm -f "${SERVER_FILES}/@${MODNAME}"
fi
echo -e "Mod id ${1} - ${red}${MODNAME}${default} - removed"
}
# Handle the Steam login information.
login(){
if [ -f "${STEAM_LOGIN}" ]
@ -125,11 +203,28 @@ update(){
fi
}
get_mods(){
workshoplist=""
mod_command_line=""
for link in $(ls -tdr ${SERVER_FILES}/@* 2> /dev/null)
do
ID=$(readlink ${link} | cut -d/ -f7)
MODNAME=$(get_mod_name ${ID})
workshoplist+=" +workshop_download_item "${release_client_appid}" "${ID}
mod_command_line+="@${MODNAME};"
done
if [[ ${mod_command_line} != "" ]]
then
mod_command_line='-mod='${mod_command_line::-1}
fi
}
# Update mods
modupdate(){
echo "Updating mods..."
dologin
# echo ${STEAMCMD} +force_install_dir ${SERVER_FILES} +login "${steamlogin}" ${workshoplist} +quit
get_mods
${STEAMCMD} +force_install_dir ${SERVER_FILES} +login "${steamlogin}" ${workshoplist} +quit
# Updated files come in with mixed cases. Fix that.
echo -ne "\nFixing file names..."
@ -187,16 +282,13 @@ shift || {
}
case "${C}" in
add)
a|add)
add "${@}"
;;
i|install)
install "${@}"
;;
l|list)
list "${@}"
;;
login)
g|login)
login "${@}"
;;
m|modupdate)
@ -211,6 +303,9 @@ case "${C}" in
u|update)
update "${@}"
;;
x|xml)
xml "${@}"
;;
*)
usage "$*"
;;