factorio: rudimentary mod support for factorio's nixos module

This commit is contained in:
Eric Litak 2016-08-03 06:44:15 -07:00
parent 4b96a2d148
commit d33540734f
4 changed files with 111 additions and 14 deletions

View File

@ -4,14 +4,17 @@ with lib;
let
cfg = config.services.factorio;
factorio = pkgs.factorio-headless;
name = "Factorio";
stateDir = "/var/lib/factorio";
mkSavePath = name: "${stateDir}/saves/${name}.zip";
configFile = pkgs.writeText "factorio.conf" ''
use-system-read-write-data-directories=true
[path]
read-data=${pkgs.factorio-headless}/share/factorio/data
read-data=${factorio}/share/factorio/data
write-data=${stateDir}
'';
modDir = pkgs.factorio-mkModDirDrv cfg.mods;
in
{
options = {
@ -32,7 +35,8 @@ in
description = ''
The name of the savegame that will be used by the server.
When not present in ${stateDir}/saves, it will be generated before starting the service.
When not present in ${stateDir}/saves, a new map with default
settings will be generated before starting the service.
'';
};
# TODO Add more individual settings as nixos-options?
@ -51,6 +55,26 @@ in
customizations.
'';
};
mods = mkOption {
type = types.listOf types.package;
default = [];
description = ''
Mods the server should install and activate.
The derivations in this list must "build" the mod by simply copying
the .zip, named correctly, into the output directory. Eventually,
there will be a way to pull in the most up-to-date list of
derivations via nixos-channel. Until then, this is for experts only.
'';
};
autosave-interval = mkOption {
type = types.nullOr types.int;
default = null;
example = 2;
description = ''
The time, in minutes, between autosaves.
'';
};
};
};
@ -74,12 +98,14 @@ in
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
preStart = ''
test -e ${stateDir}/saves/${cfg.saveName}.zip || \
${pkgs.factorio-headless}/bin/factorio \
--config=${cfg.configFile} \
--create=${stateDir}/saves/${cfg.saveName}.zip
'';
preStart = toString [
"test -e ${stateDir}/saves/${cfg.saveName}.zip"
"||"
"${factorio}/bin/factorio"
"--config=${cfg.configFile}"
"--create=${mkSavePath cfg.saveName}"
(optionalString (cfg.mods != []) "--mod-directory=${modDir}")
];
serviceConfig = {
User = "factorio";
@ -90,10 +116,12 @@ in
PrivateTmp = true;
UMask = "0007";
ExecStart = toString [
"${pkgs.factorio-headless}/bin/factorio"
"${factorio}/bin/factorio"
"--config=${cfg.configFile}"
"--port=${toString cfg.port}"
"--start-server=${stateDir}/saves/${cfg.saveName}.zip"
"--start-server=${mkSavePath cfg.saveName}"
(optionalString (cfg.mods != []) "--mod-directory=${modDir}")
(optionalString (cfg.autosave-interval != null) "--autosave-interval ${toString cfg.autosave-interval}")
];
};
};

View File

@ -1,6 +1,8 @@
{ stdenv, callPackage, fetchurl, makeWrapper
, alsaLib, libX11, libXcursor, libXinerama, libXrandr, libXi, mesa_noglu
, factorio-utils
, releaseType
, mods ? []
, username ? "" , password ? ""
}:
@ -54,14 +56,16 @@ let
fi
'';
modDir = factorio-utils.mkModDirDrv mods;
base = {
name = "factorio-${releaseType}-${version}";
src = fetch.${arch.inTar}.${releaseType};
preferLocalBuild = true;
dontBuild = true;
# TODO detangle headless/normal mode wrapping, libs, etc. test all urls 32/64/headless/gfx
installPhase = ''
mkdir -p $out/{bin,share/factorio}
cp -a data $out/share/factorio
@ -71,8 +75,6 @@ let
$out/bin/factorio
'';
preferLocalBuild = true;
meta = {
description = "A game in which you build and maintain factories";
longDescription = ''
@ -112,7 +114,23 @@ let
wrapProgram $out/bin/factorio \
--prefix LD_LIBRARY_PATH : /run/opengl-driver/lib:$libPath \
--run "$out/share/factorio/update-config.sh" \
--add-flags "-c \$HOME/.factorio/config.cfg"
--add-flags "-c \$HOME/.factorio/config.cfg ${optionalString (mods != []) "--mod-directory=${modDir}"}"
# TODO Currently, every time a mod is changed/added/removed using the
# modlist, a new derivation will take up the entire footprint of the
# client. The only way to avoid this is to remove the mods arg from the
# package function. The modsDir derivation will have to be built
# separately and have the user specify it in the .factorio config or
# right along side it using a symlink into the store I think i will
# just remove mods for the client derivation entirely. this is much
# cleaner and more useful for headless mode.
# TODO: trying to toggle off a mod will result in read-only-fs-error.
# not much we can do about that except warn the user somewhere. In
# fact, no exit will be clean, since this error will happen on close
# regardless. just prints an ugly stacktrace but seems to be otherwise
# harmless, unless maybe the user forgets and tries to use the mod
# manager.
install -m0644 <(cat << EOF
${configBaseCfg}

View File

@ -0,0 +1,49 @@
# This file provides a top-level function that will be used by both nixpkgs and nixos
# to generate mod directories for use at runtime by factorio.
{ stdenv }:
with stdenv.lib;
{
mkModDirDrv = mods: # a list of mod derivations
let
recursiveDeps = modDrv: [modDrv] ++ optionals (modDrv.deps == []) (map recursiveDeps modDrv.deps);
modDrvs = unique (flatten (map recursiveDeps mods));
in
stdenv.mkDerivation {
name = "factorio-mod-directory";
preferLocalBuild = true;
buildCommand = ''
mkdir -p $out
for modDrv in ${toString modDrvs}; do
# NB: there will only ever be a single zip file in each mod derivation's output dir
ln -s $modDrv/*.zip $out
done
'';
};
modDrv = { allRecommendedMods, allOptionalMods }:
{ src
, name ? null
, deps ? []
, optionalDeps ? []
, recommendedDeps ? []
}: stdenv.mkDerivation {
inherit src;
# Use the name of the zip, but endstrip ".zip" and possibly the querystring that gets left in by fetchurl
name = replaceStrings ["_"] ["-"] (if name != null then name else removeSuffix ".zip" (head (splitString "?" src.name)));
deps = deps ++ optionals allOptionalMods optionalDeps
++ optionals allRecommendedMods recommendedDeps;
preferLocalBuild = true;
buildCommand = ''
mkdir -p $out
srcBase=$(basename $src)
srcBase=''${srcBase#*-} # strip nix hash
srcBase=''${srcBase%\?*} # strip querystring leftover from fetchurl
cp $src $out/$srcBase
'';
};
}

View File

@ -15594,6 +15594,8 @@ in
factorio-headless = callPackage ../games/factorio { releaseType = "headless"; };
factorio-utils = callPackage ../games/factorio/utils.nix { };
fairymax = callPackage ../games/fairymax {};
fish-fillets-ng = callPackage ../games/fish-fillets-ng {};