89 lines
4 KiB
Markdown
89 lines
4 KiB
Markdown
2
|
|
Minecraft Rust Async Networking Proxy CRIU
|
|
# Minecraft servers are HUNGRY
|
|
|
|
They hunger for your ram and your cpu. This makes it either expensive or
|
|
laggy to try and host multiple servers at once.
|
|
|
|
This was something I encountered when my friend built a homeserver out of
|
|
spare computer parts that was barely powerful enough to run a minecraft
|
|
server.
|
|
|
|
The problem was that soon multiple people wanted a minecraft server
|
|
hosted by him (which included us wanting to play modded minecraft).
|
|
|
|
It was a hassle to ssh into the server and start and stop the various
|
|
servers depending on who wanted to play, especially since a lot of people
|
|
only played very rarily.
|
|
|
|
I remembered that I'd seen
|
|
[a project](https://github.com/gekware/minecraft-server-hibernation)
|
|
that claimed to be able to hibernate a minecraft server if nobody was
|
|
playing on it. The only issue was that it worked for a single server, and
|
|
did so by starting and stopping the server process.
|
|
|
|
This meant that if we wanted to join a modded server the hundreds of mods
|
|
could make us wait for several minutes before we could play.
|
|
|
|
Another issue was the fact that it could only host a single server. This
|
|
meant that we would have to run multiple intances of the watcher, and that
|
|
each server would be assigned to an arbitrary port that would be needed
|
|
when connecting.
|
|
|
|
# Building a reverse proxy for minecraft
|
|
|
|
Since my friends server was accessible through a domain we thought it
|
|
would be cool if instead of supplying a port you could connect to a
|
|
subdomain and be sent to a specific server.
|
|
|
|
The simplest way to do this would be to create a dhcp record for each
|
|
subdomain to point to a server, but that would be slow and tedious to set
|
|
up for every server.
|
|
|
|
We then tried nginx, as it seemingly could magically redirect traffic to
|
|
an internal port based on the subdomain. I quickly found out that this did
|
|
*not* work for minecraft servers (who would have guessed), but after doing
|
|
some research I decided on creating my own reverse proxy that spoke the
|
|
minecraft protocol instead of HTTP.
|
|
|
|
# The Minecraft protocol
|
|
|
|
Minecraft implements its own protocol consistent of packets. My first idea was to see if anybody had created a
|
|
rust library for dealing with minecraft packets.
|
|
|
|
While some did exist, most of them where unfinished or couldn't do what I wanted. [One crate](https://www.youtube.com/watch?v=E4WlUXrJgy4)
|
|
was useful for seeing how an implementation of parsing packets was done, but ultimately I had to write my own parser.
|
|
|
|
*images of minecraft protocol*
|
|
|
|
# Detecting the subdomain
|
|
|
|
Luckily for me, the Minecraft procotol sends the full address it is trying to connect to during the handshake. This meant that I simply
|
|
had to parse the handshake and extract the subdomain from the address, see if it matches any configured server, and then redirect all
|
|
traffic to the internal port that server runs on.
|
|
|
|
*code explanation*
|
|
|
|
# Why aren't my chunks loading?
|
|
|
|
Just as I thought I was finally done I encountered a problem. The chunks where loading *really* slowly. It felt like we we're back to
|
|
hosting on [aternos](aternos.org) (nothing against aternos, the **free** servers are just a little slow).
|
|
|
|
*image of chunks not loading*
|
|
|
|
I couldn't figure out why it was so slow. I thought it was because all the traffic had to flow through my program,
|
|
but the sockets are connected on the kernel level.
|
|
|
|
After hours of debugging I found the solution. `stream.set_nodelay(true)`
|
|
|
|
## ...
|
|
|
|
**All this time the problem was as simple as saying ** `if (slow) { dont(); }`!
|
|
|
|
To say I was pissed was an understatement, but at the same time I was glad it was finally working.
|
|
|
|
The reason this fixed it was because it disabled [Nagle's algorithm](https://en.wikipedia.org/wiki/Nagle%27s_algorithm), which bunches packets together
|
|
in order to (hopefully) speed up the sending of lots of smaller packets as one big packet.
|
|
|
|
For some reason this absolutely destroyed the chunk loading speed, and disabling it led to finally having the perfect Minecraft reverse proxy hibernation system thing.
|