diff --git a/hosts/greddost/configuration.nix b/hosts/greddost/configuration.nix index b17206e..c3cd78b 100644 --- a/hosts/greddost/configuration.nix +++ b/hosts/greddost/configuration.nix @@ -10,15 +10,25 @@ ../../hardware-configuration.nix ../../base.nix + + ../../services/minecraft ]; services.minecraft-server = { - enable = true; + enable = false; eula = true; - package = pkgs.unstable.minecraft-server; - dataDir = "/run/minecraft"; #"/fast/minecraft"; - jvmOpts = "-Xmx8192M -Xms8192M -XX:+UseG1GC -XX:ParallelGCThreads=2 -XX:MinHeapFreeRatio=5 -XX:MaxHeapFreeRatio=10"; + package = pkgs.callPackage ../../pkgs/minecraft-server-fabric { inherit (pkgs.unstable) minecraft-server; }; + dataDir = "/fast/minecraft"; #"/fast/minecraft"; + jvmOpts = "-Xms10G -Xmx10G -XX:+UnlockExperimentalVMOptions -XX:+UseZGC -XX:+DisableExplicitGC -XX:+AlwaysPreTouch -XX:+ParallelRefProcEnabled"; + + declarative = true; + serverProperties = { + view-distance = 32; + gamemode = 1; + enable-rcon = true; + "rcon.password" = "pvv"; + }; }; diff --git a/pkgs/minecraft-server-fabric/default.nix b/pkgs/minecraft-server-fabric/default.nix new file mode 100644 index 0000000..95d4f2d --- /dev/null +++ b/pkgs/minecraft-server-fabric/default.nix @@ -0,0 +1,40 @@ +{ callPackage, writeTextFile, writeShellScriptBin, minecraft-server, jre_headless }: + +let + loader = callPackage ./generate-loader.nix {}; + log4j = writeTextFile { + name = "log4j.xml"; + text = '' + + + + + + + + + + + + + + + + + + + + + + + + + + + ''; + }; +in +writeShellScriptBin "minecraft-server" '' + echo "serverJar=${minecraft-server}/lib/minecraft/server.jar" >> fabric-server-launcher.properties + exec ${jre_headless}/bin/java -Dlog4j.configurationFile=${log4j} $@ -jar ${loader} nogui +'' diff --git a/pkgs/minecraft-server-fabric/generate-loader.nix b/pkgs/minecraft-server-fabric/generate-loader.nix new file mode 100644 index 0000000..e2e4857 --- /dev/null +++ b/pkgs/minecraft-server-fabric/generate-loader.nix @@ -0,0 +1,38 @@ +{ lib, fetchurl, stdenv, unzip, zip, jre_headless }: + +let + lock = import ./lock.nix; + libraries = lib.forEach lock.libraries fetchurl; +in +stdenv.mkDerivation { + name = "fabric-server-launch.jar"; + nativeBuildInputs = [ unzip zip jre_headless ]; + + libraries = libraries; + + buildPhase = '' + for i in $libraries; do + unzip -o $i + done + + cat > META-INF/MANIFEST.MF << EOF + Manifest-Version: 1.0 + Main-Class: net.fabricmc.loader.impl.launch.server.FabricServerLauncher + Name: org/objectweb/asm/ + Implementation-Version: 9.2 + EOF + + cat > fabric-server-launch.properties << EOF + launch.mainClass=net.fabricmc.loader.impl.launch.knot.KnotServer + EOF + ''; + + installPhase = '' + jar cmvf META-INF/MANIFEST.MF "server.jar" . + zip -d server.jar 'META-INF/*.SF' 'META-INF/*.RSA' 'META-INF/*.DSA' + cp server.jar "$out" + ''; + + phases = [ "buildPhase" "installPhase" ]; +} + diff --git a/pkgs/minecraft-server-fabric/generate-lock-nix.sh b/pkgs/minecraft-server-fabric/generate-lock-nix.sh new file mode 100755 index 0000000..fba715a --- /dev/null +++ b/pkgs/minecraft-server-fabric/generate-lock-nix.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env nix-shell +#!nix-shell -i bash -p bash curl jq +curl https://meta.fabricmc.net/v2/versions/loader/1.18.1/0.12.12/server/json \ +| jq -r ' + .mainClass, + (.libraries[] + | .url as $url + | .name | split(":") as [$dir, $name, $version] + |"\($name)-\($version).zip|\($url)\($dir|sub("\\.";"/";"g"))/\($name)/\($version)/\($name)-\($version).jar" + )' \ +| { + echo '{' + read mainClass; + echo " mainClass = \"$mainClass\";" + echo " libraries = [" + while IFS="|" read name url; do + hash=$(nix-prefetch-url $url); + echo " { name = \"$name\"; sha256 = \"$hash\"; url = \"$url\"; }" + done + echo " ];" + echo '}' +} diff --git a/pkgs/minecraft-server-fabric/lock.nix b/pkgs/minecraft-server-fabric/lock.nix new file mode 100644 index 0000000..c625c45 --- /dev/null +++ b/pkgs/minecraft-server-fabric/lock.nix @@ -0,0 +1,16 @@ +{ + mainClass = "net.fabricmc.loader.impl.launch.knot.KnotServer"; + libraries = [ + { name = "tiny-mappings-parser-0.3.0+build.17.zip"; sha256 = "19kvhxfk5v01f2rvl7j02vqhn3nd2bh5jsgbk44rpzqv9f6074db"; url = "https://maven.fabricmc.net/net/fabricmc/tiny-mappings-parser/0.3.0+build.17/tiny-mappings-parser-0.3.0+build.17.jar"; } + { name = "sponge-mixin-0.10.7+mixin.0.8.4.zip"; sha256 = "18m5wksd9vjp676cxapkggnz8s3f8j89phln8gy5n8vxlrli8n0d"; url = "https://maven.fabricmc.net/net/fabricmc/sponge-mixin/0.10.7+mixin.0.8.4/sponge-mixin-0.10.7+mixin.0.8.4.jar"; } + { name = "tiny-remapper-0.6.0.zip"; sha256 = "1ynjfxg7cj9rd9c4l450w7yp20p2csjdpnk3mcx5bdkjzhbgvgzf"; url = "https://maven.fabricmc.net/net/fabricmc/tiny-remapper/0.6.0/tiny-remapper-0.6.0.jar"; } + { name = "access-widener-2.0.1.zip"; sha256 = "0a7s4x6dbaa9p59ps7pidzwrs0xwy5i17s35xrgh58i26szlsaxm"; url = "https://maven.fabricmc.net/net/fabricmc/access-widener/2.0.1/access-widener-2.0.1.jar"; } + { name = "asm-9.2.zip"; sha256 = "1xa7kccwmcqcdw1xly6n2frzhk56m8ma9v7h764g73ckf56zxm5r"; url = "https://maven.fabricmc.net/org/ow2/asm/asm/9.2/asm-9.2.jar"; } + { name = "asm-analysis-9.2.zip"; sha256 = "1i1kyirizs5sm2v0f06sdz86mbmyn61vjr9d9p8p5h1i2x9bx3w7"; url = "https://maven.fabricmc.net/org/ow2/asm/asm-analysis/9.2/asm-analysis-9.2.jar"; } + { name = "asm-commons-9.2.zip"; sha256 = "19p04mr14ahndba65v4krbvf4p5syf8wz0fp5i9bnf5270qyak5y"; url = "https://maven.fabricmc.net/org/ow2/asm/asm-commons/9.2/asm-commons-9.2.jar"; } + { name = "asm-tree-9.2.zip"; sha256 = "04g0zb7v65iz4k2m2grdpbv8jjryrzkkw7ww23yfp94i6399pgxa"; url = "https://maven.fabricmc.net/org/ow2/asm/asm-tree/9.2/asm-tree-9.2.jar"; } + { name = "asm-util-9.2.zip"; sha256 = "16759v4hh3ijpf4cglrxybz29x2hiylhsa388y09m2mf679kqnzz"; url = "https://maven.fabricmc.net/org/ow2/asm/asm-util/9.2/asm-util-9.2.jar"; } + { name = "intermediary-1.18.1.zip"; sha256 = "1rfz2gazvnivn6hlqiyjpiaycz8va87n5czy1p6w3lnrlfggj2i9"; url = "https://maven.fabricmc.net/net/fabricmc/intermediary/1.18.1/intermediary-1.18.1.jar"; } + { name = "fabric-loader-0.12.12.zip"; sha256 = "070dpcp7kcj4xr75wp1j6pb1bgfzllwg8xmqk3sk79jfqiqwzizw"; url = "https://maven.fabricmc.net/net/fabricmc/fabric-loader/0.12.12/fabric-loader-0.12.12.jar"; } + ]; +} diff --git a/services/minecraft/default.nix b/services/minecraft/default.nix new file mode 100644 index 0000000..cd1aef9 --- /dev/null +++ b/services/minecraft/default.nix @@ -0,0 +1,74 @@ +{config, lib, pkgs, ... }: + +{ + + imports = [ ./minecraft-server-fabric.nix ]; + + pvv.minecraft-server-fabric = { + enable = true; + eula = true; + + package = pkgs.callPackage ../../pkgs/minecraft-server-fabric { inherit (pkgs.unstable) minecraft-server; }; + jvmOpts = "-Xms10G -Xmx10G -XX:+UnlockExperimentalVMOptions -XX:+UseZGC -XX:+DisableExplicitGC -XX:+AlwaysPreTouch -XX:+ParallelRefProcEnabled"; + + serverProperties = { + view-distance = 32; + gamemode = 1; + enable-rcon = true; + "rcon.password" = "pvv"; + }; + + dataDir = "/fast/minecraft-fabric"; + + mods = [ + (pkgs.fetchurl { # Fabric API is a common dependency for fabric based mods + url = "https://cdn.modrinth.com/data/P7dR8mSH/versions/0.44.0+1.18/fabric-api-0.44.0+1.18.jar"; + sha256 = "0mlmj7mj073a48s8zgc1km0jwkphz01c1fvivn4mw37lbm2p4834"; + }) + (pkgs.fetchurl { # Lithium is a 100% vanilla compatible optimization mod + url = "https://cdn.modrinth.com/data/gvQqBUqZ/versions/mc1.18.1-0.7.6/lithium-fabric-mc1.18.1-0.7.6.jar"; + sha256 = "1fw1ikg578v4i6bmry7810a3q53h8yspxa3awdz7d746g91g8lf7"; + }) + (pkgs.fetchurl { # Starlight is the lighting engine of papermc + url = "https://cdn.modrinth.com/data/H8CaAYZC/versions/Starlight%201.0.0%201.18.x/starlight-1.0.0+fabric.d0a3220.jar"; + sha256 = "0bv9im45hhc8n6x57lakh2rms0g5qb7qfx8qpx8n6mbrjjz6gla1"; + }) + (pkgs.fetchurl { # Krypton is a linux optimized optimizer for minecrafts networking system + url = "https://cdn.modrinth.com/data/fQEb0iXm/versions/0.1.6/krypton-0.1.6.jar"; + sha256 = "1ribvbww4msrfdnzlxipk8kpzz7fnwnd4q6ln6mpjlhihcjb3hni"; + }) + (pkgs.fetchurl { # C2ME is a parallelizer for chunk loading and generation, experimental!!! + url = "https://cdn.modrinth.com/data/VSNURh3q/versions/0.2.0+alpha.5.104%201.18.1/c2me-fabric-mc1.18.1-0.2.0+alpha.5.104-all.jar"; + sha256 = "13zrpsg61fynqnnlm7dvy3ihxk8khlcqsif68ak14z7kgm4py6nw"; + }) + (pkgs.fetchurl { # Spark is a profiler for minecraft + url = "https://ci.lucko.me/job/spark/251/artifact/spark-fabric/build/libs/spark-fabric.jar"; + sha256 = "1clvi5v7a14ba23jbka9baz99h6wcfjbadc8kkj712fmy2h0sx07"; + }) + (pkgs.fetchurl { # Carpetmod gives you tps views in the tab menu, + # but also adds a lot of optional serverside vanilla+ features (which we arent using). + # So probably want something else + url = "https://github.com/gnembon/fabric-carpet/releases/download/1.4.56/fabric-carpet-1.18-1.4.56+v211130.jar"; + sha256 = "0rvl2yb8xymla8c052j07gqkqfkz4h5pxf6aip2v9v0h8r84p9hf"; + }) + ]; + }; + + networking.firewall.allowedTCPPorts = [ 25565 ]; + networking.firewall.allowedUDPPorts = [ 25565 ]; + + systemd.services."minecraft-backup" = { + serviceConfig.Type = "oneshot"; + script = '' + ${pkgs.mcrcon}/bin/mcrcon -p pvv "say Starting Backup" "save-off" "save-all" + ${pkgs.rsync}/bin/rsync -avz --delete ${config.pvv.minecraft-server-fabric.dataDir}/world /fast/backup # Where to put backup + ${pkgs.mcrcon}/bin/mcrcon -p pvv "save-all" "say Completed Backup" "save-on" "save-all" + ''; + }; + + systemd.timers."minecraft-backup" = { + wantedBy = ["timers.target"]; + timerConfig.OnCalendar = [ "hourly" ]; + }; + +} diff --git a/services/minecraft/minecraft-server-fabric.nix b/services/minecraft/minecraft-server-fabric.nix new file mode 100644 index 0000000..25784ab --- /dev/null +++ b/services/minecraft/minecraft-server-fabric.nix @@ -0,0 +1,180 @@ +{ lib, pkgs, config, ... }: + +with lib; + +let + cfg = config.pvv.minecraft-server-fabric; + + # 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)); + + 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.pvv.minecraft-server-fabric = { + enable = mkEnableOption "minecraft-server-fabric"; + + package = mkOption { + type = types.package; + }; + + 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. + ''; + }; + + dataDir = mkOption { + type = types.path; + default = "/var/lib/minecraft-fabric"; + description = '' + Directory to store Minecraft database and other state/data files. + ''; + }; + + + 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 = literalExpression '' + { + username1 = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"; + username2 = "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy"; + }; + ''; + }; + + serverProperties = mkOption { + type = with types; attrsOf (oneOf [ bool int str ]); + default = {}; + example = literalExpression '' + { + 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. + ''; + }; + + jvmOpts = mkOption { + type = types.separatedString " "; + default = "-Xmx2048M -Xms2048M"; + # 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."; + }; + + mods = mkOption { + type = types.listOf types.package; + example = literalExpression '' + [ + (pkgs.fetchurl { + url = "https://cdn.modrinth.com/data/P7dR8mSH/versions/0.44.0+1.18/fabric-api-0.44.0+1.18.jar"; + sha256 = "0mlmj7mj073a48s8zgc1km0jwkphz01c1fvivn4mw37lbm2p4834"; + }) + ]; + ''; + description = "List of mods to put in the mods folder"; + }; + }; + + config = mkIf cfg.enable { + users.users.minecraft = { + description = "Minecraft server service user"; + home = cfg.dataDir; + createHome = true; + isSystemUser = true; + group = "minecraft"; + }; + users.groups.minecraft = {}; + + systemd.services.minecraft-server-fabric = { + description = "Minecraft Server Service"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + serviceConfig = { + ExecStart = "${cfg.package}/bin/minecraft-server ${cfg.jvmOpts}"; + Restart = "always"; + User = "minecraft"; + WorkingDirectory = cfg.dataDir; + }; + + preStart = '' + ln -sf ${eulaFile} eula.txt + ln -sf ${whitelistFile} whitelist.json + cp -f ${serverPropertiesFile} server.properties + + ln -sfn ${pkgs.linkFarmFromDrvs "fabric-mods" cfg.mods} mods + ''; + }; + + 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."; + } + ]; + }; +}