summary refs log tree commit diff
path: root/pkgs/games/openra
diff options
context:
space:
mode:
authorMatthijs Steen <emailmatthijs@gmail.com>2019-01-02 01:05:51 +0100
committerMatthijs Steen <emailmatthijs@gmail.com>2019-01-09 20:57:29 +0100
commit1ae7384ddb3419330ab134da548dd8769b9f3d11 (patch)
treec3f3c1cea85037c6309f90beb5f2c8c66f2f577c /pkgs/games/openra
parentde6d70f52e2acbc08d1e9db8c0f9af0a671d2137 (diff)
downloadnixpkgs-1ae7384ddb3419330ab134da548dd8769b9f3d11.tar
nixpkgs-1ae7384ddb3419330ab134da548dd8769b9f3d11.tar.gz
nixpkgs-1ae7384ddb3419330ab134da548dd8769b9f3d11.tar.bz2
nixpkgs-1ae7384ddb3419330ab134da548dd8769b9f3d11.tar.lz
nixpkgs-1ae7384ddb3419330ab134da548dd8769b9f3d11.tar.xz
nixpkgs-1ae7384ddb3419330ab134da548dd8769b9f3d11.tar.zst
nixpkgs-1ae7384ddb3419330ab134da548dd8769b9f3d11.zip
openra with extra mods
Diffstat (limited to 'pkgs/games/openra')
-rw-r--r--pkgs/games/openra/common.nix87
-rw-r--r--pkgs/games/openra/default.nix129
-rw-r--r--pkgs/games/openra/engine.nix60
-rw-r--r--pkgs/games/openra/engines.nix41
-rw-r--r--pkgs/games/openra/mkdirp.sh4
-rw-r--r--pkgs/games/openra/mod-launch-game.sh25
-rw-r--r--pkgs/games/openra/mod.nix104
-rw-r--r--pkgs/games/openra/mods.nix339
-rw-r--r--pkgs/games/openra/openra-mod.desktop11
-rw-r--r--pkgs/games/openra/packages.nix60
10 files changed, 792 insertions, 68 deletions
diff --git a/pkgs/games/openra/common.nix b/pkgs/games/openra/common.nix
new file mode 100644
index 00000000000..e90f8170e18
--- /dev/null
+++ b/pkgs/games/openra/common.nix
@@ -0,0 +1,87 @@
+/*  The reusable code, and package attributes, between OpenRA engine packages (engine.nix)
+    and out-of-tree mod packages (mod.nix).
+*/
+{ stdenv, makeSetupHook, curl, unzip, dos2unix, pkgconfig, makeWrapper
+, lua, mono, dotnetPackages, python
+, libGL, openal, SDL2
+, zenity
+}:
+
+with stdenv.lib;
+
+let
+  path = makeBinPath ([ mono python ] ++ optional (zenity != null) zenity);
+  rpath = makeLibraryPath [ lua openal SDL2 ];
+  mkdirp = makeSetupHook { } ./mkdirp.sh;
+
+in {
+  patchEngine = dir: version: ''
+    sed -i \
+      -e 's/^VERSION.*/VERSION = ${version}/g' \
+      -e '/fetch-geoip-db/d' \
+      -e '/GeoLite2-Country.mmdb.gz/d' \
+      ${dir}/Makefile
+
+    sed -i 's|locations=.*|locations=${lua}/lib|' ${dir}/thirdparty/configure-native-deps.sh
+  '';
+
+  wrapLaunchGame = openraSuffix: ''
+    # Setting TERM=xterm fixes an issue with terminfo in mono: System.Exception: Magic number is wrong: 542
+    # https://github.com/mono/mono/issues/6752#issuecomment-365212655
+    wrapProgram $out/lib/openra${openraSuffix}/launch-game.sh \
+      --prefix PATH : "${path}" \
+      --prefix LD_LIBRARY_PATH : "${rpath}" \
+      --set TERM xterm
+
+    makeWrapper $out/lib/openra${openraSuffix}/launch-game.sh $(mkdirp $out/bin)/openra${openraSuffix} \
+      --run "cd $out/lib/openra${openraSuffix}"
+  '';
+
+  packageAttrs = {
+    buildInputs = with dotnetPackages; [
+      FuzzyLogicLibrary
+      MaxMindDb
+      MaxMindGeoIP2
+      MonoNat
+      NewtonsoftJson
+      NUnit3
+      NUnitConsole
+      OpenNAT
+      RestSharp
+      SharpFont
+      SharpZipLib
+      SmartIrc4net
+      StyleCopMSBuild
+      StyleCopPlusMSBuild
+    ] ++ [
+      lua
+      libGL
+      openal
+      SDL2
+    ];
+
+    # TODO: Test if this is correct.
+    nativeBuildInputs = [
+      curl
+      unzip
+      dos2unix
+      pkgconfig
+      makeWrapper
+      mkdirp
+      mono
+      python
+    ];
+
+    makeFlags = "prefix=$(out)";
+
+    doCheck = true;
+
+    dontStrip = true;
+
+    meta = {
+      maintainers = with maintainers; [ msteen rardiol ];
+      license = licenses.gpl3;
+      platforms = platforms.linux;
+    };
+  };
+}
diff --git a/pkgs/games/openra/default.nix b/pkgs/games/openra/default.nix
index 922d4f4762c..bf243e610bd 100644
--- a/pkgs/games/openra/default.nix
+++ b/pkgs/games/openra/default.nix
@@ -1,79 +1,72 @@
-{ stdenv, fetchFromGitHub, mono, makeWrapper, lua
-, SDL2, freetype, openal, systemd, pkgconfig,
-  dotnetPackages, gnome3, curl, unzip, which, python
-}:
+/*  This file defines all OpenRA packages under `openraPackages`,
+    e.g. the OpenRA release engine can be found at `openraPackages.engines.release` (see `engines.nix`),
+    or the out-of-tree mod "Combined Arms" can be found at `openraPackages.mods.ca` (see `mods.nix`).
+    The `openra` package is just an alias to `openraPackages.engines.release`,
+    and just provides the mods included in the source code of the engine.
+    Additional engines or mods can be added with `openraPackages.buildOpenRAEngine` (function around `engine.nix`)
+    and `openraPackages.buildOpenRAMod` (function around `mod.nix`), respectively.
+*/
+pkgs:
 
-stdenv.mkDerivation rec {
-  pname = "openra";
-  version = "20181215";
+with pkgs.lib;
 
-  meta = with stdenv.lib; {
-    description = "Real Time Strategy game engine recreating the C&C titles";
-    homepage    = "http://www.openra.net/";
-    maintainers = [ maintainers.rardiol ];
-    license     = licenses.gpl3;
-    platforms   = platforms.linux;
-  };
+let
+  /*  Building an engine or out-of-tree mod is very similar,
+      but different enough not to be able to build them with the same package definition,
+      so instaed we define what is common between them in a seperate file.
 
-  src = fetchFromGitHub {
-    owner = "OpenRA";
-    repo = "OpenRA";
-    rev = "release-${version}";
-    sha256 = "0p0izykjnz7pz02g2khp7msqa00jhjsrzk9y0g29dirmdv75qa4r";
+      Although `callPackage` could be used, it would require undoing `makeOverridable`,
+      because `common.nix` does not define a package, but just an attribute set,
+      which is directly passed as part of the argument to the engines and mods `callPackage`,
+      so either the attributes added by `makeOverridable` have to be removed
+      or the engine and mod package definitions will need to add `...` to the argument list.
+  */
+  common = let f = import ./common.nix; in f (builtins.intersectAttrs (functionArgs f) pkgs // {
+    lua = pkgs.lua5_1;
+    # It is not necessary to run the game, but it is nicer to be given an error dialog in the case of failure,
+    # rather than having to look to the logs why it is not starting.
+    inherit (pkgs.gnome3) zenity;
+  });
 
+  /*  Building a set of engines or mods requires some dependencies as well,
+      so the sets will actually be defined as a function instead,
+      requiring the dependencies and returning the actual set.
+
+      Not all dependencies for defining a engine or mod set are shared,
+      so additional arguments can be passed as well.
+
+      The builders for engines and mods allow to delay specifying the name,
+      by returning a function that expects a name, which we use, in this case,
+      to base the name on the attribute name instead, preventing the need to specify the name twice
+      if the attribute name and engine/mod name are equal.
+  */
+  callWithName = name: value: if isFunction value then value name else value;
+  buildOpenRASet = f: args: pkgs.recurseIntoAttrs (mapAttrs callWithName (f ({
+    inherit (pkgs) fetchFromGitHub;
+    abbrevCommit = commit: substring 0 7 commit;
     extraPostFetch = ''
-      sed -i 's,curl,curl --insecure,g' $out/thirdparty/{fetch-thirdparty-deps,noget}.sh
+      sed -i 's/curl/curl --insecure/g' $out/thirdparty/{fetch-thirdparty-deps,noget}.sh
       $out/thirdparty/fetch-thirdparty-deps.sh
     '';
-  };
-
-  dontStrip = true;
-
-  buildInputs = (with dotnetPackages;
-     [ NUnit3 NewtonsoftJson MonoNat FuzzyLogicLibrary SmartIrc4net SharpZipLib MaxMindGeoIP2 MaxMindDb SharpFont StyleCopMSBuild StyleCopPlusMSBuild RestSharp NUnitConsole OpenNAT ])
-     ++ [ curl unzip lua gnome3.zenity ];
-  nativeBuildInputs = [ curl unzip mono makeWrapper lua pkgconfig ];
-
-  postPatch = ''
-    mkdir Support
-    sed -i \
-      -e 's/^VERSION.*/VERSION = release-${version}/g' \
-      -e '/GeoLite2-Country.mmdb.gz/d' \
-      -e '/fetch-geoip-db.sh/d' \
-      Makefile
-    substituteInPlace thirdparty/configure-native-deps.sh --replace "locations=\"" "locations=\"${lua}/lib "
-  '';
-
-  preConfigure = ''
-    makeFlags="prefix=$out"
-    make version
-  '';
-
-  buildFlags = [ "DEBUG=false" "default" "man-page" ];
-
-  doCheck = true;
-
-  #TODO: check
-  checkTarget = "nunit test";
+  } // args)));
 
-  installTargets = [ "install" "install-linux-icons" "install-linux-desktop" "install-linux-appdata" "install-linux-mime" "install-man-page" ];
+in pkgs.recurseIntoAttrs rec {
+  # The whole attribute set is destructered to ensure those (and only those) attributes are given
+  # and to provide defaults for those that are optional.
+  buildOpenRAEngine = { name ? null, version, description, homepage, mods, src }@engine:
+    # Allow specifying the name at a later point if no name has been given.
+    let builder = name: pkgs.callPackage ./engine.nix (common // {
+      engine = engine // { inherit name; };
+    }); in if name == null then builder else builder name;
 
-  postInstall = with stdenv.lib; let
-    runtime = makeLibraryPath [ SDL2 freetype openal systemd lua ];
-    binaries= makeBinPath [ which mono gnome3.zenity python ];
-  in ''
-    wrapProgram $out/lib/openra/launch-game.sh \
-      --prefix PATH : "${binaries}" \
-      --prefix LD_LIBRARY_PATH : "${runtime}" \
-      --set TERM "xterm"
+  # See `buildOpenRAEngine`.
+  buildOpenRAMod = { name ? null, version, title, description, homepage, src, engine, assetsError ? "" }@mod: ({ version, mods ? [], src }@engine:
+    let builder = name: pkgs.callPackage ./mod.nix (common // {
+      mod = mod // { inherit name assetsError; };
+      engine = engine // { inherit mods; };
+    }); in if name == null then builder else builder name) engine;
 
-    mkdir -p $out/bin
-    makeWrapper $out/lib/openra/launch-game.sh $out/bin/openra --run "cd $out/lib/openra"
-    printf "#!/bin/sh\nexec $out/bin/openra Game.Mod=ra" > $out/bin/openra-ra
-    chmod +x $out/bin/openra-ra
-    printf "#!/bin/sh\nexec $out/bin/openra Game.Mod=cnc" > $out/bin/openra-cnc
-    chmod +x $out/bin/openra-cnc
-    printf "#!/bin/sh\nexec $out/bin/openra Game.Mod=d2k" > $out/bin/openra-d2k
-    chmod +x $out/bin/openra-d2k
-  '';
+  # See `buildOpenRASet`.
+  engines = buildOpenRASet (import ./engines.nix) { inherit buildOpenRAEngine; };
+  mods = buildOpenRASet (import ./mods.nix) { inherit buildOpenRAMod; };
 }
diff --git a/pkgs/games/openra/engine.nix b/pkgs/games/openra/engine.nix
new file mode 100644
index 00000000000..7d2d007d075
--- /dev/null
+++ b/pkgs/games/openra/engine.nix
@@ -0,0 +1,60 @@
+/*  The package defintion for an OpenRA engine.
+    It shares code with `mod.nix` by what is defined in `common.nix`.
+    Similar to `mod.nix` it is a generic package definition,
+    in order to make it easy to define multiple variants of the OpenRA engine.
+    For each mod provided by the engine, a wrapper script is created,
+    matching the naming convention used by `mod.nix`.
+    This package could be seen as providing a set of in-tree mods,
+    while the `mod.nix` pacakges provide a single out-of-tree mod.
+*/
+{ stdenv
+, packageAttrs
+, patchEngine
+, wrapLaunchGame
+, engine
+}:
+
+with stdenv.lib;
+
+stdenv.mkDerivation (recursiveUpdate packageAttrs rec {
+  name = "${pname}-${version}";
+  pname = "openra";
+  version = "${engine.name}-${engine.version}";
+
+  src = engine.src;
+
+  postPatch = patchEngine "." version;
+
+  configurePhase = ''
+    runHook preConfigure
+
+    make version VERSION=${escapeShellArg version}
+
+    runHook postConfigure
+  '';
+
+  buildFlags = [ "DEBUG=false" "default" "man-page" ];
+
+  checkTarget = "nunit test";
+
+  installTargets = [
+    "install"
+    "install-linux-icons"
+    "install-linux-desktop"
+    "install-linux-appdata"
+    "install-linux-mime"
+    "install-man-page"
+  ];
+
+  postInstall = ''
+    ${wrapLaunchGame ""}
+
+    ${concatStrings (map (mod: ''
+      makeWrapper $out/bin/openra $out/bin/openra-${mod} --add-flags Game.Mod=${mod}
+    '') engine.mods)}
+  '';
+
+  meta = {
+    inherit (engine) description homepage;
+  };
+})
diff --git a/pkgs/games/openra/engines.nix b/pkgs/games/openra/engines.nix
new file mode 100644
index 00000000000..e0d97f3c548
--- /dev/null
+++ b/pkgs/games/openra/engines.nix
@@ -0,0 +1,41 @@
+{ buildOpenRAEngine, fetchFromGitHub, abbrevCommit, extraPostFetch }:
+
+let
+  buildUpstreamOpenRAEngine = { version, rev, sha256 }: name: (buildOpenRAEngine {
+    inherit version;
+    description = "Open-source re-implementation of Westwood Studios' 2D Command and Conquer games";
+    homepage = https://www.openra.net/;
+    mods = [ "cnc" "d2k" "ra" "ts" ];
+    src = fetchFromGitHub {
+      owner = "OpenRA";
+      repo = "OpenRA" ;
+      inherit rev sha256 extraPostFetch;
+    };
+  } name).overrideAttrs (oldAttrs: {
+    postInstall = ''
+      ${oldAttrs.postInstall}
+      cp -r mods/ts $out/lib/openra/mods/
+      cp mods/ts/icon.png $(mkdirp $out/share/pixmaps)/openra-ts.png
+      ( cd $out/share/applications; sed -e 's/Dawn/Sun/g' -e 's/cnc/ts/g' openra-cnc.desktop > openra-ts.desktop )
+    '';
+  });
+
+in {
+  release = buildUpstreamOpenRAEngine rec {
+    version = "20181215";
+    rev = "release-${version}";
+    sha256 = "0p0izykjnz7pz02g2khp7msqa00jhjsrzk9y0g29dirmdv75qa4r";
+  };
+
+  playtest = buildUpstreamOpenRAEngine rec {
+    version = "20190106";
+    rev = "playtest-${version}";
+    sha256 = "0ps9x379plrrj1hnj4fpr26lc46mzgxknv5imxi0bmrh5y4781ql";
+  };
+
+  bleed = let commit = "6de92de8d982094a766eab97a92225c240d85493"; in buildUpstreamOpenRAEngine {
+    version = abbrevCommit commit;
+    rev = commit;
+    sha256 = "0p0izykjnz7pz02g2khp7msqa00jhjsrzk9y0g29dirmdv75qa4r";
+  };
+}
diff --git a/pkgs/games/openra/mkdirp.sh b/pkgs/games/openra/mkdirp.sh
new file mode 100644
index 00000000000..7b2cf0b89c0
--- /dev/null
+++ b/pkgs/games/openra/mkdirp.sh
@@ -0,0 +1,4 @@
+mkdirp() {
+  mkdir -p "$@"
+  echo "$@"
+}
diff --git a/pkgs/games/openra/mod-launch-game.sh b/pkgs/games/openra/mod-launch-game.sh
new file mode 100644
index 00000000000..ec03353bee5
--- /dev/null
+++ b/pkgs/games/openra/mod-launch-game.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+show_error() {
+  if command -v zenity > /dev/null; then
+    zenity --no-wrap --no-markup --error --title "OpenRA - @title@" --text "$1" 2>/dev/null
+  else
+    printf "$1\n" >&2
+  fi
+  exit 1
+}
+
+cd "@out@/lib/openra-@name@"
+
+# Check for missing assets
+assetsError='@assetsError@'
+if [ -n "$assetsError" -a ! -d "$HOME/.openra/Content/@name@" ]; then
+  show_error "$assetsError"
+fi
+
+# Run the game
+mono --debug OpenRA.Game.exe Game.Mod=@name@ Engine.LaunchPath="@out@/bin/openra-@name@" Engine.ModSearchPaths="@out@/lib/openra-@name@/mods" "$@"
+
+# Show a crash dialog if something went wrong
+if [ $? -ne 0 -a $? -ne 1 ]; then
+  show_error "OpenRA - @title@ has encountered a fatal error.\nPlease refer to the crash logs for more information.\n\nLog files are located in ~/.openra/Logs"
+fi
diff --git a/pkgs/games/openra/mod.nix b/pkgs/games/openra/mod.nix
new file mode 100644
index 00000000000..ebc65b2f5c4
--- /dev/null
+++ b/pkgs/games/openra/mod.nix
@@ -0,0 +1,104 @@
+/*  The package defintion for an OpenRA out-of-tree mod.
+    It shares code with `engine.nix` by what is defined in `common.nix`.
+    To build an out-of-tree mod it needs the source code of the engine available,
+    and they each need to be build with a specific version or fork of the engine,
+    so the engine needs to be supplied as an argument as well.
+    The engine is relatively small and quick to build, so this is not much of a problem.
+    Building a mod will result in a wrapper script that starts the mod inside the specified engine.
+*/
+{ stdenv
+, packageAttrs
+, patchEngine
+, wrapLaunchGame
+, mod
+, engine
+}:
+
+with stdenv.lib;
+
+let
+  engineSourceName = engine.src.name or "engine";
+  modSourceName = mod.src.name or "mod";
+
+# Based on: https://build.opensuse.org/package/show/home:fusion809/openra-ura
+in stdenv.mkDerivation (recursiveUpdate packageAttrs rec {
+  name = "${pname}-${version}";
+  pname = "openra-${mod.name}";
+  inherit (mod) version;
+
+  srcs = [
+    mod.src
+    engine.src
+  ];
+
+  sourceRoot = ".";
+
+  postUnpack = ''
+    mv ${engineSourceName} ${modSourceName}
+    cd ${modSourceName}
+  '';
+
+  postPatch = ''
+    cat <<'EOF' > fetch-engine.sh
+    #!/bin/sh
+    exit 0
+    EOF
+
+    sed -i 's/^VERSION.*/VERSION = ${version}/g' Makefile
+
+    dos2unix *.md
+
+    ${patchEngine engineSourceName engine.version}
+  '';
+
+  configurePhase = ''
+    runHook preConfigure
+
+    make version VERSION=${escapeShellArg version}
+    make -C ${engineSourceName} version VERSION=${escapeShellArg engine.version}
+
+    runHook postConfigure
+  '';
+
+  checkTarget = "test";
+
+  installPhase = ''
+    runHook preInstall
+
+    make -C ${engineSourceName} install-engine install-common-mod-files DATA_INSTALL_DIR=$out/lib/${pname}
+
+    cp -r ${engineSourceName}/mods/{${concatStringsSep "," ([ "common" "modcontent" ] ++ engine.mods)}} mods/${mod.name} \
+      $out/lib/${pname}/mods/
+
+    substitute ${./mod-launch-game.sh} $out/lib/${pname}/launch-game.sh \
+      --subst-var out \
+      --subst-var-by name ${escapeShellArg mod.name} \
+      --subst-var-by title ${escapeShellArg mod.title} \
+      --subst-var-by assetsError ${escapeShellArg mod.assetsError}
+    chmod +x $out/lib/${pname}/launch-game.sh
+
+    ${wrapLaunchGame "-${mod.name}"}
+
+    substitute ${./openra-mod.desktop} $(mkdirp $out/share/applications)/${pname}.desktop \
+      --subst-var-by name ${escapeShellArg mod.name} \
+      --subst-var-by title ${escapeShellArg mod.title}
+
+    cp README.md $(mkdirp $out/share/doc/packages/${pname})/README.md
+
+    [[ -e mods/${mod.name}/icon.png ]] && mod_icon=mods/${mod.name}/icon.png || {
+      [[ -e mods/${mod.name}/logo.png ]] && mod_icon=mods/${mod.name}/logo.png || mod_icon=packaging/linux/mod_256x256.png
+    }
+    cp "$mod_icon" $(mkdirp $out/share/pixmaps)/${pname}.png
+
+    for size in 16 32 48 64 128 256; do
+      size=''${size}x''${size}
+      cp packaging/linux/mod_''${size}.png $(mkdirp $out/share/icons/hicolor/''${size}/apps)/${pname}.png
+    done
+
+    runHook postInstall
+  '';
+
+  meta = {
+    inherit (mod) description homepage;
+  };
+})
diff --git a/pkgs/games/openra/mods.nix b/pkgs/games/openra/mods.nix
new file mode 100644
index 00000000000..5bfc5a224a5
--- /dev/null
+++ b/pkgs/games/openra/mods.nix
@@ -0,0 +1,339 @@
+{ buildOpenRAMod, fetchFromGitHub, abbrevCommit, extraPostFetch }:
+
+let
+  unsafeBuildOpenRAMod = attrs: name: (buildOpenRAMod attrs name).overrideAttrs (_: {
+    doCheck = false;
+  });
+
+in {
+  ca = buildOpenRAMod {
+    version = "93";
+    title = "Combined Arms";
+    description = "A game that combines units from the official OpenRA Red Alert and Tiberian Dawn mods";
+    homepage = https://github.com/Inq8/CAmod;
+    src = fetchFromGitHub {
+      owner = "Inq8";
+      repo = "CAmod";
+      rev = "16fb77d037be7005c3805382712c33cec1a2788c";
+      sha256 = "11fjyr3692cy2a09bqzk5ya1hf6plh8hmdrgzds581r9xbj0q4pr";
+    };
+    engine = let commit = "b8a7dd52ff893ed8225726d4ed4e14ecad748404"; in {
+      version = abbrevCommit commit;
+      src = fetchFromGitHub {
+        owner = "Inq8";
+        repo = "CAengine" ;
+        rev = commit;
+        sha256 = "0dyk861qagibx8ldshz7d2nrki9q550f6f0wy8pvayvf1gv1dbxj";
+        name = "engine";
+        inherit extraPostFetch;
+      };
+    };
+  };
+
+  d2 = unsafeBuildOpenRAMod rec {
+    version = "128";
+    title = "Dune II";
+    description = "A modernization of the original ${title} game";
+    homepage = https://github.com/OpenRA/d2;
+    src = fetchFromGitHub {
+      owner = "OpenRA";
+      repo = "d2";
+      rev = "bc969207b532a2def69e0d6ac09a4e8fb5d4e946";
+      sha256 = "18v154kf1fmfk2gnymb3ggsfy73ql8rr7jvbhiw60yhzwx89cdk8";
+    };
+    engine = rec {
+      version = "20181215";
+      mods = [ "cnc" "d2k" "ra" ];
+      src = fetchFromGitHub {
+        owner = "OpenRA";
+        repo = "OpenRA" ;
+        rev = "release-${version}";
+        sha256 = "0p0izykjnz7pz02g2khp7msqa00jhjsrzk9y0g29dirmdv75qa4r";
+        name = "engine";
+        inherit extraPostFetch;
+      };
+    };
+    assetsError = ''
+      The mod expects the original ${title} game assets in place:
+      https://github.com/OpenRA/d2/wiki
+    '';
+  };
+
+  dr = buildOpenRAMod rec {
+    version = "244";
+    title = "Dark Reign";
+    description = "A re-imagination of the original Command & Conquer: ${title} game";
+    homepage = https://github.com/drogoganor/DarkReign;
+    src = fetchFromGitHub {
+      owner = "drogoganor";
+      repo = "DarkReign";
+      rev = "e21db398f4d995c91b9e1a0f31ffaa7d54f43742";
+      sha256 = "1gzvdf6idmx0rr8afaxd9dsbnxljif2kic6znkd9vcrwnqmp1fjr";
+    };
+    engine = let commit = "7fcfb1dcb2bd472fa6680ffa37bd3bbedb2c44c5"; in {
+      version = abbrevCommit commit;
+      src = fetchFromGitHub {
+        owner = "drogoganor";
+        repo = "OpenRA" ;
+        rev = commit;
+        sha256 = "0x7k96j3q16dgay4jjlyv9kcgn4sc4v9ksw6ijnjws7q1r2rjs0m";
+        name = "engine";
+        inherit extraPostFetch;
+      };
+    };
+  };
+
+  gen = buildOpenRAMod {
+    version = "1133";
+    title = "Generals Alpha";
+    description = "Re-imagination of the original Command & Conquer: Generals game";
+    homepage = https://github.com/MustaphaTR/Generals-Alpha;
+    src = fetchFromGitHub {
+      owner = "MustaphaTR";
+      repo = "Generals-Alpha";
+      rev = "277d20d5a8b5e11eac9443031af133dc110c653f";
+      sha256 = "1k37545l99q7zphnh1ykvimsyp5daykannps07d4dgr2w9l7bmhg";
+    };
+    engine = rec {
+      version = "gen-20180905";
+      src = fetchFromGitHub {
+        owner = "MustaphaTR";
+        repo = "OpenRA" ;
+        rev = version;
+        sha256 = "0wy1h7fg0n8dpy6y91md7x0qnr9rk4xf6155jali4bi8gghw2g5v";
+        name = "generals-alpha-engine";
+        inherit extraPostFetch;
+      };
+    };
+  };
+
+  kknd = buildOpenRAMod rec {
+    version = "142";
+    title = "Krush, Kill 'n' Destroy";
+    description = "Re-imagination of the original ${title} game";
+    homepage = https://kknd-game.com/;
+    src = fetchFromGitHub {
+      owner = "IceReaper";
+      repo = "KKnD";
+      rev = "54d34292168d5c47529688c8d5ca7693c4001ef3";
+      sha256 = "1rsdig282cfr8b4iamr9ri6sshgppp8gllfyib6c2hvqqr301720";
+    };
+    engine = let commit = "4e8eab4ca00d1910203c8a103dfd2c002714daa8"; in {
+      version = abbrevCommit commit;
+      src = fetchFromGitHub {
+        owner = "OpenRA";
+        repo = "OpenRA" ;
+        rev = commit;
+        sha256 = "1yyqparf93x8yzy1f46gsymgkj5jls25v2yc7ighr3f7mi3igdvq";
+        name = "engine";
+        inherit extraPostFetch;
+      };
+    };
+  };
+
+  mw = buildOpenRAMod rec {
+    version = "235";
+    title = "Medieval Warfare";
+    description = "A re-imagination of the original Command & Conquer: ${title} game";
+    homepage = https://github.com/CombinE88/Medieval-Warfare;
+    src = fetchFromGitHub {
+      owner = "CombinE88";
+      repo = "Medieval-Warfare";
+      rev = "1e4fc7ea24d0806c5a7cd753490e967d804a3567";
+      sha256 = "0swa66mzb6wr8vf1yivrss54dl98jzzwh9b8qrjfwmfrq2i356iq";
+    };
+    engine = let commit = "9f9617aa359ebc1923252b7a4a79def73ecfa8a2"; in {
+      version = abbrevCommit commit;
+      src = fetchFromGitHub {
+        owner = "CombinE88";
+        repo = "OpenRA" ;
+        rev = commit;
+        sha256 = "02h29xnc1cb5zr001cnmaww5qnfnfaza4v28251jgzkby593r32q";
+        name = "engine";
+        inherit extraPostFetch;
+      };
+    };
+  };
+
+  ra2 = buildOpenRAMod rec {
+    version = "876";
+    title = "Red Alert 2";
+    description = "Re-imagination of the original Command & Conquer: ${title} game";
+    homepage = https://github.com/OpenRA/ra2;
+    src = fetchFromGitHub {
+      owner = "OpenRA";
+      repo = "ra2";
+      rev = "6a864b2a5887ae42291768fb3dec73082fee44ee";
+      sha256 = "19m4z9r00dj67746ps2f9a8i1icq8nm0iiww6dl975yl6gaxp5qy";
+    };
+    engine = rec {
+      version = "20180923";
+      src = fetchFromGitHub {
+        owner = "OpenRA";
+        repo = "OpenRA" ;
+        rev = "release-${version}";
+        sha256 = "1pgi3zaq9fwwdq6yh19bwxscslqgabjxkvl9bcn1a5agy4bfbqk5";
+        name = "engine";
+        inherit extraPostFetch;
+      };
+    };
+    assetsError = ''
+      The mod expects the original ${title} game assets in place:
+      https://github.com/OpenRA/ra2/wiki
+    '';
+  };
+
+  raclassic = buildOpenRAMod {
+    version = "171";
+    title = "Red Alert Classic";
+    description = "A modernization of the original Command & Conquer: Red Alert game";
+    homepage = https://github.com/OpenRA/raclassic;
+    src = fetchFromGitHub {
+      owner = "OpenRA";
+      repo = "raclassic";
+      rev = "a2319b3dfb367a8d4278bf7baf55a10abf615fbc";
+      sha256 = "1k67fx4d9hg8mckzp7pp8lxa6ngqxnnrnbqyfls99dqc4df1iw0a";
+    };
+    engine = rec {
+      version = "20181215";
+      src = fetchFromGitHub {
+        owner = "OpenRA";
+        repo = "OpenRA" ;
+        rev = "release-${version}";
+        sha256 = "0p0izykjnz7pz02g2khp7msqa00jhjsrzk9y0g29dirmdv75qa4r";
+        name = "engine";
+        inherit extraPostFetch;
+      };
+    };
+  };
+
+  rv = unsafeBuildOpenRAMod {
+    version = "1294";
+    title = "Romanov's Vengeance";
+    description = "Re-imagination of the original Command & Conquer: Red Alert 2 game";
+    homepage = https://github.com/MustaphaTR/Romanovs-Vengeance;
+    src = fetchFromGitHub {
+      owner = "MustaphaTR";
+      repo = "Romanovs-Vengeance";
+      rev = "c21cb11579d7e12354c5ccb5c3c47e567c6b3d4f";
+      sha256 = "1vmc5b9awx8q0mahwv11fzgplw9w7m8kzvnx5cl7xr1w5wk87428";
+    };
+    engine = let commit = "e9e99074b294c32fbe88dd8727581cb8c512c2e2"; in {
+      version = abbrevCommit commit;
+      mods = [ "as" ];
+      src = fetchFromGitHub {
+        owner = "GraionDilach";
+        repo = "OpenRA" ;
+        rev = commit;
+        sha256 = "0bibnakpmbxwglf2dka6g04xp8dzwyms1zk5kqlbm8gpdp0aqmxp";
+        name = "engine";
+        inherit extraPostFetch;
+      };
+    };
+    assetsError = ''
+      The mod expects the Command & Conquer: The Ultimate Collection assets in place:
+      https://github.com/OpenRA/ra2/wiki
+    '';
+  };
+
+  sp = unsafeBuildOpenRAMod {
+    version = "153";
+    title = "Shattered Paradise";
+    description = "Re-imagination of the original Command & Conquer: Tiberian Sun game";
+    homepage = https://github.com/ABrandau/OpenRAModSDK;
+    src = fetchFromGitHub {
+      owner = "ABrandau";
+      repo = "OpenRAModSDK";
+      rev = "89148b8cf89bf13911fafb74a1aa2b4cacf027e0";
+      sha256 = "1bb8hzd3mhnn76iqiah1161qz98f0yvyryhmrghq03xlbin3mhbi";
+    };
+    engine = let commit = "82a2f234bdf3b768cea06408e3de30f9fbbe9412"; in {
+      version = abbrevCommit commit;
+      mods = [ "as" "ts" ];
+      src = fetchFromGitHub {
+        owner = "ABrandau";
+        repo = "OpenRA" ;
+        rev = commit;
+        sha256 = "1nl3brvx1bikxm5rmpc7xmd32n722jiyjh86pnar6b6idr1zj2ws";
+        name = "engine";
+        inherit extraPostFetch;
+      };
+    };
+  };
+
+  ss = buildOpenRAMod rec {
+    version = "72";
+    title = "Sole Survivor";
+    description = "A re-imagination of the original Command & Conquer: ${title} game";
+    homepage = https://github.com/MustaphaTR/sole-survivor;
+    src = fetchFromGitHub {
+      owner = "MustaphaTR";
+      repo = "sole-survivor";
+      rev = "fad65579c8b487cef9a8145e872390ed77c16c69";
+      sha256 = "0h7is7x2qyvq7vqp0jgw5zrdkw8g7ndd82d843ldhnb0a3vyrk34";
+    };
+    engine = let commit = "becfc154c5cd3891d695339ff86883db8b5790a5"; in {
+      version = abbrevCommit commit;
+      src = fetchFromGitHub {
+        owner = "OpenRA";
+        repo = "OpenRA" ;
+        rev = commit;
+        sha256 = "0id8vf3cjr7h5pz4sw8pdaz3sc45lxr21k1fk4309kixsrpa7i0y";
+        name = "engine";
+        inherit extraPostFetch;
+      };
+    };
+  };
+
+  ura = buildOpenRAMod {
+    version = "431";
+    title = "Red Alert Unplugged";
+    description = "Re-imagination of the original Command & Conquer: Red Alert game";
+    homepage = http://redalertunplugged.com/;
+    src = fetchFromGitHub {
+      owner = "RAunplugged";
+      repo = "uRA";
+      rev = "128dc53741fae923f4af556f2293ceaa0cf571f0";
+      sha256 = "1mhr8kyh313z52gdrqv31d6z7jvdldiajalca5mcr8gzg6mph66p";
+    };
+    engine = rec {
+      version = "unplugged-cd82382";
+      src = fetchFromGitHub {
+        owner = "RAunplugged";
+        repo = "OpenRA" ;
+        rev = version;
+        sha256 = "1p5hgxxvxlz8480vj0qkmnxjh7zj3hahk312m0zljxfdb40652w1";
+        name = "engine";
+        inherit extraPostFetch;
+      };
+    };
+  };
+
+  yr = unsafeBuildOpenRAMod rec {
+    version = "117";
+    homepage = https://github.com/cookgreen/yr;
+    title = "Yuri's Revenge";
+    description = "Re-imagination of the original Command & Conquer: ${title} game";
+    src = fetchFromGitHub {
+      owner = "cookgreen";
+      repo = "yr";
+      rev = "1d4beeb0687fe4b39b01ec31f3702cfb90a7f4f7";
+      sha256 = "1rd962ja1x72rz68kbmp19yiip3iif50hzlj3v8k1f5l94r2x2pn";
+    };
+    engine = rec {
+      version = "20180923";
+      src = fetchFromGitHub {
+        owner = "OpenRA";
+        repo = "OpenRA" ;
+        rev = "release-${version}";
+        sha256 = "1pgi3zaq9fwwdq6yh19bwxscslqgabjxkvl9bcn1a5agy4bfbqk5";
+        name = "engine";
+        inherit extraPostFetch;
+      };
+    };
+    assetsError = ''
+      The mod expects the Command & Conquer: The Ultimate Collection assets in place:
+      https://github.com/OpenRA/ra2/wiki
+    '';
+  };
+}
diff --git a/pkgs/games/openra/openra-mod.desktop b/pkgs/games/openra/openra-mod.desktop
new file mode 100644
index 00000000000..090b6c1325d
--- /dev/null
+++ b/pkgs/games/openra/openra-mod.desktop
@@ -0,0 +1,11 @@
+[Desktop Entry]
+Type=Application
+Version=1.0
+Name=OpenRA - @title@
+GenericName=Real Time Strategy Game
+GenericName[de]=Echtzeit-Strategiespiel
+Comment=@description@
+Icon=openra-@name@
+Exec=openra-@name@
+Terminal=false
+Categories=Game;StrategyGame;X-RTS;X-RealTimeStrategy;X-RealTimeStrategyGame;
diff --git a/pkgs/games/openra/packages.nix b/pkgs/games/openra/packages.nix
new file mode 100644
index 00000000000..c09b697771a
--- /dev/null
+++ b/pkgs/games/openra/packages.nix
@@ -0,0 +1,60 @@
+pkgs:
+
+let
+  /*  Building an engine or out-of-tree mod is very similar,
+      but different enough not to be able to build them with the same package definition,
+      so instaed we define what is common between them in a seperate file.
+
+      Although `callPackage` could be used, it would require undoing `makeOverridable`,
+      because `common.nix` does not define a package, but just an attribute set,
+      which is directly passed as part of the argument to the engines and mods `callPackage`,
+      so either the attributes added by `makeOverridable` have to be removed
+      or the engine and mod package definitions will need to add `...` to the argument list.
+  */
+  common = let f = import ./common.nix; in f (builtins.intersectAttrs (builtins.functionArgs f) pkgs // {
+    lua = pkgs.lua5_1;
+    # It is not necessary to run the game, but it is nicer to be given an error dialog in the case of failure,
+    # rather than having to look to the logs why it is not starting.
+    inherit (pkgs.gnome3) zenity;
+  });
+
+  /*  Building a set of engines or mods requires some dependencies as well,
+      so the sets will actually be defined as a function instead,
+      requiring the dependencies and returning the actual set.
+
+      Not all dependencies for defining a engine or mod set are shared,
+      so additional arguments can be passed as well.
+
+      The builders for engines and mods allow to delay specifying the name,
+      by returning a function that expects a name, which we use, in this case,
+      to base the name on the attribute name instead, preventing the need to specify the name twice
+      if the attribute name and engine/mod name are equal.
+  */
+  buildOpenRASet = f: args: builtins.mapAttrs (name: value: if builtins.isFunction value then value name else value) (f ({
+    inherit (pkgs) fetchFromGitHub;
+    extraPostFetch = ''
+      sed -i 's/curl/curl --insecure/g' $out/thirdparty/{fetch-thirdparty-deps,noget}.sh
+      $out/thirdparty/fetch-thirdparty-deps.sh
+    '';
+  } // args));
+
+in rec {
+  # The whole attribute set is destructered to ensure those (and only those) attributes are given
+  # and to provide defaults for those that are optional.
+  buildOpenRAEngine = { name ? null, version, description, homepage, mods, src, installExperimental ? "" }@engine:
+    # Allow specifying the name at a later point if no name has been given.
+    let builder = name: pkgs.callPackage ./engine.nix (common // {
+      engine = engine // { inherit name installExperimental; };
+    }); in if name == null then builder else builder name;
+
+  # See `buildOpenRAEngine`.
+  buildOpenRAMod = { name ? null, version, title, description, homepage, src, engine }@mod: ({ version, mods ? [], src }@engine:
+    let builder = name: pkgs.callPackage ./mod.nix (common // {
+      mod = mod // { inherit name; };
+      engine = engine // { inherit mods; };
+    }); in if name == null then builder else builder name) engine;
+
+  # See `buildOpenRASet`.
+  engines = buildOpenRASet (import ./engines.nix) { inherit buildOpenRAEngine; };
+  mods = buildOpenRASet (import ./mods.nix) { inherit buildOpenRAMod; };
+}