Created
February 3, 2025 14:53
-
-
Save Tekunogosu/5277250766d9e6afd3a0b532a92fd012 to your computer and use it in GitHub Desktop.
Vintage Story dedicated server script, using TMUX instead of Screen
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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