Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more

Tech

Flox: Creating a Backend for GameMaker (Pt.1)

Posted by Mark Alexander on 19 June 2015

This weeks tech blog is the first in what we hope will be a long list of tech blogs written by guest writers from the GameMaker community. To get the ball rolling we have invited Ryan Loader to talk about how he created the fantastic Flox extension for GameMaker...


I've been working (for a while now!) on an extension for GameMaker: Studio called Flox GM. It simply allows GameMaker developers to use the Flox back-end, which includes things like analytics, leaderboards, cloud data, logging, player authentication, all in a really easy to understand API. This is an important library for GameMaker! I've seen a few leaderboard extensions, one good analytics extension, a couple for saving key-values in the cloud, but none that can offer all that and player auth and handle loads of traffic!

Flox is great. GameMaker is great. I think FloxGM is a wonderful library, but boy has it been a serious project! In this blog series I will share the experience I gained while building a backend for GameMaker and some sneaky tricks for getting the most out of the tool.

The Task at Hand

So first off, what exactly was I was trying to achieve with this extension? I started this project because I wanted to add Flox analytics for the desktop version of my previous game Boy Goes to Space, simply to keep tabs on how many installs I would get and in which countries. Flox would allow me to do this with a single line of code:

floxinit(“MYGAMEID”,”MYGAMEKEY”,”v1.1.0”);

This one line kicks off a number of tasks; It will begin recording a session to keep track of everything that happens to the player as they play. It will also record some information about the user’s device and their chosen language. This data allows me to open up my Flox console and very quickly get an overview of how my game is going.

As you can see, it’s pretty slow right now but I’m just testing so I’m not worried just yet... I have access to new vs active users, top versions, countries & languages and I also have sessions per user too, lots of numbers for me to crunch!

As I mentioned before, Flox can do much more than analytics, you can save cloud data, manage players and create leaderboards too!

Flox has an open source <a href="https://github.com/Gamua/Flox-AS3" target="blank">AS3 SDK (that's Flash), so all I had to do was translate that into GML. Flox's backend is accessed through a pretty typical REST API, allowing for SDK's to be written in any language that can make http requests, GameMaker can make http requests, we're sussed!

There were a couple of little points of difference that would set the FloxGM apart from the Flash SDK.

  1. Unlike the Flash web player, which could only make GET and POST requests, GameMaker allowed all the required request methods. This meant I could use the raw endpoints. (The flash version POSTs it's requests to the api to then 'pass on' to the correct endpoint.)

  2. Asynchronous methods should not use callbacks and instead follow typical GameMaker async request behaviour, firing the HTTP event. (Unfortunately, this was not possible for reasons I’ll discuss soon).

Armed with this knowledge, I set about building the Rest Service.

The Rest Service

The asynchronous model in GameMaker is unique and it was not clear at first how I should architect the rest service to fit it, especially for a guy used to the very callback friendly syntax of Actionscript. A Flox function in the AS3 SDK would look something like the following;

Flox.loadScores(“default”, TimeScope.ALLTIME,
    function onComplete(array:Array):void {
        // This code is run if loading scores succeeded
    },
    function onError(error:String, cachedScores:Array):void {
         // This code is run if loading scores failed
    });

 

You can see we pass two functions (you could think of them as scripts) to the load scores call and one of the functions is called. GameMaker doesn’t use function callbacks however, so a httprequest in GameMaker works as follows;

var url = "http://www.flox.cc/api";
var method = "GET";
var headers = dsmapcreate();
dsmapadd(headers,"Content-Type","application/json");
self.requestId = httprequest(url,method,headers,"");

Then when the request completes it will trigger a HTTP event in all objects that handle that event. It is then your responsibility to ensure that the request was meant for you by checking the id in the asyncload map, a map that contains details of your request;

// Http Event
if (dsmapfindvalue(asyncload,"id") == self.requestId) {
   // Do something with the response
}

I knew that because this was the way GameMaker developers are introduced to asynchronous functions it would be wise to stick as closely to this model as possible. In order to do that, I knew I needed to ensure two key things:

  1. All asynchronous Flox functions MUST return an id that could be used in the HTTP event.

  2. objFlox should have a low depth so that any processing it must do happens BEFORE the requesting object is notified.

That would have been fine, however I knew of something else too.

restService.alwaysFail = true;

For testing purposes, the Flox devs had added a property to their rest service that allowed them to force requests to fail. I knew this would be handy, what's more as it turned out it was vital to maintaining data integrity (more on this another time). But how would I implement a forced failure of a httprequest when the only way to get a valid id was to call httprequest?

Trixy Business

The first thing I searched for was an eventperformasync function (similar to the eventperform functions). I dreamed of simply being able to pass it a map and having the request completely managed by me, but alas twas not meant to be. It seems that creating this sort of functionality may be possible as part of an extension, but I wasn't biting. I really wanted the extension to be completely cross-platform so this didn't seem a very manageable route to explore as I would need to re-create the extension for each platform I wanted to support!

The next thing I thought I'd try was simply setting the url to http://invalid.flox.cc/api That way all requests that were forced to fail would do so and I could use the URL to differentiate between forced failures and actual failures. As it ended up I actually needed to be able to differentiate between different kinds forced failures too, so this method didn't work out either.

The method I settled for was a risky manoeuvre, it would involve teaching GameMaker users about a new asynchronous paradigm; callbacks. To implement callbacks, I first created a map of "in flight requests". Here I could simply add "metadata” to a request by using the request’s id as a key for the “in flight requests” map like so;

var requestId = httprequest(url,method,headers,body);
var requestMeta = dsmapcreate();

// TODO add meta data to request

dsmapaddmap(flox.serviceRequests,requestId,requestMeta);

Then I could retrieve the request meta data when the request completed, simply by looking in the serviceRequests map;

// Http event
var reqId = dsmapfindvalue(asyncload,"id");
if (dsmapexists(flox.serviceRequests,reqId)) {
   var requestMeta = dsmapfindvalue(flox._serviceRequests,reqId);

// TODO process request

dsmapdestroy(requestMeta); dsmapdelete(flox.serviceRequests,reqId); }

All nice and simple! The request meta could contain anything that I wanted, but I always added three key values, onComplete, onError and context:

var requestId = httprequest(url,method,headers,body);
var requestMeta = dsmapcreate(); 
dsmapadd(requestMeta,"onComplete",scrOnComplete);
dsmapadd(requestMeta,"onError",scrOnError);
dsmapadd(requestMeta,"context",context);
dsmapaddmap(flox.serviceRequests,requestId,requestMeta);

onComplete and onErrror are the scripts to call when the request finished, complete if successful, error if it failed. Context was an id, this was the object that would be executing the script and was always set to the id of the object calling the Flox function in the first place. This made things nice and simple for handling requests! They'd always be finished in the same place they were started AND if an object was ever removed before a request completed (eg. the room changed) then the request simply finished silently, nice and simple.

Looking Back

As proud as I was of my callback solution at the time I now see more viable options. For instance I could have maintained setting the url to be invalid and simply added error messages into that same request metadata system I built. Or today I discovered that you can actually modify the asyncload map as it bubbles through objects! So perhaps I could modify properties on the asyncload map itself to contain more details about the actual request. 

At any rate, the system I have works and hopefully this helps clarify some thinking around the rest service architecture, next time I will go into specifics about how I dealt with issues like https, multiple responses for single requests and some of the cross-platform quirks of httprequest.

<a href="https://marketplace.yoyogames.com/assets/574/flox-gm" target="blank">FloxGM is on the GameMaker Marketplace and available for free


About The Author

Ryan is a game maker / designer / programmer from Wellington, New Zealand. He started a company with two friends, One Legged Crab and together they made games. One of these games was Boy Goes to Space. Ryan has a passion for education, teaching kids how to program and students how to pursue their passion; he believes that teaching is the most effective way to learn! He also loves Burger Fuel.

Ranisputnik on the GameMaker Community
<a href="https://twitter.com/RaniSputnik" target="blank">RaniSputnik on Twitter
http://ryanloader.me 

Back to Top