grafana-alloy: don't build the frontend twice
[NixPkgs.git] / nixos / modules / services / games / mchprs.nix
blob50db7cf66bb50d99b41df659a9a986e11f7ac2cb
1 { config, lib, pkgs, ... }:
3 with lib;
5 let
6   cfg = config.services.mchprs;
7   settingsFormat = pkgs.formats.toml { };
9   whitelistFile = pkgs.writeText "whitelist.json"
10     (builtins.toJSON
11       (mapAttrsToList (n: v: { name = n; uuid = v; }) cfg.whitelist.list));
13   configToml =
14     (removeAttrs cfg.settings [ "address" "port" ]) //
15     {
16       bind_address = cfg.settings.address + ":" + toString cfg.settings.port;
17       whitelist = cfg.whitelist.enable;
18     };
20   configTomlFile = settingsFormat.generate "Config.toml" configToml;
23   options = {
24     services.mchprs = {
25       enable = mkEnableOption "MCHPRS, a Minecraft server";
27       declarativeSettings = mkOption {
28         type = types.bool;
29         default = false;
30         description = ''
31           Whether to use a declarative configuration for MCHPRS.
32         '';
33       };
35       declarativeWhitelist = mkOption {
36         type = types.bool;
37         default = false;
38         description = ''
39           Whether to use a declarative whitelist.
40           The options {option}`services.mchprs.whitelist.list`
41           will be applied if and only if set to `true`.
42         '';
43       };
45       dataDir = mkOption {
46         type = types.path;
47         default = "/var/lib/mchprs";
48         description = ''
49           Directory to store MCHPRS database and other state/data files.
50         '';
51       };
53       openFirewall = mkOption {
54         type = types.bool;
55         default = false;
56         description = ''
57           Whether to open ports in the firewall for the server.
58           Only has effect when
59           {option}`services.mchprs.declarativeSettings` is `true`.
60         '';
61       };
63       maxRuntime = mkOption {
64         type = types.str;
65         default = "infinity";
66         example = "7d";
67         description = ''
68           Automatically restart the server after
69           {option}`services.mchprs.maxRuntime`.
70           The time span format is described here:
71           https://www.freedesktop.org/software/systemd/man/systemd.time.html#Parsing%20Time%20Spans.
72           If `null`, then the server is not restarted automatically.
73         '';
74       };
76       package = mkPackageOption pkgs "mchprs" { };
78       settings = mkOption {
79         type = types.submodule {
80           freeformType = settingsFormat.type;
82           options = {
83             port = mkOption {
84               type = types.port;
85               default = 25565;
86               description = ''
87                 Port for the server.
88                 Only has effect when
89                 {option}`services.mchprs.declarativeSettings` is `true`.
90               '';
91             };
93             address = mkOption {
94               type = types.str;
95               default = "0.0.0.0";
96               description = ''
97                 Address for the server.
98                 Please use enclosing square brackets when using ipv6.
99                 Only has effect when
100                 {option}`services.mchprs.declarativeSettings` is `true`.
101               '';
102             };
104             motd = mkOption {
105               type = types.str;
106               default = "Minecraft High Performance Redstone Server";
107               description = ''
108                 Message of the day.
109                 Only has effect when
110                 {option}`services.mchprs.declarativeSettings` is `true`.
111               '';
112             };
114             chat_format = mkOption {
115               type = types.str;
116               default = "<{username}> {message}";
117               description = ''
118                 How to format chat message interpolating `username`
119                 and `message` with curly braces.
120                 Only has effect when
121                 {option}`services.mchprs.declarativeSettings` is `true`.
122               '';
123             };
125             max_players = mkOption {
126               type = types.ints.positive;
127               default = 99999;
128               description = ''
129                 Maximum number of simultaneous players.
130                 Only has effect when
131                 {option}`services.mchprs.declarativeSettings` is `true`.
132               '';
133             };
135             view_distance = mkOption {
136               type = types.ints.positive;
137               default = 8;
138               description = ''
139                 Maximal distance (in chunks) between players and loaded chunks.
140                 Only has effect when
141                 {option}`services.mchprs.declarativeSettings` is `true`.
142               '';
143             };
145             bungeecord = mkOption {
146               type = types.bool;
147               default = false;
148               description = ''
149                 Enable compatibility with
150                 [BungeeCord](https://github.com/SpigotMC/BungeeCord).
151                 Only has effect when
152                 {option}`services.mchprs.declarativeSettings` is `true`.
153               '';
154             };
156             schemati = mkOption {
157               type = types.bool;
158               default = false;
159               description = ''
160                 Mimic the verification and directory layout used by the
161                 Open Redstone Engineers
162                 [Schemati plugin](https://github.com/OpenRedstoneEngineers/Schemati).
163                 Only has effect when
164                 {option}`services.mchprs.declarativeSettings` is `true`.
165               '';
166             };
168             block_in_hitbox = mkOption {
169               type = types.bool;
170               default = true;
171               description = ''
172                 Allow placing blocks inside of players
173                 (hitbox logic is simplified).
174                 Only has effect when
175                 {option}`services.mchprs.declarativeSettings` is `true`.
176               '';
177             };
179             auto_redpiler = mkOption {
180               type = types.bool;
181               default = true;
182               description = ''
183                 Use redpiler automatically.
184                 Only has effect when
185                 {option}`services.mchprs.declarativeSettings` is `true`.
186               '';
187             };
188           };
189         };
190         default = { };
192         description = ''
193           Configuration for MCHPRS via `Config.toml`.
194           See https://github.com/MCHPR/MCHPRS/blob/master/README.md for documentation.
195         '';
196       };
198       whitelist = {
199         enable = mkOption {
200           type = types.bool;
201           default = false;
202           description = ''
203             Whether or not the whitelist (in `whitelist.json`) shoud be enabled.
204             Only has effect when {option}`services.mchprs.declarativeSettings` is `true`.
205           '';
206         };
208         list = mkOption {
209           type =
210             let
211               minecraftUUID = types.strMatching
212                 "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" // {
213                 description = "Minecraft UUID";
214               };
215             in
216             types.attrsOf minecraftUUID;
217           default = { };
218           example = literalExpression ''
219             {
220               username1 = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
221               username2 = "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy";
222             };
223           '';
224           description = ''
225             Whitelisted players, only has an effect when
226             {option}`services.mchprs.declarativeWhitelist` is
227             `true` and the whitelist is enabled
228             via {option}`services.mchprs.whitelist.enable`.
229             This is a mapping from Minecraft usernames to UUIDs.
230             You can use <https://mcuuid.net/> to get a
231             Minecraft UUID for a username.
232           '';
233         };
234       };
235     };
236   };
238   config = mkIf cfg.enable {
239     users.users.mchprs = {
240       description = "MCHPRS service user";
241       home = cfg.dataDir;
242       createHome = true;
243       isSystemUser = true;
244       group = "mchprs";
245     };
246     users.groups.mchprs = { };
248     systemd.services.mchprs = {
249       description = "MCHPRS Service";
250       wantedBy = [ "multi-user.target" ];
251       after = [ "network.target" ];
253       serviceConfig = {
254         ExecStart = "${lib.getExe cfg.package}";
255         Restart = "always";
256         RuntimeMaxSec = cfg.maxRuntime;
257         User = "mchprs";
258         WorkingDirectory = cfg.dataDir;
260         StandardOutput = "journal";
261         StandardError = "journal";
263         # Hardening
264         CapabilityBoundingSet = [ "" ];
265         DeviceAllow = [ "" ];
266         LockPersonality = true;
267         MemoryDenyWriteExecute = true;
268         PrivateDevices = true;
269         PrivateTmp = true;
270         PrivateUsers = true;
271         ProtectClock = true;
272         ProtectControlGroups = true;
273         ProtectHome = true;
274         ProtectHostname = true;
275         ProtectKernelLogs = true;
276         ProtectKernelModules = true;
277         ProtectKernelTunables = true;
278         ProtectProc = "invisible";
279         RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
280         RestrictNamespaces = true;
281         RestrictRealtime = true;
282         RestrictSUIDSGID = true;
283         SystemCallArchitectures = "native";
284         UMask = "0077";
285       };
287       preStart =
288         (if cfg.declarativeSettings then ''
289           if [ -e .declarativeSettings ]; then
291             # Settings were declarative before, no need to back up anything
292             cp -f ${configTomlFile} Config.toml
294           else
296             # Declarative settings for the first time, backup stateful files
297             cp -b --suffix=.stateful ${configTomlFile} Config.toml
299             echo "Autogenerated file that implies that this server configuration is managed declaratively by NixOS" \
300               > .declarativeSettings
302           fi
303         '' else ''
304           if [ -e .declarativeSettings ]; then
305             rm .declarativeSettings
306           fi
307         '') + (if cfg.declarativeWhitelist then ''
308           if [ -e .declarativeWhitelist ]; then
310             # Whitelist was declarative before, no need to back up anything
311             ln -sf ${whitelistFile} whitelist.json
313           else
315             # Declarative whitelist for the first time, backup stateful files
316             ln -sb --suffix=.stateful ${whitelistFile} whitelist.json
318             echo "Autogenerated file that implies that this server's whitelist is managed declaratively by NixOS" \
319               > .declarativeWhitelist
321           fi
322         '' else ''
323           if [ -e .declarativeWhitelist ]; then
324             rm .declarativeWhitelist
325           fi
326         '');
327     };
329     networking.firewall = mkIf (cfg.declarativeSettings && cfg.openFirewall) {
330       allowedUDPPorts = [ cfg.settings.port ];
331       allowedTCPPorts = [ cfg.settings.port ];
332     };
333   };
335   meta.maintainers = with maintainers; [ gdd ];