fufexan/nix-gaming

Regarding the inclusion of GoG games

Opened this issue · 7 comments

Hi everyone,

I have a bunch of DRM-free, linux native, GoG games on my library that I've decided to package.

I could have perfectly run them from lutris or other solutions but instead I've created proper packages for them.
This helps reducing overhead and, in my humble opinion, it brings a better gaming experience to NixOS.

I thought that maybe they could fit here, I would like to contribute back to the nix community and this seams like a cool way to it.

Although there are some problems, while I do already have a few working games the nix code could be drastically improved. They are my first take at packaging so I did what I could. I also lack the knowledge to even create packages outside an standalone flake...

I want to contribute but I would need some aid, and well, I don't even know if those kind of things fit here.

Thanks in advice.

I was cleaning up a bit my Darkest Dungen flake and it looks like this:
(I'm posting this as an example to what has been explained in the previous post).

{
  description = "Darkest Dungeon (GoG) Flake";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";

    # Nix code formatter.
    alejandra = {
      url = "github:kamadorueda/alejandra/3.0.0";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  outputs = inputs @ {
    self,
    nixpkgs,
    ...
  }: let
    inherit (self) outputs;
    pkgs = nixpkgs.legacyPackages."x86_64-linux";

    # There are better ways to handle this, but for now `with` would have to be present.
    # See the note regarding nix best practices.
    commonDeps = with pkgs; [
      SDL2
      libcxx
      libGL
      fmodex
      libmx
      fmodex
      xorg.libX11
      xorg.libXext
      xorg.libxcb
      xorg.libXau
      xorg.libXdmcp
    ];
  in rec {
    formatter.x86_64-linux = inputs.alejandra.defaultPackage."x86_64-linux";

    packages = {
      darkest-dungeon = pkgs.stdenvNoCC.mkDerivation rec {
        name = "darkest-dungeon";

        # The official installer is required but, obviusly, not provided by this flake.
        src = pkgs.requireFile {
          name = "darkest_dungeon_24839_28859.sh";
          url = "https://www.gog.com/game/darkest_dungeon";
          sha256 = "2b235b43d8d6ab4b2a4b80b627ce2a9c31c1b4bee16fc6f9dda68c29cb5da99c";
        };

        nativeBuildInputs = with pkgs;
          [
            autoPatchelfHook
            makeBinaryWrapper
            unzip
            zip
            copyDesktopItems
          ]
          ++ commonDeps;

        runtimeDependenciesPath = pkgs.lib.makeLibraryPath commonDeps;

        # Desktop entry.
        desktopItems = [
          (pkgs.makeDesktopItem {
            name = name;
            desktopName = "Darkest Dungeon";
            genericName = "Loosing your will to live; the game.";
            exec = "darkest";
            #icon = "CCicon";  # The proper icon needs to be set instead.
            comment = "Darkest Dungeon GoG version.";
            categories = ["Game"];
          })
        ];

        # FIXME: I know this is curserd, but for some reason, it is required.
        unpackPhase = ''
          # First, creates a new fixed zip archive from the original installer.
          ${pkgs.zip}/bin/zip -F $src --out fixed-src.zip

          # Then extracts that one instead.
          ${pkgs.unzip}/bin/unzip fixed-src.zip
        '';

        /*
        Nix would complain about not knowing how to unpack a source file ending in
        .sh, even when specifically told which command to run.

        It is specially weird since according to the logs, it unpacks the installer but fails
        at the very last moment because it tries to unpack it again.

        Maybe the fix command can be moved to a preUnpackPhase and then point the source var to it.
        This would make the unzipping happen automatically. Don't know if it's possible.
        */

        installPhase = ''
          mkdir -p $out/share/${name}

          # Only copies this folder since the rest is useless.
          mv data/noarch/game/* $out/share/${name}

          # Creates the wrapper for the game.
          makeBinaryWrapper $out/share/${name}/darkest.bin.x86_64 $out/bin/darkest \
          --prefix LD_LIBRARY_PATH : "$runtimeDependenciesPath"
        '';
      };
    };

    defaultPackage.x86_64-linux = packages.darkest-dungeon;

    # Metadata
    meta = with pkgs.lib; {
      description = "The game that would drive you into utter maddness.";
      longDescription = "Darkest Dungeon GoG version. Without any DLCs.";
      homepage = "https://www.darkestdungeon.com/";
      downloadPage = "https://www.gog.com/game/darkest_dungeon";
      license = licenses.unfree;
      mainProgram = "darkest";
      maintainers = with maintainers; [srestegosaurio];
      platforms = platforms.linux;
    };
  };
}

As for future work:
This approach lacks support for DLCs. This could be fixed by creating a simple home-manager module in which the user can declare which DLCs they have. I've never done it but it should not be difficult to change the derivation build plan based on the options provided by the user.

And a few nitpicks;

  • It does not need to extract the whole zip, I need to look up the command-line options to only get what's needed.
  • I have not tested the desktop entry yet but the icon needs to be added.

Hi! It's great that you want to contribute. I'll test the above package soon. However, I have a few questions/points to add.

  1. Do you need to be logged in in any way to download the installer? Or do you need to log into the installer to allow downloading/installing the game? If no login mechanism is in place, this can easily be categorized as pirating, which I don't want to allow in this flake.
  2. To make things easier, we can create a "builder" that has a game list as the argument. The builder will then create a package out of each game. This reduces duplication and can also be used to set global options for games, just like the legendary builder does.
  3. DLCs can be handled by the builder if we implement it.

Hi, thanks for you're reply. Here are more details:

Do you need to be logged in in any way to download the installer? Or do you need to log into the installer to allow downloading/installing the game? If no login mechanism is in place, this can easily be categorised as pirating, which I don't want to allow in this flake.

No, you don't need to be logged-in or anything similar since the derivation uses requireFile which means that each user should own a copy of the required installer beforehand. While building nix would tell you which exact command to run in order to add the installer to the nix store.

This behaviour can pose as a problem in situation like testing although I might have a few ideas regarding that which, hopefully, don't involve promoting piracy.

To make things easier, we can create a "builder" that has a game list as the argument. The builder will then create a package out of each game. This reduces duplication and can also be used to set global options for games, just like the legendary builder does.

That's a new concept to me but sound rather useful, after all I've found the only differences trend to be the set of libs and metadata info about the game. I'm up for everything which reduces boilerplate/unnecessary code.

DLCs can be handled by the builder if we implement it.

Nice, I had little to no idea regarding how to approach that.

Also, this could be extended to all DRM-free games since it does not relay on any platform specific way of downloading the games. I've mentioned GoG because I own some games there but there's also itch.io and others.

I also want to ask about creating derivation for non-native games thought wine:

  • Is it possible to achieve without resorting to weird hacks?
  • If possible, how difficult would it be?

I'm pretty certain that the answers will vary depending on the game but I wanted to know the general state.
I've ask this because I've noticed the presence of wine in the flake.

Thanks!

Sorry for the late response, I missed the notifications.

Yes, it's possible to create derivations for wine-run games. Depending on how you define "weird hacks", you may or may not like the current solution. Look at the legendaryBuilder/osu-stable derivations.

Hi,

Sorry for the late response, I missed the notifications.

Don't worry, happens sometimes. I'm not the best keeping up with notifications either.

Yes, it's possible to create derivations for wine-run games. Depending on how you define "weird hacks", you may or may not like the current solution. Look at the legendaryBuilder/osu-stable derivations.

The only "weird hack" that currently bothers me is that I have to recompress the zips before extracting them, still don't know why. I've also looked up a bit regarding wine derivations and at least they are possible which is something.

I've also read that they can be a bit hacky but that has to do with the immutability of the nix store and how windows programs operate. If I'm not mistaken.

I'm currently trying to duct tape my way into creating a builder for the games, they share most boilerplate so it might be a nice thing to have.

Also, I've updated the Darkest Dungeon flake here: https://next.forgejo.org/notevil/darkest-dungeon-nix