diff --git a/hosts/greddost/configuration.nix b/hosts/greddost/configuration.nix
index b17206e1..c3cd78ba 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 00000000..95d4f2df
--- /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 00000000..e2e48577
--- /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 00000000..fba715a2
--- /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 00000000..c625c450
--- /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 00000000..cd1aef95
--- /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 00000000..25784abe
--- /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.";
+ }
+ ];
+ };
+}