2016-08-07 01:58:38 +00:00
|
|
|
{ config, lib, pkgs, ... }:
|
|
|
|
|
|
|
|
with lib;
|
|
|
|
|
|
|
|
let
|
|
|
|
cfg = config.services.terraria;
|
2019-08-13 21:52:01 +00:00
|
|
|
worldSizeMap = { small = 1; medium = 2; large = 3; };
|
2016-08-07 01:58:38 +00:00
|
|
|
valFlag = name: val: optionalString (val != null) "-${name} \"${escape ["\\" "\""] (toString val)}\"";
|
|
|
|
boolFlag = name: val: optionalString val "-${name}";
|
2020-08-07 13:43:58 +00:00
|
|
|
flags = [
|
2016-08-07 01:58:38 +00:00
|
|
|
(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)
|
|
|
|
];
|
2018-08-07 17:57:35 +00:00
|
|
|
stopScript = pkgs.writeScript "terraria-stop" ''
|
|
|
|
#!${pkgs.runtimeShell}
|
|
|
|
|
|
|
|
if ! [ -d "/proc/$1" ]; then
|
|
|
|
exit 0
|
|
|
|
fi
|
|
|
|
|
2020-09-05 20:37:52 +00:00
|
|
|
${getBin pkgs.tmux}/bin/tmux -S ${cfg.dataDir}/terraria.sock send-keys Enter exit Enter
|
2018-08-07 17:57:35 +00:00
|
|
|
${getBin pkgs.coreutils}/bin/tail --pid="$1" -f /dev/null
|
|
|
|
'';
|
2016-08-07 01:58:38 +00:00
|
|
|
in
|
|
|
|
{
|
|
|
|
options = {
|
|
|
|
services.terraria = {
|
|
|
|
enable = mkOption {
|
|
|
|
type = types.bool;
|
|
|
|
default = false;
|
|
|
|
description = ''
|
2020-09-05 20:37:52 +00:00
|
|
|
If enabled, starts a Terraria server. The server can be connected to via <literal>tmux -S ${cfg.dataDir}/terraria.sock attach</literal>
|
2016-08-07 01:58:38 +00:00
|
|
|
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 {
|
2016-12-11 13:59:41 +00:00
|
|
|
type = types.nullOr types.path;
|
2016-08-07 01:58:38 +00:00
|
|
|
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.";
|
|
|
|
};
|
2021-05-20 09:43:17 +00:00
|
|
|
|
|
|
|
openFirewall = mkOption {
|
|
|
|
type = types.bool;
|
|
|
|
default = false;
|
|
|
|
description = "Wheter to open ports in the firewall";
|
|
|
|
};
|
|
|
|
|
2020-09-05 20:37:52 +00:00
|
|
|
dataDir = mkOption {
|
|
|
|
type = types.str;
|
|
|
|
default = "/var/lib/terraria";
|
|
|
|
example = "/srv/terraria";
|
|
|
|
description = "Path to variable state data directory for terraria.";
|
|
|
|
};
|
2016-08-07 01:58:38 +00:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
config = mkIf cfg.enable {
|
2018-06-29 23:58:35 +00:00
|
|
|
users.users.terraria = {
|
2016-08-07 01:58:38 +00:00
|
|
|
description = "Terraria server service user";
|
2020-09-05 20:37:52 +00:00
|
|
|
home = cfg.dataDir;
|
2016-08-07 01:58:38 +00:00
|
|
|
createHome = true;
|
|
|
|
uid = config.ids.uids.terraria;
|
|
|
|
};
|
|
|
|
|
2018-06-29 23:58:35 +00:00
|
|
|
users.groups.terraria = {
|
2016-08-07 01:58:38 +00:00
|
|
|
gid = config.ids.gids.terraria;
|
|
|
|
members = [ "terraria" ];
|
|
|
|
};
|
|
|
|
|
|
|
|
systemd.services.terraria = {
|
|
|
|
description = "Terraria Server Service";
|
|
|
|
wantedBy = [ "multi-user.target" ];
|
|
|
|
after = [ "network.target" ];
|
|
|
|
|
|
|
|
serviceConfig = {
|
|
|
|
User = "terraria";
|
2018-08-07 17:57:35 +00:00
|
|
|
Type = "forking";
|
|
|
|
GuessMainPID = true;
|
2020-09-05 20:37:52 +00:00
|
|
|
ExecStart = "${getBin pkgs.tmux}/bin/tmux -S ${cfg.dataDir}/terraria.sock new -d ${pkgs.terraria-server}/bin/TerrariaServer ${concatStringsSep " " flags}";
|
2018-08-07 17:57:35 +00:00
|
|
|
ExecStop = "${stopScript} $MAINPID";
|
2016-08-07 01:58:38 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
postStart = ''
|
2020-09-05 20:37:52 +00:00
|
|
|
${pkgs.coreutils}/bin/chmod 660 ${cfg.dataDir}/terraria.sock
|
|
|
|
${pkgs.coreutils}/bin/chgrp terraria ${cfg.dataDir}/terraria.sock
|
2016-08-07 01:58:38 +00:00
|
|
|
'';
|
|
|
|
};
|
2021-05-20 09:43:17 +00:00
|
|
|
|
|
|
|
networking.firewall = mkIf cfg.openFirewall {
|
|
|
|
allowedTCPPorts = [ cfg.port ];
|
|
|
|
allowedUDPPorts = [ cfg.port ];
|
|
|
|
};
|
|
|
|
|
2016-08-07 01:58:38 +00:00
|
|
|
};
|
|
|
|
}
|