Jump to content


Photo

Asynchronous Functions


  • Please log in to reply
74 replies to this topic

#1 Nocturne

Nocturne

    Nocturne Games

  • Administrators
  • 16806 posts
  • Version:GM:Studio

Posted 13 March 2012 - 03:23 PM

Introduction

Hi guys! It's come to our attention that some of you are unaware of certain changes that are being made to how GM functions with the introduction of GameMaker:Studio, specifically relating to the use of functions like show_message(), keyboard_wait() and many others which stop the game loop while waiting to be resolved. These are no longer going to be supported as mobile devices do NOT like them and GameMaker:Studio will instead be dealing with things in an asynchronous way. So what does this mean? Well, before dealing with the asynchronous functions (referred to from now on as async functions) in GameMaker:Studio it may be that you are wondering what the word "asynchronous" actually means! Well, the actual dictionary definition of asynchronous is :


"pertaining to a transmission technique that does not require a common clock between the communicating devices; timing signals are derived from special characters in the data stream itself."

In the context of GameMaker, an async function is one that sends out to a web server, or asks for user input, or even streams data, while letting GameMaker:Studio continue to run without blocking. When the information callback is received a special *Async Event is fired which, in turn, allows any instance with an Async Event assigned to it to execute further functions (although this does not have to be the case). So, basically, it's a way to communicate with some external resource (server, user or device) without the game or project blocking while waiting for a reply.

Why is this important? Well, since it allows GameMaker:Studio to continue functioning while sending or recieiving data, and this means that you can do many things all without blocking the game loop like -

  • stream data from the device into your game without the player waiting for things to load
  • have callback events to do things only when the correct information is received
  • communicate and interchange data with a web server
This all makes everything function in a much more fluid and unobtrusive way. It should be noted that the Async Events will be triggered for all instances that have them, so you can use an async function in one instance, and have the Async Event that deals with the reply in another one, or even various other ones.




The Asynchronous Event


An asynchronous event is one that is fired off when GameMaker:Studio receives a "call back" from some external source, which can be from the web or from the device running your game. Basically, you tell GameMaker:Studio to do something, like load an image, and it'll start to do this but continue doing whatever it else it has to do meanwhile its working. Then, when the request has been completed, a call-back will be sent to GameMaker:Studio and any Asynchronous Events defined for that type of call back will be fired. Please note that the Asynchronous Events are fired for all instances that have them, much like the key events, so you can do an http_get call in one instance, yet have the Asynchronous HTTP event in another. There are four types of sub-events associated with the Asynchronous event, and they are all explained below :

Image Loaded and Sound Loaded Events
These two events are triggered when you load an image or a sound into GameMaker:Studio, as long as you have used a valid URL with the applicable load file function. For example say I want to load a background image, and only change the current background to the new one when it has loaded. Well I would have something like this in a create event or an alarm event :

back = background_add("http://www.angusgame...ackground1.png", 0, 0);

This will now start to load the image into the device or the browser, but it will not block GameMaker:studio while it waits for the file to be loaded. Instead GameMaker:Studio will keep running as normal until the image is loaded and the call back triggers the Image Loaded event, where a ds_map (more commonly known as a "dictionary") is created and stored in the special variable async_load. The map contains the following information :

  • URL : The complete URL you requested.
  • id : The ID of the resource that you have loaded. This will be the same as the variable that you have assigned the resource to.
  • status : Returns a value of less than 0 for an error.

I would then assign the newly loaded image to a background in this event. The above is also true for sprites and sounds, with a ds_map being generated for each of these resources as shown above.

HTTP
The HTTP Event is one that is triggered by the call back from one of the http_ functions, like http_post_string. It actually generates a ds_map (more commonly known as a "dictionary") that is exclusive to this event and is stored in the special variable async_load. This ds_map has the following structure :

  • URL : The complete URL you requested.
  • result : The data received (string only).
  • http_status : The raw http status code (if available). This returns the standard web status codes for most browsers, eg: 304 for "Not Modified" or 204 for "No Content", etc...
  • id : The ID which was returned from the command. If you fire off a series of http_ requests then you need to know which one you are getting the reply to, and so you would use this value to compare to the value you stored when you originally sent the request to find the right one.
  • status : Returns a value of less than 0 for an error.

That is for when you use the http_post_string() function, but each of the http_ functions will return a slightly different map, so please refer to the manual entry for each function to find out the precise data that is returned for it.

User Interactions
Like the above events, the User Interaction event is only triggered when it gets a call back from one of the special asynchronous user functions, like get_login_async(). These events are the ones that ask for some type of user input, which can be a name, login details, a number or a colour etc... As most devices do not like sitting in a loop waiting for a reply, they have to be asynchronous and GameMaker:Studio will continue to run in the background while these functions are running until they get the required user input and trigger this event. Again, a ds_map is returned with the id held in the special variable async_load. The values held in this map will depend on the function used, and you should consult the individual entries for each function in this manual for more details.

Note : The variable async_load is only valid in this event, as the ds_map that is points to is created at the start of the event, then deleted again at the end, with this variable being reset to a value of -1.



Asynchronous Functions


http_get()

With this function, you connect to the specified URL in order to retrieve information. As this is an asynchronous function, GameMaker:Studio will not block while waiting for a reply, but will keep on running, whether it gets callback information or not. If it does get a callback, this information will be in the form of a string and will trigger an Async Event in any instance that has one defined in its object properties. You should also note that HTTP request parameters (the bits sometimes "tacked on" to the end of a URL when you submit a form on a web page) are perfectly acceptable when using this function, for example :

http_get("http://www.YoYoGames...ogon?username=" + name);

will pass the string held in the variable "name" to the server as well a retrieve the data from that URL (triggering all Async Events). So, essentially, any time a simple, short piece of data needs to be passed from the client to the server, this would a reasonable choice of function to use.



http_post_string()

In computing, a post request is used when the client needs to send data to the server as part of the retrieval request, such as when uploading a file or submitting a completed form, and the same is true of this function in GameMaker:Studio. In contrast to the http_get() request method where only a URL is sent to the server, http_post_string() also includes a string that is sent to the server and may result in the creation
of a new resource or the update of an existing resource or both. Here is a quick example of how it would be used :

var str;
str = ds_grid_write(game_grid);
post[0]http_post_string("http://www.YoYoGames.../game?game_id=" + string(global.game_id), str);

The above code sends a retrieval request to the specified URL with the parameters specified (it should be noted that HTTP request parameters are perfectly acceptable here too) as well as sending the additional data stored in the variable "str", and this will trigger all defined Async Events if a callback is received. This event will generate a "call back" which is picked up by any HTTP Events, in which case it will generate a ds_map (more commonly known as a "dictionary") that is exclusive to this event and is stored in the special variable async_load.



get_login_async()

This function opens a window that asks the user to input a username and password. These arguments can be set as an empty string or you can store previously entered values to use if you wish. This is an asynchronous function, and as such GameMaker:Studio does not block the device it is being run on while waiting for ansser, but rather keeps on runniing events as normal. Once the user has input the details and pressed the "Okay" button, an asynchronous User Interaction event is triggered which, for the duration of that event only, will have a ds_map stored in the variable async_load. This map will contain the two keys "username" and "password", with the corresponding user input stored in each.
  • 7

#2 alexandervrs

alexandervrs

    GMC Member

  • GMC Member
  • 421 posts
  • Version:GM:Studio

Posted 13 March 2012 - 04:18 PM

This is definitely interesting. Will this be available to external resource functions and file I/O too? I had posted a wish about making the loading of resources threaded,

Well a wish I posted a little while ago is to turn "Web Events" into "Load Events" so they are more useful than just in HTML5.
That way sprite_add() etc. would be threaded and we could use the Load Events to check if the resources are available.
It would be useful for in-game loading screens or threaded loading in the background as you play the game.


I object a little to making async the show_message type dialog messages, as sometimes I want to display an important warning or fatal error message and need to block access to the game.

Edited by alexandervrs, 13 March 2012 - 04:19 PM.

  • 0

#3 Nocturne

Nocturne

    Nocturne Games

  • Administrators
  • 16806 posts
  • Version:GM:Studio

Posted 13 March 2012 - 04:21 PM

This is definitely interesting. Will this be available to external resource functions and file I/O too? I had posted a wish about making the loading of resources threaded,


Well a wish I posted a little while ago is to turn "Web Events" into "Load Events" so they are more useful than just in HTML5.
That way sprite_add() etc. would be threaded and we could use the Load Events to check if the resources are available.
It would be useful for in-game loading screens or threaded loading in the background as you play the game.


I object a little to making async the show_message type dialog messages, as sometimes I want to display an important warning or fatal error message and need to block access to the game.


Yes it will. Streaming will become a viable option for loading resources and we are discussing the file system too, in particular a version of http_post_string that would be something like http_post_ini()... As for the show_message functionality, it will probably be available for debug purposes but the final compiled game will not allow it.
  • 0

#4 alexandervrs

alexandervrs

    GMC Member

  • GMC Member
  • 421 posts
  • Version:GM:Studio

Posted 13 March 2012 - 04:28 PM

Yes it will. Streaming will become a viable option for loading resources and we are discussing the file system too

Well, thank all of you for making my day.

As for the show_message functionality, it will probably be available for debug purposes but the final compiled game will not allow it.

Ah maybe those should be moved under a prefix then like debug_show_message(), debug_get_string() etc.
I suppose I could use an extension for modal dialogs on Windows anyway.
  • 0

#5 Destron

Destron

    GMC Member

  • GMC Member
  • 919 posts
  • Version:GM:HTML5

Posted 13 March 2012 - 04:41 PM

You can still have a popup that stops the game and waits for user input, you just have to hand code it. I agree that sometimes, although rarely, you may have a situation where this happens. Its not so nice when the game has to "wait" for something like communication with a web server though. This would make something like online storage of save files ans such MUCH easier as I always create a custom save system and never use the built in one.

This is interesting stuff, thanks for posting it!
  • 0

#6 Nocturne

Nocturne

    Nocturne Games

  • Administrators
  • 16806 posts
  • Version:GM:Studio

Posted 13 March 2012 - 04:48 PM

We will be adding in functions like (and these are NOT final names!) async_show_message() and async_get_integer() to provide the same functionality as all the deprecated functions, but working with the new way of doing things. And note that the old functions will not break your game if left in, but they will be either ignored, or automatically return the default value you specified.
  • 0

#7 Destron

Destron

    GMC Member

  • GMC Member
  • 919 posts
  • Version:GM:HTML5

Posted 13 March 2012 - 04:52 PM

Its good to know that platform dependent functions will just be ignored when needed, it makes cross platform programming that much easier without having to check what platform you are on before doing things.
  • 0

#8 alexandervrs

alexandervrs

    GMC Member

  • GMC Member
  • 421 posts
  • Version:GM:Studio

Posted 13 March 2012 - 04:56 PM

We will be adding in functions like (and these are NOT final names!) async_show_message() and async_get_integer() to provide the same functionality as all the deprecated functions, but working with the new way of doing things. And note that the old functions will not break your game if left in, but they will be either ignored, or automatically return the default value you specified.


Or you could have a GML set_async_processing(true) "switch" function to toggle this than have another set of functions.

Wait... why are we discussing about dialogs!? Oh wait, I started it. xD
But yeah, why so much emphasis on the backwards compatibility? I know things are not changing radically until GM9, but several redundant things are already removed and "break" older projects, so why not start changing things from now and drop deprecated and old functionality?

Edited by alexandervrs, 13 March 2012 - 04:57 PM.

  • 0

#9 Nocturne

Nocturne

    Nocturne Games

  • Administrators
  • 16806 posts
  • Version:GM:Studio

Posted 13 March 2012 - 05:03 PM

Some things can be changed and removed, others can't! The delphi codebase is pretty complex now and it's a lot less hassle for everybody to keep as many functions as possible until the re-write.
  • 0

#10 alexandervrs

alexandervrs

    GMC Member

  • GMC Member
  • 421 posts
  • Version:GM:Studio

Posted 13 March 2012 - 05:06 PM

Some things can be changed and removed, others can't! The delphi codebase is pretty complex now and it's a lot less hassle for everybody to keep as many functions as possible until the re-write.

Delphi codebase. Ah right.

Okay, considering the new async way of doing things, will this mean there would be a built-in feature to easily suspend/pause the game as the sleep, keyboard_wait etc. won't work?
Like for example having functions game_pause(), game_resume() and a pausable property for objects to suspend?

Edited by alexandervrs, 13 March 2012 - 05:10 PM.

  • 0

#11 Nocturne

Nocturne

    Nocturne Games

  • Administrators
  • 16806 posts
  • Version:GM:Studio

Posted 13 March 2012 - 05:12 PM

Okay, considering the new async way of doing things, will this mean there would be a built-in feature to easily suspend/pause the game as the sleep, keyboard_wait etc. won't work?
Like for example having functions game_pause(), game_resume() and a pausable property for objects to suspend?


Hopefully, although they may require you to do a bit more than just call that code.
  • 0

#12 FredFredrickson

FredFredrickson

    Artist

  • Global Moderators
  • 9196 posts
  • Version:GM8

Posted 13 March 2012 - 05:24 PM

This is excellent, thanks for putting this together! :)
  • 0

#13 True Valhalla

True Valhalla

    ಠ_ಠ

  • Retired Staff
  • 4880 posts
  • Version:GM:Studio

Posted 13 March 2012 - 10:24 PM

I'm trying to call a URL like this:

var test;

test = http_get("http://myurl.com?pass=c345reu1&gameId=10&playerId=10&ts=100000&score=1000");

show_message("returned value: " + string(test));

After calling that URL it's meant to return a value, but all I get is '0'. Am I retrieving the reply correctly or is there some other method for doing that?

Also, about the async event triggering, can I ask what the point of that is? Is it just so you know the call received a response? I feel like I'm missing something.
  • 0

#14 Mike.Dailly

Mike.Dailly

    Evil YoYo Games Employee

  • Administrators
  • 1492 posts
  • Version:GM:Studio

Posted 13 March 2012 - 10:34 PM

async is... well...async. It's not blocking. The value you get back is simply an ID to help identify which async event is being called.

Just noticed, the async event isn't documented yet - whoops. my bad. I didn't explain it to Mark - DOH.

Inside that event, a new built in variable is usable. "async_load" (just like the web events in HTML5). This gives you the index into a ds_map which has the following things filled out.

  • "url" - the URL you requested
  • "result" - the data received. (string only)
  • "http_status" - the raw http status code. (if available)
  • "id" - the ID which was returned from the command.
  • "status" - <0 for error

"async_load" is invalid at other times. We'll get this done properly tomorrow.... my bad.
  • 3

#15 True Valhalla

True Valhalla

    ಠ_ಠ

  • Retired Staff
  • 4880 posts
  • Version:GM:Studio

Posted 13 March 2012 - 10:37 PM

Ah, that should help :) Thanks.
  • 0

#16 Smarty

Smarty

    GMC Member

  • Retired Staff
  • 7221 posts
  • Version:GM:Studio

Posted 13 March 2012 - 11:13 PM

So the async event is triggered and you get an index to a map. And if I understand correctly, if you have several instances (coming from several different objects) with an async event, this means that all of them are called with the ID for this data structure. Isn't this a bit of a waste if you need to have only one of them act on the result data?

Also, what happens if more than one result comes back in a single step? Does it call all instances with a web event and run all GET/POST results through each of them? If so, this means that a single web event of a single instance may sometimes be called several times per step.

And who is responsible for cleaning up the data structure returned? Or does this happen automatically after all async events have been called using the same ID?

And what happens if a result comes back and there is no instance alive that has the async event? Will the next GET/POST result overwrite the previous result, or will data be queued internally until async events come by to deal with the results?

Also, when exactly are the async events called in the regular event flow, if they are to be handled? I assume it doesn't happen on the spot of arrival, but rather that you execute the async events at a more appropriate moment (before / after begin, middle and end step)?

Edited by Smarty, 13 March 2012 - 11:44 PM.

  • 0

#17 alexandervrs

alexandervrs

    GMC Member

  • GMC Member
  • 421 posts
  • Version:GM:Studio

Posted 13 March 2012 - 11:51 PM

http_get()

With this function, you connect to the specified URL in order to retrieve information. As this is an asynchronous function, GameMaker:Studio will not block while waiting for a reply, but will keep on running, whether it gets callback information or not. If it does get a callback, this information will be in the form of a string and will trigger an Async Event in any instance that has one defined in its object properties. You should also note that HTTP request parameters (the bits sometimes "tacked on" to the end of a URL when you submit a form on a web page) are perfectly acceptable when using this function, for example :


It now occurred to me. The naming of the function looks a little confusing. It is supposed to be POST and GET requests right? Actually there's little difference in the request methods rather than GET showing the variables on the address bar and POST just doing so without showing them. http_post_string() & just http_get() sounds a little misleading as you can "post" with GET type requests and "get" with POST. Reason why PHP 5 got a $_REQUEST array for both of these.

Maybe it should be renamed http_request(url, method); "method" being rq_post or rq_get (0 or 1) and integrate both functions into one.
  • 0

#18 alpha_centauri

alpha_centauri

    GMC Member

  • GMC Member
  • 78 posts

Posted 14 March 2012 - 05:51 AM

along with these functions will there be support for push notifications????
  • 0

#19 Mike.Dailly

Mike.Dailly

    Evil YoYo Games Employee

  • Administrators
  • 1492 posts
  • Version:GM:Studio

Posted 14 March 2012 - 08:07 AM

So the async event is triggered and you get an index to a map. And if I understand correctly, if you have several instances (coming from several different objects) with an async event, this means that all of them are called with the ID for this data structure. Isn't this a bit of a waste if you need to have only one of them act on the result data?

Also, what happens if more than one result comes back in a single step? Does it call all instances with a web event and run all GET/POST results through each of them? If so, this means that a single web event of a single instance may sometimes be called several times per step.

And who is responsible for cleaning up the data structure returned? Or does this happen automatically after all async events have been called using the same ID?

And what happens if a result comes back and there is no instance alive that has the async event? Will the next GET/POST result overwrite the previous result, or will data be queued internally until async events come by to deal with the results?

Also, when exactly are the async events called in the regular event flow, if they are to be handled? I assume it doesn't happen on the spot of arrival, but rather that you execute the async events at a more appropriate moment (before / after begin, middle and end step)?


Your over thinking it.... It acts "just" like any other event. If 2 instances have a SPACE key down event, they both get it, this is just the same. What will probably happen is that you'll have a "controller" or something actually deal with the events, I can't imagine very many instances wanting to play with the data that's returned.

You also don't "HAVE" to use the event. If your firing off a highscore using a post, you might not care about the return.

If there is no instance alive, then no event is thrown and the data is dis-guarded.

If there are several results in a single frame, then the event is thrown several times, with the ds_map being rewritten each time for the new set of data.

The data is cleaned up automatically. If you take a copy of the returned string, then...well... you have a copy of it!

The ID is simply an identifier so you know which callback your dealing with, although we also give you the URL as well.


Probably the worst case, is a controller instance that handles server stuff, and lets say another that handles hiscores, and one that handles achievements. This means 3 instances would get each event, but if you keep track of the current ID, they would ignore the ones that aren't for them.

It now occurred to me. The naming of the function looks a little confusing. It is supposed to be POST and GET requests right? Actually there's little difference in the request methods rather than GET showing the variables on the address bar and POST just doing so without showing them. http_post_string() & just http_get() sounds a little misleading as you can "post" with GET type requests and "get" with POST. Reason why PHP 5 got a $_REQUEST array for both of these.


There is little difference, but there IS a difference, which is why we have both. delivering a payload is important in web services, and the simple GET is important for normal loading.
  • 0

#20 Smarty

Smarty

    GMC Member

  • Retired Staff
  • 7221 posts
  • Version:GM:Studio

Posted 14 March 2012 - 10:28 AM

Well it works as I expected it would, so I haven't been over-thinking it at all. :whistle:

One last thing though - I think working with a data structure is a bit unwieldy. You've got to look up map values by key name which makes the code a bit less plain. There's also the risk of making a typo in the key name and not knowing you did it wrong at both run time and design time.

Would you mind splitting the data into system variables instead? At least then we have code highlighting and errors if we don't get the names right. It's also more in line with how GML works now. And we get to keep status and ID as reals, as well as save processing time doing map lookups in each async event.
  • 1




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users