From b5ef6a6f32ebed51255918ed100c12e8dfa165c6 Mon Sep 17 00:00:00 2001 From: aszlig Date: Tue, 15 Mar 2016 04:34:30 +0100 Subject: modules: Add new Starbound service and test Very preliminary and doesn't have all the option descriptions right, nor does it have convenience features such as setting allowAdminCommands based on whether any users are defined with admin privileges. Of course the latter needs to undergo the decision on how to handle RCON connections, because the latter *might* need that option. But apart from that single option, there are a lot more options we need to flesh out. Also, the test currently is very limited and only spins up a client, connects to the server and does a movement (just walk to the right). Needless to say, it's even quite fragile and relies on OCR to properly detect the custom pixel fonts from Starbound. Which unfortunately fails most of the time. Signed-off-by: aszlig --- modules/services/starbound.nix | 318 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 318 insertions(+) create mode 100644 modules/services/starbound.nix (limited to 'modules/services') diff --git a/modules/services/starbound.nix b/modules/services/starbound.nix new file mode 100644 index 00000000..2f76eb84 --- /dev/null +++ b/modules/services/starbound.nix @@ -0,0 +1,318 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.vuizvui.services.starbound; + + mkListenerOptions = what: defaultPort: { + bind = mkOption { + type = types.str; + default = "::"; + description = '' + Host/IP address to listen for incoming connections to the ${what}. + ''; + }; + + port = mkOption { + type = types.int; + default = defaultPort; + description = '' + Port to listen for incoming connections to the ${what}. + ''; + }; + }; + + serverConfig = { + allowAdminCommands = cfg.adminCommands.allow; + allowAdminCommandsFromAnyone = cfg.adminCommands.allowFromAnyone; + + allowAnonymousConnections = cfg.anonymousConnections.allow; + anonymousConnectionsAreAdmin = cfg.anonymousConnections.adminPrivileges; + + serverUsers = cfg.users; + + inherit (cfg) checkAssetsDigest clearPlayerFiles clearUniverseFiles; + inherit (cfg) maxPlayers safeScripts serverName; + inherit (cfg) upnpPortForwarding; + + gameServerBind = cfg.bind; + gameServerPort = cfg.port; + + bannedIPs = cfg.bannedIPs; + bannedUuids = cfg.bannedUUIDs; + + runRconServer = cfg.rconServer.enable; + rconServerBind = cfg.rconServer.bind; + rconServerPort = cfg.rconServer.port; + rconServerPassword = cfg.rconServer.password; + rconServerTimeout = cfg.rconServer.timeout; + + runQueryServer = cfg.queryServer.enable; + queryServerBind = cfg.queryServer.bind; + queryServerPort = cfg.queryServer.port; + } // cfg.extraConfig; + + bootConfig = pkgs.runCommand "sbboot.config" { + overrides = pkgs.writeText "sbboot.overrides" (builtins.toJSON { + logFileBackups = 0; + modSource = ""; + storageDirectory = cfg.dataDir; + defaultConfiguration = serverConfig; + }); + } '' + "${pkgs.jq}/bin/jq" -s '.[0] * .[1]' \ + "${cfg.package}/etc/sbboot.config" "$overrides" \ + > "$out" + ''; + + # Traverse a given path with ../ until we get to the root directory (/). + gotoRoot = p: concatStringsSep "/" (map (const "..") (splitString "/" p)); + +in { + options.vuizvui.services.starbound = { + enable = mkEnableOption "Starbound game server"; + + adminCommands = { + allow = mkOption { + type = types.bool; + default = true; + description = '' + Whether to allow admin commands in general. + ''; + # XXX: Make this dependant on whether an account is defined with enabled + # admin. + }; + + allowFromAnyone = mkOption { + type = types.bool; + default = false; + description = '' + Allow anyone, even anonymous users to use admin commands. + ''; + # XXX: Check whether this is true! + }; + }; + + anonymousConnections = { + allow = mkOption { + type = types.bool; + default = true; + description = '' + Whether to allow anonymous connections to the server. + + Set this to false and use + to only allow specific accounts to connect. + ''; + }; + + adminPrivileges = mkOption { + type = types.bool; + default = false; + description = '' + Whether all anonymous connections have administrative privileges. + ''; + }; + }; + + users = mkOption { + type = types.attrsOf (types.submodule { + options.admin = mkOption { + type = types.bool; + default = false; + description = '' + Whether this user has admin privileges. + ''; + }; + options.password = mkOption { + type = types.str; + example = "supersecure"; + description = '' + The password for the user. + ''; + }; + }); + }; + + checkAssetsDigest = mkOption { + type = types.bool; + default = false; + description = '' + Check whether the assets on the client match the ones from the server + and deny connection if they don't match. + ''; + }; + + clearPlayerFiles = mkOption { + # XXX: Figure out the exact semantics of this. + type = types.bool; + default = false; + description = '' + Forces players to use new characters or to have no gear or tech. + ''; + }; + + clearUniverseFiles = mkOption { + # XXX: Figure out the exact semantics of this. + type = types.bool; + default = false; + description = '' + Forces player characters to use fresh universe data and navigation maps. + ''; + }; + + bannedIPs = mkOption { + type = types.listOf types.str; + default = []; + description = '' + IP addresses disallowed for connection to the server. + ''; + }; + + bannedUUIDs = mkOption { + type = types.listOf types.str; + default = []; + description = '' + User IDs disallowed for connection to the server. + ''; + }; + + dataDir = mkOption { + type = types.path; + default = "/var/lib/starbound"; + description = '' + The directory where Starbound stores its universe/player files. + ''; + }; + + package = mkOption { + type = types.package; + default = pkgs.vuizvui.games.steam.starbound; + description = '' + The starbound package to use for running this game server. + ''; + }; + + extraConfig = mkOption { + type = types.attrs; + default = {}; + description = '' + Extra configuration options to add to the server config. + ''; + }; + + rconServer = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to run an RCON server which allows to run administrative + commands on this game server instance. + + See the RCON protocol documentation for more information about this. + ''; + }; + + password = mkOption { + type = types.str; + default = ""; + description = '' + The password needed to authorize with the RCON server. + ''; + }; + + timeout = mkOption { + type = types.int; + default = 1000; + # XXX: Find out what this timeout is for and whether it's in seconds. + description = '' + After how many seconds the RCON server drops the connection. + ''; + }; + } // mkListenerOptions "RCON server" 21026; + + queryServer = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to run a query server that shows information such as currently + connected players. + ''; + }; + } // mkListenerOptions "query server" 21025; + + safeScripts = mkOption { + type = types.bool; + default = true; + # XXX: The description is just a guess and we need to find out what this + # really does. + description = '' + This is to make sure scripts can't call unsafe functions. + ''; + }; + + serverName = mkOption { + type = types.str; + default = "A Starbound Server"; + example = "My shiny Starbound Server"; + description = '' + A short description or name of the Starbound server to run. + ''; + }; + + upnpPortForwarding = mkOption { + type = types.bool; + default = true; + description = '' + Whether to use UPnP to forward ports from NAT gateways. + ''; + }; + + maxPlayers = mkOption { + type = types.int; + default = 8; + description = '' + Maximum amount of players to allow concurrently. + ''; + }; + } // mkListenerOptions "game server" 21025; + + config = mkIf cfg.enable { + users.groups.starbound = { + gid = config.ids.gids.starbound; + }; + + users.users.starbound = { + uid = config.ids.uids.starbound; + description = "Starbound Game Server User"; + group = "starbound"; + home = cfg.dataDir; + createHome = true; + }; + + systemd.services.starbound = { + description = "Starbound Server"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" "fs.target" ]; + + serviceConfig = { + User = "starbound"; + Group = "starbound"; + PrivateTmp = true; + + KillSignal = "SIGINT"; + + ExecStart = toString [ + "${cfg.package}/bin/starbound-server" + "-bootconfig \"${bootConfig}\"" + # Workaround to disable logging to file + "-logfile \"${gotoRoot cfg.dataDir}/dev/null\"" + "-verbose" + ]; + }; + }; + }; +} -- cgit 1.4.1