In a previous post I outlined a plan to (finally) port my browser-based Steam game to Linux. (The original Windows-only release used WebView2.) The port mostly went according to plan and only took a week or two, working in my spare time.
The simplest way to run a browser-based game using Steam on Linux was to use Electron, but there was one problem: Greenworks (a Steam integration library) didn't support friend leaderboards (an important feature of my game). My plan (which optimized for having to learn the fewest new libraries/concepts) was to:
- Port the Steam release to Electron
- Refactor Steam integration code into a flat C interface
- Consume the Steam integration code using one of Node's FFI libraries
- Hope that this new version works acceptably via Proton
Porting to Electron
Porting to Electron was fairly straightforward. I was able to use Electron's tutorials to get a mostly working game pretty quickly. There were a few issues I had to iron out:
- Electron Forge doesn't have an easy way to add binaries (e.g. the Steamworks binary) to the root of the output folder, so I had to hack in an
afterExtractcallback to copy the files and switch from using
electron-forge start(which doesn't run
electron-forge package(which does) for testing
- Electron allows "Ctrl+R" to reload the page by default (fix: remove the menu)
Fortunately, adding crash reporting was trivially easy.
Refactoring Steam integration for Electron and/or Linux
My original Steam integration code used the Win32 API, so I had to rewrite it using only the C++11 standard library. I then exposed that code in a flat, synchronous C interface (which I dubbed ez-steam-api).
To support calling the C API from Electron, I used Koffi (which was great, other than one bug I hit--which has since been fixed). The wrapper is documented here.
Hoping that Electron runs on Proton
In the previous post, I acknowledged that hoping Electron ran acceptably on Proton was a risk. It turns out this risk was well founded.
In addition to wasting a lot of time testing out different Electron and Proton combinations, I ran into one insurmountable bug that was only just annoying enough to make me give up: the mouse cursor is off by about 25 pixels on Ubuntu, when windowed (no idea why or how to investigate it, although I wonder if it's related to the system bar at the top of the screen). In fullscreen, Electron-on-Proton worked great! But having windowed mode be broken was unacceptable for my programming game (for other genres, this bug might be acceptable).
So I ended up having to do a native Linux port anyway.
Native Linux port on Steam
Building an Electron app for Linux is trivial. Getting it to run on Steam was a bit frustrating, however, because the game would fail to launch. But only when run from Steam! Even Steam's test script launched the game fine.
I don't recall how I discovered the solution, but the issue was related to Electron's sandboxing. Given my game only loads its own code (and not anything from the web), the workaround was just to add
--no-sandbox to the list of command line arguments.
And just like that, I had a working Linux port!
Summary and statistics
I've said it before, but I'll say it again: I should have just used Electron from the beginning. It's inefficient, but it's known to work fine on Steam, across platforms (cf. Vampire Survivors). If I had just used Electron originally, porting to Linux would have been trivial. Fortunately, porting to Electron and then Linux wasn't too onerous.
This Linux port also resolved one of my open questions around what to do with SIC-1. And, of course, now people on a free OS can play my game, with friend leaderboards. I think the peak number of concurrent Linux players was roughly 3, which is pretty high for a game that only ever peaked at 10 concurrent players on Windows.
That's all for the Linux port! Next, I'm planning to provide an update on how SIC-1's release has been going. Spoiler: it finally met my own internal bar for success!