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.

No comments :

Post a Comment