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!

Sunday, May 3, 2015

Getting Euterpea's MIDI output to work on OS X

I've been getting back into learning Haskell again. One of the textbooks I'm using is The Haskell School of Music, a Haskell textbook with a focus on computer music. The book uses Euterpea, a computer music library developed in Haskell.

Euterpea has been well-tested on Windows and Linux, but I've been having a hard time getting its play function to work on OS X. One way around the problem is to use the writeMidi function to output a MIDI file and then play it back, but I would rather have the ability to listen to the output immediately.

It turns out that the play function has been generating MIDI events correctly, and all I have to do is to redirect the events to a (software) synthesizer. I used FluidSynth for this, which can be easily installed via Homebrew or MacPorts. Unfortunately, on OS X, running FluidSynth alone was insufficient to get any audio output. I had to enable the IAC Driver via OS X's Audio Midi Setup program, and redirect its output to FluidSynth's virtual port via a patch program (I used MIDI Patchbay for this).

And that's all that's needed. Off to make some noise!

Friday, December 19, 2014

Using OpenFL Templates to Modify Functionality in Haxe Libraries

I recently had to modify some functionality in the FlxAnimationController class in HaxeFlixel 3.3.6 for our game Spacejacked.

Since the change was rather minor, and I didn't want to clone the FlxAnimationController and FlxSprite (which uses FlxAnimationController) classes, I decided to use the templating capabilities of the OpenFL project XML format.

FlxAnimationController is located under the flixel/animation directory of the Flixel project source folder. To replace the functionality, I copied FlxAnimationController.hx to templates/haxe/flixel/animation/FlxAnimationController.hx in my own project folder and made my own changes there.

To get OpenFL to use this file instead of Flixel's, I simply added this template tag to my application.xml:
<template path="templates" />


Wednesday, October 15, 2014

Using Adobe AIR Tools to Turn an SWF into a Native Executable

Here's a post to remind myself how to do this.

We have a strange bug in the Windows build of Spacejacked: the game hangs for long periods (usually more than a minute) at random intervals. I suspect it has to do with the audio code in the game, but haven't figured out a way to track it down.

As a temporary workaround, we're packaging the Flash version of the game into a native Windows executable via Adobe AIR.

Steps:

  1. Adobe requires a code-signing certificate for digitally signing your executables. Either follow the instructions on http://help.adobe.com/en_US/air/build/WS5b3ccc516d4fbf351e63e3d118666ade46-7ff0.html and get one from a certification authority, or create your own (if you're just making a test build) like so:
    adt -certificate -cn ADigitalID 1024-RSA SigningCert.p12 39#wnetx3tl
    This creates a .p12 cert named "SigningCert.p12" with common name "ADigitalID" with password "39#wnetx3tl". The full command specification can be found at http://help.adobe.com/en_US/air/build/WS901d38e593cd1bac1e63e3d128fc240122-7ffc.html.
  2. Create an application descriptor XML file, as mentioned in http://help.adobe.com/en_US/air/build/WS5b3ccc516d4fbf351e63e3d118666ade46-7ff1.html.
    For example, here's our spacejacked.xml:
    <?xml version="1.0" encoding="utf-8" ?>
    <application xmlns="http://ns.adobe.com/air/application/3.2">
        <id>com.rottenmage.spacejacked</id>
        <versionNumber>0.0.1</versionNumber>
        <filename>SPACEJACKED</filename>
        <name>Spacejacked</name>
         <description>
            <text xml:lang="en">SPACEJACKED</text>
        </description>
        <copyright>Copyright (c) 2014 Rotten Mage Pte Ltd</copyright>
        <initialWindow>
            <title>SPACEJACKED</title>
            <content>
                SPACEJACKED.swf
            </content>
            <visible>true</visible>
            <resizable>false</resizable>
            <maximizable>false</maximizable>
            <renderMode>direct</renderMode>
        </initialWindow>
        <icon>
            <image16x16>icons/smallIcon.png</image16x16>
            <image32x32>icons/mediumIcon.png</image32x32>
            <image48x48>icons/bigIcon.png</image48x48>
            <image128x128>icons/biggerIcon.png</image128x128>
        </icon>
        <supportedProfiles>desktop extendedDesktop</supportedProfiles>
    </application>
    
    where SPACEJACKED.swf is the name of the SWF file we are wrapping. Note that the namespace specified in xmlns in the <application> tag should be set to an appropriate version that corresponds to the SWF version you are targeting; details at http://help.adobe.com/en_US/air/extensions/WSf268776665d7970d-2e74ffb4130044f3619-8000.html.
  3. Create a directory structure for packaging (details at http://help.adobe.com/en_US/air/build/WS901d38e593cd1bac1e63e3d128cdca935b-8000.html). For a quick-and-dirty solution, we use:
    /SPACEJACKED
        SPACEJACKED.swf
        spacejacked.xml 
    with the certificate just outside of this structure.
  4. Finally, from within the root of the packaging directory, run in the command prompt (for a Windows build):
    adt -package -storetype pkcs12 -keystore ..\SigningCert.p12 -target bundle ..\SPACEJACKED_game spacejacked.xml SPACEJACKED.swf
    which will prompt for the certificate's password (created in step 1). This creates the executable in a directory called SPACEJACKED_game one level above the packaging directory.
That should be it. The instructions are similar for other platforms. Note that (as of writing) native bundles/builds can only be created on the same platform, i.e. Windows bundles can only be created on Windows, Mac .app bundles on Mac OS X, Linux .deb/.rpm on the appropriate Linux distros.

Friday, June 13, 2014

Weird Fullscreen Behaviour in OpenFL + HaxeFlixel

In OpenFL projects, the initial fullscreen status of the application can be specified in the project's application.xml file via the fullscreen boolean attribute of the window tag, as given in the specification.

However, I found that this attribute interacts in strange ways with HaxeFlixel's own fullscreen setting when building for desktop profiles (using OpenFL 1.4.0 and HaxeFlixel 3.2.2). Setting fullscreen to true results in the application having a window resolution that is not the one specified. In addition, the application drops out of fullscreen mode if FlxG.fullscreen is false.

On a side note, the fullscreen attribute behaves well in the mobile profile.

My solution to that is to set fullscreen to false in desktop builds, and rely on the functionality provided through HaxeFlixel's FlxG.fullscreen and the StartFullscreen parameter of FlxGame's constructor.