nixos/mincraft-server: refactor
- allow for options to (added 2 options): - agree to eula (eula.txt) true/false will create symlink over existing eula.txt to `/nix/store/...`. - whitelist users (optional and will symlink over existing whitelist.json and create backup) - server.properties can be configured with the serverProperties option. If there is an existing server.properties it will copy it to a server.properties.old to keep the old one. server.properties MUST be writable thus symlinking is not an option. - all ports that are stated in `server.properties` are exposed properly in the firewall. (infinisil) nixos/minecraft-server: Fix, refactor and polish Adds an option `declarative` (defaulted to false), in order to stay (mostly) backwards compatible. The only thing that's not backwards compatible is that you now need to agree to the EULA on evaluation time, but that's guarded by an assertion and therefore doesn't need a release note.
This commit is contained in:
parent
9080b2eadf
commit
58c89ec26a
|
@ -4,8 +4,41 @@ with lib;
|
|||
|
||||
let
|
||||
cfg = config.services.minecraft-server;
|
||||
in
|
||||
{
|
||||
|
||||
# We don't allow eula=false anyways
|
||||
eulaFile = builtins.toFile "eula.txt" ''
|
||||
# eula.txt managed by NixOS Configuration
|
||||
eula=true
|
||||
'';
|
||||
|
||||
whitelistFile = pkgs.writeText "whitelist.json"
|
||||
(builtins.toJSON
|
||||
(mapAttrsToList (n: v: { name = n; uuid = v; }) cfg.whitelist));
|
||||
|
||||
cfgToString = v: if builtins.isBool v then boolToString v else toString v;
|
||||
|
||||
serverPropertiesFile = pkgs.writeText "server.properties" (''
|
||||
# server.properties managed by NixOS configuration
|
||||
'' + concatStringsSep "\n" (mapAttrsToList
|
||||
(n: v: "${n}=${cfgToString v}") cfg.serverProperties));
|
||||
|
||||
|
||||
# To be able to open the firewall, we need to read out port values in the
|
||||
# server properties, but fall back to the defaults when those don't exist.
|
||||
# These defaults are from https://minecraft.gamepedia.com/Server.properties#Java_Edition_3
|
||||
defaultServerPort = 25565;
|
||||
|
||||
serverPort = cfg.serverProperties.server-port or defaultServerPort;
|
||||
|
||||
rconPort = if cfg.serverProperties.enable-rcon or false
|
||||
then cfg.serverProperties."rcon.port" or 25575
|
||||
else null;
|
||||
|
||||
queryPort = if cfg.serverProperties.enable-query or false
|
||||
then cfg.serverProperties."query.port" or 25565
|
||||
else null;
|
||||
|
||||
in {
|
||||
options = {
|
||||
services.minecraft-server = {
|
||||
|
||||
|
@ -13,10 +46,32 @@ in
|
|||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
If enabled, start a Minecraft Server. The listening port for
|
||||
the server is always <literal>25565</literal>. The server
|
||||
If enabled, start a Minecraft Server. The server
|
||||
data will be loaded from and saved to
|
||||
<literal>${cfg.dataDir}</literal>.
|
||||
<option>services.minecraft-server.dataDir</option>.
|
||||
'';
|
||||
};
|
||||
|
||||
declarative = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to use a declarative Minecraft server configuration.
|
||||
Only if set to <literal>true</literal>, the options
|
||||
<option>services.minecraft-server.whitelist</option> and
|
||||
<option>services.minecraft-server.serverProperties</option> will be
|
||||
applied.
|
||||
'';
|
||||
};
|
||||
|
||||
eula = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether you agree to
|
||||
<link xlink:href="https://account.mojang.com/documents/minecraft_eula">
|
||||
Mojangs EULA</link>. This option must be set to
|
||||
<literal>true</literal> to run Minecraft server.
|
||||
'';
|
||||
};
|
||||
|
||||
|
@ -24,7 +79,7 @@ in
|
|||
type = types.path;
|
||||
default = "/var/lib/minecraft";
|
||||
description = ''
|
||||
Directory to store minecraft database and other state/data files.
|
||||
Directory to store Minecraft database and other state/data files.
|
||||
'';
|
||||
};
|
||||
|
||||
|
@ -32,21 +87,84 @@ in
|
|||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to open ports in the firewall (if enabled) for the server.
|
||||
Whether to open ports in the firewall for the server.
|
||||
'';
|
||||
};
|
||||
|
||||
whitelist = mkOption {
|
||||
type = let
|
||||
minecraftUUID = types.strMatching
|
||||
"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" // {
|
||||
description = "Minecraft UUID";
|
||||
};
|
||||
in types.attrsOf minecraftUUID;
|
||||
default = {};
|
||||
description = ''
|
||||
Whitelisted players, only has an effect when
|
||||
<option>services.minecraft-server.declarative</option> is
|
||||
<literal>true</literal> and the whitelist is enabled
|
||||
via <option>services.minecraft-server.serverProperties</option> by
|
||||
setting <literal>white-list</literal> to <literal>true</literal>.
|
||||
This is a mapping from Minecraft usernames to UUIDs.
|
||||
You can use <link xlink:href="https://mcuuid.net/"/> to get a
|
||||
Minecraft UUID for a username.
|
||||
'';
|
||||
example = literalExample ''
|
||||
{
|
||||
username1 = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
|
||||
username2 = "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy";
|
||||
};
|
||||
'';
|
||||
};
|
||||
|
||||
serverProperties = mkOption {
|
||||
type = with types; attrsOf (either bool (either int str));
|
||||
default = {};
|
||||
example = literalExample ''
|
||||
{
|
||||
server-port = 43000;
|
||||
difficulty = 3;
|
||||
gamemode = 1;
|
||||
max-players = 5;
|
||||
motd = "NixOS Minecraft server!";
|
||||
white-list = true;
|
||||
enable-rcon = true;
|
||||
"rcon.password" = "hunter2";
|
||||
}
|
||||
'';
|
||||
description = ''
|
||||
Minecraft server properties for the server.properties file. Only has
|
||||
an effect when <option>services.minecraft-server.declarative</option>
|
||||
is set to <literal>true</literal>. See
|
||||
<link xlink:href="https://minecraft.gamepedia.com/Server.properties#Java_Edition_3"/>
|
||||
for documentation on these values.
|
||||
'';
|
||||
};
|
||||
|
||||
package = mkOption {
|
||||
type = types.package;
|
||||
default = pkgs.minecraft-server;
|
||||
defaultText = "pkgs.minecraft-server";
|
||||
example = literalExample "pkgs.minecraft-server_1_12_2";
|
||||
description = "Version of minecraft-server to run.";
|
||||
};
|
||||
|
||||
jvmOpts = mkOption {
|
||||
type = types.str;
|
||||
type = types.separatedString " ";
|
||||
default = "-Xmx2048M -Xms2048M";
|
||||
description = "JVM options for the Minecraft Service.";
|
||||
# Example options from https://minecraft.gamepedia.com/Tutorials/Server_startup_script
|
||||
example = "-Xmx2048M -Xms4092M -XX:+UseG1GC -XX:+CMSIncrementalPacing "
|
||||
+ "-XX:+CMSClassUnloadingEnabled -XX:ParallelGCThreads=2 "
|
||||
+ "-XX:MinHeapFreeRatio=5 -XX:MaxHeapFreeRatio=10";
|
||||
description = "JVM options for the Minecraft server.";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
|
||||
users.users.minecraft = {
|
||||
description = "Minecraft Server Service user";
|
||||
description = "Minecraft server service user";
|
||||
home = cfg.dataDir;
|
||||
createHome = true;
|
||||
uid = config.ids.uids.minecraft;
|
||||
|
@ -57,17 +175,60 @@ in
|
|||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network.target" ];
|
||||
|
||||
serviceConfig.Restart = "always";
|
||||
serviceConfig.User = "minecraft";
|
||||
script = ''
|
||||
cd ${cfg.dataDir}
|
||||
exec ${pkgs.minecraft-server}/bin/minecraft-server ${cfg.jvmOpts}
|
||||
'';
|
||||
serviceConfig = {
|
||||
ExecStart = "${cfg.package}/bin/minecraft-server ${cfg.jvmOpts}";
|
||||
Restart = "always";
|
||||
User = "minecraft";
|
||||
WorkingDirectory = cfg.dataDir;
|
||||
};
|
||||
|
||||
preStart = ''
|
||||
ln -sf ${eulaFile} eula.txt
|
||||
'' + (if cfg.declarative then ''
|
||||
|
||||
if [ -e .declarative ]; then
|
||||
|
||||
# Was declarative before, no need to back up anything
|
||||
ln -sf ${whitelistFile} whitelist.json
|
||||
cp -f ${serverPropertiesFile} server.properties
|
||||
|
||||
else
|
||||
|
||||
# Declarative for the first time, backup stateful files
|
||||
ln -sb --suffix=.stateful ${whitelistFile} whitelist.json
|
||||
cp -b --suffix=.stateful ${serverPropertiesFile} server.properties
|
||||
|
||||
# server.properties must have write permissions, because every time
|
||||
# the server starts it first parses the file and then regenerates it..
|
||||
chmod +w server.properties
|
||||
echo "Autogenerated file that signifies that this server configuration is managed declaratively by NixOS" \
|
||||
> .declarative
|
||||
|
||||
fi
|
||||
'' else ''
|
||||
if [ -e .declarative ]; then
|
||||
rm .declarative
|
||||
fi
|
||||
'');
|
||||
};
|
||||
|
||||
networking.firewall = mkIf cfg.openFirewall {
|
||||
allowedUDPPorts = [ 25565 ];
|
||||
allowedTCPPorts = [ 25565 ];
|
||||
};
|
||||
networking.firewall = mkIf cfg.openFirewall (if cfg.declarative then {
|
||||
allowedUDPPorts = [ serverPort ];
|
||||
allowedTCPPorts = [ serverPort ]
|
||||
++ optional (! isNull queryPort) queryPort
|
||||
++ optional (! isNull rconPort) rconPort;
|
||||
} else {
|
||||
allowedUDPPorts = [ defaultServerPort ];
|
||||
allowedTCPPorts = [ defaultServerPort ];
|
||||
});
|
||||
|
||||
assertions = [
|
||||
{ assertion = cfg.eula;
|
||||
message = "You must agree to Mojangs EULA to run minecraft-server."
|
||||
+ " Read https://account.mojang.com/documents/minecraft_eula and"
|
||||
+ " set `services.minecraft-server.eula` to `true` if you agree.";
|
||||
}
|
||||
];
|
||||
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue