Distribution

Now that you've finished your game, it's time to get it into the hands of folks interested in playing it. In distributing your game, your goal is to make it as easy as possible for as many people as possible to download and play this piece of code you've worked on for such a long time.

Particularly, I'm going to focus on the "binary distribution" workflow, where you provide a zip file with game assets and compiled code that your players can download, extract, and play.

The first problem you will encounter in "just" zipping up your compiled executable and data files is missing shared libraries (.dll's on windows, .dylib's on OSX, .so's on Linux).

Dealing with dependencies requires: (1) figuring out what your code depends on and (2) including those libraries, either through static linking, or by distributing them with your game.

Finding Dependencies

Unfortunately, there is no nice cross-platform tool for doing this part of the process. However, here are recipes for OSX, Linux, and Windows.

OSX: otool -L

On OSX, you can use the otool utility, which is part of the XCode toolchain. The command otool -L executable produces a list of dylibs that the code depends on:

retchow:dist ix$ otool -L client 
client:
	/usr/local/opt/libpng/lib/libpng16.16.dylib (compatibility version 52.0.0, current version 52.0.0)
	/usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.11)
	/usr/local/opt/bullet/lib/libBulletDynamics.2.87.dylib (compatibility version 2.87.0, current version 2.87.0)
	/usr/local/opt/bullet/lib/libBulletCollision.2.87.dylib (compatibility version 2.87.0, current version 2.87.0)
	/usr/local/opt/bullet/lib/libLinearMath.2.87.dylib (compatibility version 2.87.0, current version 2.87.0)
	/usr/local/opt/sdl2/lib/libSDL2-2.0.0.dylib (compatibility version 9.0.0, current version 9.0.0)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.50.4)
	/usr/lib/libiconv.2.dylib (compatibility version 7.0.0, current version 7.0.0)
	/System/Library/Frameworks/CoreAudio.framework/Versions/A/CoreAudio (compatibility version 1.0.0, current version 1.0.0)
	/System/Library/Frameworks/AudioToolbox.framework/Versions/A/AudioToolbox (compatibility version 1.0.0, current version 492.0.0)
	/System/Library/Frameworks/ForceFeedback.framework/Versions/A/ForceFeedback (compatibility version 1.0.0, current version 1.0.2)
	/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
	/System/Library/Frameworks/CoreVideo.framework/Versions/A/CoreVideo (compatibility version 1.2.0, current version 1.5.0)
	/System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa (compatibility version 1.0.0, current version 22.0.0)
	/System/Library/Frameworks/Carbon.framework/Versions/A/Carbon (compatibility version 2.0.0, current version 158.0.0)
	/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit (compatibility version 1.0.0, current version 275.0.0)
	/System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore (compatibility version 1.2.0, current version 1.11.0)
	/System/Library/Frameworks/Metal.framework/Versions/A/Metal (compatibility version 1.0.0, current version 1.0.0)
	/System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL (compatibility version 1.0.0, current version 1.0.0)
	/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 400.9.0)

In this case, you can see that folks on other systems are going to have problems because the code I'm trying to distribute depends on dylib files they are unlikely to have -- specifically, all the things in /usr/local/opt.

Linux: objdump -p | grep NEEDED

On Linux (and probably FreeBSD), you can get a list of the required dynamic libraries by using objdump, part of binutils.

jmccann@linux2:~/bin$ objdump -p fluxbox | grep NEEDED
  NEEDED               libSM.so.6
  NEEDED               libICE.so.6
  NEEDED               libX11.so.6
  NEEDED               libXext.so.6
  NEEDED               libXft.so.2
  NEEDED               libfreetype.so.6
  NEEDED               libfontconfig.so.1
  NEEDED               libXrender.so.1
  NEEDED               libXpm.so.4
  NEEDED               libXrandr.so.2
  NEEDED               libstdc++.so.5
  NEEDED               libm.so.6
  NEEDED               libgcc_s.so.1
  NEEDED               libc.so.6

It can be a bit trickier to figure out which libraries you should expect on a Linux system -- e.g. libc.so.6 might be an old version -- but at least objdump will let you know which libraries you expect.

Windows: depends.exe

On Windows, the best thing I've found for this task is a (shudder) GUI utility called "Dependancy Walker", which you can get from http://www.dependencywalker.com/. One caveat: be aware that using the 32-bit version on a 64-bit executable or visa-versa will produce strange results.

Also, apparently development on depends.exe is long dead and it might be worth looking into https://github.com/lucasg/Dependencies for a updated alternative that can understand new dynamic library features added since Windows 7.

Including Dependencies

Once you've figured out all the dynamic libraries you need to include with your binary distribution, you now need to figure out how to include them.

Also, whichever way you decide to go, remember that you also need to include any dependencies of these libraries. So -- unless you expect the user to have already installed them -- you'll likely want to rebuild them specially with as minimal a feature set as you can manage.

Static Linking

The simplest way to include dependencies is to avoid dynamic libraries altogether and -- instead -- link statically against library code. This will require you to rebuild any libraries you depend on as static libraries (.a files on Linux/OSX, .lib files on Windows -- except those are also used for the stubs associated with dynamic libraries so it's ambiguous). Most open source packages have a --static compile option or similar, and -- if they don't -- you can do a bit of engineering and add one.

There are some downsides to static linking:

Distributing the Dynamic Libraries

If you would prefer to avoid distributing linking statically, and their licences allow it, you you can also simply add the shared libraries to the folder with your game code.

On Windows, this just works. Windows will look for DLL files in the same folder as the executable trying to load them.

On Linux, things are a bit more complicated. You'll need to set the LD_LIBRARY_PATH environment variable to include the current directory (".").

On OSX, things may be complicated an may just work. If running from the shell, the DYLD_LIBARY_PATH environment variable does the same thing as on Linux. If distributing an .app bundle, copying dylib files to the Frameworks folder should "just work" as per Windows.

Special Note: MSVC++ Runtime One DLL that you will likely fail to distribute is the Microsoft Visual C++ Runtime dll -- msvcp*.dll. You will see that it is installed int the windows/system directory and it starts with "MS" so you will assume it's a system DLL that everyone has.

It isn't.

It is, however, part of the Visual C++ redistributable package (make sure you get the right year) which you can either force your users to install, or you can read the licence agreement and realize it's okay to just copy the .dll into the folder with your game.

Package Format

On Windows and Linux, a zip file containing an executable and a data file is just fine. On OSX, it can still certainly work, but people are expecting a ".app" bundle.

Bundles are folders with a special structure and an Info.plist file. A .app bundle looks like this:


rktcr.app/
    Contents/ #everything goes in Contents
        Info.plist #describes where the executable and icon are
        MacOS/ #executables go here
            rktcr  #main executable
        Resources/ #data files go here
            icon.icns  #icon displayed for the .app
        Frameworks/ #dylib files go here

Some Caveats: This info is somewhat out of date (I haven't built a proper .app bundle for OSX for quite some time) -- particularly, you should look into Code Signing (adds a _CodeSignature directory), because unsigned apps don't get run without extra hoop-jumping from users. Also you may need special startup code to change data_path because you'll probably keep your game files in the Resources/ folder or beside the .app -- neither of which are in the same folder as the executable.

Once you have your .app packaged nicely you'll want to place it into a .dmg, which there is a handy shell one-liner to do that I have misplaced at the moment.

Side Note: Steam, Desura, etc

All of the above assume you are going to be distributing via "here, download a zip file", e.g., via itch.io, humble store, or your own web page. If you decide to enter the larger "game store app" ecosystem, be aware that many of the big platforms provide some guarantees about what library packages will be installed (Steam, Desura) and/or system images you can build against to guarantee compatibility (Steam on Linux -- quite a nice solution).

Aside: having used both Steam and Desura, I can say that Steam's workflow is substantially better. If you are thinking of going commercial with a game, I can recommend Steam.

The platform is professional, works well, and has solid build solutions for Windows, MacOS, and Linux. It is easy to (e.g.) launch private betas. Key generation is straightforward.

Note that with any store you are going to need custom assets for that store's page, and it makes sense to take the time to get them right.

Side Note: Wait

The biggest single lesson I've learned from doing releases of pretty much anything is that you should *wait* after you think everything is ready. Make sure you have your video. Make sure you have all your SKUs ready and beta tested. Make sure you have your screenshots and text and store page themes. Make sure you have your own web page for the game with web store links. Make sure you have your announcement tweet composed.

And then sleep on it, and review everything, and re-test everything before you hit that "publish" button.