add new module for satisfactory server 1.0
This commit is contained in:
parent
d9acff1fa1
commit
e13deba11f
|
@ -1,8 +1,9 @@
|
||||||
{ ... }: {
|
{ ... }: {
|
||||||
imports = [
|
imports = [
|
||||||
./modules/ghidra-server
|
./modules/ghidra-server
|
||||||
./modules/regdom
|
|
||||||
./modules/machine-info
|
./modules/machine-info
|
||||||
|
./modules/satisfactory-dedicated-server
|
||||||
|
./modules/regdom
|
||||||
];
|
];
|
||||||
|
|
||||||
# set some nix settings defaults
|
# set some nix settings defaults
|
||||||
|
|
|
@ -0,0 +1,421 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.services.satisfactory;
|
||||||
|
in {
|
||||||
|
options.services.satisfactory = with lib; {
|
||||||
|
enable = mkEnableOption "satisfactory";
|
||||||
|
|
||||||
|
package = mkPackageOption pkgs "satisfactory-dedicated-server" {};
|
||||||
|
|
||||||
|
directory = mkOption {
|
||||||
|
description = ''
|
||||||
|
Directory where Satisfactory Dedicated Server data will be stored
|
||||||
|
'';
|
||||||
|
default = "/var/lib/satisfactory";
|
||||||
|
type = types.str;
|
||||||
|
example = literalExpression "\"/data/games/satisfactory\"";
|
||||||
|
};
|
||||||
|
|
||||||
|
user = mkOption {
|
||||||
|
description = "User account under which Satisfactory Dedicated Server runs.";
|
||||||
|
default = "satisfactory";
|
||||||
|
type = types.str;
|
||||||
|
example = literalExpression "\"satisfactory2\"";
|
||||||
|
};
|
||||||
|
|
||||||
|
group = mkOption {
|
||||||
|
description = "Group under which Satisfactory Dedicated Server runs.";
|
||||||
|
default = "satisfactory";
|
||||||
|
type = types.str;
|
||||||
|
example = literalExpression "\"satisfactory2\"";
|
||||||
|
};
|
||||||
|
|
||||||
|
useACMEHost = mkOption {
|
||||||
|
description = ''
|
||||||
|
If set, the server will use the ACME-provided TLS certificate for the given host.
|
||||||
|
|
||||||
|
Note that this module does not actually provision the specified certificate; you must
|
||||||
|
use additional config (e.g., `services.nginx.virtualHosts.<name>.enableACME = true`) to
|
||||||
|
provision the certificate using a supported ACME method.
|
||||||
|
'';
|
||||||
|
default = null;
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
example = literalExpression "\"myserver.example\"";
|
||||||
|
};
|
||||||
|
|
||||||
|
port = mkOption {
|
||||||
|
description = ''
|
||||||
|
Server port number (TCP/UDP)
|
||||||
|
|
||||||
|
This corresponds to the `-Port` command line option.
|
||||||
|
'';
|
||||||
|
default = 7777;
|
||||||
|
type = types.port;
|
||||||
|
example = literalExpression "7778";
|
||||||
|
};
|
||||||
|
|
||||||
|
reliablePort = mkOption {
|
||||||
|
description = ''
|
||||||
|
Server reliable port number
|
||||||
|
|
||||||
|
This corresponds to the `-ReliablePort` command line option.
|
||||||
|
'';
|
||||||
|
default = 8888;
|
||||||
|
type = types.port;
|
||||||
|
example = literalExpression "8889";
|
||||||
|
};
|
||||||
|
|
||||||
|
externalReliablePort = mkOption {
|
||||||
|
description = ''
|
||||||
|
Server reliable port number as seen outside NAT.
|
||||||
|
|
||||||
|
This corresponds to the `-ExternalReliablePort` command line option.
|
||||||
|
'';
|
||||||
|
default = null;
|
||||||
|
type = types.nullOr types.port;
|
||||||
|
example = literalExpression "12345";
|
||||||
|
};
|
||||||
|
|
||||||
|
disableSeasonalEvents = mkOption {
|
||||||
|
description = ''
|
||||||
|
Whether to run the server with seasonal events disabled.
|
||||||
|
|
||||||
|
This corresponds to the `-DisableSeasonalEvents` command line option.
|
||||||
|
'';
|
||||||
|
default = false;
|
||||||
|
type = types.bool;
|
||||||
|
example = literalExpression "true";
|
||||||
|
};
|
||||||
|
|
||||||
|
extraIniOptions = mkOption {
|
||||||
|
description = ''
|
||||||
|
Run the server with additional ini configuration values.
|
||||||
|
|
||||||
|
This is a nested attribute set of values.
|
||||||
|
|
||||||
|
- The top level attribute specifies the ini file containing the value to set (i.e., the
|
||||||
|
first component of the `-ini` command line option), for example `Game` or `Engine`.
|
||||||
|
- The secondary level attribute specifies the ini file category, without brackets,
|
||||||
|
for example `/Script/Engine.GameSession`.
|
||||||
|
- The final level attribute specifies the option name to set, for example
|
||||||
|
`MaxPlayers`. The value of the attribute is the value to set on the command line.
|
||||||
|
|
||||||
|
This corresponds to the `-ini` command line option.
|
||||||
|
'';
|
||||||
|
default = {};
|
||||||
|
type = with types; attrsOf (attrsOf (attrsOf str));
|
||||||
|
example = literalExpression ''
|
||||||
|
{
|
||||||
|
Game."/Script/Engine.GameSession".MaxPlayers = "8";
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
initialSettings = mkOption {
|
||||||
|
description = ''
|
||||||
|
Settings to apply to the server via the server API on the first run.
|
||||||
|
'';
|
||||||
|
type = types.submodule {
|
||||||
|
options = {
|
||||||
|
serverName = mkOption {
|
||||||
|
description = ''
|
||||||
|
The name of the server.
|
||||||
|
|
||||||
|
If this is provided, `adminPasswordFile` must also be set.
|
||||||
|
'';
|
||||||
|
type = with types; nullOr str;
|
||||||
|
default = null;
|
||||||
|
example = literalExpression "\"My Dedicated Server\"";
|
||||||
|
};
|
||||||
|
|
||||||
|
adminPasswordFile = mkOption {
|
||||||
|
description = ''
|
||||||
|
Path to a file containing the initial admin password.
|
||||||
|
|
||||||
|
If this is provided, `serverName` must also be set.
|
||||||
|
'';
|
||||||
|
type = with types; nullOr path;
|
||||||
|
default = null;
|
||||||
|
example = literalExpression "\"/var/lib/secrets/admin-password.txt\"";
|
||||||
|
};
|
||||||
|
|
||||||
|
clientPasswordFile = mkOption {
|
||||||
|
description = ''
|
||||||
|
Path to a file containing the initial client password. If not set, the server will
|
||||||
|
not be configured with a client password and will be accessible to any client.
|
||||||
|
'';
|
||||||
|
type = with types; nullOr path;
|
||||||
|
default = null;
|
||||||
|
example = literalExpression "\"/var/lib/secrets/client-password.txt\"";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
assertions = [
|
||||||
|
{
|
||||||
|
assertion = with cfg.initialSettings; (serverName == null) == (adminPasswordFile == null);
|
||||||
|
message = ''
|
||||||
|
When either of services.satisfactory.initialSettings.serverName or
|
||||||
|
services.satisfactory.initialSettings.adminPasswordFile are set, the other must also be
|
||||||
|
set. The dedicated server API requires configuring both options simultaneously.
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion = with cfg.initialSettings; (clientPasswordFile == null) || (serverName != null);
|
||||||
|
message = ''
|
||||||
|
Option services.satisfactory.initialSettings.clientPasswordFile is set, but there are
|
||||||
|
no options set for the initial server claim data (i.e., serverName and adminPasswordFile).
|
||||||
|
Setting a client password is not possible without executing a server claim.
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
users.users."${cfg.user}" = {
|
||||||
|
isSystemUser = true;
|
||||||
|
home = cfg.directory;
|
||||||
|
group = cfg.group;
|
||||||
|
createHome = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
users.groups."${cfg.group}" = {};
|
||||||
|
|
||||||
|
systemd.tmpfiles.settings."satisfactory" = let
|
||||||
|
default = {
|
||||||
|
inherit (cfg) user group;
|
||||||
|
mode = "0755";
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
"${cfg.directory}".d = default;
|
||||||
|
"${cfg.directory}/saves".d = default;
|
||||||
|
"${cfg.directory}/settings".d = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services = let
|
||||||
|
base_url = "https://127.0.0.1:${builtins.toString cfg.port}/api/v1/";
|
||||||
|
binary = "${cfg.directory}/server/Engine/Binaries/Linux/FactoryServer-Linux-Shipping";
|
||||||
|
ini_list = lib.flatten (
|
||||||
|
lib.mapAttrsToList (filename: fileOpts:
|
||||||
|
lib.mapAttrsToList (section: sectionOpts:
|
||||||
|
lib.mapAttrsToList (key: value:
|
||||||
|
" -ini:${filename}:[${section}]:${key}=${value}"
|
||||||
|
) sectionOpts
|
||||||
|
) fileOpts
|
||||||
|
) cfg.extraIniOptions
|
||||||
|
);
|
||||||
|
ini_args = lib.concatStringsSep " " ini_list;
|
||||||
|
|
||||||
|
port = with builtins;
|
||||||
|
"-Port=${toString cfg.port} -ReliablePort=${toString cfg.reliablePort}";
|
||||||
|
extport =
|
||||||
|
if cfg.externalReliablePort == null then
|
||||||
|
""
|
||||||
|
else
|
||||||
|
" -ExternalReliablePort=${builtins.toString cfg.externalReliablePort}";
|
||||||
|
|
||||||
|
seasonalEvts =
|
||||||
|
if cfg.disableSeasonalEvents then
|
||||||
|
" -DisableSeasonalEvents"
|
||||||
|
else
|
||||||
|
"";
|
||||||
|
|
||||||
|
args = "${port}${extport}${seasonalEvts}${ini_args}";
|
||||||
|
server_command = "${binary} FactoryGame ${args}";
|
||||||
|
|
||||||
|
doSetup = cfg.initialSettings.serverName != null;
|
||||||
|
|
||||||
|
commonConfig = {
|
||||||
|
after = ["network.target"] ++ lib.optionals (cfg.useACMEHost != null) [
|
||||||
|
"acme-finished-${cfg.useACMEHost}.target"
|
||||||
|
];
|
||||||
|
|
||||||
|
environment = {
|
||||||
|
"LD_LIBRARY_PATH" = "${cfg.directory}/server/linux64";
|
||||||
|
};
|
||||||
|
|
||||||
|
unitConfig = {
|
||||||
|
RequiresMountsFor = cfg.directory;
|
||||||
|
};
|
||||||
|
|
||||||
|
serviceConfig = {
|
||||||
|
Nice = "-5";
|
||||||
|
User = cfg.user;
|
||||||
|
Group = cfg.user;
|
||||||
|
WorkingDirectory = cfg.directory;
|
||||||
|
StandardOutput = "journal";
|
||||||
|
|
||||||
|
LoadCredential = lib.mkIf (cfg.useACMEHost != null) (let
|
||||||
|
certDir = config.security.acme.certs.${cfg.useACMEHost}.directory;
|
||||||
|
in [
|
||||||
|
"cert_chain.pem:${certDir}/fullchain.pem"
|
||||||
|
"private_key.pem:${certDir}/key.pem"
|
||||||
|
]);
|
||||||
|
|
||||||
|
ProtectSystem = true;
|
||||||
|
ProtectHome = true;
|
||||||
|
NoNewPrivileges = true;
|
||||||
|
|
||||||
|
# virtualize the file system to synthesize what is read only with what is dedicated server
|
||||||
|
# game state
|
||||||
|
|
||||||
|
PrivateTmp = true;
|
||||||
|
|
||||||
|
TemporaryFileSystem = [
|
||||||
|
"${cfg.directory}:ro"
|
||||||
|
];
|
||||||
|
BindReadOnlyPaths = [
|
||||||
|
"${cfg.package}:${cfg.directory}/server"
|
||||||
|
];
|
||||||
|
BindPaths = [
|
||||||
|
"${cfg.directory}/saves:${cfg.directory}/.config/Epic"
|
||||||
|
"/var/tmp:${cfg.directory}/server/FactoryGame/Intermediate"
|
||||||
|
"${cfg.directory}/settings:${cfg.directory}/server/FactoryGame/Saved"
|
||||||
|
] ++ lib.optionals (cfg.useACMEHost != null) [
|
||||||
|
"%d:${cfg.directory}/server/FactoryGame/Certificates"
|
||||||
|
];
|
||||||
|
|
||||||
|
Restart = "on-failure";
|
||||||
|
RestartSec = 60;
|
||||||
|
SuccessExitStatus = 143;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
"satisfactory" = lib.mkMerge [
|
||||||
|
commonConfig
|
||||||
|
{
|
||||||
|
description = "Satisfactory Dedicated Server";
|
||||||
|
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
|
||||||
|
requires = lib.optionals doSetup ["satisfactory-first-time-setup.service"];
|
||||||
|
after = lib.optionals doSetup ["satisfactory-first-time-setup.service"];
|
||||||
|
|
||||||
|
serviceConfig = {
|
||||||
|
ExecStart = server_command;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
"satisfactory-first-time-setup" = lib.mkIf doSetup (lib.mkMerge [
|
||||||
|
commonConfig
|
||||||
|
{
|
||||||
|
description = "Satisfactory Dedicated Server first-time setup";
|
||||||
|
|
||||||
|
path = with pkgs; [
|
||||||
|
curl
|
||||||
|
jq
|
||||||
|
];
|
||||||
|
|
||||||
|
unitConfig = {
|
||||||
|
ConditionPathExists =
|
||||||
|
"!${cfg.directory}/saves/FactoryGame/Saved/SaveGames/ServerSettings.${builtins.toString cfg.port}.sav";
|
||||||
|
};
|
||||||
|
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
|
||||||
|
# isolate satisfactory during configuration
|
||||||
|
PrivateNetwork = true;
|
||||||
|
|
||||||
|
LoadCredential =
|
||||||
|
(lib.optionals (cfg.initialSettings.adminPasswordFile != null) [
|
||||||
|
"admin_password.txt:${cfg.initialSettings.adminPasswordFile}"
|
||||||
|
]) ++ (lib.optionals (cfg.initialSettings.clientPasswordFile != null) [
|
||||||
|
"client_password.txt:${cfg.initialSettings.clientPasswordFile}"
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
script = ''
|
||||||
|
set -euo pipefail
|
||||||
|
set -m
|
||||||
|
|
||||||
|
echo Starting server...
|
||||||
|
${server_command} &
|
||||||
|
server_pid=$!
|
||||||
|
|
||||||
|
server_status=""
|
||||||
|
|
||||||
|
for i in {1..5}; do
|
||||||
|
server_status="$(curl -SsLk -XPOST -H "Content-Type: application/json" \
|
||||||
|
--data '{"function":"HealthCheck","data":{"clientCustomData":""}}' \
|
||||||
|
"${base_url}" | jq -r '.data.health' || true)"
|
||||||
|
|
||||||
|
if [ "$server_status" == "healthy" ]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$server_status" != "healthy"; then
|
||||||
|
echo Server did not report healthy status in time
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
token="$(curl -SsLk -XPOST -H "Content-Type: application/json" \
|
||||||
|
--data '{"function":"PasswordlessLogin","data":{"MinimumPrivilegeLevel":"InitialAdmin"}}' \
|
||||||
|
"${base_url}" | jq -r '.data.authenticationToken')"
|
||||||
|
|
||||||
|
if [ "$token" == "null" ]; then
|
||||||
|
echo Server authentication failed
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo Executing server claim...
|
||||||
|
data="$(jq -n \
|
||||||
|
--arg "serverName" "${cfg.initialSettings.serverName}" \
|
||||||
|
--rawfile "password" "$CREDENTIALS_DIRECTORY/admin_password.txt" \
|
||||||
|
'{} |.function="ClaimServer" | .data.ServerName=$serverName | .data.AdminPassword=($password|rtrimstr("\n"))')"
|
||||||
|
new_token="$(curl -SsLk -XPOST -H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: Bearer $token" \
|
||||||
|
--data "$data" \
|
||||||
|
"${base_url}" | jq -r '.data.authenticationToken')"
|
||||||
|
|
||||||
|
if [ "$new_token" == "null" ]; then
|
||||||
|
echo Server claim failed
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
token="$new_token"
|
||||||
|
|
||||||
|
if [ -f "$CREDENTIALS_DIRECTORY/client_password.txt" ]; then
|
||||||
|
echo Setting client password...
|
||||||
|
data="$(jq -n \
|
||||||
|
--rawfile "password" "$CREDENTIALS_DIRECTORY/client_password.txt" \
|
||||||
|
'{} |.function="SetClientPassword" | .data.Password=($password|rtrimstr("\n"))')"
|
||||||
|
result="$(curl -SsLk -XPOST -H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: Bearer $token" \
|
||||||
|
--data "$data" \
|
||||||
|
"${base_url}" | jq -r '.data')"
|
||||||
|
|
||||||
|
if [ "$result" != "" ]; then
|
||||||
|
echo "Password set failed: $result"
|
||||||
|
exit 4
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo Setup complete
|
||||||
|
|
||||||
|
echo Stopping server...
|
||||||
|
kill -SIGTERM $server_pid
|
||||||
|
wait $server_pid
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
"satisfactory-restart-certs" = lib.mkIf (cfg.useACMEHost != null) {
|
||||||
|
description = "Restart Satisfactory Dedicated Server after cert provisioning";
|
||||||
|
wantedBy = ["acme-finished-${cfg.useACMEHost}.target"];
|
||||||
|
path = [config.systemd.package];
|
||||||
|
script = ''
|
||||||
|
systemctl try-restart satisfactory.service
|
||||||
|
'';
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "simple";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -37,6 +37,7 @@ final: prev: {
|
||||||
megacom = final.python312Packages.megacom;
|
megacom = final.python312Packages.megacom;
|
||||||
|
|
||||||
outer-wilds-text-adventure = prev.callPackage ./pkgs/games/outer-wilds-text-adventure {};
|
outer-wilds-text-adventure = prev.callPackage ./pkgs/games/outer-wilds-text-adventure {};
|
||||||
|
satisfactory-dedicated-server = prev.callPackage ./pkgs/games/satisfactory-dedicated-server {};
|
||||||
|
|
||||||
mkNginxServer = prev.callPackage ./lib/dev-nginx {};
|
mkNginxServer = prev.callPackage ./lib/dev-nginx {};
|
||||||
|
|
||||||
|
|
|
@ -7,32 +7,30 @@
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
appId = "1690800";
|
appId = "1690800";
|
||||||
buildId = "15636842";
|
buildId = "19234106";
|
||||||
steamworks_sdk = fetchFromSteam {
|
steamworks_sdk = fetchFromSteam {
|
||||||
name = "steamworks-sdk";
|
name = "steamworks-sdk";
|
||||||
inherit appId;
|
inherit appId;
|
||||||
depot = {
|
depot = {
|
||||||
depotId = "1006";
|
depotId = "1006";
|
||||||
manifestId = "7138471031118904166";
|
manifestId = "5587033981095108078";
|
||||||
};
|
};
|
||||||
hash = "sha256-OtPI1kAx6+9G09IEr2kYchyvxlPl3rzx/ai/xEVG4oM=";
|
hash = "sha256-CjrVpq5ztL6wTWIa63a/4xHM35DzgDR/O6qVf1YV5xw=";
|
||||||
};
|
};
|
||||||
server_dist = fetchFromSteam {
|
server_dist = fetchFromSteam {
|
||||||
name = "satisfactory-dedicated-server";
|
name = "satisfactory-dedicated-server";
|
||||||
inherit appId;
|
inherit appId;
|
||||||
depot = {
|
depot = {
|
||||||
depotId = "1690802";
|
depotId = "1690802";
|
||||||
manifestId = "1910179703516567959";
|
manifestId = "5693629351763493998";
|
||||||
};
|
};
|
||||||
hash = "sha256-TxPegZFAwiAzuHgw9xLGr5sAP7KAVMMfPFYL7TRX1O0=";
|
hash = "sha256-0svLwO4JYKIPwoNCRfT9+pocZ0n1QpSEqP41DdUhEac=";
|
||||||
};
|
};
|
||||||
in stdenv.mkDerivation {
|
in stdenv.mkDerivation {
|
||||||
pname = "satisfactory-dedicated-server";
|
pname = "satisfactory-dedicated-server";
|
||||||
version = "build-${buildId}";
|
version = "build-${buildId}";
|
||||||
|
|
||||||
src = server_dist;
|
src = server_dist;
|
||||||
buildInputs = [ steamworks_sdk ];
|
|
||||||
propagatedBuildInputs = [ SDL2 ];
|
|
||||||
|
|
||||||
dontConfigure = true;
|
dontConfigure = true;
|
||||||
dontBuild = true;
|
dontBuild = true;
|
||||||
|
@ -44,6 +42,7 @@ in stdenv.mkDerivation {
|
||||||
|
|
||||||
mkdir -p $out/FactoryGame/Intermediate
|
mkdir -p $out/FactoryGame/Intermediate
|
||||||
mkdir -p $out/FactoryGame/Saved
|
mkdir -p $out/FactoryGame/Saved
|
||||||
|
mkdir -p $out/FactoryGame/Certificates
|
||||||
|
|
||||||
rm $out/FactoryServer.sh
|
rm $out/FactoryServer.sh
|
||||||
'';
|
'';
|
||||||
|
@ -58,10 +57,12 @@ in stdenv.mkDerivation {
|
||||||
|
|
||||||
chmod +x $out/Engine/Binaries/Linux/FactoryServer-Linux-Shipping
|
chmod +x $out/Engine/Binaries/Linux/FactoryServer-Linux-Shipping
|
||||||
|
|
||||||
patchelf --add-needed ${SDL2}/lib/libSDL2-2.0.so.0 \
|
patchelf \
|
||||||
|
--add-needed ${SDL2}/lib/libSDL2-2.0.so.0 \
|
||||||
$out/linux64/steamclient.so
|
$out/linux64/steamclient.so
|
||||||
|
|
||||||
patchelf --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" \
|
patchelf \
|
||||||
|
--set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" \
|
||||||
--add-needed $out/linux64/steamclient.so \
|
--add-needed $out/linux64/steamclient.so \
|
||||||
$out/Engine/Binaries/Linux/FactoryServer-Linux-Shipping
|
$out/Engine/Binaries/Linux/FactoryServer-Linux-Shipping
|
||||||
'';
|
'';
|
||||||
|
|
Loading…
Reference in New Issue