Sunday, March 20, 2016

Native Steam Integration in Haxe, FRESteamWorks-Style

In a previous post, I mentioned that we used FRESteamWorks for Steam integration in our Adobe AIR targets of Spacejacked. FRESteamWorks is an AIR native extension (ANE), so it doesn't work for C++ targets. Possible solutions include steamwrap, but I wanted to keep the same Steam integration code between the AIR and native builds. My solution was to write a Haxe port of the ActionScript interface to the C++ code used in FRESteamWorks.

TL;DR: This post is mainly about how I integrated the C++ code of FRESteamWorks with Haxe. Interesting bits include getting Haxe and C++ to interact without NDLLs, and working with Haxe ByteArrays in C++.

Directory structure

The general layout of the Spacejacked project looks like this:
I placed Valve's official SteamWorks C++ headers in thirdparty/steamsdk/include/, and its shared libraries in thirdparty/steamsdk/lib/. I only needed the CSteam C++ code from FRESteamWorks (the code that wraps the Steam API), which I placed under thirdparty/ventero/src/CSteam/. I wrote the C++ side of the Haxe-C++ integration in thirdparty/ventero/src/HxSteamWorks/.

The Haxe interface was put under src/linux_steam/com/amanitadesign/steam/. The most interesting part of this Haxe interface is in FRESteamWorks.hx; the rest are just straightforward translations from ActionScript to Haxe.

To package Steam's shared libraries together with the application, I added the following lines to application.xml (I'm using OpenFL):

Haxe side of the interface

FRESteamWorks.hx provides the Haxe glue to FRESteamWorks. I added the following metadata to set C++ compilation flags via hxcpp:
If I'm not mistaken, the paths above (e.g. line 20) are relative to where Build.xml is located in the project output.

I kept the original FRESteamWorks' use of Flash's event dispatching system to provide status information about Steam API calls. I wanted to avoid keeping track of Haxe objects on the C++ side, however. Since there'll only be one instance of FRESteamWorks, I keep track of that instance in the Haxe code, and provide a static function for calling via C++:

Most of the methods in this class are simply calls to functions defined in HxSteamWorks.cpp via untyped __cpp__(...). For example:
More complicated ones look like this:
Super hacky, I know =P

Later on, I realized that accessing Haxe objects in C++ wasn't as hard as I had imagined.
More on that below.

C++ side of the interface

HxSteamWorks.cpp is the C++ side of the glue. It's basically adapted from the original FRESteamWorks.cpp.

OpenFL events are fired by calling the Haxe dispatch static method mentioned above:
Haxe package names translate to C++ namespaces. Note that, as mentioned by wighawag, hxcpp appends _obj to all generated class names.

Accessing the data stored in a ByteArray:
I'm not sure if this is the proper way of doing things, but Haxe objects can be passed around in C++ through hxcpp's hx::ObjectPtr<T>. This pointer-like class allows you to access the methods of the original Haxe instance of T.

The hxcpp implementation of ByteArray allows you to get the underlying bytes via byteArray->b->GetBase().

Things to improve

  • Much of the inline C++ code in FRESteamWorks.hx could be done in HxSteamWorks.cpp instead;
  • The untyped __cpp__(...) one-liners should be replaced by calls to functions with @:native declarations to give cleaner code;
  • I should have rearranged the thirdparty/steamsdk directory so that I can just drag and drop the SDK in, instead of splitting up the files the way I did;
  • I haven't adapted this for Windows, but the logic should be similar (only the compilation options should change).

Notes

All the stuff above was done with hxcpp 3.2.102 and Haxe 3.2.1. The gist I used in this post can be found at https://gist.github.com/jonongjs/57666f3900839cf734d4. I only ported the parts of the FRESteamWorks API that I needed, but I hope that what's described here can be of use to anyone who's trying to interface Haxe with C++.

Special thanks to the developers of FRESteamWorks; it's a great project.

Friday, March 11, 2016

Problems using Adobe AIR's adt over ssh

I was trying to create a build machine for Spacejacked over the last few days. The setup is a Mac Mini running Virtualbox and virtual machines for OS X Yosemite, Windows 7, and Arch Linux. We have a script on the Mac Mini that ssh's into each VM and triggers the build there.

We use Adobe AIR to deploy the game on Windows and OS X. We do this by packaging the game with AIR's captive runtime so that users don't need to install AIR on their systems. One requirement of this process is that captive runtime bundles must be created on their target OS via adt. (Hence the virtual machines.)

I noticed that our automated build process would get stuck when running adt via ssh on the OS X VM. It turns out that the Java processes that adt spawns were getting stuck, as mentioned in a question on StackOverflow. The solution to the problem was simple, just as in the accepted answer on StackOverflow: I simply configured the OS X VM to automatically log in with Terminal open, et voilĂ , everything was resolved.

Sunday, February 28, 2016

Spacejacked is Done!

Woot! My teammates and I at Rotten Mage have finally launched our action shooter and tower defense hybrid game called Spacejacked! It's currently available on Steam for Windows, OS X and Linux.

In Spacejacked, you play as space engineer Dave Paprovsky, who has to defend his ship's multiple rooms all at the same time against an alien attack. Expect fast-paced action with bits of tower defense thrown into the mix.

Some technical stuff


We built Spacejacked with Haxe, using the HaxeFlixel 2D game engine, deploying it to Windows and OS X via compilation to Flash then wrapping with Adobe AIR. (Actually, we had an issue with the OS X build on launch day, and had to deploy a native build instead.) The Linux build was a native build using hxcpp.

Steam integration was done through the FRESteamWorks Steam API wrapper for AIR. I hacked out a Haxe wrapper around the C++ CSteam class of FRESteamWorks for the native build. Steam integration was surprisingly easy to do with the help of FRESteamWorks. The people who worked on it have my gratitude.

We did a lot of tweening in Spacejacked, most of the time using either TweenX or Flixel's own FlxTween. I love TweenX's chainable syntax and feature set, which made tweening extremely simple. Something we should have done, but didn't, would be to make all the tweens run at the same rate (FlxTween uses a constant time-step per frame, while TweenX uses real-world time). In fact, we probably should have used only one tweening library, but oh well.

Problems

We started using Haxe in the beginning of 2014. Flixel was at 3.2.2, and its dependencies were OpenFL 1.4.0 and Lime 0.9.7. These library versions are sorely outdated today, but because upgrading these libraries sometimes broke our builds, we decided to stick to the versions above.

Using outdated libraries has caused us some problems, though. For instance, I had to recompile the SDL dependency of Lime to include udev support just to enable in-game gamepad connect/disconnect detection. This, in turn, led to recompiling the Lime NDLLs.

Another problem we encountered was in the native builds, where we were running out of OpenAL audio sources because they weren't getting released fast enough. We wrote some hacks in Lime to fix this.

We also had to write our own gamepad handler code because Flixel back then didn't have gamepad support in Flash. Thankfully, jdpalmer's ActionScript 3 gamepad library took care of that for us.

Final thoughts

Overall, I enjoyed developing Spacejacked in Haxe, though using some of the libraries in their early stages caused a bit of grief. Things are much better today, and I look forward to developing another project in Haxe. I like that Haxe has a powerful macro system, and would love to harness it more.

I learnt a lot through this project, and did a lot of manual work that could have been avoided just by writing a little more Haxe. Here's hoping that I don't commit the same mistakes in the next project!

Lastly, I would like to express my appreciation of the Haxe and HaxeFlixel communities, as well as the developers of all the libraries we rely on. Many thanks!