diff options
Diffstat (limited to 'modules/services/starbound.nix')
-rw-r--r-- | modules/services/starbound.nix | 351 |
1 files changed, 351 insertions, 0 deletions
diff --git a/modules/services/starbound.nix b/modules/services/starbound.nix new file mode 100644 index 00000000..991ed2be --- /dev/null +++ b/modules/services/starbound.nix @@ -0,0 +1,351 @@ +{ 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 = mapAttrs (user: attrs: { + inherit (attrs) admin password; + }) cfg.users; + + inherit (cfg) + allowAssetsMismatch maxPlayers maxTeamSize serverName serverFidelity; + + clearPlayerFiles = false; + clearUniverseFiles = false; + + safeScripts = cfg.safeScripts.enable; + scriptInstructionLimit = cfg.safeScripts.instructionLimit; + scriptInstructionMeasureInterval = + cfg.safeScripts.instructionMeasureInterval; + scriptProfilingEnabled = cfg.safeScripts.profiling.enable; + scriptRecursionLimit = cfg.safeScripts.recursionLimit; + + 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.writeText "sbinit.config" (builtins.toJSON { + logFileBackups = 0; + storageDirectory = cfg.dataDir; + assetDirectories = singleton (cfg.package.assets); + defaultConfiguration = serverConfig; + }); + + # 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 <literal>false</literal> and use + <option>serverUsers</option> 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. + ''; + }; + }); + default = {}; + description = '' + User accounts to allow connection to the Starbound server. + ''; + }; + + allowAssetsMismatch = 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. + ''; + }; + + 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.humblebundle.starbound; + defaultText = "pkgs.vuizvui.games.humblebundle.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 <link xlink:href="${ + "https://developer.valvesoftware.com/wiki/Source_RCON_Protocol" + }">RCON protocol documentation</link> 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 = { + enable = mkOption { + type = types.bool; + default = true; + description = '' + Enable certain limitations of LUA scripts. + ''; + }; + + instructionLimit = mkOption { + type = types.int; + default = 10000000; + description = '' + The maximum amount of instructions a LUA function can have. + ''; + }; + + instructionMeasureInterval = mkOption { + type = types.int; + default = 10000; + description = '' + The amount of milliseconds to wait between consecutive checks of the + <option>instructionLimit</option> on LUA scripts. + ''; + }; + + recursionLimit = mkOption { + type = types.int; + default = 100; + description = '' + Maximum depth of recursion for LUA scripts. + ''; + }; + + profiling.enable = mkEnableOption "LUA script profiling"; + }; + + 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. + ''; + }; + + serverFidelity = mkOption { + type = types.enum [ "automatic" "minimum" "low" "medium" "high" ]; + default = "automatic"; + example = "high"; + description = '' + The fidelity profile to use for this server as defined in + <filename>worldserver.config</filename> inside the packed assets. + + If this is set to <literal>automatic</literal> the server will + automatically switch between these profiles. + ''; + }; + + maxPlayers = mkOption { + type = types.int; + default = 8; + description = '' + Maximum amount of players to allow concurrently. + ''; + }; + + maxTeamSize = mkOption { + type = types.int; + default = 4; + description = '' + Maximum amount of players to allow within a party. + ''; + }; + } // 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" + ]; + }; + }; + }; +} |