Start turning this project into a provisioning system: The main image will manage the base server files and mod files, then run servers as separate containers with these volumes mounted read-only, so they can be shared across server instances.

Add command line xml merge tool for when that time comes.
Add Red Falcon Heliz mod as the work-in-progress for getting a turnkey system that merges many different XML files that a full server mod installation will require.
Fix finding a mod by index and use that for all mod operations.
Start re-working how mods are added/removed/activated/deactivated.
Add a template system for handling mod XML files.
Add lots of comments.
This commit is contained in:
Daniel Ceregatti 2023-05-10 19:35:36 -07:00
parent 291d869ed1
commit 169018665f
16 changed files with 194 additions and 96 deletions

1
.gitignore vendored
View file

@ -1 +1,2 @@
.idea .idea
*.iml

View file

@ -17,6 +17,7 @@ RUN apt-get update && apt-get -y upgrade && apt-get -y install --no-install-reco
ca-certificates \ ca-certificates \
gdb \ gdb \
git \ git \
gwenhywfar-tools \
jq \ jq \
lib32gcc-s1 \ lib32gcc-s1 \
lib32stdc++6 \ lib32stdc++6 \
@ -52,9 +53,8 @@ ENV PATH /usr/games:/files:${PATH}
# Setup a non-privileged user # Setup a non-privileged user
RUN groupadd user && \ RUN groupadd user && \
useradd -l -g user user && \ useradd -l -g user user && \
mkdir /home/user && \ mkdir -p /home/user /serverfiles/mpmissions /serverfiles/steamapps/workshop/content /profiles /mods && \
chown user:user /home/user chown -R user:user /home/user /serverfiles /profiles /mods
# Use our non-privileged user # Use our non-privileged user
USER user USER user
@ -62,4 +62,4 @@ USER user
WORKDIR /home/user WORKDIR /home/user
# Run the server. # Run the server.
CMD ["dayzserver", "start"] CMD ["server.sh"]

View file

@ -1,11 +1,16 @@
version: "3.3" version: "3.3"
# This is where the server files, profile, mods, and logs will reside.
# The server script does its best to clean up the copious logs that the
# server creates, but with mods, this volume will grow rather large.
# It's best to keep an eye on its size.
volumes: volumes:
# For steamcmd files and resource files used by the scripts
homedir: homedir:
# Where the server files will be installed
serverfiles:
# Server profile files
profiles:
# Server maps
mpmissions:
# Mods
mods:
services: services:
@ -13,6 +18,10 @@ services:
build: . build: .
volumes: volumes:
- homedir:/home/user - homedir:/home/user
- serverfiles:/serverfiles
- mods:/serverfiles/steamapps/workshop/content
- mpmissions:/serverfiles/mpmissions
- profiles:/profiles
- ./files:/files - ./files:/files
# To have the server show up in the LAN tab of the DayZ launcher, # To have the server show up in the LAN tab of the DayZ launcher,
# it must run under host mode. # it must run under host mode.
@ -21,9 +30,11 @@ services:
# the server to show up on the LAN, comment out the network_mode above # the server to show up on the LAN, comment out the network_mode above
# and uncomment the port mappings below. # and uncomment the port mappings below.
# ports: # ports:
# # Game port
# - 2302:2302/udp # - 2302:2302/udp
# - 2303:2303/udp # # RCON port
# - 2304:2304/udp # - 2302:2302/udp
# # Steam port
# - 27016:27016/udp # - 27016:27016/udp
# Always restart, unless stopped # Always restart, unless stopped
restart: unless-stopped restart: unless-stopped

View file

@ -33,8 +33,8 @@ release_client_appid=221100
# Base directories # Base directories
CFG_SRC_FILES="/files" CFG_SRC_FILES="/files"
SERVER_FILES="${HOME}/serverfiles" SERVER_FILES="/serverfiles"
SERVER_PROFILE="${HOME}/profiles" SERVER_PROFILE="/profiles"
mkdir -p ${SERVER_FILES}/battleye ${SERVER_PROFILE} mkdir -p ${SERVER_FILES}/battleye ${SERVER_PROFILE}
@ -111,13 +111,13 @@ report() {
rm -f /tmp/mod_command_line /tmp/parameters rm -f /tmp/mod_command_line /tmp/parameters
echo echo
echo -e "${yellow}========================================== error.log ==========================================" echo -e "${yellow}========================================== error.log =========================================="
find "${HOME}" -name error.log -exec head {} \; -exec tail -n 30 {} \; -exec rm -f {} \; find "${SERVER_PROFILE}" -name error.log -exec head {} \; -exec tail -n 30 {} \; -exec rm -f {} \;
echo echo
echo -e "========================================== script*.log ========================================" echo -e "========================================== script*.log ========================================"
find "${HOME}" -name "script*.log" -exec head {} \; -exec tail -n 30 {} \; -exec rm -f {} \; find "${SERVER_PROFILE}" -name "script*.log" -exec head {} \; -exec tail -n 30 {} \; -exec rm -f {} \;
echo echo
echo -e "========================================== *.RPT ==============================================" echo -e "========================================== *.RPT =============================================="
find "${HOME}" -name "*.RPT" -exec ls -la {} \; -exec tail -n 30 {} \; -exec rm -f {} \; find "${SERVER_PROFILE}" -name "*.RPT" -exec ls -la {} \; -exec tail -n 30 {} \; -exec rm -f {} \;
echo echo
echo -e "========================================== End log ======================================${default}" echo -e "========================================== End log ======================================${default}"
} }
@ -161,8 +161,8 @@ loadconfig(){
# battleye config and rconpassword setup # battleye config and rconpassword setup
# The server creates a new file from this file, which it then uses. # The server creates a new file from this file, which it then uses.
# Let's make sure to delete it first # Let's make sure to delete it first
BE_SERVER_FILE="${HOME}/serverfiles/battleye/beserver_x64.cfg" BE_SERVER_FILE="${SERVER_FILES}/battleye/beserver_x64.cfg"
ALT_BE_SERVER_FILE=$(find ${HOME}/serverfiles/battleye -name "beserver_x64_active*") ALT_BE_SERVER_FILE=$(find ${SERVER_FILES}/battleye -name "beserver_x64_active*")
if [ ! -f "${BE_SERVER_FILE}" ] && [ ! -f "${ALT_BE_SERVER_FILE}" ] if [ ! -f "${BE_SERVER_FILE}" ] && [ ! -f "${ALT_BE_SERVER_FILE}" ]
then then
passwd=$(openssl rand -base64 8 | tr -dc 'A-Za-z0-9') passwd=$(openssl rand -base64 8 | tr -dc 'A-Za-z0-9')
@ -364,7 +364,7 @@ update(){
fi fi
} }
# Assemble the workshop list variable # Assemble the workshop variables
get_mods(){ get_mods(){
mapfile -t workshopID < "${WORKSHOP_CFG}" mapfile -t workshopID < "${WORKSHOP_CFG}"
workshoplist="" workshoplist=""
@ -375,16 +375,47 @@ get_mods(){
done 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[@]}"
do
ID=$(echo ${i} | cut -d: -f1)
if [[ ${X} = ${1} ]]
then
echo ${ID}
return
fi
X=$((X+1))
done
}
# Get mod name by ID or index
get_mod_name(){ get_mod_name(){
# Check for an ID
if [ -d "${workshopfolder}/${1}" ] if [ -d "${workshopfolder}/${1}" ]
then then
grep name ${workshopfolder}/${1}/meta.cpp | cut -d '"' -f2 | sed -r 's/\s+//g' ID=${1}
else
ID=$(get_mod_id_by_index ${1})
fi fi
if ! [ -d "${workshopfolder}/${ID}" ]
then
echo "Mod ID ${1} doesn't exist" >&2
exit 1
fi
NAME=$(grep name ${workshopfolder}/${ID}/meta.cpp | cut -d '"' -f2 | sed -r 's/\s+//g')
echo ${NAME}
} }
# Update mods # Update mods
modupdate(){ modupdate(){
get_mods
echo "Updating mods..." echo "Updating mods..."
dologin dologin
# echo ${STEAMCMD} +force_install_dir ${SERVER_FILES} +login "${steamlogin}" ${workshoplist} +quit # echo ${STEAMCMD} +force_install_dir ${SERVER_FILES} +login "${steamlogin}" ${workshoplist} +quit
@ -436,31 +467,32 @@ add(){
# Set the mod name in the workshop config file, as we don't know this at the start. # 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}/" sed -i "${WORKSHOP_CFG}" -e "s/${1}:MODNAME/${1}:${MODNAME}/"
echo -e "Mod id ${1} - ${green}${MODNAME}${default} - added" echo -e "Mod id ${1} - ${green}${MODNAME}${default} - added"
checkTypesXML ${1} install checkXML ${1} install
checkInstall ${1} install checkInstall ${1} install
} }
# Remove a mod # Remove a mod
remove(){ remove(){
checkTypesXML ${1} uninstall ID=$(get_mod_id_by_index ${1})
checkInstall ${1} uninstall MODNAME=$(get_mod_name ${1})
if [ -d "${workshopfolder}/${1}" ] checkXML ${ID} uninstall
checkInstall ${ID} uninstall
if [ -d "${workshopfolder}/${ID}" ]
then then
MODNAME=$(get_mod_name ${1}) echo "Removing directory ${workshopfolder}/${ID}"
echo "Removing directory ${workshopfolder}/${1}" rm -rf "${workshopfolder}/${ID}"
rm -rf "${workshopfolder}/${1}"
fi fi
if [ -L "${SERVER_FILES}/@${MODNAME}" ] if [ -L "${SERVER_FILES}/@${MODNAME}" ]
then then
echo "Removing symlink ${SERVER_FILES}/@${MODNAME}" echo "Removing symlink ${SERVER_FILES}/@${MODNAME}"
rm -f "${SERVER_FILES}/@${MODNAME}" rm -vf "${SERVER_FILES}/@${MODNAME}"
fi fi
if grep -q ${1} "${WORKSHOP_CFG}" if grep -q ${ID} "${WORKSHOP_CFG}"
then then
echo "Removing workshop file entry" echo "Removing workshop file entry"
sed -i "${WORKSHOP_CFG}" -e "/${1}:/d" sed -i "${WORKSHOP_CFG}" -e "/${ID}:/d"
fi fi
echo -e "Mod id ${1} - ${red}${MODNAME}${default} - removed" echo -e "Mod id ${ID} - ${red}${MODNAME}${default} - removed"
} }
# Activate / Deactivate a mod # Activate / Deactivate a mod
@ -468,46 +500,40 @@ activate(){
W=${1} W=${1}
shift shift
WW="" WW=""
COLOR="${green}"
if [[ ${W} = 0 ]] if [[ ${W} = 0 ]]
then then
WW="de" WW="de"
UU="un" UU="un"
COLOR="${red}"
fi
ID=$(get_mod_id_by_index ${1})
MODNAME=$(get_mod_name ${1})
# Toggle state or report nothing burger
if [[ "${ACTIVE}" != "${W}" ]]
then
sed -i "${WORKSHOP_CFG}" -e "s/${ID}:${MODNAME}:[0-1]/${ID}:${MODNAME}:${W}/"
symlink ${W} ${ID} "${MODNAME}"
copy_keys ${W} ${ID}
checkInstall ${ID} ${UU}install
echo -e "Mod id ${ID} - ${COLOR}${MODNAME}${default} ${WW}activated"
else
echo -e "Mod id ${ID} - ${COLOR}${MODNAME}${default} - is already ${WW}active"
fi fi
get_mods
X=1
# Loop over mod list
for i in "${workshopID[@]}"
do
ID=$(echo ${i} | cut -d: -f1)
NAME=$(echo ${i} | cut -d: -f2)
ACTIVE=$(echo ${i} | cut -d: -f3)
# Find mod by ID or index
if [[ ${ID} = ${1} ]] || [[ ${X} = ${1} ]]
then
# Toggle state or report nothing burger
if [[ "${ACTIVE}" != "${W}" ]]
then
sed -i "${WORKSHOP_CFG}" -e "s/${ID}:${NAME}:[0-1]/${ID}:${NAME}:${W}/"
symlink ${W} ${ID} "${NAME}"
copy_keys ${W} ${ID}
checkTypesXML ${ID} ${UU}install
checkInstall ${ID} ${UU}install
echo "Mod id ${ID} - ${WW}activated"
else
echo -e "Mod id ${ID} - ${green}${NAME}${default} - is already ${WW}active"
fi
fi
X=$((X+1))
done
list list
} }
# List mods # List mods
list(){ list(){
# The state may have changed since we started
get_mods get_mods
if [[ "${workshopID[@]}" = "" ]]
then
return
fi
X=1 X=1
spaces=" " spaces=" "
echo -e " ID Name Active URL Size" echo -e "\n ID Name Active URL Size"
echo "------------------------------------------------------------------------------------------------------------------------" echo "------------------------------------------------------------------------------------------------------------------------"
for i in "${workshopID[@]}" for i in "${workshopID[@]}"
do do
@ -531,8 +557,7 @@ copy_keys(){
if [[ ${1} = 1 ]] if [[ ${1} = 1 ]]
then then
echo "Copying key files..." echo "Copying key files..."
cp -v ${workshopfolder}/${2}/keys/* "${SERVER_FILES}/keys/" || \ find "${workshopfolder}/${2}" -name "*.bikey" -exec cp "{}" "${SERVER_FILES}/keys/" \;
cp -v ${workshopfolder}/${2}/key/* "${SERVER_FILES}/keys/" # Because mod authors can't stick to one way of doing things...
fi fi
} }
@ -553,7 +578,6 @@ symlink(){
# Assemble the mod command line # Assemble the mod command line
mod_cmd(){ mod_cmd(){
get_mods
mod_command_line="" mod_command_line=""
for i in "${workshopID[@]}" for i in "${workshopID[@]}"
do do
@ -570,23 +594,42 @@ mod_cmd(){
fi fi
} }
checkTypesXML(){ checkXML(){
# See if this mod has a types.xml. If so, manage it. # See if we have a template for it
for path in "${workshopfolder}/${1}/extras" "${workshopfolder}/${1}" "/files/mods/${1}" if [ -f "/files/mods/${1}/types.env" ]
do then
if [ -f "${path}/types.xml" ] echo "Found a templatate for mod ID ${1}, merging..."
then source "/files/mods/${1}/types.env"
echo -n "The mod id ${1} has a types.xml: ${path}/types.xml. " for xml in CFGEVENTSPAWNS CFGSPAWNABLETYPES EVENTS TYPES
if [[ ${2} = "install" ]] do
NAME=$(echo ${xml} | tr A-Z a-z)
if echo ${!xml} | grep -q http
then then
echo "Merging to missions..." echo "${NAME} file is remote, downloading..."
curl -o ${workshopfolder}/${1}/${NAME}.xml ${!xml}
else else
echo "Removing contents from missions..." echo "${NAME} file is in the mod, copying..."
cp -vf ${!xml} ${workshopfolder}/${1}/${NAME}.xml
fi fi
/files/mods/types.sh ${1} ${2} ${path}/types.xml done
break # else
fi # See if this mod has a types.xml in the "standard" locations
done # for path in "${workshopfolder}/${1}/extras" "${workshopfolder}/${1}"
# do
# if [ -f "${path}/types.xml" ]
# then
# echo -n "The mod id ${1} has a types.xml: ${path}/types.xml. "
# if [[ ${2} = "install" ]]
# then
# echo "Merging to missions..."
# else
# echo "Removing contents from missions..."
# fi
# /files/mods/types.sh ${1} ${2} ${path}/types.xml
# break
# fi
# done
fi
} }
checkInstall(){ checkInstall(){
@ -615,7 +658,6 @@ status(){
INSTALLED="${NO}" INSTALLED="${NO}"
LOGGED_IN="${NO}" LOGGED_IN="${NO}"
RUNNING="${NO}" RUNNING="${NO}"
get_mods
# DayZ Server files installation # DayZ Server files installation
if [ -f "${SERVER_INSTALL_FILE}" ] if [ -f "${SERVER_INSTALL_FILE}" ]
@ -648,23 +690,38 @@ status(){
RUNNING="${RUNNING}\nRunning Parameters: $(cat /tmp/parameters)\nRunning mod parameter: $(cat /tmp/mod_command_line)" RUNNING="${RUNNING}\nRunning Parameters: $(cat /tmp/parameters)\nRunning mod parameter: $(cat /tmp/mod_command_line)"
fi fi
mod_cmd mod_cmd
MAP="none"
# Map name # Map name
# MAP=$(grep -E "template=" ${SERVER_CFG_DST} | grep -vE "^//") if [[ -f ${SERVER_CFG_DST} ]]
then
MAP=$(grep -E "template=" ${SERVER_CFG_DST} | grep -vE "^//")
fi
# Number of mods plus the list denoting on or off # Number of mods plus the list denoting on or off
echo -e " echo -ne "
Logged in to Steam: ${LOGGED_IN} ${ANONYMOUS} Logged in to Steam: ${LOGGED_IN} ${ANONYMOUS}
Server files installed: ${INSTALLED} Server files installed: ${INSTALLED}"
Mods: if [[ "${INSTALLED}" = "${NO}" ]]
" then
echo
list echo
exit 0
echo -e " fi
echo -ne "
Mods: "
MODS=$(list)
if [[ ${MODS} == "" ]]
then
echo -n "none"
fi
echo -e "${MODS}
Server running: ${RUNNING} Server running: ${RUNNING}
Working parameters: ${parameters} Working parameters: ${parameters}
Working mod parameter: ${mod_command_line}" Working mod parameter: ${mod_command_line}"
MAP=$(grep template ${SERVER_CFG_DST} | grep -v "^//" | cut -d= -f2 | cut -d\; -f1) if [[ "${INSTALLED}" = "${YES}" ]]
echo "Map: ${MAP}" then
MAP=$(grep template ${SERVER_CFG_DST} | grep -v "^//" | cut -d= -f2 | cut -d\; -f1)
echo "Map: ${MAP}"
fi
} }
backup(){ backup(){
@ -685,6 +742,8 @@ shift || {
usage usage
} }
get_mods
case "${C}" in case "${C}" in
a|activate) a|activate)
activate 1 "${@}" activate 1 "${@}"

View file

@ -0,0 +1 @@
TYPES=types-v6.xml

View file

@ -1 +0,0 @@
//home/user/serverfiles/steamapps/workshop/content/221100/1964490092/types-v6.xml

0
files/mods/2415195639/install.env Executable file → Normal file
View file

View file

@ -0,0 +1 @@
xml_and_clasnames/snafu_types.xml

View file

@ -1 +0,0 @@
/home/user/serverfiles/steamapps/workshop/content/221100/2443122116/xml_and_clasnames/snafu_types.xml

View file

@ -0,0 +1,8 @@
#!/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
#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/Namalsk/RFFSHelis_cfgeventspawns.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

@ -0,0 +1 @@
TYPES=extras/types/rag_baseitems.xml

View file

@ -1 +0,0 @@
/home/user/serverfiles/steamapps/workshop/content/221100/2878980498/extras/types/rag_baseitems.xml

View file

@ -1 +1 @@
@RaG_BaseItems 2878980498

View file

@ -0,0 +1 @@
2692979668

View file

@ -5,26 +5,26 @@ set -eE
source /files/mods/${1}/install.env source /files/mods/${1}/install.env
echo echo
if echo ${0} | grep -q "uninstall.sh" if [[ ${2} = "uninstall" ]]
then then
echo "Backing up, as uninstalling will remove the ${MAP} mpmissions directory" echo "Backing up, as uninstalling will remove the ${MAP} mpmissions directory"
dayzserver backup dayzserver backup
echo "Uninstalling mpmissions..." echo "Uninstalling mpmissions..."
echo echo
rm -rf ${HOME}/serverfiles/mpmissions/${MPDIR} rm -rf ${SERVER_FILES}/mpmissions/${MPDIR}
elif echo ${0} | grep -q "update.sh" elif [[ ${2} = "update" ]]
then then
echo "Updating mpmissions directory..." echo "Updating mpmissions directory..."
echo echo
cd /tmp cd /tmp
git clone ${REPO} 2> /dev/null 1> /dev/null git clone ${REPO} 2> /dev/null 1> /dev/null
cp -a ${DIR}/${MPDIR} ${HOME}/serverfiles/mpmissions cp -a ${DIR}/${MPDIR} ${SERVER_FILES}/mpmissions
rm -rf ${DIR} rm -rf ${DIR}
else else
echo "Installing mpmissions files..." echo "Installing mpmissions files..."
echo echo
cd /tmp cd /tmp
git clone ${REPO} 2> /dev/null 1> /dev/null git clone ${REPO} 2> /dev/null 1> /dev/null
cp -a ${DIR}/${MPDIR} ${HOME}/serverfiles/mpmissions cp -a ${DIR}/${MPDIR} ${SERVER_FILES}/mpmissions
rm -rf ${DIR} rm -rf ${DIR}
fi fi

18
files/server.sh Executable file
View file

@ -0,0 +1,18 @@
#!/usr/bin/env bash
# Set PS1 so we know we're in the container
if ! [ -f .bashrc ]
then
echo "Creating .bashrc..."
cat > .bashrc <<EOF
alias ls='ls --color'
export PS1="${debian_chroot:+($debian_chroot)}\u@dayzdockerserver:\w\$ "
EOF
fi
# Uncomment the line below to run things manually in the container, then run:
# docker compose exec main bash
tail -f /dev/null
# Otherwise, start the server normally
#/files/dayzserver start