diff --git a/hosts/Infini-DESKTOP/default.nix b/hosts/Infini-DESKTOP/default.nix index 69c5684..e93ede8 100644 --- a/hosts/Infini-DESKTOP/default.nix +++ b/hosts/Infini-DESKTOP/default.nix @@ -18,4 +18,25 @@ eth0.useDHCP = true; wlp41s0.useDHCP = true; }; + + # services.minecraft-servers = { + # enable = true; + # openFirewall = true; + + # servers = { + # test = { + # enable = true; + # eula = true; + # # declarative = true; + # # serverProperties.server-port = 25565; + # }; + + # # test2 = { + # # enable = true; + # # eula = true; + # # declarative = true; + # # serverProperties.server-port = 25566; + # # }; + # }; + # }; } diff --git a/modules/minecraft-servers.nix b/modules/minecraft-servers.nix new file mode 100644 index 0000000..4435cea --- /dev/null +++ b/modules/minecraft-servers.nix @@ -0,0 +1,211 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.minecraft-servers; + + eulaFile = builtins.toFile "eula.txt" '' + # eula.txt managed by NixOS Configuration + eula=true + ''; + + whitelistFile = server: + pkgs.writeText "whitelist.json" (builtins.toJSON (mapAttrsToList (n: v: { + name = n; + uuid = v; + }) server.whitelist)); + + cfgToString = v: if builtins.isBool v then boolToString v else toString v; + + serverPropertiesFile = server: + pkgs.writeText "server.properties" ('' + # server.properties managed by NixOS configuration + '' + concatStringsSep "\n" + (mapAttrsToList (n: v: "${n}=${cfgToString v}") server.serverProperties)); + + tmux = "${getBin pkgs.tmux}/bin/tmux"; + + stopScript = name: + pkgs.writeScript "minecraft-stop" '' + #!${pkgs.runtimeShell} + + if ! [ -d "/proc/$1" ]; then + exit 0 + fi + + ${tmux} -S ${cfgdataDir}/${name}.sock send-keys Enter stop Enter + ${getBin pkgs.coreutils}/bin/tail --pid-"$1" -f /dev/null + ''; + + cfgdataDir = "/var/lib/minecraft"; + + mkService = name: inst: + builtins.trace "${name} building" { + name = "minecraft-server-${name}"; + value = builtins.trace "${name}: value: build" { + description = "Minecraft Server Service: ${name}"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + serviceConfig = + builtins.trace "${name}: value: serviceConfig: building" { + ExecStart = '' + ${tmux} -S ${cfgdataDir}/${name}.sock new \ + -d ${inst.package}/bin/minecraft-server ${inst.jvmOpts} + # ''; + ExecStop = "${stopScript name} $MAINPID"; + Restart = "always"; + Type = "forking"; + GuessMainPIP = true; + User = "minecraft"; + WorkingDirectory = "${cfgdataDir}/${name}"; + }; + + preStart = '' + ln -sf ${eulaFile} eula.txt + '' + (if inst.declarative then '' + if [ -e .declarative ]; then + # Was declarative before, no need to back up anything + ln -sf ${whitelistFile inst} whitelist.json + cp -f ${serverPropertiesFile inst} server.properties + else + # Declarative for the first time, backup stateful files + ln -sb --suffix=.stateful ${whitelistFile inst} whitelist.json + cp -b --suffix=.stateful ${ + serverPropertiesFile inst + } 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 + ''); + + postStart = '' + ${pkgs.coreutils}/bin/chmod 660 ${cfgdataDir}/${name}.sock + ${pkgs.coreutils}/bin/chgrp minecraft ${cfgdataDir}/${name}.sock + ''; + }; + }; + + generatedServices = mapAttrs' mkService cfg.servers; + +in { + options = { + services.minecraft-servers = { + enable = mkEnableOption "minecraft servers"; + # TODO: verbose minecraft-servers enable option + + openFirewall = + mkEnableOption "opening the firewall for each server by default"; + # TODO: verbose outer openFirewall option + + dataDir = mkOption { + type = types.path; + default = "/var/lib/minecraft"; + # TODO: verbose dataDir option + }; + + servers = mkOption { + type = types.attrsOf (types.submodule { + enable = mkEnableOption "this minecraft server"; + # TODO: verbose server enable option + + declarative = mkEnableOption "declarative server management"; + # TODO: verbose declarative option + + eula = mkEnableOption "accepting the eula"; + # TODO: verbose eula option + + openFirewall = mkOption { + type = types.bool; + default = true; # cfg.openFirewall; + # TODO: openFirewall description + }; + + jvmOpts = mkOption { + type = types.separatedString " "; + default = "-Xmx2048M -Xms2048M"; + # TODO: verbose jvmOpts option + }; + + package = mkOption { + type = types.package; + default = pkgs.minecraft-server; + # TODO: verbose package option + }; + + 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 = { }; + # TODO: verbose whitelist option + }; + + serverProperties = mkOption { + type = with types; attrsOf (oneOf [ bool int str ]); + default = { server-port = 25565; }; + # TODO: verbose serverProperties option + }; + }); + }; + }; + }; + + 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 = + mkIf (cfg.enable && cfg.servers != { }) generatedServices; + + }; + + # networking.firewall = let + + # propertyFunc = attrsets.mapAttrsToList (name: value: + # let props = value.serverProperties; + # in (if value.declarative then [ + # (props.server-port or 25565) + # (if props.enable-rcon or false then + # props."rcon.port" or 25575 + # else + # null) + # (if props.enable-query or false then + # props "query.port" or 25565 + # else + # null) + # ] else + # null)) cfg.servers; + + # openedPorts = lists.flatten [ serverPorts queryPorts rconPorts ]; + # in { + # allowedUDPPorts = openedPorts; + # allowedTCPPorts = openedPorts; + # }; + + # assertions = [{ + # assertion = all (i: i.eula == true) (attrValues cfg.servers); + # message = "You must agree to Mojangs EULA to run minecraft-server." + # + " Read https://account.mojang.com/documents/minecraft_eula and" + # + " set `services.minecraft-servers.servers..eula` to `true` for all servers if you agree."; + # }]; +} diff --git a/users/infinidoge/default.nix b/users/infinidoge/default.nix index df5f8f7..68df86b 100644 --- a/users/infinidoge/default.nix +++ b/users/infinidoge/default.nix @@ -103,7 +103,7 @@ "PASSWORD SET IN THE FUTURE"; description = "Infinidoge"; isNormalUser = true; - extraGroups = [ "wheel" ]; + extraGroups = [ "wheel" "minecraft" ]; shell = pkgs.zsh; }; }