Skip to content

Instantly share code, notes, and snippets.

@Tekunogosu
Created February 3, 2025 14:53
Show Gist options
  • Save Tekunogosu/5277250766d9e6afd3a0b532a92fd012 to your computer and use it in GitHub Desktop.
Save Tekunogosu/5277250766d9e6afd3a0b532a92fd012 to your computer and use it in GitHub Desktop.
Vintage Story dedicated server script, using TMUX instead of Screen
#!/bin/bash
# /etc/init.d/vintagestory.sh
# version 0.4.2 2016-02-09 (YYYY-MM-DD)
# This shell script launches the game server on linux
#
### BEGIN INIT INFO
# Provides: vintagestory
# Required-Start: $local_fs $remote_fs tmux-cleanup
# Required-Stop: $local_fs $remote_fs
# Should-Start: $network
# Should-Stop: $network
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: vintagestory server
# Description: Starts the vintagestory server
### END INIT INFO
#set -x
# Shamelessly adapted from http://minecraft.gamepedia.com/Tutorials/Server_startup_script
#Settings
HISTORY=1024
USERNAME='vintagestory'
VSPATH='/home/vintagestory/server'
DATAPATH='/var/vintagestory/data'
## change the TMUXNAME (and DATAPATH) when running multiple servers on the same system
TMUXNAME='vintagestory_server'
SERVICE="VintagestoryServer.dll"
OPTIONS=""
INVOCATION="dotnet ${SERVICE} --dataPath \"${DATAPATH}\" ${OPTIONS}"
PGREPTEST="dotnet ${SERVICE} --dataPath ${DATAPATH}"
#Warning
[ -n "${NO_SUPPORT_MODE}" ] && echo "Warning! You are running in 'NO_SUPPORT_MODE', disabling some requirements checks. Have fun but please avoid to ask for support!"
#Commands
command -v pgrep >/dev/null 2>&1 || {
echo "Fatal! I require pgrep but it's not installed. (apt install procps) Aborting." >&2
exit 1
}
command -v tmux >/dev/null 2>&1 || {
echo "Fatal! I require tmux but it's not installed. Aborting." >&2
exit 1
}
command -v dotnet >/dev/null 2>&1 || {
echo "Fatal! I require dotnet but it's not installed. (https://dotnet.microsoft.com/en-us/download) Aborting." >&2
exit 1
}
#Possibly run as user of group vintagestory
unset GROUPNAME
ME=$(whoami)
if [ "${ME}" != "${USERNAME}" ]; then
groups "${ME}" | grep -q "${USERNAME}" && {
GROUPNAME="${USERNAME}"
USERNAME="${ME}"
}
fi
as_user() {
if [ "${ME}" = "${USERNAME}" ]; then
bash -c "${1}"
return $?
else
su "${USERNAME}" -s /bin/bash -c "${1}"
return $?
fi
}
vs_version() {
instdir="${1}"
if ! [ -f "${instdir}/${SERVICE}" ]; then
echo "Fatal! ${instdir}/${SERVICE} not found. Make sure to set a correct VSPATH and DATA in the server.sh file. Aborting." >&2
exit 1
fi
if [ ! -d "${DATAPATH}" ]; then
mkdir -m 775 -p "${DATAPATH}" >/dev/null 2>&1 || {
echo "Fatal! Cannot create new "${DATAPATH}". Aborting." >&2
exit 1
}
fi
if [ "${ME}" != "${USERNAME}" ]; then
chown "${USERNAME}:${USERNAME}" -R "${DATAPATH}" >/dev/null 2>&1 || {
echo "Fatal! ${ME} failed to adjust data privileges. Aborting." >&2
exit 1
}
elif [ -n "${GROUPNAME}" ]; then
touch "${DATAPATH}/.${PPID}" >/dev/null 2>&1
find "${DATAPATH}" -user "${ME}" | xargs chgrp "${GROUPNAME}" >/dev/null 2>&1 || {
echo "Fatal! ${ME} failed to adjust data privileges. Aborting." >&2
exit 1
}
rm "${DATAPATH}/.${PPID}" >/dev/null 2>&1
fi
}
vs_start() {
vs_version "${VSPATH}" # otherwise abort
if pgrep -u "${USERNAME}","${GROUPNAME:-root}" -f "${PGREPTEST}" >/dev/null; then
echo "${SERVICE} is already running!"
else
cd "${VSPATH}"
echo "Starting ${SERVICE} ..."
if as_user "cd \"${VSPATH}\" && tmux set-option -g history-limit ${HISTORY} \; new-session -d -s ${TMUXNAME} -n game ${INVOCATION}"; then
sleep 7
else
echo "Warning! Problems with ${SERVICE}! Make sure user ${USERNAME} has read/write access."
fi
if [ -n "${NO_SUPPORT_MODE}" ]; then
vs_command "/announce WARNING - ${SERVICE} was started in NO_SUPPORT_MODE" >/dev/null
fi
cd - >/dev/null 2>&1
if ! vs_status; then
echo "Error! User ${USERNAME} could not start ${SERVICE}! Please check ${DATAPATH}/Logs." >&2
return 1
else
echo "Please be aware that as of 1.20, servers default configurations have changed - servers no longer register themselves to the public servers list and are invite-only (whitelisted) out of the box. If you desire so, you can enable server advertising with '/serverconfig advertise on' and disable the whitelist mode with '/serverconfig whitelistmode off'"
return 0
fi
fi
}
vs_status() {
if ! pgrep -u "${USERNAME}","${GROUPNAME:-root}" -f "${PGREPTEST}" >/dev/null; then
echo "${SERVICE} is not running."
return 1
elif statusmessage=$(vs_command "/stats"); then
sleep 1
if pgrep -u "${USERNAME}","${GROUPNAME:-root}" -f "${PGREPTEST}" >/dev/null; then
echo "${statusmessage}"
echo "${SERVICE} is up and running!"
return 0
elif ! pgrep -u "${USERNAME}","${GROUPNAME:-root}" -f "${PGREPTEST}" >/dev/null; then
echo "${SERVICE} is not running."
return 1
else
echo "${statusmessage}"
echo "${SERVICE} is only partially running (not as expected)."
return 1
fi
else
echo "${statusmessage}"
echo "${SERVICE} status is indetermined."
return 1
fi
}
vs_stop() {
if pgrep -u "${USERNAME}","${GROUPNAME:-root}" -f "${PGREPTEST}" >/dev/null; then
echo "Stopping ${SERVICE} ..."
if as_user "tmux send-keys -t ${TMUXNAME} \"SERVER SHUTTING DOWN IN 10 SECONDS. Saving map..\"" ENTER; then
sleep 10
else
echo "Warning! ${USERNAME} encountered problems sending a server shutdown notification."
fi
if ! as_user "tmux send-keys -t ${TMUXNAME} \"/stop\"" ENTER; then
echo "Warning! ${USERNAME} encountered problems executing the server stop command."
fi
sleep 7
if pgrep -u "${USERNAME}","${GROUPNAME:-root}" -f "${PGREPTEST}" >/dev/null; then
if ! as_user "tmux kill-session -t ${TMUXNAME} \"/$command\""; then
echo "Warning! ${USERNAME} encountered problems terminating the ${SERVICE} processes."
fi
fi
else
echo "${SERVICE} was not running."
return 0
fi
if [ $(pgrep -u "${USERNAME}","${GROUPNAME:-root}" -f "${PGREPTEST}" -c) = "0" ]; then
echo "${SERVICE} is stopped."
return 0
else
echo "Error! ${SERVICE} could not be stopped." >&2
return 1
fi
}
vs_command() {
command="${1}"
servicelog="${DATAPATH}/Logs/server-main.log"
if pgrep -u "${USERNAME}","${GROUPNAME:-root}" -f "${PGREPTEST}" >/dev/null; then
touch "${servicelog}" >/dev/null 2>&1
pre_log_len=$(wc -l "${servicelog}" | awk '{print $1}')
echo "${SERVICE} process found ... executing command"
if as_user "tmux send-keys -t ${TMUXNAME} "; then
sleep .1 # assumes that the command will run and print to the log file in less than .1 seconds
# print output, number of lines computed by arithmetic expansion
if [ -f "${servicelog}" ]; then
tail -n $(($(wc -l "${servicelog}" | awk '{print $1}') - ${pre_log_len})) "${servicelog}"
return 0
else
echo "Error! Commmand output could not be read from ${servicelog}." >&2
return 1
fi
else
echo "Error! ${USERNAME} encountered problems with command execution." >&2
return 1
fi
fi
}
vs_update() {
echo "Auto update is disabled because unreliable."
echo "Personal recommendation: Copy aside the server.sh once, e.g. to the parent folder, then for every update do (assuming your data path is somewhere else):"
echo "1. ./server.sh stop"
echo "2. rm -rf *"
echo "3. wget [server archive link copied from account maanger]"
echo "4. tar -zxvf [hit tab to get the downloaded file name]"
echo "5. cp ../server.sh ."
echo "6. ./server.sh start"
}
vs_setup() {
read -p "Please confirm setup of basic server environment: user ${1} path ${2:-/home/${1}/server} (y/n) " -n 1 -r
echo
if [ "${REPLY,,}" = "y" ]; then
USERNAME="${1}"
VSPATH="${2:-/home/${1}/server}"
if [ "${ME}" != "${USERNAME}" ]; then
if ! getent passwd "${USERNAME}" >/dev/null; then
useradd -r -s "/bin/false" -U "${USERNAME}" >/dev/null 2>&1 || {
echo "Fatal! User setup failed. Aborting." >&2
exit 1
}
echo "Setup new user: $(getent passwd "${USERNAME}")"
fi
if ! getent group "${USERNAME}" >/dev/null; then
groupadd -r "${USERNAME}" >/dev/null 2>&1 || {
echo "Fatal! Group setup failed. Aborting." >&2
exit 1
}
echo "Setup new group: $(getent group "${USERNAME}")"
fi
if ! [ -d "${VSPATH}" ]; then
mkdir -m 775 -p "${VSPATH}" >/dev/null || {
echo "Fatal! Server path setup failed. Aborting." >&2
exit 1
}
chown "${USERNAME}:${USERNAME}" -R "${VSPATH%/*}" >/dev/null 2>&1 || {
echo "Fatal! Server privileges setup failed. Aborting." >&2
exit 1
}
echo "Setup new server path: $(ls -ld "${VSPATH}")"
fi
if [ ! -d "${DATAPATH%/*}" ]; then
mkdir -m 775 -p "${DATAPATH%/*}" >/dev/null || {
echo "Fatal! Data path setup failed. Aborting." >&2
exit 1
}
chown "${USERNAME}:${USERNAME}" -R "${DATAPATH%/*}" >/dev/null 2>&1 || {
echo "Fatal! Data privileges setup failed. Aborting." >&2
exit 1
}
echo "Setup new data path: $(ls -ld "${DATAPATH%/*}")"
fi
else
if ! getent group "${USERNAME}" >/dev/null 2>&1; then
echo "Fatal! User ${USERNAME} cannot setup missing group. Aborting." >&2
exit 1
elif ! id -nG | grep "${USERNAME}" >/dev/null 2>&1; then
echo "Fatal! User cannot use existing group ${USERNAME}. Aborting." >&2
exit 1
fi
if ! [ -d "${VSPATH}" ]; then
mkdir -m 775 -p "${VSPATH}" >/dev/null 2>&1 || {
echo "Fatal! Server path setup failed. Aborting." >&2
exit 1
}
chgrp "${USERNAME}" -R "${VSPATH}" >/dev/null 2>&1 || {
echo "Fatal! Server privileges setup failed. Aborting." >&2
exit 1
}
echo "Setup new server path: $(ls -ld "${VSPATH}")"
fi
if ! [ -d "${DATAPATH%/*}" ]; then
mkdir -m 775 -p "${DATAPATH%/*}" >/dev/null 2>&1 || {
echo "Fatal! Data path setup failed. Aborting." >&2
exit 1
}
chgrp "${USERNAME}" -R "${DATAPATH%/*}" >/dev/null 2>&1 || {
echo "Fatal! Data privileges setup failed. Aborting." >&2
exit 1
}
echo "Setup new data path: $(ls -ld "${DATAPATH%/*}")"
fi
fi
echo "Basic environment setup finished. Now check server.sh header if all info is correct."
return 0
else
echo "Fatal! Setup for user ${1} cancelled. Aborting." >&2
return 1
fi
}
#Installation check
unset missing
getent passwd "${USERNAME}" >/dev/null || missing=" user ${USERNAME}"
getent group "${USERNAME}" >/dev/null || missing="${missing} group ${USERNAME}"
[ -d "${VSPATH}" ] || missing="${missing} path ${VSPATH}"
if [ -n "${missing}" ]; then
echo "Username, Group or data path missing. Server environment setup needed (create${missing})"
vs_setup "${USERNAME}"
fi
# main
case "${1}" in
start)
vs_start
exit $?
;;
stop)
vs_stop
exit $?
;;
restart)
vs_stop && vs_start
exit $?
;;
update)
vs_update
exit $?
;;
setup)
if [ "$(id -u)" = 0 -o "${ME}" = "${2:-${GROUPNAME:-$USERNAME}}" ]; then
vs_setup "${2:-${GROUPNAME:-$USERNAME}}" "${3}"
exit $?
else
echo "The setup command is only available for the users root and ${2:-${GROUPNAME:-$USERNAME}}."
exit 1
fi
;;
status)
vs_version "${VSPATH}" # otherwise abort
echo "Data path is ${DATAPATH}"
vs_status
exit $?
;;
command)
vs_version "${VSPATH}" # otherwise abort
if [ $# -gt 1 ]; then
shift
vs_command "$*"
exit $?
else
echo "Must specify server command (try 'help'?)"
exit 1
fi
;;
*)
echo "Usage: ${0} {start|stop|status|update|restart|setup [\"user name\"] [\"path\"]|command \"server command\"}"
exit 1
;;
esac
exit 1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment