Add server hibernation feature
What does this MR do and why?
This MR adds support for the automatic opening and closing of inactive Minecraft servers that are hosted using Craft Controller. In summary, the changes add an option that can be enabled in the server configuration page. The mentioned setting enables two things on the server it was enabled in: the automatic closing of the server when there are zero players, putting it in "hibernation mode"; and the hosting of a "dummy server" that wakes up (starts) the "hibernated" real server when someone tries to connect to it.
This is a feature that was of great interest to me, so i'm putting together this MR in case it is of anyone else's interest as well.
Use case/Why
This feature can be useful if you want/need to reserve the maximum amount of system resources possible, such as RAM and CPU usage.
Example system resource usage if there are no players on a server (forge server, 1.19.2, with 50 mods or so):
Another example (vanilla server, 1.19.2):
System resource usage when the server is not running:
It's then possible to conclude that we can save quite a chunk of RAM and a bit of processing power by keeping the server closed when there are no players currently logged on it. That of course, comes with a clear disadvantage: while the server is in "hibernation mode" (effectively turned off) there will be no server-side calculations done, like chunk updating, for example.
The host used for testing is an old pc that i am using as a simple 24/7 home server (only running crafty while testing), specs:
OS: Fedora Server 38
Host: Docker container
CPU: Intel i3 9100f
RAM: 16gb RAM (dual-channel)
STORAGE: 240gb SATA SSD
GPU: None (headless)
How it works - in short
An schedule task was added that checks all servers periodically for their player count. If the server has been up for an X amount of time (defined in config.json, 10 minutes by default) and there are no players online, it will close the server and run a "fake" server on the same port as the real one, it shows a custom message for the players that try to connect to the (now closed) real server, like so:
If someone tries connecting to the fake server, it will run the real server again and close the fake one, then the player can connect to the real server and play as they would normally. Whenever the server is empty again, it will re-execute the described process.
How it works - in detail
Dummy servers
A "dummy server" in this context refers to a server that is "fake", it shows on the Minecraft multiplayer screen and allows players to connect normally, but will not host any type of complex communication. In this case, the servers are created using the Quarry library, as it allows us to define a custom Minecraft protocol handler using a simple and lightweight TCP server.
The handlers used here are present in the files dummy_server_controller.py and dummy_servers.py, implementing the control of dummy servers and the handler itself, respectively. The controller creates a simple server that listens on a specific port and shows a custom description (motd). The handler is responsible for what happens when a player connects to the server, in this case, it only kicks the player out with a custom message that is defined by the user (per server).
How was this integrated in Crafty? A new simple thread that listens for connections to the dummy servers on the background was implemented. In addition, an scheduled task that runs every minute checking all servers was added, it starts/stop servers when needed.
All changes
Server management - front-end
Three fields were added to the server config screen, one that activates the server hibernation and two more for the custom server description and disconnect message:
The two text fields should appear only if the checkbox is checked.
Back-end
Models:
- Three new fields were added to the Servers model:
close_when_inactive
for the configuration option,hibernation_motd
for the custom server description andwake_up_message
for the message that is shown to players when they wake the hibernated server.
Server management:
- A call to stop a dummy server was added when removing a server, in case you delete a server that was using the hibernation option.
- Location:
servers_controller.py:remove_server
- Location:
- A call to check the dummy servers was added when updating a server, in case the user enabled/disabled hibernation when it is stopped, needing to run/stop a dummy server for it or in the case the server port was changed in the server config, needing to rerun the currently running dummy server on a different port.
- Location:
servers_controller.py:update_server
- Location:
- A call to stop a dummy server was added when starting a server, to prevent port binding conflicts when starting any server manually.
- Location:
server.py:start_server
(The method's existing logic was not changed, i just needed to put it inside a callback for the dummy server stop action)
- Location:
Tasks/Schedule:
- A job that checks all servers to take an action starting/stopping dummy servers that runs every minute has been added.
- A new thread for dummy servers was added in
dummy_servers.py
.
Translations:
- Translations for the new front-end fields were added for the en_EN and pt_BR languages (only two languages i'm fluent on).
Configuration:
- Added the
inactive_server_timeout
configuration key, that specifies the timeout in minutes before putting a server in hibernation.
New feature performance impact
The performance impact caused by this feature is as presented here:
We could say the impact is minimal overall, the two instances have 2 severs installed, with the new feature enabled on the craft_davi_test container.
How to set up and validate locally
The new configuration should work out of the box without the need for any set up.
Validation suggestion:
- Create new servers with and without the new feature.
- Disable and enable the new configuration on servers that are stopped, noting if the dummy servers are stopped and started correctly.
- Check if the servers are stopped correctly when there are no players online after the set timeout (and if a dummy server is started for the server).
- Try joining a dummy server and checking if it shows the correct messages and is stopped and starts the real server correctly.
- Save a stopped server that has the option enabled and try changing the configuration and saving, then check if the dummy server is stopped/started correctly (and has changed ports, if the server port was modified).
- Check if servers without the feature enabled are working as they would.
MR acceptance checklist
This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.
-
Have you checked this doesn't interfere/conflict/duplicate someone elses work? -
Have you fully tested your changes? - Partly, tested over a few days using my and my friends' servers -
Have you resolved any lint issues? - Yes, using black -
Have you assigned a reviewer? -
Have you applied correct labels?