grafana-alloy: don't build the frontend twice
[NixPkgs.git] / nixos / modules / services / games / xonotic.nix
blob2b79e11838062a4f84d9b4ce0d211f6e6a0788fe
1 { config
2 , pkgs
3 , lib
4 , ...
5 }:
7 let
8   cfg = config.services.xonotic;
10   serverCfg = pkgs.writeText "xonotic-server.cfg" (
11     toString cfg.prependConfig
12       + "\n"
13       + builtins.concatStringsSep "\n" (
14         lib.mapAttrsToList (key: option:
15           let
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
23                 " "
24                 (builtins.map (x: toValue x) option)
25             else
26               toValue option
27             );
28           in
29           "${key} ${value}"
30         ) cfg.settings
31       )
32       + "\n"
33       + toString cfg.appendConfig
34   );
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;
45       default = false;
46       description = ''
47         Open the firewall for TCP and UDP on the specified port.
48       '';
49     };
51     dataDir = lib.mkOption {
52       type = lib.types.path;
53       readOnly = true;
54       default = "/var/lib/xonotic";
55       description = ''
56         Data directory.
57       '';
58     };
60     settings = lib.mkOption {
61       description = ''
62         Generates the `server.cfg` file. Refer to [upstream's example][0] for
63         details.
65         [0]: https://gitlab.com/xonotic/xonotic/-/blob/master/server/server.cfg
66       '';
67       default = {};
68       type = lib.types.submodule {
69         freeformType = with lib.types; let
70           scalars = oneOf [ singleLineStr int float ];
71         in
72         attrsOf (oneOf [ scalars (nonEmptyListOf scalars) ]);
74         options.sv_public = lib.mkOption {
75           type = lib.types.int;
76           default = 0;
77           example = [ (-1) 1 ];
78           description = ''
79             Controls whether the server will be publicly listed.
80           '';
81         };
83         options.hostname = lib.mkOption {
84           type = lib.types.singleLineStr;
85           default = "Xonotic $g_xonoticversion Server";
86           description = ''
87             The name that will appear in the server list. `$g_xonoticversion`
88             gets replaced with the current version.
89           '';
90         };
92         options.sv_motd = lib.mkOption {
93           type = lib.types.singleLineStr;
94           default = "";
95           description = ''
96             Text displayed when players join the server.
97           '';
98         };
100         options.sv_termsofservice_url = lib.mkOption {
101           type = lib.types.singleLineStr;
102           default = "";
103           description = ''
104             URL for the Terms of Service for playing on your server.
105           '';
106         };
108         options.maxplayers = lib.mkOption {
109           type = lib.types.int;
110           default = 16;
111           description = ''
112             Number of player slots on the server, including spectators.
113           '';
114         };
116         options.net_address = lib.mkOption {
117           type = lib.types.singleLineStr;
118           default = "0.0.0.0";
119           description = ''
120             The address Xonotic will listen on.
121           '';
122         };
124         options.port = lib.mkOption {
125           type = lib.types.port;
126           default = 26000;
127           description = ''
128             The port Xonotic will listen on.
129           '';
130         };
131       };
132     };
134     # Still useful even though we're using RFC 42 settings because *some* keys
135     # can be repeated.
136     appendConfig = lib.mkOption {
137       type = with lib.types; nullOr lines;
138       default = null;
139       description = ''
140         Literal text to insert at the end of `server.cfg`.
141       '';
142     };
144     # Certain changes need to happen at the beginning of the file.
145     prependConfig = lib.mkOption {
146       type = with lib.types; nullOr lines;
147       default = null;
148       description = ''
149         Literal text to insert at the start of `server.cfg`.
150       '';
151     };
152   };
154   config = lib.mkIf cfg.enable {
155     systemd.services.xonotic = {
156       description = "Xonotic server";
157       wantedBy = [ "multi-user.target" ];
159       environment = {
160         # Required or else it tries to write the lock file into the nix store
161         HOME = cfg.dataDir;
162       };
164       serviceConfig = {
165         DynamicUser = true;
166         User = "xonotic";
167         StateDirectory = "xonotic";
168         ExecStart = "${cfg.package}/bin/xonotic-dedicated";
170         # Symlink the configuration from the nix store to where Xonotic actually
171         # looks for it
172         ExecStartPre = [
173           "${pkgs.coreutils}/bin/mkdir -p ${cfg.dataDir}/.xonotic/data"
174           ''
175             ${pkgs.coreutils}/bin/ln -sf ${serverCfg} \
176               ${cfg.dataDir}/.xonotic/data/server.cfg
177           ''
178         ];
180         # Cargo-culted from search results about writing Xonotic systemd units
181         ExecReload = "${pkgs.util-linux}/bin/kill -HUP $MAINPID";
183         Restart = "on-failure";
184         RestartSec = 10;
185         StartLimitBurst = 5;
186       };
187     };
189     networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [
190       cfg.settings.port
191     ];
192     networking.firewall.allowedUDPPorts = lib.mkIf cfg.openFirewall [
193       cfg.settings.port
194     ];
195   };
197   meta.maintainers = with lib.maintainers; [ CobaltCause ];