Analytics Manager: Haxe & OpenFL

This week I want to talk about a very useful and simple tool I made for collecting gameplay information and basic (anonymous) information from devices where players play. You can fork and use the extension from github. I haven’t tested this with the new version of OpenFL and haxe but it should work, if for any reason doesn’t work please let me know.

How to Use it: Client Side

First of all you have to instantiate the AnalyticsManager class:

actionscript3

  1. var analyticsManager : AnalyticsManager;
  2. var analyticsDB : String = "DataBase"; //You should write the name of the database you created to store the data
  3. analyticsManager = AnalyticsManager.InitInstance(analyticsDB);

There is a class called AnalyticsData, you just create a new class and make it extend from this class.

actionscript3

  1. class AnaTest extends AnalyticsData
  2. {
  3.    public function new()
  4.    {
  5.       super();
  6.       //These are just test values, you can use parameters here
  7.       this.AddValue("par1", "val1");
  8.       this.AddValue("par2", 65);
  9.       this.AddValue("par3", 42.028);
  10.    }
  11. }

The class above is just a sample, you can add parameters to the constructor and pass different values to it depending on the case.

After you create a custom class to pass store whatevergameplay data you want to store, you need to call the AnalyticsManager and create a new instance with the data you want to send.

actionscript3

  1. var url : String = "URLToMyServer"; //You have to create a script on the server side
  2. var table : String = "TableName"
  3. AnalyticsManager.SendDataToServer(url,table,new AnaTest());

The data is automatically converted to JSON so you can just parse that on the server side and store it. This default behavior can be changed as well but I have to implement it.

How to Use it: Server Side

I add a sample of a very simple script in PHP to take the data sent fromthe server and do something with it. Of course you can write your own script usign whatever language you prefer.

  1. $table = $_POST["table"];
  2. //Get the data and decode from JSON
  3. $json = urldecode($_POST["data"]);
  4. $array = json_decode($json);
  5.  
  6. foreach($array as $key => $val)
  7. //Do whatever you want with the attributes
  8.  
  9. echo "success"; //this tells the client everything is ok

Please notice that this probably needs some security, just to protect your data from bad people, you know. Be careful with the way you send the data.

With these few steps you should be able to use the extension and store some interesting data from gameplay and then improve your game.

How is the Extension Structured

There are three different base classes, each one with a clear purpose for this extension: AnalyticsData, AnalyticsLoader and AnalyticsManager.

AnalyticsData

This is an abstraction of the data you want to send to the database. Basically this class stores pairs of values: (Key,Value) to be passed to the server and then eventually stored in a table which contains columns of the same names of those keys.

actionscript3

  1. class AnalyticsData
  2. {
  3.    private var values : Map<String,Dynamic>;
  4.    public function new()
  5.    {
  6.       values = new Map<String,Dynamic>();
  7.    }
  8.  
  9.    private function AddValue(key : String, value : Dynamic) : Void
  10.    {
  11.       values.set(key, value);
  12.    }
  13.  
  14.    public function ToString() : String
  15.    {
  16.       var data : String;
  17.       data = "";
  18.       for (k in values.keys())
  19.       {
  20.          data += k + "=" + values.get(k);
  21.          data += "&";
  22.       }
  23.  
  24.       return data.substring(0, data.length - 1);
  25.    }
  26.  
  27.    public function ToJSON() : String
  28.    {
  29.       return Json.stringify(values);
  30.    }
  31. }

Use the AddValue function to include more pairs (key,value) to the instance of this class and before sending it to the server, use ToJSON or ToString to be sent as parameter in a long string.

AnalyticsLoader

This class has common event functions such as onComplete and onIOError, in order to send data to the server, a loader is needed. Once the data is successfully sent or an error occurs, this class manages that response.

actionscript3

  1. class AnalyticsLoader extends URLLoader
  2. {
  3.    private var onComplete : Dynamic -> Void;
  4.    private var onIOError : Dynamic -> Void;
  5.  
  6.    public function new(?request:URLRequest,?onComplete : Dynamic -> Void,onIOError : Dynamic -> Void)
  7.    {
  8.       super(request);
  9.       this.onComplete = onComplete;
  10.       this.onIOError = onIOError;
  11.       if(onComplete != null)
  12.          addEventListener(Event.COMPLETE, onComplete);
  13.  
  14.       if(onIOError != null)
  15.          addEventListener(IOErrorEvent.IO_ERROR, onIOError);
  16.    }
  17.  
  18.    public function Clean() : Void
  19.    {
  20.       if (onComplete != null)
  21.       {
  22.          if (hasEventListener(Event.COMPLETE))
  23.             removeEventListener(Event.COMPLETE,onComplete);
  24.       }
  25.  
  26.       if (onIOError != null)
  27.       {
  28.          if (hasEventListener(IOErrorEvent.IO_ERROR))
  29.          removeEventListener(IOErrorEvent.IO_ERROR,onIOError);
  30.       }
  31.    }
  32. }

Calling the Clean function will remove all the handlers from this class.

AnalyticsManager

This is the core class of the extension, handling calls and managing the data internally without the hassle of doing the same process again and again. This class is a singleton, that’s why can only be instanciated from the available methods: first InitInstance and GetInstance if you need to use it.

actionscript3

  1. class AnalyticsManager
  2. {
  3.    /*
  4.    * Analytics manager instance.
  5.    */
  6.    private static var instance : AnalyticsManager;
  7.    private static var loaders : Array;
  8.    private static var database : String;
  9.    public static function InitInstance(database : String = ""): AnalyticsManager
  10.    {
  11.       if (instance == null)
  12.          instance = new AnalyticsManager(database);
  13.  
  14.       return instance;
  15.    }
  16.  
  17.    /*
  18.    * Creates and returns a analyrics manager instance if it's not created yet.
  19.    * Returns the current instance of this class if it already exists.
  20.    */
  21.    public static function GetInstance(): AnalyticsManager
  22.    {
  23.       if ( instance == null )
  24.          throw "The Analytics Manager is not initialized. Use function 'InitInstance'";
  25.  
  26.       return instance;
  27.    }
  28.  
  29.    /*
  30.    * Constructor
  31.    */
  32.    private function new(db : String = "")
  33.    {
  34.       loaders = new Array();
  35.       database = db;
  36.    }
  37.  
  38.    public static function SendDataToServer(url : String,table : String, anaData : AnalyticsData,onComplete : Dynamic -> Void =
  39.    null,onIOError : Dynamic -> Void = null, db : String = "")
  40.    {
  41.       var request : URLRequest;
  42.       var loader : AnalyticsLoader;
  43.       var variables : URLVariables;
  44.       var databaseName : String;
  45.  
  46.       databaseName = db != "" ? db : database;
  47.       request = new URLRequest(url);
  48.       //I also decided to do every request through POST method, if needed could be change in the future
  49.       request.method = URLRequestMethod.POST;
  50.       //I decided to use only JSON, we could change this in the future
  51.       request.data = "database=" + databaseName + "&table=" + table + "&data=" + anaData.ToJSON();
  52.       trace(request.data);
  53.       loader = new AnalyticsLoader(request,onComplete,onIOError);
  54.  
  55.       loaders.push(loader);
  56.    }
  57.  
  58.    public static function Clean() : Void
  59.    {
  60.       for (l in loaders)
  61.         l.Clean();
  62.    }
  63. }

An array of all the loaders is kept here for easy cleaning whenever the programmers needs to do it. This class also stores the name of the database (in the server side), currently this extension only handles one database per game.

The SendDataToServer function creates a loader and with it sends the data to the server and let the loader handle whatever response comes from it.

Wrapping Up

In addition to the three base classes I explained here, there is a BasicData class which contains general information you might be interested in storing from the player’s device. This information is anonymous so the privacy of that user is protected in any case.

This was the post for today, a very simple way to collect and store gameplay information to improve your creations. If you have questions or comments, just let me know.

 

Analytics Manager: Haxe & OpenFL

Leave a Reply

Your email address will not be published.

*