grafana-alloy: don't build the frontend twice
[NixPkgs.git] / nixos / modules / services / games / armagetronad.nix
blobdfeadb19026f777df2411d3500edffdac6e5aa2b
1 { config, lib, pkgs, ... }:
2 let
3   inherit (lib) mkEnableOption mkIf mkOption mkMerge literalExpression;
4   inherit (lib) mapAttrsToList filterAttrs unique recursiveUpdate types;
6   mkValueStringArmagetron = with lib; v:
7     if isInt v then toString v
8     else if isFloat v then toString v
9     else if isString v then v
10     else if true == v then "1"
11     else if false == v then "0"
12     else if null == v then ""
13     else throw "unsupported type: ${builtins.typeOf v}: ${(lib.generators.toPretty {} v)}";
15   settingsFormat = pkgs.formats.keyValue {
16     mkKeyValue = lib.generators.mkKeyValueDefault
17       {
18         mkValueString = mkValueStringArmagetron;
19       } " ";
20     listsAsDuplicateKeys = true;
21   };
23   cfg = config.services.armagetronad;
24   enabledServers = lib.filterAttrs (n: v: v.enable) cfg.servers;
25   nameToId = serverName: "armagetronad-${serverName}";
26   getStateDirectory = serverName: "armagetronad/${serverName}";
27   getServerRoot = serverName: "/var/lib/${getStateDirectory serverName}";
30   options = {
31     services.armagetronad = {
32       servers = mkOption {
33         description = "Armagetron server definitions.";
34         default = { };
35         type = types.attrsOf (types.submodule {
36           options = {
37             enable = mkEnableOption "armagetronad";
39             package = lib.mkPackageOption pkgs "armagetronad-dedicated" {
40               example = ''
41                 pkgs.armagetronad."0.2.9-sty+ct+ap".dedicated
42               '';
43               extraDescription = ''
44                 Ensure that you use a derivation which contains the path `bin/armagetronad-dedicated`.
45               '';
46             };
48             host = mkOption {
49               type = types.str;
50               default = "0.0.0.0";
51               description = "Host to listen on. Used for SERVER_IP.";
52             };
54             port = mkOption {
55               type = types.port;
56               default = 4534;
57               description = "Port to listen on. Used for SERVER_PORT.";
58             };
60             dns = mkOption {
61               type = types.nullOr types.str;
62               default = null;
63               description = "DNS address to use for this server. Optional.";
64             };
66             openFirewall = mkOption {
67               type = types.bool;
68               default = true;
69               description = "Set to true to open the configured UDP port for Armagetron Advanced.";
70             };
72             name = mkOption {
73               type = types.str;
74               description = "The name of this server.";
75             };
77             settings = mkOption {
78               type = settingsFormat.type;
79               default = { };
80               description = ''
81                 Armagetron Advanced server rules configuration. Refer to:
82                 <https://wiki.armagetronad.org/index.php?title=Console_Commands>
83                 or `armagetronad-dedicated --doc` for a list.
85                 This attrset is used to populate `settings_custom.cfg`; see:
86                 <https://wiki.armagetronad.org/index.php/Configuration_Files>
87               '';
88               example = literalExpression ''
89                 {
90                   CYCLE_RUBBER = 40;
91                 }
92               '';
93             };
95             roundSettings = mkOption {
96               type = settingsFormat.type;
97               default = { };
98               description = ''
99                 Armagetron Advanced server per-round configuration. Refer to:
100                 <https://wiki.armagetronad.org/index.php?title=Console_Commands>
101                 or `armagetronad-dedicated --doc` for a list.
103                 This attrset is used to populate `everytime.cfg`; see:
104                 <https://wiki.armagetronad.org/index.php/Configuration_Files>
105               '';
106               example = literalExpression ''
107                 {
108                   SAY = [
109                     "Hosted on NixOS"
110                     "https://nixos.org"
111                     "iD Tech High Rubber rul3z!! Happy New Year 2008!!1"
112                   ];
113                 }
114               '';
115             };
116           };
117         });
118       };
119     };
120   };
122   config = mkIf (enabledServers != { }) {
123     systemd.tmpfiles.settings = mkMerge (mapAttrsToList
124       (serverName: serverCfg:
125         let
126           serverId = nameToId serverName;
127           serverRoot = getServerRoot serverName;
128           serverInfo = (
129             {
130               SERVER_IP = serverCfg.host;
131               SERVER_PORT = serverCfg.port;
132               SERVER_NAME = serverCfg.name;
133             } // (lib.optionalAttrs (serverCfg.dns != null) { SERVER_DNS = serverCfg.dns; })
134           );
135           customSettings = serverCfg.settings;
136           everytimeSettings = serverCfg.roundSettings;
138           serverInfoCfg = settingsFormat.generate "server_info.${serverName}.cfg" serverInfo;
139           customSettingsCfg = settingsFormat.generate "settings_custom.${serverName}.cfg" customSettings;
140           everytimeSettingsCfg = settingsFormat.generate "everytime.${serverName}.cfg" everytimeSettings;
141         in
142         {
143           "10-armagetronad-${serverId}" = {
144             "${serverRoot}/data" = {
145               d = {
146                 group = serverId;
147                 user = serverId;
148                 mode = "0750";
149               };
150             };
151             "${serverRoot}/settings" = {
152               d = {
153                 group = serverId;
154                 user = serverId;
155                 mode = "0750";
156               };
157             };
158             "${serverRoot}/var" = {
159               d = {
160                 group = serverId;
161                 user = serverId;
162                 mode = "0750";
163               };
164             };
165             "${serverRoot}/resource" = {
166               d = {
167                 group = serverId;
168                 user = serverId;
169                 mode = "0750";
170               };
171             };
172             "${serverRoot}/input" = {
173               "f+" = {
174                 group = serverId;
175                 user = serverId;
176                 mode = "0640";
177               };
178             };
179             "${serverRoot}/settings/server_info.cfg" = {
180               "L+" = {
181                 argument = "${serverInfoCfg}";
182               };
183             };
184             "${serverRoot}/settings/settings_custom.cfg" = {
185               "L+" = {
186                 argument = "${customSettingsCfg}";
187               };
188             };
189             "${serverRoot}/settings/everytime.cfg" = {
190               "L+" = {
191                 argument = "${everytimeSettingsCfg}";
192               };
193             };
194           };
195         }
196       )
197       enabledServers
198     );
200     systemd.services = mkMerge (mapAttrsToList
201       (serverName: serverCfg:
202         let
203           serverId = nameToId serverName;
204         in
205         {
206           "armagetronad-${serverName}" = {
207             description = "Armagetron Advanced Dedicated Server for ${serverName}";
208             wants = [ "basic.target" ];
209             after = [ "basic.target" "network.target" "multi-user.target" ];
210             wantedBy = [ "multi-user.target" ];
211             serviceConfig =
212               let
213                 serverRoot = getServerRoot serverName;
214               in
215               {
216                 Type = "simple";
217                 StateDirectory = getStateDirectory serverName;
218                 ExecStart = "${lib.getExe serverCfg.package} --daemon --input ${serverRoot}/input --userdatadir ${serverRoot}/data --userconfigdir ${serverRoot}/settings --vardir ${serverRoot}/var --autoresourcedir ${serverRoot}/resource";
219                 Restart = "on-failure";
220                 CapabilityBoundingSet = "";
221                 LockPersonality = true;
222                 NoNewPrivileges = true;
223                 PrivateDevices = true;
224                 PrivateTmp = true;
225                 PrivateUsers = true;
226                 ProtectClock = true;
227                 ProtectControlGroups = true;
228                 ProtectHome = true;
229                 ProtectHostname = true;
230                 ProtectKernelLogs = true;
231                 ProtectKernelModules = true;
232                 ProtectKernelTunables = true;
233                 ProtectProc = "invisible";
234                 ProtectSystem = "strict";
235                 RestrictNamespaces = true;
236                 RestrictSUIDSGID = true;
237                 User = serverId;
238                 Group = serverId;
239               };
240           };
241         })
242       enabledServers
243     );
245     networking.firewall.allowedUDPPorts =
246       unique (mapAttrsToList (serverName: serverCfg: serverCfg.port) (filterAttrs (serverName: serverCfg: serverCfg.openFirewall) enabledServers));
248     users.users = mkMerge (mapAttrsToList
249       (serverName: serverCfg:
250         {
251           ${nameToId serverName} = {
252             group = nameToId serverName;
253             description = "Armagetron Advanced dedicated user for server ${serverName}";
254             isSystemUser = true;
255           };
256         })
257       enabledServers
258     );
260     users.groups = mkMerge (mapAttrsToList
261       (serverName: serverCfg:
262         {
263           ${nameToId serverName} = { };
264         })
265       enabledServers
266     );
267   };