grafana-alloy: don't build the frontend twice
[NixPkgs.git] / nixos / modules / services / games / terraria.nix
blob144186dc7701fc27ab6e3b5b0533bedf19457d50
1 { config, lib, options, pkgs, ... }:
2 let
3   cfg   = config.services.terraria;
4   opt   = options.services.terraria;
5   worldSizeMap = { small = 1; medium = 2; large = 3; };
6   valFlag = name: val: lib.optionalString (val != null) "-${name} \"${lib.escape ["\\" "\""] (toString val)}\"";
7   boolFlag = name: val: lib.optionalString val "-${name}";
8   flags = [
9     (valFlag "port" cfg.port)
10     (valFlag "maxPlayers" cfg.maxPlayers)
11     (valFlag "password" cfg.password)
12     (valFlag "motd" cfg.messageOfTheDay)
13     (valFlag "world" cfg.worldPath)
14     (valFlag "autocreate" (builtins.getAttr cfg.autoCreatedWorldSize worldSizeMap))
15     (valFlag "banlist" cfg.banListPath)
16     (boolFlag "secure" cfg.secure)
17     (boolFlag "noupnp" cfg.noUPnP)
18   ];
20   tmuxCmd = "${lib.getExe pkgs.tmux} -S ${lib.escapeShellArg cfg.dataDir}/terraria.sock";
22   stopScript = pkgs.writeShellScript "terraria-stop" ''
23     if ! [ -d "/proc/$1" ]; then
24       exit 0
25     fi
27     lastline=$(${tmuxCmd} capture-pane -p | grep . | tail -n1)
29     # If the service is not configured to auto-start a world, it will show the world selection prompt
30     # If the last non-empty line on-screen starts with "Choose World", we know the prompt is open
31     if [[ "$lastline" =~ ^'Choose World' ]]; then
32       # In this case, nothing needs to be saved, so we can kill the process
33       ${tmuxCmd} kill-session
34     else
35       # Otherwise, we send the `exit` command
36       ${tmuxCmd} send-keys Enter exit Enter
37     fi
39     # Wait for the process to stop
40     tail --pid="$1" -f /dev/null
41   '';
44   options = {
45     services.terraria = {
46       enable = lib.mkOption {
47         type        = lib.types.bool;
48         default     = false;
49         description = ''
50           If enabled, starts a Terraria server. The server can be connected to via `tmux -S ''${config.${opt.dataDir}}/terraria.sock attach`
51           for administration by users who are a part of the `terraria` group (use `C-b d` shortcut to detach again).
52         '';
53       };
55       port = lib.mkOption {
56         type        = lib.types.port;
57         default     = 7777;
58         description = ''
59           Specifies the port to listen on.
60         '';
61       };
63       maxPlayers = lib.mkOption {
64         type        = lib.types.ints.u8;
65         default     = 255;
66         description = ''
67           Sets the max number of players (between 1 and 255).
68         '';
69       };
71       password = lib.mkOption {
72         type        = lib.types.nullOr lib.types.str;
73         default     = null;
74         description = ''
75           Sets the server password. Leave `null` for no password.
76         '';
77       };
79       messageOfTheDay = lib.mkOption {
80         type        = lib.types.nullOr lib.types.str;
81         default     = null;
82         description = ''
83           Set the server message of the day text.
84         '';
85       };
87       worldPath = lib.mkOption {
88         type        = lib.types.nullOr lib.types.path;
89         default     = null;
90         description = ''
91           The path to the world file (`.wld`) which should be loaded.
92           If no world exists at this path, one will be created with the size
93           specified by `autoCreatedWorldSize`.
94         '';
95       };
97       autoCreatedWorldSize = lib.mkOption {
98         type        = lib.types.enum [ "small" "medium" "large" ];
99         default     = "medium";
100         description = ''
101           Specifies the size of the auto-created world if `worldPath` does not
102           point to an existing world.
103         '';
104       };
106       banListPath = lib.mkOption {
107         type        = lib.types.nullOr lib.types.path;
108         default     = null;
109         description = ''
110           The path to the ban list.
111         '';
112       };
114       secure = lib.mkOption {
115         type        = lib.types.bool;
116         default     = false;
117         description = "Adds additional cheat protection to the server.";
118       };
120       noUPnP = lib.mkOption {
121         type        = lib.types.bool;
122         default     = false;
123         description = "Disables automatic Universal Plug and Play.";
124       };
126       openFirewall = lib.mkOption {
127         type = lib.types.bool;
128         default = false;
129         description = "Whether to open ports in the firewall";
130       };
132       dataDir = lib.mkOption {
133         type        = lib.types.str;
134         default     = "/var/lib/terraria";
135         example     = "/srv/terraria";
136         description = "Path to variable state data directory for terraria.";
137       };
138     };
139   };
141   config = lib.mkIf cfg.enable {
142     users.users.terraria = {
143       description = "Terraria server service user";
144       group       = "terraria";
145       home        = cfg.dataDir;
146       createHome  = true;
147       uid         = config.ids.uids.terraria;
148     };
150     users.groups.terraria = {
151       gid = config.ids.gids.terraria;
152     };
154     systemd.services.terraria = {
155       description   = "Terraria Server Service";
156       wantedBy      = [ "multi-user.target" ];
157       after         = [ "network.target" ];
159       serviceConfig = {
160         User    = "terraria";
161         Group = "terraria";
162         Type = "forking";
163         GuessMainPID = true;
164         UMask = 007;
165         ExecStart = "${tmuxCmd} new -d ${pkgs.terraria-server}/bin/TerrariaServer ${lib.concatStringsSep " " flags}";
166         ExecStop = "${stopScript} $MAINPID";
167       };
168     };
170     networking.firewall = lib.mkIf cfg.openFirewall {
171       allowedTCPPorts = [ cfg.port ];
172       allowedUDPPorts = [ cfg.port ];
173     };
175   };