···11+{ config, pkgs, lib, ... }:
22+with lib;
33+44+let
55+ cfg = config.services.quake3-server;
66+ configFile = pkgs.writeText "q3ds-extra.cfg" ''
77+ set net_port ${builtins.toString cfg.port}
88+99+ ${cfg.extraConfig}
1010+ '';
1111+ defaultBaseq3 = pkgs.requireFile rec {
1212+ name = "baseq3";
1313+ hashMode = "recursive";
1414+ sha256 = "5dd8ee09eabd45e80450f31d7a8b69b846f59738726929298d8a813ce5725ed3";
1515+ message = ''
1616+ Unfortunately, we cannot download ${name} automatically.
1717+ Please purchase a legitimate copy of Quake 3 and change into the installation directory.
1818+1919+ You can either add all relevant files to the nix-store like this:
2020+ mkdir /tmp/baseq3
2121+ cp baseq3/pak*.pk3 /tmp/baseq3
2222+ nix-store --add-fixed sha256 --recursive /tmp/baseq3
2323+2424+ Alternatively you can set services.quake3-server.baseq3 to a path and copy the baseq3 directory into
2525+ $services.quake3-server.baseq3/.q3a/
2626+ '';
2727+ };
2828+ home = pkgs.runCommand "quake3-home" {} ''
2929+ mkdir -p $out/.q3a/baseq3
3030+3131+ for file in ${cfg.baseq3}/*; do
3232+ ln -s $file $out/.q3a/baseq3/$(basename $file)
3333+ done
3434+3535+ ln -s ${configFile} $out/.q3a/baseq3/nix.cfg
3636+ '';
3737+in {
3838+ options = {
3939+ services.quake3-server = {
4040+ enable = mkEnableOption "Quake 3 dedicated server";
4141+4242+ port = mkOption {
4343+ type = types.port;
4444+ default = 27960;
4545+ description = ''
4646+ UDP Port the server should listen on.
4747+ '';
4848+ };
4949+5050+ openFirewall = mkOption {
5151+ type = types.bool;
5252+ default = false;
5353+ description = ''
5454+ Open the firewall.
5555+ '';
5656+ };
5757+5858+ extraConfig = mkOption {
5959+ type = types.lines;
6060+ default = "";
6161+ example = ''
6262+ seta rconPassword "superSecret" // sets RCON password for remote console
6363+ seta sv_hostname "My Quake 3 server" // name that appears in server list
6464+ '';
6565+ description = ''
6666+ Extra configuration options. Note that options changed via RCON will not be persisted. To list all possible
6767+ options, use "cvarlist 1" via RCON.
6868+ '';
6969+ };
7070+7171+ baseq3 = mkOption {
7272+ type = types.either types.package types.path;
7373+ default = defaultBaseq3;
7474+ example = "/var/lib/q3ds";
7575+ description = ''
7676+ Path to the baseq3 files (pak*.pk3). If this is on the nix store (type = package) all .pk3 files should be saved
7777+ in the top-level directory. If this is on another filesystem (e.g /var/lib/baseq3) the .pk3 files are searched in
7878+ $baseq3/.q3a/baseq3/
7979+ '';
8080+ };
8181+ };
8282+ };
8383+8484+ config = let
8585+ baseq3InStore = builtins.typeOf cfg.baseq3 == "set";
8686+ in mkIf cfg.enable {
8787+ networking.firewall.allowedUDPPorts = mkIf cfg.openFirewall [ cfg.port ];
8888+8989+ systemd.services.q3ds = {
9090+ description = "Quake 3 dedicated server";
9191+ wantedBy = [ "multi-user.target" ];
9292+ after = [ "networking.target" ];
9393+9494+ environment.HOME = if baseq3InStore then home else cfg.baseq3;
9595+9696+ serviceConfig = with lib; {
9797+ Restart = "always";
9898+ DynamicUser = true;
9999+ WorkingDirectory = home;
100100+101101+ # It is possible to alter configuration files via RCON. To ensure reproducibility we have to prevent this
102102+ ReadOnlyPaths = if baseq3InStore then home else cfg.baseq3;
103103+ ExecStartPre = optionalString (!baseq3InStore) "+${pkgs.coreutils}/bin/cp ${configFile} ${cfg.baseq3}/.q3a/baseq3/nix.cfg";
104104+105105+ ExecStart = "${pkgs.ioquake3}/ioq3ded.x86_64 +exec nix.cfg";
106106+ };
107107+ };
108108+ };
109109+110110+ meta.maintainers = with maintainers; [ f4814n ];
111111+}