nixpkgs mirror (for testing)
github.com/NixOS/nixpkgs
nix
1{
2 lib,
3 alsa-lib,
4 factorio-utils,
5 fetchurl,
6 libGL,
7 libICE,
8 libSM,
9 libX11,
10 libXcursor,
11 libXext,
12 libXi,
13 libXinerama,
14 libXrandr,
15 libpulseaudio,
16 libxkbcommon,
17 makeDesktopItem,
18 makeWrapper,
19 releaseType ? "alpha",
20 stdenv,
21 wayland,
22
23 mods-dat ? null,
24 versionsJson ? ./versions.json,
25 username ? "",
26 token ? "", # get/reset token at https://factorio.com/profile
27 experimental ? false, # true means to always use the latest branch
28 ...
29}@args:
30
31assert
32 releaseType == "alpha"
33 || releaseType == "headless"
34 || releaseType == "demo"
35 || releaseType == "expansion";
36
37let
38
39 inherit (lib) importJSON;
40
41 mods = args.mods or [ ];
42
43 helpMsg =
44 { dlName, storeName }:
45 ''
46
47 ===FETCH FAILED===
48 Please ensure you have set the username and token with config.nix, or
49 /etc/nix/nixpkgs-config.nix if on NixOS.
50
51 Your token can be seen at https://factorio.com/profile (after logging in).
52 Beware this will add the credentials to the Nix store, which is
53 world-readable on the local machine. It is not as sensitive as your
54 password, but should still be safeguarded. There is a link on that page to
55 revoke/invalidate the token, if you believe it has been leaked or wish to
56 take precautions.
57
58 Example:
59 {
60 packageOverrides = pkgs: {
61 factorio = pkgs.factorio.override {
62 username = "FactorioPlayer1654";
63 token = "d5ad5a8971267c895c0da598688761";
64 };
65 };
66 }
67
68 Alternatively, instead of providing the username+token, you may manually
69 download the release through https://factorio.com/download , then add it to
70 the store using e.g.:
71
72 nix-prefetch-url file://\''$HOME/Downloads/${dlName} --name ${storeName}
73
74 If you go this route you might want to tell Nix to explicitly hold on to the
75 source tarball. Otherwise it could get GC'd from the Nix store and you'd
76 have to redownload it next time the package wants to rebuild to use a newer
77 dependency. E.g. if you're using NixOS:
78
79 system.extraDependencies = [
80 factorio.src
81 ];
82 '';
83
84 desktopItem = makeDesktopItem {
85 name = "factorio";
86 desktopName = "Factorio";
87 comment = "A game in which you build and maintain factories.";
88 exec = "factorio";
89 icon = "factorio";
90 categories = [ "Game" ];
91 };
92
93 branch = if experimental then "experimental" else "stable";
94
95 # NB `experimental` directs us to take the latest build, regardless of its branch;
96 # hence the (stable, experimental) pairs may sometimes refer to the same distributable.
97 versions = importJSON versionsJson;
98 binDists = makeBinDists versions;
99
100 actual =
101 binDists.${stdenv.hostPlatform.system}.${releaseType}.${branch}
102 or (throw "Factorio ${releaseType}-${branch} binaries for ${stdenv.hostPlatform.system} are not available for download.");
103
104 makeBinDists =
105 versions:
106 let
107 f =
108 path: name: value:
109 if builtins.isAttrs value then
110 if value ? "name" then makeBinDist value else builtins.mapAttrs (f (path ++ [ name ])) value
111 else
112 throw "expected attrset at ${toString path} - got ${toString value}";
113 in
114 builtins.mapAttrs (f [ ]) versions;
115 makeBinDist =
116 {
117 name,
118 version,
119 tarDirectory,
120 url,
121 sha256,
122 needsAuth,
123 candidateHashFilenames ? [ ],
124 }:
125 {
126 inherit version tarDirectory;
127 src =
128 if !needsAuth then
129 fetchurl { inherit name url sha256; }
130 else
131 (lib.overrideDerivation
132 (fetchurl {
133 inherit name url sha256;
134 curlOptsList = [
135 "--get"
136 "--data-urlencode"
137 "username@username"
138 "--data-urlencode"
139 "token@token"
140 ];
141 })
142 (_: {
143 # This preHook hides the credentials from /proc
144 preHook =
145 if username != "" && token != "" then
146 ''
147 echo -n "${username}" >username
148 echo -n "${token}" >token
149 ''
150 else
151 ''
152 # Deliberately failing since username/token was not provided, so we can't fetch.
153 exit 1
154 '';
155 failureHook = ''
156 cat <<EOF
157 ${helpMsg {
158 dlName = if candidateHashFilenames != [ ] then builtins.head candidateHashFilenames else name;
159 storeName = name;
160 }}
161 EOF
162 '';
163 })
164 );
165 };
166
167 configBaseCfg = ''
168 use-system-read-write-data-directories=false
169 [path]
170 read-data=$out/share/factorio/data/
171 [other]
172 check_updates=false
173 '';
174
175 updateConfigSh = ''
176 #! $SHELL
177 if [[ -e ~/.factorio/config.cfg ]]; then
178 # Config file exists, but may have wrong path.
179 # Try to edit it. I'm sure this is perfectly safe and will never go wrong.
180 sed -i 's|^read-data=.*|read-data=$out/share/factorio/data/|' ~/.factorio/config.cfg
181 else
182 # Config file does not exist. Phew.
183 install -D $out/share/factorio/config-base.cfg ~/.factorio/config.cfg
184 fi
185 '';
186
187 modDir = factorio-utils.mkModDirDrv mods mods-dat;
188
189 base = with actual; {
190 # remap -expansion to -space-age to better match the attr name in nixpkgs.
191 pname = "factorio-${if releaseType == "expansion" then "space-age" else releaseType}";
192 inherit version src;
193
194 preferLocalBuild = true;
195 dontBuild = true;
196
197 installPhase = ''
198 mkdir -p $out/{bin,share/factorio}
199 cp -a data $out/share/factorio
200 cp -a bin/${tarDirectory}/factorio $out/bin/factorio
201 patchelf \
202 --set-interpreter $(cat $NIX_CC/nix-support/dynamic-linker) \
203 $out/bin/factorio
204 '';
205
206 passthru.updateScript = ./update.py;
207
208 meta = {
209 description = "Game in which you build and maintain factories";
210 longDescription = ''
211 Factorio is a game in which you build and maintain factories.
212
213 You will be mining resources, researching technologies, building
214 infrastructure, automating production and fighting enemies. Use your
215 imagination to design your factory, combine simple elements into
216 ingenious structures, apply management skills to keep it working and
217 finally protect it from the creatures who don't really like you.
218
219 Factorio has been in development since spring of 2012, and reached
220 version 1.0 in mid 2020.
221 '';
222 homepage = "https://www.factorio.com/";
223 sourceProvenance = with lib.sourceTypes; [ binaryNativeCode ];
224 license = lib.licenses.unfree;
225 maintainers = with lib.maintainers; [
226 Baughn
227 priegger
228 lukegb
229 ];
230 platforms = [ "x86_64-linux" ];
231 mainProgram = "factorio";
232 };
233 };
234
235 releases = rec {
236 headless = base;
237 demo = base // {
238
239 nativeBuildInputs = [ makeWrapper ];
240 buildInputs = [ libpulseaudio ];
241
242 libPath = lib.makeLibraryPath [
243 alsa-lib
244 libGL
245 libICE
246 libSM
247 libX11
248 libXcursor
249 libXext
250 libXi
251 libXinerama
252 libXrandr
253 libpulseaudio
254 libxkbcommon
255 wayland
256 ];
257
258 installPhase = base.installPhase + ''
259 wrapProgram $out/bin/factorio \
260 --prefix LD_LIBRARY_PATH : /run/opengl-driver/lib:$libPath \
261 --run "$out/share/factorio/update-config.sh" \
262 --argv0 "" \
263 --add-flags "-c \$HOME/.factorio/config.cfg" \
264 ${lib.optionalString (mods != [ ]) "--add-flags --mod-directory=${modDir}"}
265
266 # TODO Currently, every time a mod is changed/added/removed using the
267 # modlist, a new derivation will take up the entire footprint of the
268 # client. The only way to avoid this is to remove the mods arg from the
269 # package function. The modsDir derivation will have to be built
270 # separately and have the user specify it in the .factorio config or
271 # right along side it using a symlink into the store I think i will
272 # just remove mods for the client derivation entirely. this is much
273 # cleaner and more useful for headless mode.
274
275 # TODO: trying to toggle off a mod will result in read-only-fs-error.
276 # not much we can do about that except warn the user somewhere. In
277 # fact, no exit will be clean, since this error will happen on close
278 # regardless. just prints an ugly stacktrace but seems to be otherwise
279 # harmless, unless maybe the user forgets and tries to use the mod
280 # manager.
281
282 install -m0644 <(cat << EOF
283 ${configBaseCfg}
284 EOF
285 ) $out/share/factorio/config-base.cfg
286
287 install -m0755 <(cat << EOF
288 ${updateConfigSh}
289 EOF
290 ) $out/share/factorio/update-config.sh
291
292 mkdir -p $out/share/icons/hicolor/{64x64,128x128}/apps
293 cp -a data/core/graphics/factorio-icon.png $out/share/icons/hicolor/64x64/apps/factorio.png
294 cp -a data/core/graphics/factorio-icon@2x.png $out/share/icons/hicolor/128x128/apps/factorio.png
295 ln -s ${desktopItem}/share/applications $out/share/
296 '';
297 };
298 alpha = demo // {
299
300 installPhase = demo.installPhase + ''
301 cp -a doc-html $out/share/factorio
302 '';
303 };
304 expansion = alpha;
305 };
306
307in
308stdenv.mkDerivation (releases.${releaseType})