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).


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 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.

No comments :

Post a Comment