Bundling With AIR Captive Runtime


Sometimes, the hardest part of making a game is distributing the game after it's been made. If your game is designed to be played as a standalone experience, playing a game in-browser can be a large disappointment. This tutorial is designed to show you how to turn your finished game into a standalone, executable file (either a .exe or .app, depending on your target operating system) that doesn't require installing Adobe AIR — just double-click and play.

This guide is written mainly from a Windows perspective, but the adt tools are the same on OS X.



Setting Everything Up

Before you start powering out some command-line hacker crap, you'll need to be sure you have your AIR binaries added to your system's path.

To make sure the AIR tools are properly added to your path, open a command line window and attempt to call the AIR Developer Tool, adt. You should get something like this:

$ adt
Adobe (R) AIR (R) Developer Tool (ADT)
Copyright (c) 2008-2014 Adobe Systems Incorporated. All rights reserved.

No arguments were found
  adt -checkstore SIGNING_OPTIONS
  adt -certificate -cn <name> ( -ou <org-unit> )? ( -o <org-name> )? ( -c <country> )? ( -validityPeriod <years> )? ( 1024-RSA | 2048-RSA ) <pfx-file> <password>
  adt -help
  adt -migrate SIGNING_OPTIONS ( <air-file-in> | <airn-file-in> ) <output-file>
  adt -package SIGNING_OPTIONS ( -target air )? <output-package> ( <app-desc> FILE_OPTIONS | <input-package> )
  adt -package SIGNING_OPTIONS -target airn <output-package> ( <app-desc> FILE-AND-PATH-OPTIONS | <input-package> )
  adt -package -target ( apk | apk-debug | apk-emulator | apk-captive-runtime ) ( CONNECT_OPTIONS? | LISTEN_OPTIONS? ) ( -airDownloadURL <url> )? ( ARCH_OPTIONS )? SIGNING_OPTIONS <output-package> ( <app-desc> PLATFORM-SDK-OPTION? FILE-AND-PATH-OPTIONS | <input-package> PLATFORM-SDK-OPTION? )
  adt -package -target ( ipa-test | ipa-debug | ipa-app-store | ipa-ad-hoc | ipa-test-interpreter | ipa-debug-interpreter | ipa-test-interpreter-simulator | ipa-debug-interpreter-simulator ) ( CONNECT_OPTIONS? | LISTEN_OPTIONS? ) ( -sampler )? ANE_LINK_OPTIONS? AOT_MODE_OPTIONS? SIGNING_OPTIONS <output-package> ( <app-desc> PLATFORM-SDK-OPTION? FILE-AND-PATH-OPTIONS | <input-package> PLATFORM-SDK-OPTION? )
  adt -package SIGNING_OPTIONS? -target native SIGNING_OPTIONS? <output-package> ( <app-desc> FILE-AND-PATH-OPTIONS | <input-package> )
  adt -package SIGNING_OPTIONS? -migrate SIGNING_OPTIONS -target native SIGNING_OPTIONS? <output-package> <app-desc> FILE_OPTIONS PATH-OPTION
  adt -package SIGNING_OPTIONS? -target bundle SIGNING_OPTIONS? <output-package> ( <app-desc> FILE-AND-PATH-OPTIONS | <input-package> )
  adt -package SIGNING_OPTIONS? -target ane <output-package> <ext-desc> ANE_OPTIONS
  adt -prepare <airi-file> <app-desc> FILE_AND_PATH_OPTIONS
  adt -sign SIGNING_OPTIONS ( -target ( air | airn | ane ) )? ( <airi-file> | <unsigned-ane-file> ) <output-file>
  adt -installRuntime   PLATFORM-OPTION PLATFORM-SDK-OPTION? DEVICE-OPTION? ( -package <apk-file> )?
  adt -installApp       PLATFORM-OPTION PLATFORM-SDK-OPTION? DEVICE-OPTION? -package <apk-file | ipa-file>
  adt -uninstallApp     PLATFORM-OPTION PLATFORM-SDK-OPTION? DEVICE-OPTION? -appid <app-id>
  adt -launchApp        { PLATFORM-OPTION PLATFORM-SDK-OPTION? DEVICE-OPTION? ( -debuggerPort port )? -appid <app-id> }
  adt -appVersion       PLATFORM-OPTION PLATFORM-SDK-OPTION? DEVICE-OPTION? -appid <app-id>
  adt -version

SIGNING_OPTIONS      : -storetype <type> ( -keystore <store> )? ( -storepass <pass> )? ( -alias <aliasName> )? ( -keypass <pass> )? ( -providerName <name> )? ( -tsa <url> )? ( -provisioning-profile <profile> )?
FILE_OPTIONS         : <fileOrDir>* ( ( -C <dir> <fileOrDir>+ ) | ( -e <file> <path> ) )*
ARCH_OPTIONS              : -arch (armv7 | x86)
CONNECT_OPTIONS      : -connect <host>
LISTEN_OPTIONS       : -listen <port>
ANE_LINK_OPTIONS     : -hideAneLibSymbols ( yes | no )
ANE_OPTIONS          : -swc <swc> ( -platform <name> (-platformoptions <file>)? <fileOrDir>* ( -C <dir> <fileOrDir>+ )* )*
PATH-OPTION          : -extdir <dir>
PLATFORM-OPTION      : -platform (android | ios)
PLATFORM-SDK-OPTION  : -platformsdk <platform-sdk-home-dir>
DEVICE-OPTION        : -device ( deviceID | ios-simulator )
AOT_MODE_OPTIONS     : -useLegacyAOT ( yes | no )

Those are the tools you'll be working with, so get ready. It's time to cook.

Basic Bundling

To create a bundle with the AIR Captive Runtime (remember, that's the bit that allows it to run without requiring AIR to be installed), you'll be using the bundle target. That means, you'll want to use the item from the list that adt graciously provided you with the -target bundle argument. In case you can't find it, here it is:

adt -package SIGNING_OPTIONS? -target bundle SIGNING_OPTIONS? <output-package> ( <app-desc> FILE-AND-PATH-OPTIONS | <input-package> )

Got that? Let's break it down.

Bundling In Action

Now that you know the tools, let's look at a sample game, Super Sample. You would probably have a bundle call that looks very similar to this:

$ adt -package -tsa none -storetype pkcs12 -keystore "cert\supersample.p12" -storepass sspassword123 -target bundle dist\windows application.xml -e "bin/supersample.swf" supersample.swf -C "icons/desktop/icons" . -extdir lib

Let's dissect this call.

$ adt -package

Call the AIR Developer Tool, instructing it to package an application for distribution.

-tsa none -storetype pkcs12 -keystore "cert\supersample.p12" -storepass sspassword123

Here are the signing options.

-tsa none -storetype pkcs12 -keystore "cert\supersample.p12" -storepass sspassword123

This section instructs the tool to not check the certificate timestamp. This can be useful if your computer isn't connected to the internet or if your certificate file is out of date (shame on you).

-tsa none -storetype pkcs12 -keystore "cert\supersample.p12" -storepass sspassword123

This section tells the tool what certificate to use for signing. The conversation might go something like this:

The certificate is a PKCS 12 certificate. The certificate is found at cert\supersample.p12. The password for the certificate is "sspassword123."

-target bundle dist\windows application.xml

This tells the tool to create an executable bundle with the AIR Captive Runtime in the dist\windows directory based on the information in application.xml. The XML file will describe the name of the application as well as any other files needed by the application. Since the application's name is stored in application.xml, it will use this to name the resulting executable (in this case, it'll probably be supersample.exe).

-e "bin/supersample.swf" supersample.swf -C "icons/desktop/icons" . -extdir lib

Here are the file and path options. As I mentioned before, these can be tricky, but I'm sure you'll get the hang of them. The main thing you need to know are the two flags, -e and -C. -e describes an explicit file, while -C describes a directory change. Here we go.

-e "bin/supersample.swf" supersample.swf -C "icons/desktop/icons" . -extdir lib

The bundle should include the file located at bin/supersample.swf, and this file should be in the root directory and named supersample.swf.

If for some reason you needed to change the location or name of the file, you can do that as well. Supposing you goofed real bad and built your file to output-files/goofed.swf and set your application.xml to look for swfs/main.swf, you could change this to:

-e output-files/goofed.swf swfs/main.swf
-e "bin/supersample.swf" supersample.swf -C "icons/desktop/icons" . -extdir lib

This describes an entire folder (in this case, this folder contains the icons to use for the executable file).

Switch to the directory icons/desktop/icons. Now, put all the icons there into the root of the bundle.

Again, you can change the output, ., to point to the proper location in your bundle.

-e "bin/supersample.swf" supersample.swf -C "icons/desktop/icons" . -extdir lib

This specifies any external libraries you may need in your application. This is often used when working with Native Extensions, (such as FRESteamWorks, which provides your AIR application access to the steamworks libraries).

Look for Native Extensions in the lib directory and bundle those on up as well.

Advanced Bundling Maneuvers

Once you've gotten the hang of bundling your AIR applications, make life easier on yourself with simple tools.

Power Bundling

It can be a pain to keep a command window open, and typing all your stuff in sucks. Make life easier by writing a little batch script like this:


@echo off

set SIGNING_OPTIONS=-storetype pkcs12 -keystore "cert\supersample.p12" -storepass sspassword123
set APP_XML=application.xml
set DIST_PATH=dist
set DIST_NAME=windows
set FILE_OR_DIR=-e "bin/supersample.swf" supersample.swf -C "icons/desktop/icons" .


if not exist "%DIST_PATH%" md "%DIST_PATH%"

set AIR_PACKAGE=adt -package -tsa none %SIGNING_OPTIONS% -target bundle %OUTPUT% %APP_XML% %FILE_OR_DIR% -extdir lib

call where adt
call adt -version
echo $ %AIR_PACKAGE%

This little bad-boy will not only bundle Super Sample by just double-clicking on it, but will also check the location and version of adt, create your output folder (if it doesn't exist) and show you the exact command it is calling. Additionally, editing it is easy since all the variables are broken out.

Automatic Build & Bundle

Once you've created a bundling script like bundle.bat, you can instruct FlashDevelop to bundle that puppy upon successful build of your game — now that's a next-level bundling maneuver!

In FlashDevelop, open your project's properties and cruise over to the Build tab. Add your bundle batch file to the "Post-Build Command Line" area, making sure that "Always execute" is off (this prevents bundle.bat from being run if the build fails).

Now, whenever you build, FlashDevelop will also bundle for you!

If you want to get really fancy and always test your bundled application, hit up that Output tab, select "Run Custom Command..." under "Test Project" and click "Edit...." From there, enter the location of your bundle and save that beast. Now, when you run your application from FlashDevelop, it'll run your standalone executable. Wow!

Originally posted on the FlashPunk Developers forum.