so i decided to host a small java minecraft server, should be easy right?
so i decided to host a small java minecraft server, should be easy right?
so let's start with containers i don't use docker, i don't like it, so i use lxc
the host is an arch system, which comes with lxc-net, a script that automatically sets up a bridge, iptables (or nftables if you have it, which you do because it requires dnsmasq), dnsmasq for dhcp and that's about it
setting up the container was simple enough, i used alpine, just editing subuid and subgid was enough to get it running unprivileged
the container instantly got internet access with no more setup, but had a dynamic ip and couldn't get any incoming new connections
making dnsmasq give it a static ip based on hostname worked, but sometimes it gets stuck as leased and gets a dynamic one :\
getting it to receive connections was annoying, iptables is annoying, nftables was so much better (and iptables apparently is backed by nftables on arch if you have it installed!)
here's the rule i got eventually:
table ip papermc {
chain prerouting {
type nat hook prerouting priority dstnat; policy accept;
tcp dport 25565 dnat to 10.0.3.100
}
}
now for actually starting the server, it can receive commands from the server operator in-game, RCON or the java console. i picked putting the java console inside tmux because i had no idea RCON existed lol
so the server is running under its own user (why would you run minecraft as root?) but i use root, since that's what i get when attaching to the container
solution? tmux sockets! normally they're owned by the user (and tmux enforces that, i think), but root can use them anyway (i hope you weren't relying on that for privacy and security)
so for actually sending commands, i write them with
tmux send "$@" Enter
.
basic stuff, but what about reading back the output?
tmux pipe-pane -o "cat >> $PIPE"
tmux send "$@" Enter
sleep 0.5
tmux pipe-pane sed '1d,$d' $PIPE
rm $PIPE
yikes.
for those those that don't know what pipe-pane does, it writes the changes from a pane (terminal view) onto a file. yeah, gotta be careful grepping that, the server can output anything while you're reading the pipe, or the command takes more than 0.5 seconds!
that horror aside, now the server can stay on
without any players and eat resources, what do?
after snapshot 24w33a, minecraft servers got the
pause-when-empty-seconds
property, which only pauses gameplay, bummer. need a
new solution.
so, first step is stopping the server once nobody has been off for long enough
there are probably bukkit / paper plugins for that, but i didn't bother, i have the power of tmux!
using the earlier reading-output-from-command shell
script, i can send
list
and get a list of
players, but even more useful it writes
There are N of a max of N players
!
using that periodically, what to do to wake up?
well, i tried socat
, so
when a player connected, the connection got closed
and the java server starts back up, which worked
but... the server appears offline! and there is no
indication of it starting back up! so what exists to
act like a minecraft server?
well there is mcsleepingserverstarter, but javascript... i would rather have a java server running
there's also lazymc, it's in rust but it's an entire proxy, overkill
so time to write my own!
sleepy is my own attempt at a sleep minecraft server!
the details are kinda boring, it's most just parsing minecraft java protocol datatypes, replying to status and ping requests and kicking on join
the most interesting part is that it prints on first join so you can make a shell script that starts the java server like so
nft destroy table ip papermc
sleepy "0.0.0.0:25565" | while read -r line;
do echo "$line"
start_java_server
sleep 15
nft add table ip papermc
nft add chain ip papermc prerouting "{ type nat hook prerouting priority filter; policy accept; }"
nft add rule ip papermc prerouting tcp dport 25565 redirect to 25566
pkill sleepy
break
done &
it changes nftables rules on the fly, mostly because minecraft binds to the port early before actually being able to reply, so it will look offline for a bit, so i just make it use another port and redirect traffic there after it's done
also credits to the minecraft wiki for their awesome protocol docs!