From GameMaker To Ubuntu 14, 16, 18, and Beyond

Or: How to save GameMaker: Studio 1.4 games from YoYo Games Ltd neglect

I recently published a game built in GameMaker: Studio 1.4 (GM:S1) for Linux (and Windows). It was a process. No, it was 100 times that. Not only that, but I ran into others with similar troubles who were getting a general ¯\_(ツ)_/¯ response. I aim to fix that here.

Hopefully, this may also help to run on more Linux flavors than just Ubuntu.

If you are a gamer hoping to fix a game you found (such as Legend of Xenia, Spelunky SD or Spelunky Classic HD), jump to the “Include all needed libs” section and follow the hint. If you are getting a message regarding libcrypto.so.1.0.0 missing or libcurl.so.4 errors, you are definitely in the right place.

First, I want to point you to the sites that saved me. They aren’t required reading here, but they should help if you get stuck or need to diverge from what’s here.

YoYo’s own post is probably required reading even though it, um, uh, well:

32-bit Linux limitation for no good reason

GM:S1 targets only 32-bit Linux. This terribly odd given that their Linux dev setup instructions are for 64-bit Linux. Furthermore, my GM:S1 install includes a make file for 64-bit Linux but no 64-bit library (libyoyo_yyc-x64.a) to go with it. (I wonder if any other 1.x releases had it.)

This is not terribly surprising given YoYo’s rotten bait-and-switch business practice. They sold v1 at a deep discount on Humble Bundle a few months before release of v2 (with no clue to this given on Humble Bundle). Only then to desupport v1 only a year after that. v2 didn’t contain much new other than, maybe, 64-bit and other new target platforms. That’s a horrible reason to hold back on 64-bit support, in my opinion, especially given the high cost of the upgrade.

I wonder if the OSX lib is 64-bit? It would be insane if that was only 32-bit since that expired in, what?, 2011! I wonder if it is linkable in Linux with clang? Doubtful. Enough wondering.

Okay, done with the hating, onto the fixing.

Build on Ubuntu 14.04, 32-bit

Ubuntu 14.04 was the supported target version of GM:S 1.4.

Okay, so you’ve got only one Linux box and it’s running something else. Cool. But… I’m guessing that Ubuntu 14 runs pretty okay for the occasional GM:S build on an 8GB USB stick. I haven’t tried it yet… but I did boot Ubuntu 16 from a DVD once and that wasn’t too terribly bad… once.

[[I’m fortunate to have an eSATA port on my old laptop, for which I bought a cable (12v/5v eSATA to 22-pin SATA) and a stack of salvaged 2.5″ HDs — all for maybe $20. My Ubuntu 14 install takes only 4.9GB on one of those HDs. That’s the default install, so, less if you whack the office apps etc.]]

You should be able to build fine on 64-bit Ubuntu 14.04, but I was having trouble installing 32-bit multi-architecture support on this so I just installed 32-bit Ubuntu 14.04 instead. Keep it simple.

I succeeded at testing and building on 64-bit Mint 19.2 as well, but this build wasn’t terribly compatible with older Linux versions. Thus, test on what suits you but… Do your final build on the oldest version of Linux you want to target.

Dev env setup

You can install that longer list of packages that YoYo recommends, but this all that I found to be necessary for YYC builds on 32-bit Ubuntu 14.04. (P-code builds can probably leave out clang and libssl-dev, but I haven’t verified that yet. They don’t hurt p-code builds on Ubuntu 14.)

sudo apt update
sudo apt install openssh-server
sudo apt install clang
sudo apt install libopenal1
sudo apt install libssl-dev

Build to ZIP

Always build to ZIP. Then, uh, ignore the ZIP. We’re gonna work with the files it created before zipping them up.

BTW, you can totally shorten those default build directories to something more like:

  • Linux Install Dir: gamemaker
  • Application Install Dir: gamemaker

Include all needed libs

Hint: If you trust my packaging, you can skip the rest of this by just grabbing my River Raid Squadron game and copying its lib folder and launch script (the script will need to be edited with the name of your game). You may also need to read my help for running on 64-bit linux.

This is where it gets interesting and where those links up top saved the day.

If you ldd a YYC build of your game, you will get a long list of dependencies. This is everything I needed to copy to meet those dependencies, aside from built-in and other very standard libraries:

mkdir lib
cd lib
cp -av /lib/i386-linux-gnu/libssl.so.1.0.0* .
cp -av /lib/i386-linux-gnu/libcrypto.so.1.0.0* .
cp -av /usr/lib/i386-linux-gnu/libXrandr.so.2* .
cp -av /usr/lib/i386-linux-gnu/libXrender.so.1* .
cp -av /usr/lib/i386-linux-gnu/libopenal.so.1* .
cp -av /usr/lib/i386-linux-gnu/libcurl.so.4* .
cp -av /usr/lib/i386-linux-gnu/libidn.so.11* .
cp -av /usr/lib/i386-linux-gnu/librtmp.so.0* .
cp -av /usr/lib/i386-linux-gnu/libgnutls.so.26* .
cp -av /lib/i386-linux-gnu/libgcrypt.so.11* .
cp -av /lib/i386-linux-gnu/libgpg-error.so.0* .
cp -av /usr/lib/i386-linux-gnu/libpulse.so.0* .
cp -av /usr/lib/i386-linux-gnu/pulseaudio/libpulsecommon-11.1.so* .
cp -av /lib/i386-linux-gnu/libdbus-1.so.3* .
#cp -av /lib/i386-linux-gnu/libgcrypt.so.20* .
#cp -av /lib/i386-linux-gnu/libgpg-error.so.0* .
#cp -av /lib/i386-linux-gnu/liblzma.so.5* .
#cp -av /lib/i386-linux-gnu/libsystemd.so.0* .
cp -av /lib/i386-linux-gnu/libwrap.so.0* .
cp -av /usr/lib/i386-linux-gnu/libasyncns.so.0* .
cp -av /usr/lib/i386-linux-gnu/libFLAC.so.8* .
#cp -av /usr/lib/i386-linux-gnu/liblz4.so.1* .
cp -av /usr/lib/i386-linux-gnu/libogg.so.0* .
cp -av /usr/lib/i386-linux-gnu/libsndfile.so.1* .
cp -av /usr/lib/i386-linux-gnu/libvorbisenc.so.2* .
cp -av /usr/lib/i386-linux-gnu/libvorbis.so.0* .
cp -av /lib/i386-linux-gnu/libjson-c.so.2* .
cp -av /usr/lib/i386-linux-gnu/libgssapi_krb5.so.2* .
cp -av /usr/lib/i386-linux-gnu/liblber-2.4.so.2* .
cp -av /usr/lib/i386-linux-gnu/libldap_r-2.4.so.2* .
cp -av /usr/lib/i386-linux-gnu/libkrb5.so.3* .
cp -av /usr/lib/i386-linux-gnu/libk5crypto.so.3* .
cp -av /lib/i386-linux-gnu/libcom_err.so.2* .
cp -av /usr/lib/i386-linux-gnu/libkrb5support.so.0* .
#cp -av /lib/i386-linux-gnu/libresolv.so.2* .
#cp -av /lib/i386-linux-gnu/libresolv-2.* .
cp -av /usr/lib/i386-linux-gnu/libsasl2.so.2* .
cp -av /usr/lib/i386-linux-gnu/libgssapi.so.3* .
cp -av /usr/lib/i386-linux-gnu/libtasn1.so.6* .
cp -av /usr/lib/i386-linux-gnu/libp11-kit.so.0* .
#cp -av /lib/i386-linux-gnu/libgpg-error.so.0* .
cp -av /lib/i386-linux-gnu/libkeyutils.so.1* .
cp -av /usr/lib/i386-linux-gnu/libheimntlm.so.0* .
cp -av /usr/lib/i386-linux-gnu/libkrb5.so.26* .
cp -av /usr/lib/i386-linux-gnu/libasn1.so.8* .
cp -av /usr/lib/i386-linux-gnu/libhcrypto.so.4* .
cp -av /usr/lib/i386-linux-gnu/libroken.so.18* .
cp -av /usr/lib/i386-linux-gnu/libffi.so.6* .   # Added for Ubuntu 20
cp -av /usr/lib/i386-linux-gnu/libwind.so.0* .
cp -av /usr/lib/i386-linux-gnu/libheimbase.so.1* .
cp -av /usr/lib/i386-linux-gnu/libhx509.so.5* .
cp -av /usr/lib/i386-linux-gnu/libsqlite3.so.0* .
#cp -av /lib/i386-linux-gnu/libcrypt.so.1* .
#cp -av /lib/i386-linux-gnu/libcrypt-2.* .
cp -av /lib/i386-linux-gnu/libz.so.1* .
cp -av /usr/lib/i386-linux-gnu/libGLU.so.1* .

Side note: libz.so.1 wasn’t really needed but, because it is the first that pops up if not having 32-bit Mesa packages on your 64-bit OS yet, it can be a red herring. It’s only 100kB so it adds less than 1% to the total size.

Also note the ones I commented out. They aren’t needed but I didn’t remove them from the list for… I don’t know why. Maybe I suspect they’ll come back and bite me later.

Anyway, this amounts to 86 files/linked files. Just under 14MB. Not great; but not too bad for a game engine either.

Note that this is for YYC builds but also works for p-code builds. If you really want to know, the p-code list of libs is probably quite a bit shorter, but I haven’t gotten around to narrowing that down yet. I’ll add more info here if and when I do.

CAUTION: Be aware that if you build (or are trying to run a game that was built) on another version of Ubuntu, these libs can shift slightly to other versions and those games may or may not work with this set. Maybe someday I’ll post a complete resource for all possible permutations (if there is any demand for it) but not today.

You will need a launch script

In order to set the env to find those libs, you need a launch script. This is the script I use for testing (the filename is test):

#!/bin/bash
# Adapted from:
#   https://itch.io/docs/itch/integrating/platforms/linux.html

# Move to exe's directory (required for audio libs)
cd "`dirname "$0"`"

# Set the libpath and execute the binary
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./lib/
exec ./River_Raid_Squadron $@

Drop this in the same folder as your game (~/gamemaker/yyc/River_Raid_Squadron) and change the filename on the last line to match your game. (Make sure the script has execute privs.)

Of course, running this on your Ubuntu 14 build box doesn’t really test much. You can copy it to another box and test, or you can move on to the next step before testing.

Prep for packaging

Okay, this gets a little weird because I want to shift files and folders around so that the user sees only one executable point of entry to the game. Solve this part how you like, this is how I chose to do it.

In short, I wrote a script to copy and rename the script and executable, and copy in the assets and lib folder, to look like this:

Some Game/
  game
  x86/
    game.x86
    assets/
    lib/

I also edit the launch script for the new location and names.

Here’s my script:

#!/bin/bash

gmsname="River_Raid_Squadron"
gamename="River Raid Squadron"
shortname="rrs"
arch="x86"
exename="$shortname.$arch"
version_in="Game/gml_Script_Title.gml.cpp"

yellow="\e[33m"
ltyellow="\e[1;33m"
colorend="\e[0m"

if test -d "$gamename"; then
  echo Clearing folder: $gamename
  rm -fr "$gamename"/*
else
  echo Making folder: $gamename
  mkdir "$gamename"
fi
mkdir "$gamename/$arch"

echo Copy with renaming:
echo -ne $yellow
cp -av "yyc/$gmsname/$gmsname" "$gamename/$arch/$exename"
cp -av "yyc/$gmsname/test"     "$gamename/$shortname"
echo -ne $colorend
echo Copying lib and assets folders...
cp -a  "yyc/$gmsname/lib"      "$gamename/$arch"
cp -a  "yyc/$gmsname/assets"   "$gamename/$arch"

echo
echo Parsing out the version into VERSION.txt...
egrep -oh "[0-9]+\.[0-9]+\.[0-9]+" "yyc/$gmsname/$version_in" > $gamename/$arch/VERSION.txt"
echo -ne $ltyellow
cat "$gamename/$arch/VERSION.txt"
echo -ne $colorend

echo
echo Editing launch script from:
echo -ne $yellow
grep "$gmsname\|$exename" "$gamename/$shortname"
echo -ne $colorend

sed -i "s/\bexec\b/cd $arch; exec/" "$gamename/$shortname"
sed -i "s/$gmsname/$exename/" "$gamename/$shortname"

echo Edited launch script to:
echo -ne $ltyellow
grep "$gmsname\|$exename" "$gamename/$shortname"
echo -ne $colorend

# If using butler with itch.io, validate what we just did.
#~/itchio/butler/butler validate "$gamename"

echo
read -p "Press [Enter] to continue..."

Here’s the output launch script with the last line changed:

#!/bin/bash
# Adapted from:
#   https://itch.io/docs/itch/integrating/platforms/linux.html

# Move to exe's directory (required for audio libs)
cd "`dirname "$0"`"

# Set the libpath and execute the binary
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./lib/
cd x86; exec ./rrs.x86 $@

Why not just start with that launch script? You can. This is just the workflow that works best for me at the moment. (One could, of course, also combine the two cd commands. Up to you.)

Packaging

From here, packaging is up to you. A zip of the Some Game folder from above would be enough. Or, you could follow Ethan Lee’s advice (that second link up top) and use MojoSetup for packaging.

Personally, I have a script that sends that folder to itch.io via butler, and that’s all I need for distribution (no zip needed). I have another script that zips it up and tags it with a version number simply for archiving. I could share those scripts but they feel beyond the point of this post.

32-bit on 64-bit

This is info you may need to pass on to your users (from my game’s help post here):


If running 64-bit Linux, you need to install multi-architecture (multiarch) support for i386, if it didn’t come built into your flavor. It’s hard (impossible) to find authoritative sites on how to do this… so, basically (and at your own risk, of course), for Ubuntu 14+ and GM:S games:

sudo dpkg --add-architecture i386
sudo apt update
sudo apt install libc6-i386
sudo apt install libstdc++6:i386

If wanting more help, this site came the closest: https://unix.stackexchange.com/questions/12956/how-do-i-run-32-bit-programs-on-a…

You will also need to install i386 support for Mesa/OpenGL/X11 (whatever you want to call it) if your graphics drivers didn’t install it/these already. Again, I found no authoritative source for this. (Hint: mesa-utils:i386 is all you may need — although, be forewarned that installing this has been known to uninstall your desktop environment, such as Cinnamon. Watch for such funny business and compensate as needed. UDPATE: 32-bit mesa-utils is now dead in Ubuntu 20. Hint: libgl1:i386 is all you may need.)


C is a basic requirement, and I consider the standard packages from C++, Mesa, OpenGL, and X11 to be common enough to expect most users to already have them. And, if not, they should if they expect most games to run. (There are a few less-standard libs from this group that I do provide: libGLU, libXrandr, and libXrender.) Side note: common collision points if missing the standard packages are libstdc++.so.6 and libXxf86vm.so.1.

Miscellaneous GameMaker issues

  1. For me, at least, code changes don’t make it into YYC builds. P-code builds run fine but YYC builds do not. Both Linux and Windows. I found no help on this so, dunno, maybe I broke something unique in GM:S — maybe when I renamed my project, because GM:S names the build according to folder name instead of the name in project settings! Or, maybe, few do YYC builds to ZIP (I doubt ZIP has anything to do with it, however). Eventually, I found the YYC build cache and saw that YYC builds never update files in the cache if they already exist. Thus, I have to manually clear it out before final build. Ugh.
  2. My GM:S install had set its temp folder to AppData\Local instead of AppData\Local\Temp, which is stupid. Change it and then go clean out all the gm_* folders and random ????????.??? files from Local (again, at your own risk).

UPDATE June 2021: Linus Torvalds appears to agree that including all the libraries like this, as very large distributions of binaries, is the only solution for Linux desktop (what he says Valve is now doing). He also explains why I didn’t have to include the core libraries (yet). Basically, library developers keep breaking their libraries by “fixing” them; and it’s a continuous fight for him to keep others from breaking the core libraries.

3 thoughts on “From GameMaker To Ubuntu 14, 16, 18, and Beyond

  1. this is an almost complete how-to GMS1.x + AppImage guide haha it works great, thanks. although you also need to include libffi.so.6 to run in 21.04 (latest version as of this comment) but yeah you can skip the launch script if you pack it as an appimage instead!

    Liked by 1 person

    1. Ah, man, libffi is not standard anymore?… or they broke it? Ugh. Thanks for the note. I don’t remember why, exactly, I left it out. Looks like it is part of both clang and mesa-utils:i386. Do you not need mesa-utils:i386 in your setup? Next thing you know, I’ll need to include all of libc6! Anyway, yes, I’ll take another look if I ever get back to finishing River Raid Squadron — which I hope will be soon.

      Like

    2. I finally got back around to publishing an update to RRS. I dug into the libffi thing and found that mesa-utils:i386 appears to be dead as of Ubuntu 20. Digging deeper I found that libgl1 has moved on from libffi6 to libffi7 which, of course, breaks things like a good Linux package always does. Anyway, yes, thanks for the note. I’ve updated RRS and this post accordingly.

      Like

Leave a comment