Skip to content

Instantly share code, notes, and snippets.

@carchrae
Last active July 30, 2023 15:25
Show Gist options
  • Save carchrae/3dc0da023c8e9bb36441dcd439950870 to your computer and use it in GitHub Desktop.
Save carchrae/3dc0da023c8e9bb36441dcd439950870 to your computer and use it in GitHub Desktop.
stop (pause) inactive minecraft server process
#!/bin/bash
# /home/minecraft/bin/minecraft-sleep.sh
#set -v
cd "$(dirname "$0")"
# service name (i'm using systemd)
SERVICE_NAME="$1"
# set this to the port your minecraft server is on
#PORT=25565
PORT=`grep server-port ../$SERVICE_NAME/server.properties | cut -d '=' -f2`
# delay before starting
START_DELAY=600
# how frequently do we check for connections (seconds)
CHECK_DELAY=600
# script will start the server briefly duing each cycle
POLL_CYCLE=20
# how long to resume server during poll cycle
POLL_RUN=1
# delay in checking for connections during poll
POLL_DELAY=1
# maybe you want to customise this, eg if not using systemd
function find_pid(){
echo $(ps -o 'pid,ppid,args,unit' `pgrep java` | grep $SERVICE_NAME | xargs | cut -d ' ' -f1)
}
function pause(){
# this stops the process (like ctrl-z)
PID=$(find_pid)
if [ -n "$PID" ]; then
kill -STOP $PID
else
echo "could not find PID"
fi
}
function resume(){
PID=$(find_pid)
if [ -n "$PID" ]; then
kill -CONT $PID
else
echo "could not find PID"
fi
}
resume
sleep $START_DELAY
while true; do
# wait a bit before looping again
sleep 120
# some random so processes don't wake up all at the same time
sleep $[ ( $RANDOM % 10 ) + 1 ]s
echo "checking for activity on $PORT"
# while ( netstat -tn | grep $PORT | grep ESTABLISHED ); do
while ( netstat -tn | grep $PORT ); do
#echo "still active..."
sleep $CHECK_DELAY
done
echo "no active connections on $PORT, so pausing process $(find_pid)"
pause
echo "waiting for activity on $PORT, will run server for $POLL_RUN seconds every $POLL_CYCLE seconds."
ACTIVE=""
while [ -z "$ACTIVE" ]; do
#echo "Resume process $(find_pid) for $POLL_RUN seconds to prevent crashes/timeouts"
resume
sleep $POLL_RUN
pause
#echo "waiting for activity on $PORT"
POLL_UNTIL=$((SECONDS+POLL_CYCLE))
while [ $SECONDS -lt $POLL_UNTIL ]; do
ACTIVE=`netstat -tn | grep $PORT`
#echo "ACTIVE=$ACTIVE"
if [ -n "$ACTIVE" ]; then
break
else
sleep $POLL_DELAY
fi
done
done;
echo "Resuming process $(find_pid) due to activty $ACTIVE"
resume
done
#!/bin/bash
# /home/minecraft/bin/minecraft-wake.sh
## this just pokes the port of a minecraft instance, making it wake up. equivalent to `resume` in minecraft-sleep.sh
## you might want to use this if you have some scripts that run, eg backups, that need the server responding
## as if a user was logged in
cd "$(dirname "$0")"
#set -v
# service name (i'm using systemd)
SERVICE_NAME="$1"
# set this to the port your minecraft server is on
#PORT=25565
PORT=`grep server-port ../$SERVICE_NAME/server.properties | cut -d '=' -f2`
echo "boop" | nc -q 3 localhost $PORT
# /etc/systemd/system/minecraft@.service
# this is a systemd service, use start and/or enable (over reboots)
# presumes that minecraft runs as a restricted user 'minecraft'
[Unit]
Description=Minecraft Server: %i
After=network.target
[Service]
WorkingDirectory=/home/minecraft/%i
User=minecraft
Group=minecraft
Restart=always
ExecStartPre=/home/minecraft/bin/start-minecraft-sleep.sh %i
ExecStart=/usr/bin/screen -DmS mc-%i /home/minecraft/%i/start.sh
ExecStop=/home/minecraft/bin/stop-minecraft-sleep.sh %i || true
ExecStop=/bin/sleep 3
ExecStop=/usr/bin/screen -p 0 -S mc-%i -X eval 'stuff "say SERVER SHUTTING DOWN IN 15 SECONDS..."\015' || true
ExecStop=/bin/sleep 5
ExecStop=/usr/bin/screen -p 0 -S mc-%i -X eval 'stuff "say SERVER SHUTTING DOWN IN 10 SECONDS..."\015' || true
ExecStop=/bin/sleep 5
ExecStop=/usr/bin/screen -p 0 -S mc-%i -X eval 'stuff "say SERVER SHUTTING DOWN IN 5 SECONDS..."\015' || true
ExecStop=/bin/sleep 5
ExecStop=/usr/bin/screen -p 0 -S mc-%i -X eval 'stuff "save-all"\015' || true
ExecStop=/usr/bin/screen -p 0 -S mc-%i -X eval 'stuff "stop"\015' || true
[Install]
WantedBy=multi-user.target
#!/bin/bash
# /home/minecraft/bin/start-minecraft-sleep.sh
cd "$(dirname "$0")"
# the gawk is to add timestamps
./minecraft-sleep.sh $1 | gawk '{ print strftime("[%Y-%m-%d %H:%M:%S]"), $0 }' >> ../$1/logs/sleep.log &
#!/bin/bash
# /home/minecraft/bin/stop-minecraft-sleep.sh
cd "$(dirname "$0")"
#set -v
SERVICE_NAME=$1
# maybe you want to customise this, eg if not using systemd
function find_pid(){
echo $(ps -o 'pid,ppid,args,unit' `pgrep java` | grep $SERVICE_NAME | cut -d ' ' -f1)
}
function resume(){
PID=$(find_pid)
if [ -n "$PID" ]; then
kill -CONT $PID
else
echo "could not find PID"
fi
}
echo "stopping minecraft-sleep.sh"
pkill -f "bin/minecraft-sleep.sh $1" || true
sleep 1
echo "resuming process $(find_pid)"
resume
@carchrae
Copy link
Author

glad it was of help. and i'm not sure this is my most elegant code - but is bash ever elegant!? :D

and indeed, adapt to fit. that is what i did based on michiel's code. i just had to generalise it a bit, since i am running three servers on the same box, and knockd seemed like overkill.

as an aside, i looked at this briefly too; https://github.com/jkutner/heroku-buildpack-minecraft - which is pretty clever (and free, but low RAM), but convoluted in how it exposes the port using ngrok. in the end, i hosted the minecraft servers on a pc at home, since 512mb isn't enough for a minecraft server unless you run an old version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment