How does my Minecraft SMP work
NOTE: This tutorial expects you to know your basics of working with a remote Linux server via SSH and most importantly have a remote server to SSH into; I also expect you to be mentally capable to deal with both systemd and java antics.
Recently, the VPS I host my Minecraft SMP on, has been upgraded and the owner decided to revise the way they share it with the people currently using it; instead of all the users sharing a single environment (and having no root access which calls for using quirky ways of automation) now everyone is given a separate virtual container and have network ports remapped with a prefix (example: outside port 15443 maps to port 443 in the container).
Because using crontab is no longer required, I decided to re-setup NRMC’s automation using stuff that systemd provides and share it here with everyone else.
Creating a directory
By default (and I have no idea if this is patchable) systemd prevents any service from touching stuff anywhere near the /home/
directory, so instead we will be using a different dedicated path in the /srv/
which makes more sense than /home/
:
# mkdir -p /srv/minecraft
After that you have to copy all the necessary files like a server JAR, configs and really anything else your admincraft heart desires. This can be done by either using an SFTP client or downloading files from the terminal using either curl or wget.
Creating a dummy user
Running the service as root may result in an accident, so we create a separate user for the server:
# useradd -d /srv/minecraft -s /sbin/nologin minecraft
This will create a minecraft
user that is not able to login and has home directory set to /srv/minecraft
which we dedicated for the Minecraft server.
Now that the user is created and files are copied, it’s a good idea to recursively transfer ownership of the directory to the dedicated user:
# chown -R minecraft:minecraft /srv/minecraft
Stuffing commands
Because there is no longer a GNU screen session to stuff input commands into, now we use systemd’s socket units to define a FIFO file which is then linked with the server’s standard input.
File: systemd/minecraft.socket
[Unit]
Description=Minecraft Server STDIN [NRMC]
Wants=network-online.target
After=network-online.target
[Socket]
ListenFIFO=%t/minecraft-stdin.fifo
Service=minecraft.service
Then running a command is as simple as piping output to a file:
$ echo "say this is a test" > /run/minecraft-stdin.fifo
Daemonizing
The service file defines what to run, what user-group ID pair to use and generally what environment the service is supposed to have. This includes piping standard input socket into the process.
File: systemd/minecraft.service
[Unit]
Description=Minecraft Server [NRMC]
Wants=network-online.target
After=network-online.target
[Service]
User=minecraft
Group=minecraft
WorkingDirectory=/srv/minecraft
ExecStart=/usr/bin/java -Xms1024M -Xmx4096M -XX:ActiveProcessorCount=4 -jar paper-1.20.4-497.jar --nogui --universe save
Sockets=minecraft.socket
StandardInput=socket
StandardOutput=journal
StandardError=journal
Restart=always
RestartSec=30
[Install]
WantedBy=multi-user.target
Daemon updates
Because the server scripts and configs are open-source I needed a simple way to edit a daemon file in the repository root, then apply changes and do a system-wide daemon reload. This script does exactly what I need:
File: systemd/update.sh
#!/bin/bash
cd $(realpath $(dirname ${0}))
cp ${PWD}/minecraft.service /etc/systemd/system/minecraft.service
cp ${PWD}/minecraft.socket /etc/systemd/system/minecraft.socket
systemctl daemon-reload
Announcements
The server is scheduled to restart every day, so there needs to be a way of warning the users half an hour in advance. The script to send a visual and audible message runs the following Minecraft JE commands:
execute at @a run playsound mineraft:item.goat_horn.sound.1 ambient @p ~ ~ ~ 10 1
say <announcement text>
title @a reset
title @a subtitle "<announcement text>"
title @a title ""
NOTE: the
playsound
command is broken for some reason which requires testing. I only noticed this because I helped a bloke at work to set up his SMP using my scripts. For some reason it was working just fine before the systemd migration?
These commands reset the title, print a text, play a sound and display the same exact text as a subtitle. The announcement looks like this:
File: systemd/announce.sh
#!/bin/sh
printf "%s\n" "execute at @a run playsound mineraft:item.goat_horn.sound.1 ambient @p ~ ~ ~ 10 1" > /run/minecraft-stdin.fifo
printf "%s\n" "say ${*}" > /run/minecraft-stdin.fifo
printf "%s\n" "title @a reset" > /run/minecraft-stdin.fifo
printf "%s\n" "title @a subtitle \"${*}\"" > /run/minecraft-stdin.fifo
printf "%s\n" "title @a title \"\"" > /run/minecraft-stdin.fifo
You cannot escape crontab
The server is still supposed to restart every day, so a bunch of cron jobs is necessary:
30 19 * * * /srv/minecraft/systemd/announce.sh Server restart in 30 minutes
45 19 * * * /srv/minecraft/systemd/announce.sh Server restart in 15 minutes
55 19 * * * /srv/minecraft/systemd/announce.sh Server restart in 5 minutes
59 19 * * * /srv/minecraft/systemd/announce.sh Server restart in 1 minute
00 20 * * * /usr/bin/systemctl restart minecraft.service