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.2)

Posted by Mark Alexander on 26 June 2015

This weeks Friday tech blog is the second tech blog written by our guest writer Ryan Loader, and follows on from the one we posted last week.


Last time I discussed how I had set-up a rest service for GameMaker that could make httprequests and then ‘callback’ using scripts with either a success or failure. Requests also had ‘meta data’ bundled with them that carried information about the request, including where to callback. This time I want to delve more into the specifics of how I handle Flox responses in GameMaker and give you some tips on how to avoid some platform specific quirks.

Handling the HTTP Event

It has taken a lot of iteration to develop the correct checks for determining the state of a response from the server. Here’s the different possible scenarios:

  1. Is this the final response? Certain network configurations and responses will trigger the http event more than once for a single request. All we want to do is simply wait for the final response to be dispatched.

  2. Was this request intended for us? GameMaker requires that you check the id of responses to ensure that they match your request id. This should be pretty familiar if you’ve worked with any of GameMaker’s async events before.

  3. Did GameMaker accept the request? This can happen if we ask GameMaker to send a malformed request. You have to work pretty hard to get this to happen on Desktop but the mobile phones are a bit more fussy, so make sure to test well! This is an issue that should only crop up when you’ve made a mistake so the focus is on being able to debug this issue, not on making it pretty for the user.

  4. Could the server be reached? This is a basic issue prevalent in all networking, sometimes the user’s device is offline, we should handle this gracefully!

  5. Did the server respond with data we expected? This again is an uncommon occurrence but should be handled gracefully. Some network configurations do some crazy stuff to your request eg. a public network in Wellington sometimes throws ads into your response and so you receive HTML rather the JSON you were expecting. Because you can’t predict what on earth is going on, handle this with an ‘Unknown error.’

  6. Was the servers response processable? This is related to the issue above but sometimes the server sends us a crazy response. Usually this would be where you would touch up your server code, but because we don’t control the server in this case, we handle this with another ‘Unknown error’.

  7. Did the server accept our request? If we made it through everything above, did the server actually accept our request? Or did we get a status code 400 / 500?

  8. If yes to all of the above, the request succeeded!

So that’s our logic. Here’s what it looks like in code:

// 1. Is this the final response?
if not maphas(asyncload,"httpstatus") then exit;
// 2. Was the request intended for us?
if maphas(self.serviceRequests, mapget(asyncload,"id")) {
    var status = mapget(asyncload,"status");
    var httpStatus = mapget(asyncload,"httpstatus");
    var headers = mapget(asyncload,"responseheaders");
    var body = mapget(asyncload,"result");

var requestInfo = map_get(self._serviceRequests,requestId);
var onComplete  = map_default(requestInfo,"onComplete",noone);
var onError     = map_default(requestInfo,"onError",noone);

// 3. Did GameMaker accept the request?
// 4. Could the server be reached?
if not map_exists(headers) {
    var error = "Flox server unreachable";
    script_execute(onError,requestInfo,error,http_status_unknown);
}
else {
    var response = json_decode(body);
    // 5. Did the server respond with the data we expected?
    // 6. Was the servers response processable
    if not map_exists(response) {
        var error = "Invalid response from Flox server: "+body;
        script_execute(onError,requestInfo,error,httpStatus);
    }
    else {
        // 7. Did the server accept our request?
        if httpStatus > 0 and httpStatus < 400 {
            // We made it! SUCCESS! Now call the onComplete script
            script_execute(onComplete,requestInfo,response,httpStatus);
        }
        else {
            var error = map_get(response,"message");
            script_execute(onError,requestInfo,error,httpStatus);
        }  
        map_destroy(response);
    }
}
map_destroy(requestInfo);
map_delete(self._serviceRequests,requestId);

}

You can see we actually don’t use the status property of asyncmap to check for a valid response, we instead check for headers. All servers will send headers with a response, but a response that GameMaker creates does not, so this is the best way to both knock off #2 and #3. In addition to this, status can sometimes be set to -1 for valid responses too, eg. 204 no content sometimes gives status -1 when it actually indicates a success, so using the headers is not only the easiest but also the most reliable way for us to check.

You might notice too that I use special scripts called mapget, mapdefault and mapdestroy - these are simple wrappers for dsmapfindvalue and dsmapdestroy respectively, we’ll discuss why I use these more in part 3.

So there you go, this is the most robust and cross-platform solution I have found for handling responses and while it seems like a lot of code (especially considering it doesn’t do much other than detect a successful response!) it handles every network configuration and every response case I’ve encountered. For our needs, a stable rest service is essential and this is what it takes to have one. Now for actually handling some data...

JSON

JavaScript Object Notation is the language in which the rest service communicates with the Flox servers. Any information we want to send to Flox must be formatted in this way for the server to be able to read it. Fortunately JSON has a pleasantly simple syntax, at it’s core it simply stores key/value pairs. Say we have a map something like this:

var myMap = dsmapcreate();
dsmapadd(myMap,”hello”,”world”);
var someMap = dsmapcreate();
dsmapadd(someMap,”foo”,”bar”);
dsmapadd(someMap,”sometimes”,5);
dsmapaddmap(myMap,”another”,someMap);

Then your JSON might look something like this:

{
    “hello”:”world”,
    “another”:{
        “foo”:”bar”,
        “sometimes”:5
    }
}

Nice! You can convert back and forth between dsmaps/dslists and JSON using GameMaker’s jsonencode and jsondecode. But there’s one problem, and I’ll demonstrate it to you now:

var json = ‘{ “JsonIsAwesome”: true }’;
var myMap = jsondecode(json);
var newJson = jsonencode(json);
showdebugmessage(newJson);
// Prints ‘{ “JsonIsAwesome”: 1.000000 }’)

Javascript (and so therefore JSON) makes a distinction between int (a whole number), float (a decimal number) and boolean (true/false). You might not have noticed this while programming in Javascript because it never asks you to declare types, but it does keep a secret record of what type your variables are.

GameMaker on the other hand groups all of these types into a single type called ‘real’. Normally this isn’t too much of a problem, but the backend for Flox is written in Java and the server returns errors if we try and supply it with decimal numbers when it’s expecting true/false. Nooo!

So what can we do? Well perhaps the easiest solution would be to change our server to be more flexible and ‘transform’ any numbers we supply when it is expecting boolean values, however as we don’t have access to the Flox server we must make our transformations on the client instead. GameMaker’s jsonencode isn’t going to help us out, and there’s no way for us to write our own (again, there is no isboolean function in GameMaker). So the only thing left to do is write our JSON by hand:

var queryJSON = ‘{“where”:”’+where+’”,”offset”:’+string(round(offset))...

This may be quite ugly, but it’s foolproof and as it happens it solves another issue for us too.

Malformed Headers

Every Flox request must be authenticated with a special header called ‘X-Flox’ that contains all the necessary details for the server to know you’re authorized to send the request. Some of these properties include your game key, the time the request was dispatched, the sdk details etc. This header is another JSON string, however when it came to test on Android, I noticed this message appearing in the console:

HttpRequest: Malformed Header X-Flox

Unfortunately, GameMaker’s jsonencode doesn’t offer any formatting options and will format the json with a single space between each property. Spaces can cause problems, especially when transferring data, the issue lies in string escaping. If you aren’t sure what that is, there is a <a href="http://stackoverflow.com/questions/10646142/what-does-it-mean-to-escape-a-string?answertab=votes#tab-top" target="blank">fantastic answer on Stack Overflow about this.

Something is different here with the way the Android platform treats the headers and it causes this “Malformed Header” issue. I could try modifying the JSON string, removing all the spaces, but I opted to use the same tactic as before; writing the JSON out manually.

var XFloxPlayer = '{"id":"' + mapget(auth,"id") +
    '","authType":"' + mapget(auth,"authType") + '"';
if maphas(auth,"authId") 
    then XFloxPlayer += ',"authId":"' + mapget(auth,"authId") + '"';
if maphas(auth,"authToken") 
    then XFloxPlayer += ',"authToken":"' + mapget(auth,"authToken") + '"'; 
XFloxPlayer += "}";
var XFloxSDK = '{"type":"gml","version":"' + fxsdkversion + '"}';
var ctime = ifloxgetcurrenttimeutc();
var XFlox = '{"player":' + XFloxPlayer +
    ',"dispatchTime":"' + floxdate_encode(ctime) +
    '","sdk":' + XFloxSDK + 
    ',"bodyCompression":"none","gameKey":"' + self.gameKey + '"}';

var headers = mapcreate("request-headers"); mapset(headers,"Content-Type","application/json"); mapset(headers,"X-Flox",XFlox);

Because none of this will need to be modified for the foreseeable future and only happens in one place in code, I’m pretty comfortable with this change.

HTTPS on Prehistoric Android

So I ran into one final problem; SSL on Android < 3.0. Https is a web protocol that allows for a secure connection between server and client and implements a “trusted authority” model. Your web browser has a number of organisations it trusts and sites that want to communicate over https must have their certificate issued by one of these trusted organisations in order for the browser to consider the communication secure. When a new trusted authority comes along, browsers must be updated with the credentials of that new authority to ensure that the SSL certificates they issue are considered secure.

Android works the same, however it doesn’t force users to update their OS and so it’s possible that older versions of Android (shipped before a trusted authority was added to the OS) will not be able to communicate with some websites securely. 

<a href="http://www.flox.cc" target="blank">www.flox.cc is one such website. If you have an Android phone with Android 2.3 or earlier, try going to https://www.flox.cc/api in your browser. You are met with a connection error, you simply can not go there using the https protocol.

There is a way to get around this for native Android, you can create your own trust manager and add the SSL certificate of your site to it, however I can’t see a way of doing this without having to implement my own custom httprequest function. <a href="https://developer.android.com/about/dashboards/index.html?utmsource=suzunone#Platform" target="blank">The platform still has more than a 5% distribution, but I decided it was just too far out of scope, so Flox will simply fall back to http in those cases. That said, I’d recommend simply not supporting older Android versions. Flox is designed to be used over https and using it over http will most definitely open up security holes, however the option is still there for those who would like to risk it.

Wow!

Being able to make web requests in GameMaker is a relatively novel experience and so I was not surprised that there was a lot of tweaking to get it right. It’s all in the knowing though and hopefully this article helps expedite some of your own exploration! The REST service I have created for Flox can really be used as a base to communicate with any REST API, all it would need are a few tweaks. This means you could create services for other great backends like <a href="https://documentation.mailgun.com/apireference.html" target="blank">MailGun, <a href="https://parse.com/docs/rest/guide" target="blank">Parse… even Marvel Comics! The possibilities are endless, so get cracking!

FloxGM is on the GameMaker Marketplace and is 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