8 cfg = config.services.xonotic;
10 serverCfg = pkgs.writeText "xonotic-server.cfg" (
11 toString cfg.prependConfig
13 + builtins.concatStringsSep "\n" (
14 lib.mapAttrsToList (key: option:
16 escape = s: lib.escape [ "\"" ] s;
17 quote = s: "\"${s}\"";
19 toValue = x: quote (escape (toString x));
21 value = (if lib.isList option then
22 builtins.concatStringsSep
24 (builtins.map (x: toValue x) option)
33 + toString cfg.appendConfig
38 options.services.xonotic = {
39 enable = lib.mkEnableOption "Xonotic dedicated server";
41 package = lib.mkPackageOption pkgs "xonotic-dedicated" {};
43 openFirewall = lib.mkOption {
44 type = lib.types.bool;
47 Open the firewall for TCP and UDP on the specified port.
51 dataDir = lib.mkOption {
52 type = lib.types.path;
54 default = "/var/lib/xonotic";
60 settings = lib.mkOption {
62 Generates the `server.cfg` file. Refer to [upstream's example][0] for
65 [0]: https://gitlab.com/xonotic/xonotic/-/blob/master/server/server.cfg
68 type = lib.types.submodule {
69 freeformType = with lib.types; let
70 scalars = oneOf [ singleLineStr int float ];
72 attrsOf (oneOf [ scalars (nonEmptyListOf scalars) ]);
74 options.sv_public = lib.mkOption {
79 Controls whether the server will be publicly listed.
83 options.hostname = lib.mkOption {
84 type = lib.types.singleLineStr;
85 default = "Xonotic $g_xonoticversion Server";
87 The name that will appear in the server list. `$g_xonoticversion`
88 gets replaced with the current version.
92 options.sv_motd = lib.mkOption {
93 type = lib.types.singleLineStr;
96 Text displayed when players join the server.
100 options.sv_termsofservice_url = lib.mkOption {
101 type = lib.types.singleLineStr;
104 URL for the Terms of Service for playing on your server.
108 options.maxplayers = lib.mkOption {
109 type = lib.types.int;
112 Number of player slots on the server, including spectators.
116 options.net_address = lib.mkOption {
117 type = lib.types.singleLineStr;
120 The address Xonotic will listen on.
124 options.port = lib.mkOption {
125 type = lib.types.port;
128 The port Xonotic will listen on.
134 # Still useful even though we're using RFC 42 settings because *some* keys
136 appendConfig = lib.mkOption {
137 type = with lib.types; nullOr lines;
140 Literal text to insert at the end of `server.cfg`.
144 # Certain changes need to happen at the beginning of the file.
145 prependConfig = lib.mkOption {
146 type = with lib.types; nullOr lines;
149 Literal text to insert at the start of `server.cfg`.
154 config = lib.mkIf cfg.enable {
155 systemd.services.xonotic = {
156 description = "Xonotic server";
157 wantedBy = [ "multi-user.target" ];
160 # Required or else it tries to write the lock file into the nix store
167 StateDirectory = "xonotic";
168 ExecStart = "${cfg.package}/bin/xonotic-dedicated";
170 # Symlink the configuration from the nix store to where Xonotic actually
173 "${pkgs.coreutils}/bin/mkdir -p ${cfg.dataDir}/.xonotic/data"
175 ${pkgs.coreutils}/bin/ln -sf ${serverCfg} \
176 ${cfg.dataDir}/.xonotic/data/server.cfg
180 # Cargo-culted from search results about writing Xonotic systemd units
181 ExecReload = "${pkgs.util-linux}/bin/kill -HUP $MAINPID";
183 Restart = "on-failure";
189 networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [
192 networking.firewall.allowedUDPPorts = lib.mkIf cfg.openFirewall [
197 meta.maintainers = with lib.maintainers; [ CobaltCause ];