diff --git a/nixos/modules/services/games/minecraft-server.nix b/nixos/modules/services/games/minecraft-server.nix
index f50d2897843..7d26d150165 100644
--- a/nixos/modules/services/games/minecraft-server.nix
+++ b/nixos/modules/services/games/minecraft-server.nix
@@ -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 25565. The server
+ If enabled, start a Minecraft Server. The server
data will be loaded from and saved to
- ${cfg.dataDir}.
+ .
+ '';
+ };
+
+ declarative = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Whether to use a declarative Minecraft server configuration.
+ Only if set to true, the options
+ and
+ will be
+ applied.
+ '';
+ };
+
+ eula = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Whether you agree to
+
+ Mojangs EULA. This option must be set to
+ true 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
+ is
+ true and the whitelist is enabled
+ via by
+ setting white-list to true.
+ This is a mapping from Minecraft usernames to UUIDs.
+ You can use 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
+ is set to true. See
+
+ 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.";
+ }
+ ];
+
};
}
diff --git a/pkgs/games/minecraft-server/default.nix b/pkgs/games/minecraft-server/default.nix
index 050e0126fa7..c2f20f53e9a 100644
--- a/pkgs/games/minecraft-server/default.nix
+++ b/pkgs/games/minecraft-server/default.nix
@@ -1,37 +1,63 @@
{ stdenv, fetchurl, jre }:
-stdenv.mkDerivation rec {
- name = "minecraft-server-${version}";
- version = "1.13.2";
+let
+ common = { version, sha256, url }:
+ stdenv.mkDerivation (rec {
+ name = "minecraft-server-${version}";
+ inherit version;
- src = fetchurl {
- # Old url
- # https://s3.amazonaws.com/Minecraft.Download/versions/${version}/minecraft_server.${version}.jar
+ src = fetchurl {
+ inherit url sha256;
+ };
+
+ preferLocalBuild = true;
+
+ installPhase = ''
+ mkdir -p $out/bin $out/lib/minecraft
+ cp -v $src $out/lib/minecraft/server.jar
+
+ cat > $out/bin/minecraft-server << EOF
+ #!/bin/sh
+ exec ${jre}/bin/java \$@ -jar $out/lib/minecraft/server.jar nogui
+ EOF
+
+ chmod +x $out/bin/minecraft-server
+ '';
+
+ phases = "installPhase";
+
+ meta = {
+ description = "Minecraft Server";
+ homepage = "https://minecraft.net";
+ license = stdenv.lib.licenses.unfreeRedistributable;
+ platforms = stdenv.lib.platforms.unix;
+ maintainers = with stdenv.lib.maintainers; [ thoughtpolice tomberek costrouc];
+ };
+ });
+
+in {
+ minecraft-server_1_13_2 = common {
+ version = "1.13.2";
url = "https://launcher.mojang.com/v1/objects/3737db93722a9e39eeada7c27e7aca28b144ffa7/server.jar";
sha256 = "13h8dxrrgqa1g6sd7aaw26779hcsqsyjm7xm0sknifn54lnamlzz";
};
- preferLocalBuild = true;
-
- installPhase = ''
- mkdir -p $out/bin $out/lib/minecraft
- cp -v $src $out/lib/minecraft/server.jar
-
- cat > $out/bin/minecraft-server << EOF
- #!/bin/sh
- exec ${jre}/bin/java \$@ -jar $out/lib/minecraft/server.jar nogui
- EOF
-
- chmod +x $out/bin/minecraft-server
- '';
-
- phases = "installPhase";
-
- meta = {
- description = "Minecraft Server";
- homepage = "https://minecraft.net";
- license = stdenv.lib.licenses.unfreeRedistributable;
- platforms = stdenv.lib.platforms.unix;
- maintainers = [ stdenv.lib.maintainers.thoughtpolice stdenv.lib.maintainers.tomberek ];
+ minecraft-server_1_13_1 = common {
+ version = "1.13.1";
+ url = "https://launcher.mojang.com/mc/game/1.13.1/server/fe123682e9cb30031eae351764f653500b7396c9/server.jar";
+ sha256 = "1lak29b7dm0w1cmzjn9gyix6qkszwg8xgb20hci2ki2ifrz099if";
};
+
+ minecraft-server_1_13_0 = common {
+ version = "1.13.0";
+ url = "https://launcher.mojang.com/mc/game/1.13/server/d0caafb8438ebd206f99930cfaecfa6c9a13dca0/server.jar";
+ sha256 = "1fahqnylxzbvc0fdsqk0x15z40mcc5b7shrckab1qcsdj0kkjvz7";
+ };
+
+ minecraft-server_1_12_2 = common {
+ version = "1.12.2";
+ url = "https://s3.amazonaws.com/Minecraft.Download/versions/1.12.2/minecraft_server.1.12.2.jar";
+ sha256 = "0zhnac6yvkdgdaag0gb0fgrkgizbwrpf7s76yqdiknfswrs947zy";
+ };
+
}
diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix
index 035889134a0..a2a7d5185c1 100644
--- a/pkgs/top-level/all-packages.nix
+++ b/pkgs/top-level/all-packages.nix
@@ -20783,7 +20783,13 @@ in
minecraft = callPackage ../games/minecraft { };
- minecraft-server = callPackage ../games/minecraft-server { };
+ minecraft-server = minecraft-server_1_13_2;
+
+ inherit (callPackages ../games/minecraft-server { })
+ minecraft-server_1_13_2
+ minecraft-server_1_13_1
+ minecraft-server_1_13_0
+ minecraft-server_1_12_2;
moon-buggy = callPackage ../games/moon-buggy {};