summaryrefslogtreecommitdiffstats
path: root/nixos/modules/services/games
diff options
context:
space:
mode:
authorJeremy <jtbx@duck.com>2023-08-07 11:20:09 +1200
committerJeremy Baxter <jtbx@disroot.org>2024-02-14 10:57:08 +1300
commite01eda6edd426904679d78c1803ce8ce57666487 (patch)
tree3900f707bb8c2b3431fd99a34a74a935652a9ef0 /nixos/modules/services/games
parent32c95ed9d53bc1827fc66082c89c0aca9ee0ecba (diff)
nixos/teeworlds: add more configuration options, revise
- add 'package' option - add 'game' and 'server' attrset - reduce repetition by using functions bool and optionalSetting - add default value for cfg.name - revise some option descriptions
Diffstat (limited to 'nixos/modules/services/games')
-rw-r--r--nixos/modules/services/games/teeworlds.nix310
1 files changed, 298 insertions, 12 deletions
diff --git a/nixos/modules/services/games/teeworlds.nix b/nixos/modules/services/games/teeworlds.nix
index bd0df1ffca57..04b611fb3cb1 100644
--- a/nixos/modules/services/games/teeworlds.nix
+++ b/nixos/modules/services/games/teeworlds.nix
@@ -6,13 +6,86 @@ let
cfg = config.services.teeworlds;
register = cfg.register;
+ bool = b: if b != null && b then "1" else "0";
+ optionalSetting = s: setting: optionalString (s != null) "${setting} ${s}";
+ lookup = attrs: key: default: if attrs ? key then attrs."${key}" else default;
+
+ inactivePenaltyOptions = {
+ "spectator" = "1";
+ "spectator/kick" = "2";
+ "kick" = "3";
+ };
+ skillLevelOptions = {
+ "casual" = "0";
+ "normal" = "1";
+ "competitive" = "2";
+ };
+ tournamentModeOptions = {
+ "disable" = "0";
+ "enable" = "1";
+ "restrictSpectators" = "2";
+ };
+
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}"}
+ sv_register ${bool cfg.register}
+ sv_name ${cfg.name}
+ ${optionalSetting cfg.motd "sv_motd"}
+ ${optionalSetting cfg.password "password"}
+ ${optionalSetting cfg.rconPassword "sv_rcon_password"}
+
+ ${optionalSetting cfg.server.bindAddr "bindaddr"}
+ ${optionalSetting cfg.server.hostName "sv_hostname"}
+ sv_high_bandwidth ${bool cfg.server.enableHighBandwidth}
+ sv_inactivekick ${lookup inactivePenaltyOptions cfg.server.inactivePenalty "spectator/kick"}
+ sv_inactivekick_spec ${bool cfg.server.kickInactiveSpectators}
+ sv_inactivekick_time ${toString cfg.server.inactiveTime}
+ sv_max_clients ${toString cfg.server.maxClients}
+ sv_max_clients_per_ip ${toString cfg.server.maxClientsPerIP}
+ sv_skill_level ${lookup skillLevelOptions cfg.server.skillLevel "normal"}
+ sv_spamprotection ${bool cfg.server.enableSpamProtection}
+
+ sv_gametype ${cfg.game.gameType}
+ sv_map ${cfg.game.map}
+ sv_match_swap ${bool cfg.game.swapTeams}
+ sv_player_ready_mode ${bool cfg.game.enableReadyMode}
+ sv_player_slots ${toString cfg.game.playerSlots}
+ sv_powerups ${bool cfg.game.enablePowerups}
+ sv_scorelimit ${toString cfg.game.scoreLimit}
+ sv_strict_spectate_mode ${bool cfg.game.restrictSpectators}
+ sv_teamdamage ${bool cfg.game.enableTeamDamage}
+ sv_timelimit ${toString cfg.game.timeLimit}
+ sv_tournament_mode ${lookup tournamentModeOptions cfg.server.tournamentMode "disable"}
+ sv_vote_kick ${bool cfg.game.enableVoteKick}
+ sv_vote_kick_bantime ${toString cfg.game.voteKickBanTime}
+ sv_vote_kick_min ${toString cfg.game.voteKickMinimumPlayers}
+
+ ${optionalSetting cfg.server.bindAddr "bindaddr"}
+ ${optionalSetting cfg.server.hostName "sv_hostname"}
+ sv_high_bandwidth ${bool cfg.server.enableHighBandwidth}
+ sv_inactivekick ${lookup inactivePenaltyOptions cfg.server.inactivePenalty "spectator/kick"}
+ sv_inactivekick_spec ${bool cfg.server.kickInactiveSpectators}
+ sv_inactivekick_time ${toString cfg.server.inactiveTime}
+ sv_max_clients ${toString cfg.server.maxClients}
+ sv_max_clients_per_ip ${toString cfg.server.maxClientsPerIP}
+ sv_skill_level ${lookup skillLevelOptions cfg.server.skillLevel "normal"}
+ sv_spamprotection ${bool cfg.server.enableSpamProtection}
+
+ sv_gametype ${cfg.game.gameType}
+ sv_map ${cfg.game.map}
+ sv_match_swap ${bool cfg.game.swapTeams}
+ sv_player_ready_mode ${bool cfg.game.enableReadyMode}
+ sv_player_slots ${toString cfg.game.playerSlots}
+ sv_powerups ${bool cfg.game.enablePowerups}
+ sv_scorelimit ${toString cfg.game.scoreLimit}
+ sv_strict_spectate_mode ${bool cfg.game.restrictSpectators}
+ sv_teamdamage ${bool cfg.game.enableTeamDamage}
+ sv_timelimit ${toString cfg.game.timeLimit}
+ sv_tournament_mode ${lookup tournamentModeOptions cfg.server.tournamentMode "disable"}
+ sv_vote_kick ${bool cfg.game.enableVoteKick}
+ sv_vote_kick_bantime ${toString cfg.game.voteKickBanTime}
+ sv_vote_kick_min ${toString cfg.game.voteKickMinimumPlayers}
+
${concatStringsSep "\n" cfg.extraOptions}
'';
@@ -22,17 +95,19 @@ in
services.teeworlds = {
enable = mkEnableOption (lib.mdDoc "Teeworlds Server");
+ package = mkPackageOptionMD pkgs "teeworlds-server" { };
+
openPorts = mkOption {
type = types.bool;
default = false;
- description = lib.mdDoc "Whether to open firewall ports for Teeworlds";
+ description = lib.mdDoc "Whether to open firewall ports for Teeworlds.";
};
name = mkOption {
- type = types.nullOr types.str;
- default = null;
+ type = types.str;
+ default = "unnamed server";
description = lib.mdDoc ''
- Name of the server. Defaults to 'unnamed server'.
+ Name of the server.
'';
};
@@ -41,7 +116,7 @@ in
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.
+ Whether the server registers as a public server in the global server list. This is disabled by default for privacy reasons.
'';
};
@@ -49,7 +124,7 @@ in
type = types.nullOr types.str;
default = null;
description = lib.mdDoc ''
- Set the server message of the day text.
+ The server's message of the day text.
'';
};
@@ -85,6 +160,217 @@ in
'';
example = [ "sv_map dm1" "sv_gametype dm" ];
};
+
+ server = {
+ bindAddr = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ description = lib.mdDoc ''
+ The address the server will bind to.
+ '';
+ };
+
+ enableHighBandwidth = mkOption {
+ type = types.bool;
+ default = false;
+ description = lib.mdDoc ''
+ Whether to enable high bandwidth mode on LAN servers. This will double the amount of bandwidth required for running the server.
+ '';
+ };
+
+ hostName = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ description = lib.mdDoc ''
+ Hostname for the server.
+ '';
+ };
+
+ inactivePenalty = mkOption {
+ type = types.enum [ "spectator" "spectator/kick" "kick" ];
+ example = "spectator";
+ default = "spectator/kick";
+ description = lib.mdDoc ''
+ Specify what to do when a client goes inactive (see [](#opt-services.teeworlds.server.inactiveTime)).
+
+ - `spectator`: send the client into spectator mode
+
+ - `spectator/kick`: send the client into a free spectator slot, otherwise kick the client
+
+ - `kick`: kick the client
+ '';
+ };
+
+ kickInactiveSpectators = mkOption {
+ type = types.bool;
+ default = false;
+ description = lib.mdDoc ''
+ Whether to kick inactive spectators.
+ '';
+ };
+
+ inactiveTime = mkOption {
+ type = types.ints.unsigned;
+ default = 3;
+ description = lib.mdDoc ''
+ The amount of minutes a client has to idle before it is considered inactive.
+ '';
+ };
+
+ maxClients = mkOption {
+ type = types.ints.unsigned;
+ default = 12;
+ description = lib.mdDoc ''
+ The maximum amount of clients that can be connected to the server at the same time.
+ '';
+ };
+
+ maxClientsPerIP = mkOption {
+ type = types.ints.unsigned;
+ default = 12;
+ description = lib.mdDoc ''
+ The maximum amount of clients with the same IP address that can be connected to the server at the same time.
+ '';
+ };
+
+ skillLevel = mkOption {
+ type = types.enum [ "casual" "normal" "competitive" ];
+ default = "normal";
+ description = lib.mdDoc ''
+ The skill level shown in the server browser.
+ '';
+ };
+
+ enableSpamProtection = mkOption {
+ type = types.bool;
+ default = true;
+ description = lib.mdDoc ''
+ Whether to enable chat spam protection.
+ '';
+ };
+ };
+
+ game = {
+ gameType = mkOption {
+ type = types.str;
+ example = "ctf";
+ default = "dm";
+ description = lib.mdDoc ''
+ The game type to use on the server.
+
+ The default gametypes are `dm`, `tdm`, `ctf`, `lms`, and `lts`.
+ '';
+ };
+
+ map = mkOption {
+ type = types.str;
+ example = "ctf5";
+ default = "dm1";
+ description = lib.mdDoc ''
+ The map to use on the server.
+ '';
+ };
+
+ swapTeams = mkOption {
+ type = types.bool;
+ default = true;
+ description = lib.mdDoc ''
+ Whether to swap teams each round.
+ '';
+ };
+
+ enableReadyMode = mkOption {
+ type = types.bool;
+ default = false;
+ description = lib.mdDoc ''
+ Whether to enable "ready mode"; where players can pause/unpause the game
+ and start the game in warmup, using their ready state.
+ '';
+ };
+
+ playerSlots = mkOption {
+ type = types.ints.unsigned;
+ default = 8;
+ description = lib.mdDoc ''
+ The amount of slots to reserve for players (as opposed to spectators).
+ '';
+ };
+
+ enablePowerups = mkOption {
+ type = types.bool;
+ default = true;
+ description = lib.mdDoc ''
+ Whether to allow powerups such as the ninja.
+ '';
+ };
+
+ scoreLimit = mkOption {
+ type = types.ints.unsigned;
+ example = 400;
+ default = 20;
+ description = lib.mdDoc ''
+ The score limit needed to win a round.
+ '';
+ };
+
+ restrictSpectators = mkOption {
+ type = types.bool;
+ default = false;
+ description = lib.mdDoc ''
+ Whether to restrict access to information such as health, ammo and armour in spectator mode.
+ '';
+ };
+
+ enableTeamDamage = mkOption {
+ type = types.bool;
+ default = false;
+ description = lib.mdDoc ''
+ Whether to enable team damage; whether to allow team mates to inflict damage on one another.
+ '';
+ };
+
+ timeLimit = mkOption {
+ type = types.ints.unsigned;
+ default = 0;
+ description = lib.mdDoc ''
+ Time limit of the game. In cases of equal points, there will be sudden death.
+ Setting this to 0 disables a time limit.
+ '';
+ };
+
+ tournamentMode = mkOption {
+ type = types.enum [ "disable" "enable" "restrictSpectators" ];
+ default = "disable";
+ description = lib.mdDoc ''
+ Whether to enable tournament mode. In tournament mode, players join as spectators.
+ If this is set to `restrictSpectators`, tournament mode is enabled but spectator chat is restricted.
+ '';
+ };
+
+ enableVoteKick = mkOption {
+ type = types.bool;
+ default = true;
+ description = lib.mdDoc ''
+ Whether to enable voting to kick players.
+ '';
+ };
+
+ voteKickBanTime = mkOption {
+ type = types.ints.unsigned;
+ default = 5;
+ description = lib.mdDoc ''
+ The amount of minutes that a player is banned for if they get kicked by a vote.
+ '';
+ };
+
+ voteKickMinimumPlayers = mkOption {
+ type = types.ints.unsigned;
+ default = 5;
+ description = lib.mdDoc ''
+ The minimum amount of players required to start a kick vote.
+ '';
+ };
+ };
};
};
@@ -100,7 +386,7 @@ in
serviceConfig = {
DynamicUser = true;
- ExecStart = "${pkgs.teeworlds-server}/bin/teeworlds_srv -f ${teeworldsConf}";
+ ExecStart = "${cfg.package}/bin/teeworlds_srv -f ${teeworldsConf}";
# Hardening
CapabilityBoundingSet = false;