version 1.2, 2021/09/02 16:17:35 |
version 1.10, 2023/05/02 18:59:16 |
|
|
#--------------------------------------------------------------------- |
#--------------------------------------------------------------------- |
# Filename: mtctl |
# Filename: mtctl |
# Purpose: Minetest server Control |
# Purpose: Minetest Server Control |
# License: Copyright (C) 2021 by Miniontoby <miniontoby@ircnow.org> |
# License: Copyright (C) 2021-2022 by Miniontoby <miniontoby@ircnow.org> |
#--------------------------------------------------------------------- |
#--------------------------------------------------------------------- |
VERSION="1.0" |
VERSION="1.6.1" |
config=[] |
config=[] |
config[0]="no"; config[1]="/usr/local/share/minetest/" |
config[0]="no"; config[1]="/usr/local/share/minetest/" |
|
|
|
|
echo -e "mtctl: $MSG" | sed -e 's/\\n/\\nmtctl: /' |
echo -e "mtctl: $MSG" | sed -e 's/\\n/\\nmtctl: /' |
} |
} |
mthelp() { |
mthelp() { |
echo -e "usage: mtctl start|stop|restart|status|create worldname\n mtctl list|help|version|check_updates"; exit 1 |
echo -e "usage: mtctl start|stop|restart|status|create|backup|enable|disable worldname\n mtctl list|help|version|check_updates" |
} |
} |
if [ "x$1" == "x" ]; then mthelp; exit 1; fi |
if [ "x$1" == "x" ]; then mthelp; exit 0; fi |
|
|
|
|
#--------------------------------------------------------------------- |
#--------------------------------------------------------------------- |
Line 28 if [ \! -d $CONFDIR ]; then mkdir -p $CONFDIR || exit |
|
Line 28 if [ \! -d $CONFDIR ]; then mkdir -p $CONFDIR || exit |
|
if [ \! -f $CONFDIR/config ]; then |
if [ \! -f $CONFDIR/config ]; then |
mtinfo "\nNo configfile found!\nCreating it now!\n\n" |
mtinfo "\nNo configfile found!\nCreating it now!\n\n" |
echo -n 'Use custom build minetest folder (yes/no): '; read MTBUILD |
echo -n 'Use custom build minetest folder (yes/no): '; read MTBUILD |
case $MTBUILD in |
case $MTBUILD in |
YES|yes) MTBUILD='yes' |
YES|yes) MTBUILD='yes' |
EXAMPLEDIR="$HOME/minetest" |
EXAMPLEDIR="$HOME/minetest" |
;; |
;; |
|
|
if [ "${config[0]}" == "yes" ]; then SERVEREXE="${config[1]}/bin/minetestserver"; else SERVEREXE="`which minetestserver`"; fi |
if [ "${config[0]}" == "yes" ]; then SERVEREXE="${config[1]}/bin/minetestserver"; else SERVEREXE="`which minetestserver`"; fi |
WORLDBASE=${config[1]}/worlds |
WORLDBASE=${config[1]}/worlds |
LOGDIR=${config[1]}/log |
LOGDIR=${config[1]}/log |
EXITFLAGALL=$EXITDIR/minestop.all |
|
OK=0; cd ${config[1]} || exit 1 |
OK=0; cd ${config[1]} || exit 1 |
touch temp.test && OK=1; if [ "@$OK" == "@0" ]; then mtinfo "Error: Directory tree should be owned by the MT user:\n${config[1]}"; exit 1; fi |
touch temp.test && OK=1; if [ "@$OK" == "@0" ]; then mtinfo "Error: Directory tree should be owned by the MT user:\n${config[1]}"; exit 1; fi |
rm temp.test || exit 1 |
rm temp.test || exit 1 |
|
|
NAME=$1; if [ "x$NAME" == "x" ]; then mthelp; exit 1; fi |
NAME=$1; if [ "x$NAME" == "x" ]; then mthelp; exit 1; fi |
WORLDDIR=$WORLDBASE/$NAME; if [ \! -d $WORLDDIR ]; then mtstatus "Failed"; mtinfo "Error: World not found: $NAME"; exit 1; fi |
WORLDDIR=$WORLDBASE/$NAME; if [ \! -d $WORLDDIR ]; then mtstatus "Failed"; mtinfo "Error: World not found: $NAME"; exit 1; fi |
PORT=`grep '^port.*=' $WORLDDIR/world.conf | sed -e "s/^.*= *//" -e "s/ .*//"`; if [ "x$PORT" == "x" ]; then mtstatus "Failed"; mtinfo "Error: Port for $NAME not defined in cfg file"; exit 1; fi |
PORT=`grep '^port.*=' $WORLDDIR/world.conf | sed -e "s/^.*= *//" -e "s/ .*//"`; if [ "x$PORT" == "x" ]; then mtstatus "Failed"; mtinfo "Error: Port for $NAME not defined in cfg file"; exit 1; fi |
|
OWNER=`grep '^name.*=' $WORLDDIR/world.conf | sed -e "s/^.*= *//" -e "s/ .*//"`; if [ "x$OWNER" == "x" ]; then mtstatus "Failed"; mtinfo "Error: Owner for $NAME not defined in cfg file"; exit 1; fi |
} |
} |
mtstatus() { |
mtstatus() { |
CHECK="$1" |
CHECK="$1" |
|
|
CHECK="Offline" |
CHECK="Offline" |
fi |
fi |
EXTRA="\n" |
EXTRA="\n" |
else if [ "$CHECK" == "Failed" ]; then EXTRA=" \n"; fi; fi |
elif [ "$CHECK" == "Failed" ]; then EXTRA="\n"; |
|
elif [ "$CHECK" == "Backup failed" ]; then EXTRA="\n"; |
|
elif [ "$CHECK" == "Backup success" ]; then EXTRA="\n"; fi |
echo -ne "\r$NAME($CHECK)$EXTRA" |
echo -ne "\r$NAME($CHECK)$EXTRA" |
} |
} |
|
|
|
|
exit 1 |
exit 1 |
fi |
fi |
|
|
EXITFLAGWORLD=$EXITDIR/minestop.$NAME |
EXITFLAGWORLD=$EXITDIR/mtctlstop.$NAME |
MINETEST_SUBGAME_PATH=${config[1]}/games |
MINETEST_SUBGAME_PATH=${config[1]}/games |
# MAYBE Export if linux doesnt work |
# MAYBE Export if linux doesnt work |
|
|
rm -f $EXITFLAGALL $EXITFLAGWORLD |
|
sleep 1; DIR=`pwd` || exit 1; cd $WORLDDIR || exit 1 |
sleep 1; DIR=`pwd` || exit 1; cd $WORLDDIR || exit 1 |
F1=env_meta.txt; F2=env_meta.old; if [ -s $F1 ]; then N=`grep EnvArgsEnd $F1 | wc -l` || exit 1; if [ "x$N" == "x0" ]; then rm -f $F1 || exit 1; fi; fi; if [ -s $F1 ]; then rm -f $F2; cp -p $F1 $F2 || exit 1; else if [ -s $F2 ]; then rm -f $F1; cp -p $F2 $F1 || exit 1; else rm -f $F1 || exit 1; fi; fi |
F1=env_meta.txt; F2=env_meta.old; if [ -s $F1 ]; then N=`grep EnvArgsEnd $F1 | wc -l` || exit 1; if [ "x$N" == "x0" ]; then rm -f $F1 || exit 1; fi; fi; if [ -s $F1 ]; then rm -f $F2; cp -p $F1 $F2 || exit 1; else if [ -s $F2 ]; then rm -f $F1; cp -p $F2 $F1 || exit 1; else rm -f $F1 || exit 1; fi; fi |
cd $DIR || exit 1 |
cd $DIR || exit 1 |
|
|
( |
( |
while true; do |
while true; do |
$SERVEREXE --config $WORLDDIR/world.conf --port $PORT --logfile $LOGDIR/debug-$NAME.log --map-dir $WORLDDIR >> $LOGDIR/$NAME.log 2>&1 |
$SERVEREXE --config $WORLDDIR/world.conf --port $PORT --logfile $LOGDIR/debug-$NAME.log --map-dir $WORLDDIR >> $LOGDIR/$NAME.log 2>&1 |
if [ -f $EXITFLAGALL ]; then exit 0; fi; if [ -f $EXITFLAGWORLD ]; then exit 0; fi; sleep 10 |
if [ -f $EXITFLAGWORLD ]; then exit 0; fi; sleep 10 |
done |
done |
) > $LOGDIR/start-$NAME.txt & |
) > $LOGDIR/start-$NAME.txt & |
mtstatus "Ok" |
mtstatus "Ok" |
|
|
SetWorld "$1"; mtstatus |
SetWorld "$1"; mtstatus |
GetPIDS "$WORLDDIR" "$NAME" |
GetPIDS "$WORLDDIR" "$NAME" |
if [ "x$FOO$BAR" == "x" ]; then mtstatus "Failed"; mtinfo "$NAME seems to be stopped already"; exit 0; fi |
if [ "x$FOO$BAR" == "x" ]; then mtstatus "Failed"; mtinfo "$NAME seems to be stopped already"; exit 0; fi |
if [ "x$FOO" \!= "x" ]; then kill -SIGINT $FOO 2> /dev/null ; sleep 1; kill -SIGINT $FOO 2> /dev/null ; sleep 1; kill -SIGINT $FOO 2> /dev/null ; sleep 1; kill -SIGINT $FOO 2> /dev/null ; fi |
touch $EXITDIR/mtctlstop.$NAME |
if [ "x$BAR" \!= "x" ]; then kill -9 $BAR; fi |
|
|
|
|
if [ "x$FOO" \!= "x" ]; then |
|
kill -SIGINT $FOO |
|
elif [ "x$BAR" \!= "x" ]; then |
|
kill -9 $BAR |
|
fi |
|
|
mtstatus "Waiting" |
mtstatus "Waiting" |
sleep 15; GetPIDS "$WORLDDIR" "$NAME" |
sleep 5; GetPIDS "$WORLDDIR" "$NAME" |
|
|
if [ "x$FOO$BAR" == "x" ]; then |
if [ "x$FOO$BAR" == "x" ]; then |
mtstatus "Ok" |
mtstatus "Ok" |
else |
else |
kill -9 $FOO; kill -9 $FOO; kill -9 $FOO; kill -9 $FOO; |
|
if [ "x$BAR" \!= "x" ]; then kill -9 $BAR; fi |
|
|
|
mtstatus "Waiting." |
mtstatus "Waiting." |
sleep 15; GetPIDS "$WORLDDIR" "$NAME" |
sleep 5; GetPIDS "$WORLDDIR" "$NAME" |
if [ "x$FOO$BAR" == "x" ]; then |
if [ "x$FOO$BAR" == "x" ]; then |
mtstatus "Ok" |
mtstatus "Ok" |
else |
else |
|
|
} |
} |
|
|
statusMT() { |
statusMT() { |
SetWorld "$1"; |
SetWorld "$1" |
mtstatus "Ok" |
GetPIDS "$WORLDDIR" "$NAME" |
|
if [ "x$FOO$BAR" \!= "x" ]; then |
|
STATUS="Online" |
|
else |
|
STATUS="Offline" |
|
fi |
|
LASTLOG="`tail -n 5 $LOGDIR/$NAME.log 2> /dev/null`" |
|
echo -e "mtctl.$1 - The Minetest Server Control\n Active: $STATUS\n Process: $FOO\n Main PID: $BAR\n\n$LASTLOG" |
} |
} |
|
|
listMT() { |
listMT() { |
for WORLDNAME in `ls $WORLDBASE 2> /dev/null | grep '^[a-zA-Z0-9]*$' | sort`; do |
for WORLDNAME in `ls $WORLDBASE 2> /dev/null | grep '^[a-zA-Z0-9]*$' | sort`; do |
SetWorld "$WORLDNAME" |
SetWorld "$WORLDNAME" |
mtinfo "World: $WORLDNAME Port: $PORT" |
mtinfo "World: $WORLDNAME, Port: $PORT, Owner: $OWNER" |
done |
done |
} |
} |
|
|
|
|
if [ "x$NAME" == "x" ]; then echo -e "Failed\nError: Worlds need a name\n"; exit 1; else if [ -d $WORLDBASE/$NAME ]; then echo -e "Failed\nError: World already exist\n"; exit 1; else echo -e "Server name: Ok"; fi; fi |
if [ "x$NAME" == "x" ]; then echo -e "Failed\nError: Worlds need a name\n"; exit 1; else if [ -d $WORLDBASE/$NAME ]; then echo -e "Failed\nError: World already exist\n"; exit 1; else echo -e "Server name: Ok"; fi; fi |
echo -ne "\nServer Description: "; read SERVER_DESCRIPTION |
echo -ne "\nServer Description: "; read SERVER_DESCRIPTION |
PortCheck () { |
PortCheck () { |
CHECK='yes'; if [ -z "${PORT##*[!0-9]*}" ]; then CHECK='no'; ERROR="Ports are only numeric"; return; fi; for x in `ls /home/$MTUSER/minetest/worlds | sort`; do export WCFILE=$WORLDBASE/$x/world.conf; if [ -f $WCFILE ]; then export PORTF=`grep '^port.*=' $WCFILE | sed -e "s/^.*= *//" -e "s/ .*//"`; if [ "x$PORTF" == "x$1" ]; then CHECK='no'; ERROR="Port already in use"; return; fi; fi; done |
CHECK='yes'; if [ -z "${PORT##*[!0-9]*}" ]; then CHECK='no'; ERROR="Ports are only numeric"; return; fi; for x in `ls $WORLDBASE | sort`; do export WCFILE=$WORLDBASE/$x/world.conf; if [ -f $WCFILE ]; then export PORTF=`grep '^port.*=' $WCFILE | sed -e "s/^.*= *//" -e "s/ .*//"`; if [ "x$PORTF" == "x$1" ]; then CHECK='no'; ERROR="Port already in use"; return; fi; fi; done |
} |
} |
while true; do echo -ne "\nPort: "; read PORT; PortCheck $PORT; if [ "$CHECK" == "no" ]; then echo -e "Failed\nError: $ERROR"; sleep 1; else echo -e "Ok"; break; fi; done |
while true; do |
|
echo -ne "\nPort: "; read PORT; PortCheck $PORT; if [ "$CHECK" == "no" ]; then echo -e "Failed\nError: $ERROR"; exit 1; else echo -e "Ok"; break; fi; |
|
done |
echo -ne "\nMessage Of The Day [MOTD]: "; read MOTD; echo -ne "\nSeed: "; read SEED; echo -ne "\nCreative (true/false): "; read CREATIVE_MODE |
echo -ne "\nMessage Of The Day [MOTD]: "; read MOTD; echo -ne "\nSeed: "; read SEED; echo -ne "\nCreative (true/false): "; read CREATIVE_MODE |
echo -ne "\nEnable Damage (true/false): "; read ENABLE_DAMAGE; echo -ne "\nEnable Player Versus Player [PVP] (true/false): "; read ENABLE_PVP |
echo -ne "\nEnable Damage (true/false): "; read ENABLE_DAMAGE; echo -ne "\nEnable Player Versus Player [PVP] (true/false): "; read ENABLE_PVP |
echo -ne "\nUsername: "; read USERNAME |
echo -ne "\nUsername: "; read USERNAME |
|
|
cd $WORLDBASE; mkdir $NAME; cd $NAME |
cd $WORLDBASE; mkdir $NAME; cd $NAME |
echo -e "server_name = $NAME\nserver_description = $SERVER_DESCRIPTION\nport = $PORT\nmotd = $MOTD\nfixed_map_seed = $SEED\ncreative_mode = $CREATIVE_MODE\nenable_damage = $ENABLE_DAMAGE\nenable_pvp = $ENABLE_PVP\nname = $USERNAME\nserver_announce = true\nserverlist_url = servers.minetest.net" > world.conf |
echo -e "server_name = $NAME\nserver_description = $SERVER_DESCRIPTION\nport = $PORT\nmotd = $MOTD\nfixed_map_seed = $SEED\ncreative_mode = $CREATIVE_MODE\nenable_damage = $ENABLE_DAMAGE\nenable_pvp = $ENABLE_PVP\nname = $USERNAME\nserver_announce = true\nserverlist_url = servers.minetest.net\nsqlite_synchronous = 0\nserver_unload_unused_data_timeout = 900\nserver_map_save_interval = 900.0" > world.conf |
echo -e "creative_mode = $CREATIVE_MODE\nenable_damage = $ENABLE_DAMAGE\nauth_backend = sqlite3\nbackend = sqlite3\nplayer_backend = sqlite3\ngameid = minetest\nworld_name = $NAME" > world.mt |
echo -e "creative_mode = $CREATIVE_MODE\nenable_damage = $ENABLE_DAMAGE\nbackend = sqlite3\nplayer_backend = sqlite3\nmod_storage_backend = sqlite3\nauth_backend = sqlite3\ngameid = minetest\nworld_name = $NAME" > world.mt |
echo -ne "\nWorld created!\nWant to start the world?(yes/no): "; read startit |
echo -ne "\nWorld created!\nWant to start the world?(yes/no): "; read startit |
if [[ "$startit" == "yes" ]]; then /usr/bin/mtctl start $NAME; fi |
if [[ "$startit" == "yes" ]]; then /usr/bin/mtctl start $NAME; fi |
echo -e "\nSuccess: \033[1;32mDone \033[m\nFeel free to join your new server at port $PORT" |
echo -e "\nSuccess: \033[1;32mDone \033[m\nFeel free to join your new server at port $PORT" |
} |
} |
|
|
|
backupMT() { |
|
SetWorld "$1" |
|
# Backup script from mtlbak by Minix |
|
# Copyright (C) 2021 Minix from FreedomTest (freedomtest@protonmail.com) |
|
|
|
BACKUP_DIR="$CONFDIR/backups/$NAME" |
|
LOG_FILE="$LOGDIR/backup_$NAME.log" |
|
mkdir -p $BACKUP_DIR |
|
echo -ne "" >> $LOG_FILE |
|
|
|
if [ -f /tmp/$(basename $WORLDDIR)_local_backup_in_progress ]; then |
|
echo "\nBackup for $(basename $WORLDDIR) on $(date) aborted because another backup is already in progress, this may be caused by a backup that is taking longer than expected" >> $LOG_FILE |
|
mtinfo "Backup for $(basename $WORLDDIR) on $(date) aborted because another backup is already in progress, this may be caused by a backup that is taking longer than expected" |
|
exit 1 |
|
fi |
|
|
|
touch /tmp/$(basename $WORLDDIR)_local_backup_in_progress |
|
rm $BACKUP_DIR/map.sqlite.tmp* 2> /dev/null #Cleaning in case of crashed attempts |
|
|
|
echo -e "\n$(date) backup started" >> $LOG_FILE |
|
mtstatus "Backup started" |
|
|
|
try=0 |
|
while [ $try -lt 5 ]; do |
|
echo "Starting backup attempt #$(expr $try + 1) on $(date)" >> $LOG_FILE |
|
sqlite3 $WORLDDIR/map.sqlite ".backup $BACKUP_DIR/map.sqlite.tmp" 2> /dev/null |
|
if [ $? -eq 0 ]; then |
|
mv $BACKUP_DIR/{map.sqlite.tmp,map.sqlite} |
|
#Backup auth.sqlite and players.sqlite files properly |
|
until sqlite3 $WORLDDIR/auth.sqlite ".backup $BACKUP_DIR/auth.sqlite" 2> /dev/null; do |
|
continue |
|
done |
|
until sqlite3 $WORLDDIR/players.sqlite ".backup $BACKUP_DIR/players.sqlite" 2> /dev/null; do |
|
continue |
|
done |
|
rsync -ptrW --delete --exclude "*.sqlite" $WORLDDIR/ $BACKUP_DIR/ |
|
echo "Backup finished succesfully on $(date)" >> $LOG_FILE |
|
mtstatus "Backup success" |
|
rm /tmp/$(basename $WORLDDIR)_local_backup_in_progress 2> /dev/null |
|
exit 0 |
|
else |
|
rm $BACKUP_DIR/map.sqlite.tmp 2> /dev/null |
|
try=$(expr $try + 1) |
|
if [ $try -eq 5 ]; then |
|
echo "Backup failed 5 times, aborting on $(date)" >> $LOG_FILE |
|
mtstatus "Backup failed" |
|
rm /tmp/$(basename $WORLDDIR)_local_backup_in_progress 2> /dev/null |
|
exit 1 |
|
else |
|
echo "map.sqlite backup attempt #$try failed, retrying in 60 seconds" >> $LOG_FILE |
|
sleep 60 |
|
fi |
|
fi |
|
done |
|
} |
|
|
|
startupMT() { |
|
if [[ -f "$CONFDIR/startup" ]]; then |
|
if [[ -f "$CONFDIR/.booted" ]]; then |
|
exit 1 |
|
fi |
|
failed="" |
|
while read -r line |
|
do |
|
/usr/bin/mtctl start "$line" || failed="$failed, $line" |
|
done < "$CONFDIR/startup" |
|
if [[ "x$failed" == "x" ]]; then |
|
echo "Success!" |
|
else |
|
echo "Servers have failed to start$failed" |
|
fi |
|
touch $CONFDIR/.booted |
|
fi |
|
} |
|
|
|
enableMT() { |
|
SetWorld "$1" |
|
if [[ -f "$CONFDIR/startup" ]]; then |
|
INCLUDES=$(grep -x "$NAME" $CONFDIR/startup) |
|
if [[ "X$INCLUDES" == "X$NAME" ]]; then |
|
echo "$NAME is already enabled!" |
|
return |
|
fi |
|
fi |
|
if [[ $(crontab -l | egrep -v "^(#|$)" | grep -q '/usr/bin/mtctl startup'; echo $?) == 1 ]]; then |
|
set -f |
|
crontab -l > temp |
|
echo '@reboot rm $HOME/.mtctl/.booted 2>/dev/null; /usr/bin/mtctl startup' >> temp |
|
cat temp | crontab - |
|
rm temp |
|
set +f |
|
fi |
|
echo "$NAME" >> $CONFDIR/startup |
|
echo "$NAME is enabled!" |
|
} |
|
|
|
disableMT() { |
|
SetWorld "$1" |
|
if [[ -f "$CONFDIR/startup" ]]; then |
|
INCLUDES=$(grep -x "$NAME" $CONFDIR/startup) |
|
if [[ "X$INCLUDES" == "X$NAME" ]]; then |
|
grep -xv "$NAME" $CONFDIR/startup > temp && mv temp $CONFDIR/startup |
|
echo "$NAME is disabled!" |
|
return |
|
fi |
|
fi |
|
echo "$NAME is already disabled!" |
|
} |
|
|
|
|
#--------------------------------------------------------------------- |
#--------------------------------------------------------------------- |
# Handle the actions |
# Handle the actions |
ACTION="$1" |
ACTION="$1" |
|
|
create) |
create) |
mtsetup |
mtsetup |
createMT "$2" |
createMT "$2" |
|
;; |
|
backup) |
|
mtsetup |
|
backupMT "$2" |
|
;; |
|
startup) |
|
startupMT |
|
;; |
|
enable) |
|
mtsetup |
|
enableMT "$2" |
|
;; |
|
disable) |
|
mtsetup |
|
disableMT "$2" |
;; |
;; |
version) |
version) |
mtinfo "Version: $VERSION" |
mtinfo "Version: $VERSION" |