# Deploy guard check for the Minecraft server. # # Queries the standard Server List Ping (SLP) handshake on the game port — # no RCON, no query, no extra config. SLP is always enabled and returns the # live player count plus (usually) a short name sample. # # Contract (deploy-guard-check plug-in): # - exit 0: no players online, or the server isn't reachable at all (down ⇒ # no users to disrupt). # - exit 1: at least one player is connected; stdout lists the names that # made it into the SLP sample. { config, lib, pkgs, service_configs, ... }: let minecraftPort = service_configs.ports.public.minecraft.port; check = pkgs.writers.writePython3Bin "deploy-guard-check-minecraft" { libraries = [ pkgs.python3Packages.mcstatus ]; flakeIgnore = [ "E501" "E402" ]; } '' import sys try: from mcstatus import JavaServer except ImportError as e: print(f"minecraft: mcstatus unavailable ({e}); assuming safe", file=sys.stderr) sys.exit(0) try: status = JavaServer.lookup("127.0.0.1:${toString minecraftPort}", timeout=5).status() except Exception as e: print(f"minecraft: unreachable ({e}); assuming safe to deploy", file=sys.stderr) sys.exit(0) online = status.players.online if online <= 0: sys.exit(0) sample = getattr(status.players, "sample", None) or [] names = ", ".join(p.name for p in sample) or "" print(f"Minecraft: {online} player(s) online: {names}") sys.exit(1) ''; in { imports = [ ../modules/server-deploy-guard.nix ]; config = lib.mkIf config.services.minecraft-servers.enable { services.deployGuard.checks.minecraft = { description = "Players connected to the Minecraft server"; command = check; }; }; }