diff --git a/lib.nix b/lib.nix index 102b6290..b888d9d6 100644 --- a/lib.nix +++ b/lib.nix @@ -6,9 +6,9 @@ nixlib = inputs.nixpkgs.lib; inherit (nixlib.modules) mkOrder mkOverride defaultOverridePriority; inherit (nixlib.strings) splitString toLower; - inherit (nixlib.lists) imap0 elemAt findFirst; + inherit (nixlib.lists) imap0 elemAt findFirst foldl; inherit (nixlib.attrsets) mapAttrs listToAttrs nameValuePair; - inherit (nixlib.strings) hasPrefix hasInfix removePrefix substring fixedWidthString replaceStrings concatMapStringsSep match toInt; + inherit (nixlib.strings) hasPrefix hasInfix removePrefix substring fixedWidthString replaceStrings concatMapStringsSep stringToCharacters match toInt; inherit (nixlib.trivial) flip toHexString bitOr; toHexStringLower = v: toLower (toHexString v); @@ -19,6 +19,7 @@ idx = listToAttrs pairs; in char: idx.${char}; + hexToInt = str: foldl (value: chr: value * 16 + hexCharToInt chr) 0 (stringToCharacters (toLower str)); eui64 = mac: let parts = map toLower (splitString ":" mac); @@ -98,7 +99,7 @@ in { domain = "gensokyo.zone"; inherit treeToModulesOutput userIs eui64 parseUrl mkWinPath mkBaseDn mkAddress6 - toHexStringLower hexCharToInt + toHexStringLower hexToInt hexCharToInt mapListToAttrs coalesce mkAlmostOptionDefault mkAlmostDefault mkAlmostForce mapOverride mapOptionDefaults mapAlmostOptionDefaults mapDefaults overrideOptionDefault overrideAlmostOptionDefault overrideDefault overrideAlmostDefault overrideNone overrideAlmostForce overrideForce overrideVM diff --git a/modules/nixos/minecraft-bedrock.nix b/modules/nixos/minecraft-bedrock.nix index 21c7e0ec..a3f90489 100644 --- a/modules/nixos/minecraft-bedrock.nix +++ b/modules/nixos/minecraft-bedrock.nix @@ -1,7 +1,37 @@ -{ config, lib, pkgs, ... }: let +let + allowListModule = {config, name, gensokyo-zone, lib, ...}: let + inherit (gensokyo-zone.lib) hexToInt; + inherit (lib.options) mkOption; + inherit (lib.modules) mkOptionDefault; + inherit (builtins) typeOf; + in { + options = with lib.types; { + name = mkOption { + type = str; + default = name; + }; + xuid = mkOption { + type = oneOf [ int str ]; + }; + settings = mkOption { + type = attrsOf str; + }; + }; + config = { + settings = { + name = mkOptionDefault config.name; + xuid = mkOptionDefault { + string = toString (hexToInt config.xuid); + int = toString config.xuid; + }.${typeOf config.xuid}; + }; + }; + }; +in { config, gensokyo-zone, lib, pkgs, ... }: let # see https://gist.github.com/datakurre/cfdf627fb23ed8ff62bb7b3520b92674 + inherit (gensokyo-zone.lib) mapOptionDefaults; inherit (lib.options) mkOption mkPackageOption; - inherit (lib.modules) mkIf; + inherit (lib.modules) mkIf mkMerge mkOptionDefault; inherit (lib.attrsets) mapAttrsToList; inherit (lib.strings) concatStringsSep; inherit (lib.trivial) boolToString; @@ -35,32 +65,6 @@ in { serverProperties = mkOption { type = attrsOf (oneOf [ bool int str float ]); - default = { - server-name = "Dedicated Server"; - gamemode = "survival"; - difficulty = "easy"; - allow-cheats = false; - max-players = 10; - online-mode = false; - white-list = false; - server-port = 19132; - server-portv6 = 19133; - view-distance = 32; - tick-distance = 4; - player-idle-timeout = 30; - max-threads = 8; - level-name = "Bedrock level"; - level-seed = ""; - default-player-permission-level = "member"; - texturepack-required = false; - content-log-file-enabled = false; - compression-threshold = 1; - server-authoritative-movement = "server-auth"; - player-movement-score-threshold = 20; - player-movement-distance-threshold = 0.3; - player-movement-duration-threshold-in-ms = 500; - correct-player-movement = false; - }; example = literalExample '' { server-name = "Dedicated Server"; @@ -111,43 +115,123 @@ in { type = str; default = cfg.user; }; + + allowPlayers = mkOption { + type = nullOr (attrsOf (submoduleWith { + modules = [ allowListModule ]; + specialArgs = { + inherit gensokyo-zone; + nixosConfig = config; + }; + })); + default = null; + }; + + allowList = mkOption { + type = nullOr path; + }; }; - config = mkIf cfg.enable { - users.users.${cfg.user} = { + config = let + confService.services.minecraft-bedrock-server = { + serverProperties = mapOptionDefaults { + server-name = "Dedicated Server"; + gamemode = "survival"; + difficulty = "easy"; + allow-cheats = false; + max-players = 10; + online-mode = false; + allow-list = cfg.allowList != null; + server-port = 19132; + server-portv6 = 19133; + view-distance = 32; + tick-distance = 4; + player-idle-timeout = 30; + max-threads = 8; + level-name = "Bedrock level"; + level-seed = ""; + default-player-permission-level = "member"; + texturepack-required = false; + content-log-file-enabled = false; + compression-threshold = 1; + server-authoritative-movement = "server-auth"; + player-movement-score-threshold = 20; + player-movement-distance-threshold = 0.3; + player-movement-duration-threshold-in-ms = 500; + correct-player-movement = false; + }; + allowList = let + allowPlayers = mapAttrsToList (_: allow: allow.settings) cfg.allowPlayers; + allowListJson = pkgs.writeText "minecraft-bedrock-server-allowlist.json" ( + builtins.toJSON allowPlayers + ); + in mkOptionDefault ( + if cfg.allowPlayers != null then allowListJson + else null + ); + }; + conf.users.users.${cfg.user} = { inherit (cfg) group; description = "Minecraft server service user"; home = cfg.dataDir; createHome = true; isSystemUser = true; }; - users.groups.${cfg.group} = {}; + conf.users.groups.${cfg.group} = {}; - systemd.services.minecraft-bedrock-server = { + conf.systemd.services.minecraft-bedrock-server = { description = "Minecraft Bedrock Server Service"; wantedBy = [ "multi-user.target" ]; after = [ "network.target" ]; serviceConfig = { + BindReadOnlyPaths = let + packageResources = map (subpath: "${cfg.package}/var/lib/${subpath}:${cfg.dataDir}/${subpath}") [ + "definitions/attachables" + "definitions/biomes" + "definitions/feature_rules" + "definitions/features" + "definitions/persona" + "definitions/sdl_layouts" + "definitions/spawn_groups" + "resource_packs/vanilla" + "resource_packs/chemistry" + "config/default" + "bedrock_server_symbols.debug" + "env-vars" + "permissions.json" + ]; + in mkMerge [ + packageResources + (mkIf (cfg.allowList != null) "${cfg.allowList}:${cfg.dataDir}/allowlist.json") + ]; ExecStart = [ "${cfg.package}/bin/bedrock_server" ]; Restart = "always"; User = cfg.user; WorkingDirectory = cfg.dataDir; + LogFilterPatterns = [ + "~.*minecraft:trial_chambers/chamber/end" + "~Running AutoCompaction" + ]; }; preStart = '' - cp -a -n ${cfg.package}/var/lib/* . + mkdir -p behavior_packs + ln -sf ${cfg.package}/var/lib/behavior_packs/* behavior_packs/ cp -f ${serverPropertiesFile} server.properties chmod +w server.properties ''; }; - networking.firewall = let + conf.networking.firewall = let ports = [ cfg.serverProperties.server-port cfg.serverProperties.server-portv6 ]; in mkIf cfg.openFirewall { allowedUDPPorts = ports; }; - }; + in mkMerge [ + confService + (mkIf cfg.enable conf) + ]; } diff --git a/nixos/dnsmasq.nix b/nixos/dnsmasq.nix index 6f856713..f50d83e8 100644 --- a/nixos/dnsmasq.nix +++ b/nixos/dnsmasq.nix @@ -90,6 +90,7 @@ in { "play.inpvp.net" "mco.lbsg.net" "play.galaxite.net" + "mco.cubecraft.net" ]; bedrockRecords = map (flip mkHostRecord bedrockRecord) bedrockRecordNames; in mkMerge [ diff --git a/nixos/minecraft/bedrock.nix b/nixos/minecraft/bedrock.nix index f48361f5..12adc1c2 100644 --- a/nixos/minecraft/bedrock.nix +++ b/nixos/minecraft/bedrock.nix @@ -6,29 +6,20 @@ in { enable = mkDefault true; serverProperties = { server-name = "Kat's Server"; - gamemode = "survival"; - difficulty = "easy"; - allow-cheats = false; - max-players = 10; online-mode = true; - white-list = false; - server-port = 19132; - server-portv6 = 19133; - view-distance = 32; - tick-distance = 4; - player-idle-timeout = 30; - max-threads = 8; - level-name = "Bedrock level"; - level-seed = ""; - default-player-permission-level = "member"; - texturepack-required = false; - content-log-file-enabled = false; - compression-threshold = 1; - server-authoritative-movement = "server-auth"; - player-movement-score-threshold = 20; - player-movement-distance-threshold = 0.3; - player-movement-duration-threshold-in-ms = 500; - correct-player-movement = false; + level-name = "KatBedrock"; + }; + allowPlayers = let + base = 2535460000000000; + in { + Kyxna.xuid = base + 4308966797; + arcnmx.xuid = base + 13399068799; + }; + }; + systemd.services.minecraft-bedrock-server = mkIf cfg.enable { + gensokyo-zone.sharedMounts."minecraft/bedrock" = {config, ...}: { + root = config.rootDir + "/${config.subpath}"; + path = mkDefault cfg.dataDir; }; }; users = mkIf cfg.enable { diff --git a/systems/reisen/setup.sh b/systems/reisen/setup.sh index b35b1781..9ee83b1d 100644 --- a/systems/reisen/setup.sh +++ b/systems/reisen/setup.sh @@ -160,7 +160,7 @@ mkshared plex 100193 100193 0750 mkshared postgresql 100071 100071 0750 mkshared unifi 100990 100990 0750 mkshared zigbee2mqtt 100317 100317 0700 -mkshared minecraft 0 0 0750 +mkshared minecraft 100913 100913 0750 mkshared minecraft/bedrock 100913 100913 0750 ln -sf /lib/systemd/system/auth-rpcgss-module.service /etc/systemd/system/