ansible-later: 2.0.22 -> 2.0.23
[NixPkgs.git] / pkgs / games / factorio / default.nix
blob17f9947b49824ad70c574784a2dd7ae9bee17cad
1 { lib, stdenv, fetchurl, makeWrapper, makeDesktopItem
2 , alsa-lib, libpulseaudio, libX11, libXcursor, libXinerama, libXrandr, libXi, libGL
3 , libSM, libICE, libXext, factorio-utils
4 , releaseType
5 , mods ? []
6 , username ? "", token ? "" # get/reset token at https://factorio.com/profile
7 , experimental ? false # true means to always use the latest branch
8 }:
10 assert releaseType == "alpha"
11     || releaseType == "headless"
12     || releaseType == "demo";
14 let
16   inherit (lib) importJSON;
18   helpMsg = ''
20     ===FETCH FAILED===
21     Please ensure you have set the username and token with config.nix, or
22     /etc/nix/nixpkgs-config.nix if on NixOS.
24     Your token can be seen at https://factorio.com/profile (after logging in). It is
25     not as sensitive as your password, but should still be safeguarded. There is a
26     link on that page to revoke/invalidate the token, if you believe it has been
27     leaked or wish to take precautions.
29     Example:
30     {
31       packageOverrides = pkgs: {
32         factorio = pkgs.factorio.override {
33           username = "FactorioPlayer1654";
34           token = "d5ad5a8971267c895c0da598688761";
35         };
36       };
37     }
39     Alternatively, instead of providing the username+token, you may manually
40     download the release through https://factorio.com/download , then add it to
41     the store using e.g.:
43       releaseType=alpha
44       version=0.17.74
45       nix-prefetch-url file://\''$HOME/Downloads/factorio_\''${releaseType}_x64_\''${version}.tar.xz --name factorio_\''${releaseType}_x64-\''${version}.tar.xz
47     Note the ultimate "_" is replaced with "-" in the --name arg!
48   '';
50   desktopItem = makeDesktopItem {
51     name = "factorio";
52     desktopName = "Factorio";
53     comment = "A game in which you build and maintain factories.";
54     exec = "factorio";
55     icon = "factorio";
56     categories = [ "Game" ];
57   };
59   branch = if experimental then "experimental" else "stable";
61   # NB `experimental` directs us to take the latest build, regardless of its branch;
62   # hence the (stable, experimental) pairs may sometimes refer to the same distributable.
63   versions = importJSON ./versions.json;
64   binDists = makeBinDists versions;
66   actual = binDists.${stdenv.hostPlatform.system}.${releaseType}.${branch} or (throw "Factorio ${releaseType}-${branch} binaries for ${stdenv.hostPlatform.system} are not available for download.");
68   makeBinDists = versions:
69     let f = path: name: value:
70       if builtins.isAttrs value then
71         if value ? "name" then
72           makeBinDist value
73         else
74           builtins.mapAttrs (f (path ++ [ name ])) value
75       else
76         throw "expected attrset at ${toString path} - got ${toString value}";
77     in
78       builtins.mapAttrs (f []) versions;
79   makeBinDist = { name, version, tarDirectory, url, sha256, needsAuth }: {
80     inherit version tarDirectory;
81     src =
82       if !needsAuth then
83         fetchurl { inherit name url sha256; }
84       else
85         (lib.overrideDerivation
86           (fetchurl {
87             inherit name url sha256;
88             curlOptsList = [
89               "--get"
90               "--data-urlencode" "username@username"
91               "--data-urlencode" "token@token"
92             ];
93           })
94           (_: { # This preHook hides the credentials from /proc
95                 preHook =
96                   if username != "" && token != "" then ''
97                     echo -n "${username}" >username
98                     echo -n "${token}"    >token
99                   '' else ''
100                     # Deliberately failing since username/token was not provided, so we can't fetch.
101                     # We can't use builtins.throw since we want the result to be used if the tar is in the store already.
102                     exit 1
103                   '';
104                 failureHook = ''
105                   cat <<EOF
106                   ${helpMsg}
107                   EOF
108                 '';
109               }));
110   };
112   configBaseCfg = ''
113     use-system-read-write-data-directories=false
114     [path]
115     read-data=$out/share/factorio/data/
116     [other]
117     check_updates=false
118   '';
120   updateConfigSh = ''
121     #! $SHELL
122     if [[ -e ~/.factorio/config.cfg ]]; then
123       # Config file exists, but may have wrong path.
124       # Try to edit it. I'm sure this is perfectly safe and will never go wrong.
125       sed -i 's|^read-data=.*|read-data=$out/share/factorio/data/|' ~/.factorio/config.cfg
126     else
127       # Config file does not exist. Phew.
128       install -D $out/share/factorio/config-base.cfg ~/.factorio/config.cfg
129     fi
130   '';
132   modDir = factorio-utils.mkModDirDrv mods;
134   base = with actual; {
135     pname = "factorio-${releaseType}";
136     inherit version src;
138     preferLocalBuild = true;
139     dontBuild = true;
141     installPhase = ''
142       mkdir -p $out/{bin,share/factorio}
143       cp -a data $out/share/factorio
144       cp -a bin/${tarDirectory}/factorio $out/bin/factorio
145       patchelf \
146         --set-interpreter $(cat $NIX_CC/nix-support/dynamic-linker) \
147         $out/bin/factorio
148     '';
150     passthru.updateScript = if (username != "" && token != "") then [
151       ./update.py "--username=${username}" "--token=${token}"
152     ] else null;
154     meta = {
155       description = "A game in which you build and maintain factories";
156       longDescription = ''
157         Factorio is a game in which you build and maintain factories.
159         You will be mining resources, researching technologies, building
160         infrastructure, automating production and fighting enemies. Use your
161         imagination to design your factory, combine simple elements into
162         ingenious structures, apply management skills to keep it working and
163         finally protect it from the creatures who don't really like you.
165         Factorio has been in development since spring of 2012, and reached
166         version 1.0 in mid 2020.
167       '';
168       homepage = "https://www.factorio.com/";
169       sourceProvenance = with lib.sourceTypes; [ binaryNativeCode ];
170       license = lib.licenses.unfree;
171       maintainers = with lib.maintainers; [ Baughn elitak erictapen priegger lukegb ];
172       platforms = [ "x86_64-linux" ];
173     };
174   };
176   releases = rec {
177     headless = base;
178     demo = base // {
180       nativeBuildInputs = [ makeWrapper ];
181       buildInputs = [ libpulseaudio ];
183       libPath = lib.makeLibraryPath [
184         alsa-lib
185         libpulseaudio
186         libX11
187         libXcursor
188         libXinerama
189         libXrandr
190         libXi
191         libGL
192         libSM
193         libICE
194         libXext
195       ];
197       installPhase = base.installPhase + ''
198         wrapProgram $out/bin/factorio                                \
199           --prefix LD_LIBRARY_PATH : /run/opengl-driver/lib:$libPath \
200           --run "$out/share/factorio/update-config.sh"               \
201           --argv0 ""                                                 \
202           --add-flags "-c \$HOME/.factorio/config.cfg"               \
203           ${if mods!=[] then "--add-flags --mod-directory=${modDir}" else ""}
205           # TODO Currently, every time a mod is changed/added/removed using the
206           # modlist, a new derivation will take up the entire footprint of the
207           # client. The only way to avoid this is to remove the mods arg from the
208           # package function. The modsDir derivation will have to be built
209           # separately and have the user specify it in the .factorio config or
210           # right along side it using a symlink into the store I think i will
211           # just remove mods for the client derivation entirely. this is much
212           # cleaner and more useful for headless mode.
214           # TODO: trying to toggle off a mod will result in read-only-fs-error.
215           # not much we can do about that except warn the user somewhere. In
216           # fact, no exit will be clean, since this error will happen on close
217           # regardless. just prints an ugly stacktrace but seems to be otherwise
218           # harmless, unless maybe the user forgets and tries to use the mod
219           # manager.
221         install -m0644 <(cat << EOF
222         ${configBaseCfg}
223         EOF
224         ) $out/share/factorio/config-base.cfg
226         install -m0755 <(cat << EOF
227         ${updateConfigSh}
228         EOF
229         ) $out/share/factorio/update-config.sh
231         mkdir -p $out/share/icons/hicolor/{64x64,128x128}/apps
232         cp -a data/core/graphics/factorio-icon.png $out/share/icons/hicolor/64x64/apps/factorio.png
233         cp -a data/core/graphics/factorio-icon@2x.png $out/share/icons/hicolor/128x128/apps/factorio.png
234         ln -s ${desktopItem}/share/applications $out/share/
235       '';
236     };
237     alpha = demo // {
239       installPhase = demo.installPhase + ''
240         cp -a doc-html $out/share/factorio
241       '';
242     };
243   };
245 in stdenv.mkDerivation (releases.${releaseType})