Blog

I write code, this blog is a dream.

2013.09.05openFL broken, again?

That was my main concern with NME: regression.

The project is huge and it is easy to break something. Often, after an update, something which worked perfectly would fail. NME was renamed openFL, regressions still happens.

Today I cannot work with iOS, my program launch a white screen on my iPad and I don't have the time to investigate yet.

Someone also told me some days ago that my extension example was not working anymore with the latest openFL.

My advice is, when you have a setup which works, keep it somewhere:

$ haxelib list > haxelib.backup

The next time you have a problem after an haxelib update you will be able to go back to a working state looking at this file and reseting openfl versions to what they where.

As far as I am concerned, I am back to june/july (it may not work for you, sorry):

$ haxelib set openfl 1.0.1
$ haxelib set openfl-compatibility 1.0.1
$ haxelib set openfl-native 1.0.1
$ haxelib set openfl-tools 1.0.2

I am thinking about creating a little script to let me switch to stable / update / git. It would allow me to test updates without worrying too much.

2013.06.28MiliWorlds on Android tablets

Here's my little 2 players on one tablet, action and "strategy" game for this month.

I had great fun building it and openFL worked really well for this two weeks project!

Play MiliWorlds with a friend and tell me what you think!

2013.06.28OpenFL AndroidManifest.xml and greater Android SDK version

Say you want to publish your android application but need to restrict it to some devices screen or need for any reason to have access to the AndroidManifest.xml.

First find the location of you openfl native library:

$ haxelib path openfl-native
-L openfl-native/1,0,1/ndll/
/Users/lbedubourg/Library/haxe3/openfl-native/1,0,1/
-D openfl-native

Then get the regular template and put it in your project:

$ mkdir -p templates/android
$ cp /Users/lbedubourg/Library/haxe3/openfl-native/1,0,1/templates/android/template/AndroidManifest.xml templates/android

And finally tell openfl to use this template version in your project.xml file:

<template
  path="templates/android/AndroidManifest.xml"
  rename="AndroidManifest.xml"
  if="android"
/>

Now you can edit your manifest to your taste.

What if you need some feature from a more recent Android SDK? We need to modify the hxcpp build script too:

$ cp /Users/lbedubourg/Library/haxe3/openfl-native/1,0,1/templates/android/template/build.xml templates/android

Edit templates/android/build.xml to change your target (that's the target value):

<property name="target" value="android-17"/>

Here I used the lastest API: 17 but you might want to use the 'android' tool of the Android SDK to install older APIs in the hope of supporting more devices.

You might also want to modify your AndroidManifest.xml file to reflect your change:

<uses-sdk android:minSdkVersion="17"/>

And finally tell openfl to use it in your project.xml:

<template path="templates/android/build.xml" rename="build.xml" if="android" />

I am sure the template node will prove pretty useful to hack things around and not only for android target!

See the useful openfl project.xml format documentation.

2013.06.25OpenFL extension (3)

While looking for a way to show my next game's help as an embedded HTML page I encountered this project: https://github.com/SuatEyrice/NMEWebview

It is a good example of how we can write java code and use it from haxe on android.

Perfect timing to test a little bit of java interaction :)

cd project
mkdir android
mkdir android/testextension

android/testextension/TestExtension.java:

package testextension;

class TestExtension {
    public static String doSomething(String in){
        return in+"\n"+in;
    }
}

The idea is to export this testextension.TestExtension.doSomething(String) : String method so we can use it from haxe.

Since we are defining things in java, we cannot use cpp.Lib.load for this, we have to use openfl.utils.JNI.

This is how I created the binding in TestExtension.hx:

#if android
// Sadly we cannot use JNI here, we have to wait for the main() function to be executed.
// I tried but it failed :)
private static var testextension_dosomething : Dynamic;

private static function init(){
    if (testextension_dosomething != null)
        return;
    testextension_dosomething = openfl.utils.JNI.createStaticMethod(
        "testextension.TestExtension",
        "doSomething",
        "(Ljava/lang/String;)Ljava/lang/String;"
    );
}

public static function doSomething(str:String) : String {
    init();
    return testextension_dosomething(str);
}
#end

openfl.utils.JNI.createStaticMethod() works like cpp.Lib.load. You give the java class name and the static method your are looking for. The third argument is the difficult part, it's the method signature.

Basically () represents the method, what's inside are arguments and what's after is the result type.

(Ljava/lang/String;)Ljava/lang/String; is equal to haxe String -> String signature.

You can read more about JNI method signatures and examples here but this is just the first doc I found on Google. I imagine there's a more straightforward documentation somewhere…

We don't need to compile the java part before usage but we must tell user applications that they have to.

We edit the extension's include.xml file to add this information:

<java path="project/android" if="android" />

And that's it for this first baby step. Calling TestExtension.doSomething() in TestApp will work.

You can see this new addition on github.

Please note that I also created an android/Tweet.cpp file (but not the Tweet function implementation) to avoid having to add "#if ios" in the code.

The next step for java/haxe is certainly to study how we can pass HaxeObject around and call things here and there (the openfl.utils.JNI API is quite limited right now but after all we could write an extension to expose all C++ JNI methods to haxe I required).

2013.06.24OpenFL extension (2)

For the second part of this little personnal study let's create a Tweet feature for our haxe iOS application.

We should learn:

  • how to organize the code inside our extension for each platform
  • how to create the binding between haxe and our own native function
  • how to link an iOS framework (the Twitter framework)

Let's start with a look at another project's organization: NME github project.

The Build.xml is pretty complex but will be useful, looking at the folder's content we can see:

include/: defines headers which will be implemented by common/ and platforms

common/: platform independent c++ code (well more or less independant, we can use #if defined(HX_xxxx))

common/ExternalInterface.cpp: exports functions for our runtime (DEFINE_PRIM)

'platform' directories: implements things for custom platforms (iPhone, mac, windows, etc.)

Looks like a nice way to organize things, let's use it!

Starting with our previous extension, we are going to define a Tweet() function in the existing include/Utils.h file (we should use our own header but I am lazy today).

namespace testextension {

	int SampleMethod(int inputValue);

	bool Tweet(const char* msg);

}

Then we are going to implement it for the iphone platform:

cd project
mkdir iPhone

iPhone/Tweet.mm:

#import <Foundation/Foundation.h>
#import <Twitter/Twitter.h>

namespace testextension {
    bool Tweet(const char* message){
        // Sorry about this Objective-C block,
        // A quick and dirty dump from another project,
        // We should check that Twitter can be used but
        // that's another problem you will have to solve alone :)
        NSString* str = [[NSString alloc] initWithUTF8String:message];
        TWTweetComposeViewController* tweetView = [[TWTweetComposeViewController alloc] init];
        [tweetView setInitialText:str];
        TWTweetComposeViewControllerCompletionHandler completionHandler =
            ^(TWTweetComposeViewControllerResult result) {
                [[[[UIApplication sharedApplication] keyWindow] rootViewController] dismissModalViewControllerAnimated:YES];
            };
        [tweetView setCompletionHandler:completionHandler];
        [[[[UIApplication sharedApplication] keyWindow] rootViewController]  presentModalViewController:tweetView animated:YES];
        return true;
    }
}

We need to expose it to haxe, let's edit common/ExternalInterface.cpp:

static value testextension_tweet(value message){
    // message is a String, just get its value
    const char* cStr = val_get_string(message);
    // call our Tweet function with it
    // and return true or false to haxe
    if (testextension::Tweet(cStr))
        return val_true;
    return val_false;
}
// This will register our function with 1 argument
DEFINE_PRIM(testextension_tweet, 1)

DEFINE_PRIM, val get string, val true and many more comes from hxcpp: hx/CFFI.h

Again looking at ExternalInterface.cpp should help you understand how to register your stuff.

The haxe part in TestExtension.hx:

class TestExtension {

// load the defined primitive
private static var testextension_tweet = Lib.load("testextension", "testextension_tweet", 1);

// wrap its call inside a typed method
public static function tweet(message:String) : Bool {
    return testextension_tweet(message);
}

}

I had to fight a little to find out how to compile and link things but the result is pretty simple.

First the project/Build.xml:

<!-- we add the list of files for ios -->
<files id="iphone">
  <file name="iPhone/Tweet.mm"/>
</files>

<!--
  I had to compile the ndll for mac too to get an ndll file and make the system happy.
  "Library TestExtension version dev does not have a neko dll for your system"
  I just commented things in this file, see github.
-->
<files id="mac">
  <file name="Mac/Tweet.mm"/>
</files>

In the target id=NDLL we need to add the different files to compile:

<files id="mac" if="mac"/>
<files id="iphone" if="ios"/>

Finally we have to add the Twitter framework dependency in the include.xml. This way, hxcpp will link the framework to our applications and unicorns will be happy.

<dependency name="Twitter.framework" if="ios"/>

I compiled the extension from the project folder just like I did in part 1:

haxelib run hxcpp Build.xml -Dmac
haxelib run hxcpp Build.xml -Diphoneos -DHXCPP_ARMV7
haxelib run hxcpp Build.xml -Diphonesim

To test the feature I simply added to my TestApp:

TestExtension.tweet("This is my tweet message");

And run it in the simulator and on my iOS device:

cd TestApp
openfl test project.xml iphone -simulator
openfl test project.xml iphone

We can tweet using native api from our application! How cool it that?! :)

I hope these instructions will work as well on your system… sometimes a little bit of dust can destroy an empire. Please refer to the github repository to ensure that you are using the most up to date code I wrote.

What's important is the way I found the information: in hxcpp and nme sources, on github.

I experienced a few problems too while trying to make things work. Cleaning the ndll/ and the project/obj folders and recompiling helped a lot until I found the good Build.xml and include.xml format :)

So far the only real problem comes from the lack of real documentation of hxcpp Build.xml features. Thanks god we have a few examples to learn from.

My next experience should be with android to make sure things work there too. Learning and documenting how to write extensions in java may be useful too I eared :)

I updated the github repository of the TestExtension in the hope that my little contribution will be of some use to other haxers!