Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nixos/teeworlds: migrate to rfc42 settings #185492

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
184 changes: 105 additions & 79 deletions nixos/modules/services/games/teeworlds.nix
Original file line number Diff line number Diff line change
Expand Up @@ -4,93 +4,119 @@ with lib;

let
cfg = config.services.teeworlds;
register = cfg.register;

teeworldsConf = pkgs.writeText "teeworlds.cfg" ''
sv_port ${toString cfg.port}
sv_register ${if cfg.register then "1" else "0"}
${optionalString (cfg.name != null) "sv_name ${cfg.name}"}
${optionalString (cfg.motd != null) "sv_motd ${cfg.motd}"}
${optionalString (cfg.password != null) "password ${cfg.password}"}
${optionalString (cfg.rconPassword != null) "sv_rcon_password ${cfg.rconPassword}"}
${concatStringsSep "\n" cfg.extraOptions}
'';

configFile = pkgs.writeText "teeworlds.conf" (concatStringsSep "\n" (mapAttrsToList (key: value: "${key} ${toString value}") cfg.settings));
in
{
options = {
services.teeworlds = {
enable = mkEnableOption (lib.mdDoc "Teeworlds Server");

openPorts = mkOption {
type = types.bool;
default = false;
description = lib.mdDoc "Whether to open firewall ports for Teeworlds";
};

name = mkOption {
type = types.nullOr types.str;
default = null;
description = lib.mdDoc ''
Name of the server. Defaults to 'unnamed server'.
'';
};

register = mkOption {
type = types.bool;
example = true;
default = false;
description = lib.mdDoc ''
Whether the server registers as public server in the global server list. This is disabled by default because of privacy.
'';
};

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

password = mkOption {
type = types.nullOr types.str;
default = null;
description = lib.mdDoc ''
Password to connect to the server.
'';
};

rconPassword = mkOption {
type = types.nullOr types.str;
default = null;
description = lib.mdDoc ''
Password to access the remote console. If not set, a randomly generated one is displayed in the server log.
'';
};

port = mkOption {
type = types.port;
default = 8303;
description = lib.mdDoc ''
Port the server will listen on.
'';
};
imports = [
(mkRenamedOptionModule
[ "services" "teeworlds" "name" ]
[ "services" "teeworlds" "settings" "sv_name" ])
(mkRenamedOptionModule
[ "services" "teeworlds" "register" ]
[ "services" "teeworlds" "settings" "sv_register" ])
(mkRenamedOptionModule
[ "services" "teeworlds" "motd" ]
[ "services" "teeworlds" "settings" "sv_motd" ])
(mkRenamedOptionModule
[ "services" "teeworlds" "password" ]
[ "services" "teeworlds" "settings" "password" ])
(mkRenamedOptionModule
[ "services" "teeworlds" "rcon_password" ]
[ "services" "teeworlds" "settings" "sv_rcon_password" ])
(mkRenamedOptionModule
[ "services" "teeworlds" "port" ]
[ "services" "teeworlds" "settings" "sv_port" ])
(mkRemovedOptionModule [ "services" "teeworlds" "extraOptions" ]
"Use services.teeworlds.settings instead.")
];

options.services.teeworlds = {
enable = mkEnableOption (lib.mdDoc "Teeworlds Server");

openPorts = mkOption {
type = types.bool;
default = false;
description = lib.mdDoc ''
Whether to open firewall ports for Teeworlds
'';
};

extraOptions = mkOption {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should keep this for backwards compatibility and append those lines to the generated config. Maybe add a warning to switch to settings if extraOptions is defined.

type = types.listOf types.str;
default = [];
description = lib.mdDoc ''
Extra configuration lines for the {file}`teeworlds.cfg`. See [Teeworlds Documentation](https://www.teeworlds.com/?page=docs&wiki=server_settings).
'';
example = [ "sv_map dm1" "sv_gametype dm" ];
settings = mkOption {
default = {};
example = ''
{
sv_motd = "Welcome to this teeworlds ctf server!";
sv_map = "ctf1";
sv_gametype = "ctf";
}
'';
description = lib.mdDoc ''
See [Teeworlds Documentation](https://www.teeworlds.com/?page=docs&wiki=server_settings) for all configuration options.
'';
type = lib.types.submodule {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs a freeformType. Probably something like attrsOf (nullOr (oneOf [ str int bool ])). bool should be mapped to 0/1, null shouldn't be emitted.

freeformType = with lib.types; attrsOf (nullOr (oneOf [ str int bool ]));

options = {
sv_name = mkOption {
type = types.nullOr types.str;
default = null;
description = lib.mdDoc ''
Name of the server. Defaults to 'unnamed server'.
'';
};

sv_register = mkOption {
type = types.int;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we change the type, we should use mkChangedOptionModule; however here we should keep this as bool and map to 0/1 when writing the config.

example = 1;
default = 0;
description = lib.mdDoc ''
Whether the server registers as public server in the global server list. This is disabled (0) by default because of privacy.
'';
};

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

password = mkOption {
type = types.nullOr types.str;
default = null;
description = lib.mdDoc ''
Password to connect to the server.
'';
};

sv_rcon_password = mkOption {
type = types.nullOr types.str;
default = null;
description = lib.mdDoc ''
Password to access the remote console. If not set, a randomly generated one is displayed in the server log.
'';
};

sv_port = mkOption {
type = types.port;
default = 8303;
description = lib.mdDoc ''
Port the server will listen on.
'';
};
};
};
apply = value:
if isBool value
then (if value then 1 else 0)
else value;
};
};

config = mkIf cfg.enable {
networking.firewall = mkIf cfg.openPorts {
allowedUDPPorts = [ cfg.port ];
allowedUDPPorts = [ cfg.settings.sv_port ];
};

systemd.services.teeworlds = {
Expand All @@ -100,7 +126,7 @@ in

serviceConfig = {
DynamicUser = true;
ExecStart = "${pkgs.teeworlds}/bin/teeworlds_srv -f ${teeworldsConf}";
ExecStart = "${pkgs.teeworlds}/bin/teeworlds_srv -f ${configFile}";

# Hardening
CapabilityBoundingSet = false;
Expand Down