Running Kerbal Space Program under NixOS

Recently, I decided to play Kerbal Space Program (KSP, the first one) again. It runs under Linux natively, so one would assume it should be fine under NixOS.

It does mostly work fine under NixOS, with a caveat. When I last played KSP years ago, I had a large number of mods installed. Naturally, when I decided to play it again, one of the first things I did was add a large number of mods. While the game itself ran with no problems, there was something decidedly off-nominal about the mods:

A semi-transparent widget with a bunch of buttons arranged in two columns. There is obvious space for the widget's title, and space next to the buttons for labels, but that space is empty and there is no text.

This MechJeb widget should have text in it.

Solution

Turns out, something involved in the technology stack that KSP mods end up using makes some assumptions about fonts under Linux. Namely, the assumption is that that certain fonts—fonts usually available on any Windows system—will be available under /usr/share/fonts. The solution is to run KSP in an environment where these fonts are available. One way to do so is to add these fonts to steam-run, and use that to run the game:

(steam.override {
  extraPkgs = (pkgs: [
    pkgs.corefonts
    pkgs.vistafonts
  ]); 
}).run;

Overriding steam-run to add Microsoft's corefonts to it. For a good measure, we also add vistafonts.

This package with override can be added to something like home.packages in Home Manager, in order to put the modified steam-run in PATH. KSP can then be launched with steam-run ./KSP.x86_64.

If, for whatever reason, we want to leave the original steam-run untouched, we could also, for example, symlink the modified version under a new name, like steam-run-ksp:

{ steam, runCommandLocal }:
let
  steam-run = (steam.override {
    extraPkgs = (pkgs: [ pkgs.corefonts pkgs.vistafonts ]);
  }).run;
in runCommandLocal "steam-run-ksp" { } ''
  mkdir -p $out/bin
  ln -s ${steam-run}/bin/steam-run $out/bin/steam-run-ksp
''

A package (that can be callPackage'd) that will give us a bin/steam-run-ksp that can be used to run KSP.

If launching the Steam version of KSP from within Steam itself is preferable, we can also add the fonts to the Steam package. Using NixOS-level Steam configuration, we can override its package using the relevant option:

# configuration.nix
{ config, pkgs, ... }: {
  programs.steam = {
    enable = true;
    package = pkgs.steam.override {
      extraPkgs = (pkgs: [ pkgs.corefonts pkgs.vistafonts ]);
    };
  };
}

Using the NixOS Steam module with the Steam package modified with extra fonts.

The why

One of the notable things about Nix is that it does not use the Filesystem Hierarchy Standard (FHS). The FHS is the specification that gives us all the paths commonly found on your garden variety Linux distro, like /usr/share or /usr/lib.

Nix's deviation from the usual way of doing things is evident in, among other things, dynamic linking. A usual Linux system, when starting a dynamically linked executable, will look for the relevant libraries in a place like /usr/lib. Packages under Nixpkgs, on the other hand, will emit executables configured in such a way that, on launch, the libraries used will be from specific paths in the Nix store. Packages will also sometimes need to be configured or patched to address other assumptions about the FHS, like when a package wants to look for image assets under /usr/share .

Note that this is Nix and Nixpkgs in general, not NixOS specifically. NixOS notably does not use the FHS either, as it is built from those non-FHS packages, and so does not need to have all the usual FHS paths at all.

There is a problem with this setup: proprietary software distributed in binary form. While it is generally possible to patch anything we build ourselves to work with Nix's (and NixOS's) way of doing things, patching an opaque binary is more difficult. While such binaries can often be convinced to use a dynamic linker that will give them the right libraries from the Nix store, for more complicated situations there is buildFHSEnv.

buildFHSEnv is a Nixpkgs builder that provides a way to execute arbitrary binaries in what will look to that binary like an ordinary FHS environment. The FHS environment directory tree—itself a Nix store path—is constructed by copying or symlinking from the relevant paths in the Nix store. Then, bubblewrap is used to execute the opaque binary in a namespace, where that environment is used to form the root directory tree (/). While bubblewrap is a sandboxing tool, the way it is used with buildFHSEnv is not for the security-oriented kind of sandboxing; rather it is a convenient way to use namespaces and bind mounts, on the user level.

The Steam package in Nixpkgs runs using buildFHSEnv. The FHS environment used by it includes a bunch of libraries useful for running video games under Linux, which means that games installed via Steam often just work. This FHS environment happens to be handy for running non-Steam games as well, which is why the Nixpgks Steam package exports steam-run. steam-run allows running arbitrary binaries—not just Steam—in the same environment that Steam runs in.

Years ago, Kerbal Space Program apparently used to have a problem with expecting fonts to be in /usr/share/fonts specifically, at least according to an issue on the KSP bug tracker. This issue does not seem to affect KSP itself today, but something similar seems to happen to mods. While a large portion of KSP mods are open source, I am not familar enough with KSP or Unity development to tell if this is a recurring problem commonly caused by mods themselves, or something in a different place in the stack. Nevertheless, things seem to work fine when the usual Windows fonts are available under /usr/share/fonts.

Nixpkgs includes both corefonts, which contains such classics as Arial, as well as vistafonts, which contains newer common fonts, like Calibri. Adding both of these packages to a buildFHSEnv environment makes them available under the expected /usr/share/fonts locations. The Nixpkgs Steam package has an extraPkgs attr which can be used to insert extra packages into its FHS environment. So, adding corefonts and vistafonts there provides a quick and expedient solution to the problem.