How to get Landlubbers to run?

Hi all-

Mainly as a matter of learning how to do this in general, can someone walk me through how to get Landlubbers (https://www.landlubbersgame.com/) to run under NixOS?

I have tried a few hacky things, just to see if I can get it working (before even considering what the actual right way would be):

The game comes with an executable and several libraries. If I go to the unzipped directory and do something like LD_LIBRARY_PATH=libs64 ./visplayer, I get an error about libGL being missing.

I can extend LD_LIBRARY_PATH with a store path to libGL. Then I get an error about libstdc++.so.6 being missing. So the next thing I try is adding $(nix eval --raw nixpkgs.stdenv.cc.cc.lib)/lib to my growing LD_LIBRARY_PATH. Now I get a more interesting error message:

./visplayer: /nix/store/la5imi1602jxhpds9675n2n2d0683lbq-glibc-2.20/lib/libc.so.6: version `GLIBC_2.26' not found (required by /nix/store/jripyj27m75nsl968ik6w64vc9p50j5r-libX11-1.6.8/lib/libX11.so.6)
./visplayer: /nix/store/la5imi1602jxhpds9675n2n2d0683lbq-glibc-2.20/lib/libc.so.6: version `GLIBC_2.25' not found (required by /nix/store/4zsyw0hf0akwm85ski6axj5bgk3pk0yg-libXdmcp-1.1.3/lib/libXdmcp.so.6)

What does this error message mean? Why is it looking for two different versions of glibc?

This is where I get stuck. I can add a lib path for glibc 2.25, but then I get a similar error message about 2.26. And if I add another path for 2.26, then I still get the same error message.

Halp! Thanks :slight_smile:

roni

The way to package things like that (or anything really) is to write a Nix expression for it. I tinkered around a bit on my own and this is what I came up with:

with import <nixpkgs> {};

stdenv.mkDerivation {
  name = "landlubber";
  src = fetchzip {
    url = "https://www.landlubbersgame.com/Linux%20(0.2)%20-%20Landlubbers%20Episode%201.zip";
    # If I didn't specify this, an error would point out that I need stripRoot
    stripRoot = false;
    sha256 = "0dx8kl46n6n7m7hp2m3klpa11q9chy5v2pznbayc9y3mkq2hn7sn";
  };

  nativeBuildInputs = [
    # This automatically patches binaries with the libraries it needs
    autoPatchelfHook
    # This is needed for wrapping the final binary
    makeWrapper
  ];

  # autoPatchelfHook uses buildInputs (among others) for looking up libraries
  buildInputs = [
    stdenv.cc.cc
    libGL
  ];

  installPhase = ''
    mkdir -p $out/{bin,libexec}
    mv visplayer $out/bin
    # Install the libraries it ships with into the result
    mv libs64 $out/lib
    # We use $out/libexec as the working directory. It expects some things there
    mv * $out/libexec
    # Wrap the binary to first cd into the working directory we put the files in
    wrapProgram $out/bin/visplayer \
      --run "cd $out/libexec"
  '';
}

You can nix-build file.nix this and then run result/bin/visplayer. This at least shows the initial game screen, though I couldn’t audio and controls to work. The code contains some comments for how I went about it to get this far. Hope this helps :slight_smile:

1 Like

Finally got around to trying this out, and it works very nicely. Thanks so much for your help! Looking at what you did, everything more or less makes sense. My main question is how I might go about learning enough about Nix to get to this level of intuition.

I have a few questions, I hope you don’t mind:

  1. How did you learn about autoPatchelfHook? This seems like a critical tool for bootstrapping existing binaries into NixOS.
  2. How did moving the bundled libraries into $out/lib/libs64 cause them to be found by the patched visplayer binary?
  3. How the heck did you know to move the rest of the files into libexec, and how on earth did you know to invoke wrapProgram in just that way??

As you can tell, question 3 is a critical one for me :slight_smile:

(FYI, the controls are a bit weird: you are supposed to click and hold the left mouse button on an object to pop up a little context menu for interacting with it. Not sure if that’s what you meant about the controls not working. However, audio also did not work for me. I get a message during startup, AL lib: (WW) alc_initconfig: Failed to initialize backend "pulse". Any ideas?)

Thanks again for your help! I’m really glad your approach worked, but I’m equally worried that I have no idea how I would have come up with that approach myself.

roni

Oh yeah it does work if I just click somewhere, neat!

  1. I learned about that function just from seeing it being used in nixpkgs IIRC. I guess there’s also a bit of docs here though
  2. I didn’t actually know that, I just noticed that it found the libraries when they were present in $out/lib. Looking up the code that implements this, it actually even searches for libraries in all of $out. So ideally the line mv libs64 $out/lib should be removed, because $out/lib is usually reserved for libraries that the package itself provides.
  3. libexec is just a conventional directory for “stuff the program needs to run”. It would work with any other directory name too. And wrapProgram is a very useful function that’s used all throughout nixpkgs, there’s some docs here, but the best docs are in the source. I’ve just seen and used it so many times :). Here specifically I used the --run argument to make it change the directory to the libexec dir beforehand, because I saw that the program expects to run where all its files are.

I guess this all boils down to experience and knowing where to look in the source :slight_smile:

I tried investigating the audio problem for a bit but I wasn’t able to figure it out. I suspect the version of libopenal it uses might just not support pulseaudio, so maybe try using standard alsa instead (turning off pulseaudio). I also tried making it use an updated libopenal version from nixpkgs (by removing libopenal.so from $out and putting pkgs.openal in buildInputs), but then it fails to start, seems that it’s not forward compatible for that.

Thanks for your insights.

It seems I will need to do a bit more reading of source code and the docs, and perhaps solve my own problems and write down how I did so. I have grand designs of blogging about some of these things in order to help the newbie community, but we’ll see if that ever happens…

Thanks again!

roni

1 Like