summary refs log tree commit diff
path: root/nixos/modules/services/games/terraria.nix
blob: 413660321ec3cb8f0bf0f0aeec3232a056e61c5a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
{ config, lib, pkgs, ... }:

with lib;

let
  cfg   = config.services.terraria;
  worldSizeMap = { small = 1; medium = 2; large = 3; };
  valFlag = name: val: optionalString (val != null) "-${name} \"${escape ["\\" "\""] (toString val)}\"";
  boolFlag = name: val: optionalString val "-${name}";
  flags = [
    (valFlag "port" cfg.port)
    (valFlag "maxPlayers" cfg.maxPlayers)
    (valFlag "password" cfg.password)
    (valFlag "motd" cfg.messageOfTheDay)
    (valFlag "world" cfg.worldPath)
    (valFlag "autocreate" (builtins.getAttr cfg.autoCreatedWorldSize worldSizeMap))
    (valFlag "banlist" cfg.banListPath)
    (boolFlag "secure" cfg.secure)
    (boolFlag "noupnp" cfg.noUPnP)
  ];
  stopScript = pkgs.writeScript "terraria-stop" ''
    #!${pkgs.runtimeShell}

    if ! [ -d "/proc/$1" ]; then
      exit 0
    fi

    ${getBin pkgs.tmux}/bin/tmux -S /var/lib/terraria/terraria.sock send-keys Enter exit Enter
    ${getBin pkgs.coreutils}/bin/tail --pid="$1" -f /dev/null
  '';
in
{
  options = {
    services.terraria = {
      enable = mkOption {
        type        = types.bool;
        default     = false;
        description = ''
          If enabled, starts a Terraria server. The server can be connected to via <literal>tmux -S /var/lib/terraria/terraria.sock attach</literal>
          for administration by users who are a part of the <literal>terraria</literal> group (use <literal>C-b d</literal> shortcut to detach again).
        '';
      };

      port = mkOption {
        type        = types.int;
        default     = 7777;
        description = ''
          Specifies the port to listen on.
        '';
      };

      maxPlayers = mkOption {
        type        = types.int;
        default     = 255;
        description = ''
          Sets the max number of players (between 1 and 255).
        '';
      };

      password = mkOption {
        type        = types.nullOr types.str;
        default     = null;
        description = ''
          Sets the server password. Leave <literal>null</literal> for no password.
        '';
      };

      messageOfTheDay = mkOption {
        type        = types.nullOr types.str;
        default     = null;
        description = ''
          Set the server message of the day text.
        '';
      };

      worldPath = mkOption {
        type        = types.nullOr types.path;
        default     = null;
        description = ''
          The path to the world file (<literal>.wld</literal>) which should be loaded.
          If no world exists at this path, one will be created with the size
          specified by <literal>autoCreatedWorldSize</literal>.
        '';
      };

      autoCreatedWorldSize = mkOption {
        type        = types.enum [ "small" "medium" "large" ];
        default     = "medium";
        description = ''
          Specifies the size of the auto-created world if <literal>worldPath</literal> does not
          point to an existing world.
        '';
      };

      banListPath = mkOption {
        type        = types.nullOr types.path;
        default     = null;
        description = ''
          The path to the ban list.
        '';
      };

      secure = mkOption {
        type        = types.bool;
        default     = false;
        description = "Adds additional cheat protection to the server.";
      };

      noUPnP = mkOption {
        type        = types.bool;
        default     = false;
        description = "Disables automatic Universal Plug and Play.";
      };
    };
  };

  config = mkIf cfg.enable {
    users.users.terraria = {
      description = "Terraria server service user";
      home        = "/var/lib/terraria";
      createHome  = true;
      uid         = config.ids.uids.terraria;
    };

    users.groups.terraria = {
      gid = config.ids.gids.terraria;
      members = [ "terraria" ];
    };

    systemd.services.terraria = {
      description   = "Terraria Server Service";
      wantedBy      = [ "multi-user.target" ];
      after         = [ "network.target" ];

      serviceConfig = {
        User    = "terraria";
        Type = "forking";
        GuessMainPID = true;
        ExecStart = "${getBin pkgs.tmux}/bin/tmux -S /var/lib/terraria/terraria.sock new -d ${pkgs.terraria-server}/bin/TerrariaServer ${concatStringsSep " " flags}";
        ExecStop = "${stopScript} $MAINPID";
      };

      postStart = ''
        ${pkgs.coreutils}/bin/chmod 660 /var/lib/terraria/terraria.sock
        ${pkgs.coreutils}/bin/chgrp terraria /var/lib/terraria/terraria.sock
      '';
    };
  };
}