A call to an extension on any platform generally works like any other GML function call, ie: you call the function and it does something, sometimes returning a value or a string back to the code to be used or stored. However, there will be cases when the extension being used requires some processing to achieve the desired result, particularly on the mobile targets, like when you have an extension to "pull" advertising from a given source. The result of these function calls is not instantaneous and may take some time to come through. In these cases you would want the extension to trigger an event in an instance, where you can then process the results of the function call.
Well, to resolve this issue on the mobile targets you can create extensions with access to the Asynchronous Event. This event is used in GameMaker: Studio to "catch" things that have been getting processed in the background, like the http_ function calls, or the loading of a sprite etc... However, you can also add to your external extension code to give it access to this event, which is very useful for third party SDKs, particularly advertising and monetisation ones.
Use dsMaps
One of the most convenient ways of returning information back to your game from a third party SDK is via dsMaps. These can be created in your extension code and returned to the Asynchronous Event (note that you can return a dsMap as the return value from your defined extension function in to GameMaker: Studio as a regular return too).
In order to do this we provide certain functions to aid in your creating and returning dsMaps, which will be shown below in the full code, and which are also available as attachments to this document for downloading at the bottom of the page.
The Social Asynchronous Event
Currently, you can get access to the GameMaker: Studio Social Asynchronous event. You also have access to a number of GameMaker: Studio constants which can be returned as part of your dsMap, namely those related to leaderboards, image retrieval and friends etc... This is to help with the integration of third party SDKs from companies like LeadBolt or inMobi, but there is no obligation for you to use them and they can be ignored if not needed.
iOS
For extensions on iOS you will need to have a header file with the following:
enum eAchievementMessages{ e_achievement_our_info=1002, e_achievement_friends_info=1003, e_achievement_leaderboard_info=1004, e_achievement_achievement_info=1005, e_achievement_pic_loaded=1006, e_achievement_challenge_completed=1007, e_achievement_challenge_completed_by_remote=1008, e_achievement_challenge_received=1009, e_achievement_challenge_list_received=1010, e_achievement_challenge_launched=1011, e_achievement_player_info=1012, e_achievement_purchase_info=1013 }; enum eAchievementShowTypes{ e_achievement_show_ui=0, e_achievement_show_profile=1, e_achievement_show_leaderboard=2, e_achievement_show_achievement=3, e_achievement_show_bank=4, e_achievement_show_friend_picker=5, e_achievement_show_purchase_prompt=6 }; const int EVENT_OTHER_SOCIAL = 70; extern UIView *g_glView; extern "C" NSString* findOption( const char* _key ); extern bool F_DsMapAdd_Internal(int _index, char* _pKey, double _value); extern bool F_DsMapAdd_Internal(int _index, char* _pKey, char* _pValue); extern int CreateDsMap( int _num, ... ); extern void CreateAsynEventWithDSMap(int dsmapindex, int event_index);
Android
For Android extensions, your Java file should contain the following:
import ${YYAndroidPackageName}.R; import com.yoyogames.runner.RunnerJNILib; int EVENT_OTHER_SOCIAL = 70; int e_achievement_our_info=1002; int e_achievement_friends_info=1003; int e_achievement_leaderboard_info=1004; int e_achievement_achievement_info=1005; int e_achievement_pic_loaded=1006; int e_achievement_challenge_completed=1007; int e_achievement_challenge_completed_by_remote=1008; int e_achievement_challenge_received=1009; int e_achievement_challenge_list_received=1010; int e_achievement_challenge_launched=1011; int e_achievement_player_info=1012; int e_achievement_purchase_info=1013; int e_achievement_show_ui=0; int e_achievement_show_profile=1; int e_achievement_show_leaderboard=2; int e_achievement_show_achievement=3; int e_achievement_show_bank=4; int e_achievement_show_friend_picker=5; int e_achievement_show_purchase_prompt=6; //DsMaps are handled through RunnerJNILib which enables the following methods: public static native int jCreateDsMap(String[] keys, String[] svals, double[] sdoublevals); public static native void DsMapAddDouble(int mapindex, String key, double val); public static native void DsMapAddString(int mapindex, String key, String val); public static native void CreateAsynEventWithDSMap(int mapindex, int eventindex);
//sample call RunnerJNILib.DsMapAddString(dsmapindex, "userpicurl", info[6]);
//Constants are read from the iniBundle RunnerActivity.mYYPrefs which enables the following methods: public String getString( String _name );
public int getInt( String _name ); public boolean getBoolean( String _name );
public boolean keyExists( String _name);
// sample call String key = RunnerActivity.mYYPrefs.getString("YYAndroidMobageKey");
Windows / Linux / Mac
To return values from your extension on Windows, Linux, or Mac, you cannot just extern the functions outlined above. Instead you should declare them as function pointers like so:
void (*CreateAsynEventWithDSMap)(int,int) = NULL; int (*CreateDsMap)(int _num, ... ) = NULL; bool (*DsMapAddDouble)(int _index,char *_pKey,double value)=NULL; bool (*DsMapAddString)(int _index, char *_pKey, char *pVal)=NULL;
and then export a function called RegisterCallbacks to your extension defined functions:
__declspec (dllexport) void RegisterCallbacks(char *arg1, char *arg2, char *arg3, char *arg4 ) { void (*CreateAsynEventWithDSMapPtr)(int,int) = (void (*)(int,int))(arg1); int(*CreateDsMapPtr)(int _num,...) = (int(*)(int _num,...)) (arg2); CreateAsynEventWithDSMap = CreateAsynEventWithDSMapPtr; CreateDsMap = CreateDsMapPtr; bool (*DsMapAddDoublePtr)(int _index,char *_pKey,double value)= (bool(*)(int,char*,double))(arg3); bool (*DsMapAddStringPtr)(int _index, char *_pKey, char *pVal)= (bool(*)(int,char*,char*))(arg4); DsMapAddDouble = DsMapAddDoublePtr; DsMapAddString = DsMapAddStringPtr; }
When your extension is loaded this callback should fire immediately and be passed in pointers to the four functions. To use them you should call CreateDsMap, and add data to it using the DsMapAddDouble/DsMapAddString calls, then queue up the async event by calling CreateAsynEventWithDSMap:
const int EVENT_OTHER_SOCIAL = 70; int my_map_index = CreateDsMap( 0 ); DsMapAddDouble(my_map_index, "another_number", 42);
DsMapAddString(my_map_index, "another_string", "hello, world"); CreateAsynEventWithDSMap(my_map_index, EVENT_OTHER_SOCIAL);
Always pass 0 into CreateDsMap and add your extra data using the add double/ add string calls.