Native command listeners

Eventually, developers may need to perform platform-specific operations, such as controlling ads (showing and hiding), connecting the game to an on-line source in order to retrieve and exchange data, and much more. For this purpose, Ethanon Engine provides interfaces called Native Command Listeners that allow developers to send custom string-based commands from the game script (AngelScript) to the platform native code, like Java on Android, Objective-C on iOS/OSX or C++ on Windows and OSX.

Forwarding custom commands

The function ForwardCommand can be used from the inside of the AngelScript's gameplay code to forward any string-based command, e.g.:

ForwardCommand("enable_ads");
ForwardCommand("disable_ads");
ForwardCommand("connect_to_facebook");
ForwardCommand("submit_score " + score);

iOS and Android implementations already parse and execute some basic commands by default:

ForwardCommand("upen_url http://ethanonengine.com/"); // opens the Ethanon website
ForwardCommand("vibrate 100"); // vibrates the device during 100 ms

In order to read custom commands, the developer must implement a native command listener on each platform.

Listening to custom commands

Once forwarded by the ForwardCommand function, each string is sent to the local command listener object, so the developer may parse, identify and then perform anything he needs. Normally it happens in a place where he has access to the entire platform API.

If multiple commands are sent in a single game frame, they come bundled in a single string separated by '\n'.

Android

On Android, the standard Ethanon project includes the Java interface net.asantee.gs2d.io.NativeCommandListener that, once registered to the main Ethanon Activity, calls its parseAndExecuteCommands passing a list of commands separated by '\n':

package net.asantee.gs2d.io;

public interface NativeCommandListener {
	public void parseAndExecuteCommands(String commands);
}

Let's create a simple command listener that submits high scores to an on-line server. On the AngelScript side:

uint points = 9999;
ForwardCommand("submit_score " + points);

On the Java side:

package com.mygame.io;

import net.asantee.gs2d.io

public class MyGameCommandListener implements NativeCommandListener {
	
	public void parseAndExecuteCommands(String commands) {
		if (!commands.equals("")) {
			String[] commandArray = commands.split("\n");
			for (int t = 0; t < commandArray.length; t++) {
				executeCommand(commandArray[t]);
			}
		}
	}

	private void executeCommand(String cmd) {
		String[] words = cmd.split(" ");
		if (words[0].equals("submit_score")) {
			MyGame.submitScore(Integer.parseInt(words[1]));
		}
	}

}

Finally, we must register our listener to the Ethanon activity. The net.asantee.ethanon.EthanonActivity class inherits the void insertCommandListener(NativeCommandListener commandListener) method which must be used (preferably in the onCreate method) in order to register our custom command listener inside your activity's onStart method:

public void onStart() {
	super.onStart();
	insertCommandListener(new MyGameCommandListener());
}

After MyGameCommandListener is inserted, Ethanon will automatically call our custom parseAndExecuteCommands every time our game receives commands.

Notice that custom listeners also receive messages sent by the engine's runtime processes.

iOS

The C++ class Platform::NativeCommandListener declared in the source file ethanon/toolkit/Source/src/gs2d/src/Platform/NativeCommandListener.h provides an interface that, once registered, listens to every custom command sent by the game code, where the developer can use the Objective-C-based iOS frameworks and do anything an iOS app can do:

namespace Platform {

class NativeCommandListener
{
public:
	virtual void ParseAndExecuteCommands(const gs2d::str_type::string& commands) = 0;
};

} // namespace Platform

The Platform::NativeCommandListener behaves like the Java's NativeCommandListener on Android. Here is a small sample:

class MyGameCommmandListener : public Platform::NativeCommandListener
{
	void ExecuteCommand(const gs2d::str_type::string& commandLine);

public:
	void ParseAndExecuteCommands(const gs2d::str_type::string& commands);
};
void MyGameCommmandListener::ParseAndExecuteCommands(const gs2d::str_type::string& commands)
{
	std::vector<gs2d::str_type::string> commandLines = Platform::SplitString(commands, GS_L("\n"));
	for (std::size_t t = 0; t < commandLines.size(); t++)
	{
		ExecuteCommand(commandLines[t]);
	}
}

void MyGameCommmandListener::ExecuteCommand(const gs2d::str_type::string &commandLine)
{
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

	NSString* line = [NSString stringWithUTF8String:commandLine.c_str()];

	NSArray *words = [line componentsSeparatedByString:@" "];
	NSString* word0 = [words objectAtIndex:0];
	if ([word0 isEqual:@"submit_score"])
	{
		NSString* word1 = [words objectAtIndex:1];

		const double score = [word1 doubleValue];
		MyGameAPI.SubmitScore(score);
	}

	[pool release];
}

For a more concrete working sample, take a look at the IOSNativeCommandListener.h and IOSNativeCommandListener.mm source files in the iOS Xcode project.

To register iOS's listener use the GLView's method insertCommandListener:

[self insertCommandListener:(Platform::NativeCommandListenerPtr(new MyGameCommmandListener))];

The GLView is an Objective-C interface located in the iOS Xcode project.