feat(nixos): add Palworld Tracion guest
This commit is contained in:
146
configs/nixos/guests/palworld.nix
Normal file
146
configs/nixos/guests/palworld.nix
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
cfg = config.guests.palworld;
|
||||||
|
steamDir = "/data/steamcmd";
|
||||||
|
installDir = "/data/server";
|
||||||
|
stateDir = "/data/state";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.guests.palworld = {
|
||||||
|
steamPath = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = null;
|
||||||
|
example = "/srv/steamcmd";
|
||||||
|
description = "Host path bind-mounted as SteamCMD's metadata directory.";
|
||||||
|
};
|
||||||
|
|
||||||
|
installPath = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = null;
|
||||||
|
example = "/srv/palworld/server";
|
||||||
|
description = "Host path bind-mounted as Palworld's server install directory.";
|
||||||
|
};
|
||||||
|
|
||||||
|
statePath = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = null;
|
||||||
|
example = "/srv/palworld/state";
|
||||||
|
description = "Host path bind-mounted as Palworld's durable state directory.";
|
||||||
|
};
|
||||||
|
|
||||||
|
serverName = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "palworld";
|
||||||
|
description = "Name shown for the Palworld server.";
|
||||||
|
};
|
||||||
|
|
||||||
|
port = lib.mkOption {
|
||||||
|
type = lib.types.port;
|
||||||
|
default = 8211;
|
||||||
|
description = "UDP port for the Palworld server.";
|
||||||
|
};
|
||||||
|
|
||||||
|
publicIP = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = null;
|
||||||
|
description = "Public IP address advertised by the Palworld server.";
|
||||||
|
};
|
||||||
|
|
||||||
|
passwordSecret = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "services/game-servers/password";
|
||||||
|
description = "SOPS secret name containing the Palworld server password.";
|
||||||
|
};
|
||||||
|
|
||||||
|
adminPasswordSecret = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "services/game-servers/password";
|
||||||
|
description = "SOPS secret name containing the Palworld admin password.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
assertions = [
|
||||||
|
{
|
||||||
|
assertion = cfg.steamPath != null;
|
||||||
|
message = "guests.palworld.steamPath must point at the host directory that stores SteamCMD metadata.";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion = cfg.installPath != null;
|
||||||
|
message = "guests.palworld.installPath must point at the host directory that stores Palworld server files.";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion = cfg.statePath != null;
|
||||||
|
message = "guests.palworld.statePath must point at the host directory that stores Palworld durable state.";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
guests.palworld = {
|
||||||
|
interior.modules.services.games.palworld = {
|
||||||
|
enable = true;
|
||||||
|
inherit (cfg) serverName port publicIP;
|
||||||
|
inherit steamDir installDir stateDir;
|
||||||
|
passwordFile = "/run/secrets/${cfg.passwordSecret}";
|
||||||
|
adminPasswordFile = "/run/secrets/${cfg.adminPasswordSecret}";
|
||||||
|
};
|
||||||
|
|
||||||
|
mounts =
|
||||||
|
{
|
||||||
|
${steamDir} = {
|
||||||
|
directory = cfg.steamPath;
|
||||||
|
owner = config.registry."stable-uid".${config.networking.hostName}.steamcmd;
|
||||||
|
group = "storage";
|
||||||
|
mode = "0770";
|
||||||
|
};
|
||||||
|
${installDir} = {
|
||||||
|
directory = cfg.installPath;
|
||||||
|
owner = config.registry."stable-uid".${config.networking.hostName}.steamcmd;
|
||||||
|
group = "storage";
|
||||||
|
mode = "0770";
|
||||||
|
};
|
||||||
|
${stateDir} = {
|
||||||
|
directory = cfg.statePath;
|
||||||
|
owner = config.registry."stable-uid".${config.networking.hostName}.steamcmd;
|
||||||
|
group = "storage";
|
||||||
|
mode = "0770";
|
||||||
|
};
|
||||||
|
${"/run/secrets/${cfg.passwordSecret}"} = {
|
||||||
|
file = config.sops.secrets.${cfg.passwordSecret}.path;
|
||||||
|
readOnly = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// lib.optionalAttrs (cfg.adminPasswordSecret != cfg.passwordSecret) {
|
||||||
|
${"/run/secrets/${cfg.adminPasswordSecret}"} = {
|
||||||
|
file = config.sops.secrets.${cfg.adminPasswordSecret}.path;
|
||||||
|
readOnly = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
limits = {
|
||||||
|
memory = lib.mkDefault "8G";
|
||||||
|
cpu = lib.mkDefault "400%";
|
||||||
|
tasksMax = lib.mkDefault 2048;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
sops.secrets =
|
||||||
|
{
|
||||||
|
${cfg.passwordSecret} = {
|
||||||
|
owner = "root";
|
||||||
|
group = "storage";
|
||||||
|
mode = "0440";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// lib.optionalAttrs (cfg.adminPasswordSecret != cfg.passwordSecret) {
|
||||||
|
${cfg.adminPasswordSecret} = {
|
||||||
|
owner = "root";
|
||||||
|
group = "storage";
|
||||||
|
mode = "0440";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -86,6 +86,24 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
guests.palworld = {
|
||||||
|
enable = true;
|
||||||
|
vlan = 20;
|
||||||
|
address = "10.23.20.198";
|
||||||
|
steamPath = "/pikachu/data/steamcmd";
|
||||||
|
installPath = "/pikachu/data/servers/palworld/tracion";
|
||||||
|
statePath = "/pikachu/media/games/servers/palworld/tracion";
|
||||||
|
serverName = "Tracion";
|
||||||
|
publicIP = config.guests.palworld.address;
|
||||||
|
reverseProxy = {
|
||||||
|
enable = true;
|
||||||
|
route = {
|
||||||
|
domain = "palworld.alexion.dev";
|
||||||
|
udp.ports = [ config.guests.palworld.port ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
guests.actualbudget = {
|
guests.actualbudget = {
|
||||||
enable = true;
|
enable = true;
|
||||||
vlan = 20;
|
vlan = 20;
|
||||||
|
|||||||
148
configs/nixos/modules/services/games/palworld.nix
Normal file
148
configs/nixos/modules/services/games/palworld.nix
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
{
|
||||||
|
options.modules.services.games.palworld = {
|
||||||
|
enable = lib.mkEnableOption "Palworld dedicated server";
|
||||||
|
|
||||||
|
steamDir = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
example = "/data/steamcmd";
|
||||||
|
description = "Directory where SteamCMD stores metadata and cache files.";
|
||||||
|
};
|
||||||
|
|
||||||
|
installDir = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
example = "/data/servers/palworld/tracion";
|
||||||
|
description = "Directory where SteamCMD installs the Palworld server files.";
|
||||||
|
};
|
||||||
|
|
||||||
|
stateDir = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
example = "/data/state/palworld/tracion";
|
||||||
|
description = "Directory where Palworld stores durable server state.";
|
||||||
|
};
|
||||||
|
|
||||||
|
serverName = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "palworld";
|
||||||
|
description = "Name shown for the Palworld server.";
|
||||||
|
};
|
||||||
|
|
||||||
|
port = lib.mkOption {
|
||||||
|
type = lib.types.port;
|
||||||
|
default = 8211;
|
||||||
|
description = "UDP port for the Palworld server.";
|
||||||
|
};
|
||||||
|
|
||||||
|
publicIP = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = null;
|
||||||
|
description = "Public IP address advertised by the Palworld server.";
|
||||||
|
};
|
||||||
|
|
||||||
|
maxPlayers = lib.mkOption {
|
||||||
|
type = lib.types.ints.between 1 32;
|
||||||
|
default = 32;
|
||||||
|
description = "Maximum player count for the Palworld server.";
|
||||||
|
};
|
||||||
|
|
||||||
|
publicLobby = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = true;
|
||||||
|
description = "Advertise the server in Palworld's public lobby.";
|
||||||
|
};
|
||||||
|
|
||||||
|
rcon = {
|
||||||
|
enable = lib.mkEnableOption "Palworld RCON" // {
|
||||||
|
default = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
port = lib.mkOption {
|
||||||
|
type = lib.types.port;
|
||||||
|
default = 25575;
|
||||||
|
description = "TCP port for Palworld RCON.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
passwordFile = lib.mkOption {
|
||||||
|
type = lib.types.path;
|
||||||
|
description = "Runtime file containing the Palworld server password.";
|
||||||
|
};
|
||||||
|
|
||||||
|
adminPasswordFile = lib.mkOption {
|
||||||
|
type = lib.types.path;
|
||||||
|
description = "Runtime file containing the Palworld admin password.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf config.modules.services.games.palworld.enable (
|
||||||
|
let
|
||||||
|
service = config.modules.services.games.palworld;
|
||||||
|
savedDir = "${service.installDir}/Pal/Saved";
|
||||||
|
startScript = pkgs.writeShellScript "palworld-start" ''
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
${pkgs.coreutils}/bin/install -d \
|
||||||
|
-m 0770 \
|
||||||
|
${lib.escapeShellArg service.stateDir} \
|
||||||
|
${lib.escapeShellArg (builtins.dirOf savedDir)}
|
||||||
|
|
||||||
|
if [ -L ${lib.escapeShellArg savedDir} ]; then
|
||||||
|
current_target="$(${pkgs.coreutils}/bin/readlink ${lib.escapeShellArg savedDir})"
|
||||||
|
if [ "$current_target" != ${lib.escapeShellArg service.stateDir} ]; then
|
||||||
|
${pkgs.coreutils}/bin/rm ${lib.escapeShellArg savedDir}
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -d ${lib.escapeShellArg savedDir} ] && [ ! -L ${lib.escapeShellArg savedDir} ]; then
|
||||||
|
if ${pkgs.findutils}/bin/find ${lib.escapeShellArg service.stateDir} -mindepth 1 -maxdepth 1 | ${pkgs.gnugrep}/bin/grep -q .; then
|
||||||
|
${pkgs.coreutils}/bin/rm -rf ${lib.escapeShellArg savedDir}
|
||||||
|
else
|
||||||
|
${pkgs.coreutils}/bin/cp -a ${lib.escapeShellArg savedDir}/. ${lib.escapeShellArg service.stateDir}/
|
||||||
|
${pkgs.coreutils}/bin/rm -rf ${lib.escapeShellArg savedDir}
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -e ${lib.escapeShellArg savedDir} ]; then
|
||||||
|
${pkgs.coreutils}/bin/ln -s ${lib.escapeShellArg service.stateDir} ${lib.escapeShellArg savedDir}
|
||||||
|
fi
|
||||||
|
|
||||||
|
password="$(${pkgs.coreutils}/bin/tr -d '\n' < ${lib.escapeShellArg service.passwordFile})"
|
||||||
|
admin_password="$(${pkgs.coreutils}/bin/tr -d '\n' < ${lib.escapeShellArg service.adminPasswordFile})"
|
||||||
|
|
||||||
|
exec ./PalServer.sh \
|
||||||
|
"$@" \
|
||||||
|
"-serverpassword=$password" \
|
||||||
|
"-adminpassword=$admin_password"
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
{
|
||||||
|
modules.services.steamcmd = {
|
||||||
|
enable = true;
|
||||||
|
inherit (service) steamDir installDir stateDir;
|
||||||
|
serviceName = "palworld-${lib.toLower service.serverName}";
|
||||||
|
appId = 2394010;
|
||||||
|
executable = "${startScript}";
|
||||||
|
arguments =
|
||||||
|
[
|
||||||
|
"-useperfthreads"
|
||||||
|
"-NoAsyncLoadingThread"
|
||||||
|
"-UseMultithreadForDS"
|
||||||
|
"-port=${toString service.port}"
|
||||||
|
"-publicport=${toString service.port}"
|
||||||
|
"-servername=${service.serverName}"
|
||||||
|
"-players=${toString service.maxPlayers}"
|
||||||
|
]
|
||||||
|
++ lib.optional service.publicLobby "-publiclobby"
|
||||||
|
++ lib.optionals (service.publicIP != null) [ "-publicip=${service.publicIP}" ]
|
||||||
|
++ lib.optional service.rcon.enable "-rcon";
|
||||||
|
udpPorts = [ service.port ];
|
||||||
|
tcpPorts = [ service.port ];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -59,6 +59,12 @@
|
|||||||
default = [ ];
|
default = [ ];
|
||||||
description = "UDP ports opened for the game service.";
|
description = "UDP ports opened for the game service.";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
tcpPorts = lib.mkOption {
|
||||||
|
type = lib.types.listOf lib.types.port;
|
||||||
|
default = [ ];
|
||||||
|
description = "TCP ports opened for the game service.";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkMerge [
|
config = lib.mkMerge [
|
||||||
@@ -159,6 +165,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
networking.firewall.allowedUDPPorts = service.udpPorts;
|
networking.firewall.allowedUDPPorts = service.udpPorts;
|
||||||
|
networking.firewall.allowedTCPPorts = service.tcpPorts;
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
];
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user