Asset - Scripts Request Master: A new era for HTTP requests in GMS 2.3+

FrostyCat

Redemption Seeker
Request Master
Bringing out the true potential of http_request() since 2020

Overview

This library for GMS 2.3+ provides a set of extensions for easily encoding, decoding, and sending HTTP requests. The encoding tools support application/x-www-form-urlencoded, multipart/form-data and application/json request formats, using familiar structs and arrays instead of manual string manipulations. The HTTP helper functions also enable sending requests and receiving responses at source, without a separate HTTP asynchronous event.

Downloads / Links
YoYo Marketplace: Link
GitHub: Link 1 (Request Master + JSON Struct) | Link 2 (Request Master only) | Repository | Wiki

Comparison

Post-2020 GML with Request Master:

GML:
xhr_get("https://api.steampowered.com/ISteamNews/GetNewsForApp/v2/", {
    params: { appid: 282800, count: 1 },
    done: function(res) {
        show_message("Latest news from 100% Orange Juice: " + res.data.appnews.newsitems[0].title);
    },
    fail: function() {
        show_message("Can't fetch headlines from Steam.");
    }
});
GML:
xhr_download("http://web.archive.org/web/20060821000040im_/http://gamemaker.nl/images/header.jpg", working_directory + "gmlegacy.jpg", {
    done: function(res) {
        sprite_index = sprite_add(res.file, 1, false, false, 0, 0);
    },
    fail: function(res) {
        show_message("Failed to download the image!");
    }
});
Pre-2020 stock GML
GML:
xhr_oj = http_get("https://api.steampowered.com/ISteamNews/GetNewsForApp/v2/?appid=282800&count=1");
GML:
if (async_load[? "id"] == xhr_oj) {
    switch (async_load[? "status"]) {
        case 1: break;
        case 0:
            var data = json_decode(async_load[? "result"]);
            var newsTitle = data[? "appnews"];
            newsTitle = newsTitle[? "newsitems"];
            newsTitle = newsTitle[| 0];
            newsTitle = newsTitle[? "title"];
            show_message("Latest news from 100% Orange Juice: " + newsTitle);
            ds_map_destroy(data);
        break;
        default:
            show_message("Can't fetch headlines from Steam.");
    }
}
GML:
xhr_gmlegacy = http_get_file("http://web.archive.org/web/20060821000040im_/http://gamemaker.nl/images/header.jpg", working_directory + "gmlegacy.jpg");
GML:
if (async_load[? "id"] == xhr_gmlegacy) {
    switch (async_load[? "status"]) {
        case 1: break;
        case 0:
            sprite_index = sprite_add(async_load[? "result"], 1, false, false, 0, 0);
        break;
        default:
            show_message("Failed to download the image!");
    }
}

Feedback Welcome!
If you have any suggestions for new constructors/use cases or bug reports, please open an issue or contribute on GitHub.
 
Last edited:
D

Deleted member 16767

Guest
I like it but I can't draw it, I have to use show_message. Or am I wrong?

Edit: Also, for anyone who want the content and not the title. Write this:

GML:
           // newsTitle = newsTitle[? "title"];
            newsTitle = newsTitle[? "contents"];
 
Last edited by a moderator:

FrostyCat

Redemption Seeker
I like it but I can't draw it, I have to use show_message. Or am I wrong?
You can't put draw_text() in the callback, this is still an asynchronous procedure. You have to deposit it into an instance variable and then draw that.

Create:
GML:
theTitle = "Loading...";
xhr_get("https://api.steampowered.com/ISteamNews/GetNewsForApp/v2/", {
    params: { appid: 282800, count: 1 },
    done: function(res) {
        theTitle = "Latest news from 100% Orange Juice: " + res.data.appnews.newsitems[0].title;
    },
    fail: function() {
        theTitle = "Can't fetch headlines from Steam.";
    }
});
Draw:
GML:
draw_text(x, y, theTitle);
 
D

Deleted member 16767

Guest
Excellent. I have the content on the start menu, and the title on the live thing.
 

FrostyCat

Redemption Seeker
Update: Request Master v1.2.0 now supports file downloads with support for headers and inline callbacks as usual!

GML:
xhr_download("http://web.archive.org/web/20060821000040im_/http://gamemaker.nl/images/header.jpg", working_directory + "gmlegacy.jpg", {
    done: function(res) {
        sprite_index = sprite_add(res.file, 1, false, false, 0, 0);
    },
    fail: function(res) {
        show_message_async("Failed to download the image!");
    }
});
 
D

Deleted member 16767

Guest
Hi. So my app is now live with your extension. It works great! Sprite could be replaced and it worked to copy and paste a url image with get_string().

Looking forward to more on this "daemon script". ;)

EDIT: However, I do get an error if I cancel the import and exit the app.

GML:
___________________________________________
############################################################################################
ERROR in
action number 1
of <Unknown Event>
for object __obj_download_master_daemon__:

Cannot delete buffer, it's in use by 1 others
at gml_Object___obj_download_master_daemon___CleanUp_0
############################################################################################
gml_Object___obj_download_master_daemon___CleanUp_0 (line -1)
 
Last edited by a moderator:
D

Deleted member 16767

Guest
I found the solution, here it is in bold text (well bold text doesnt work there but I think you know where the solution to that was).

GML:
        if point_in_rectangle(device_mouse_x_to_gui(0), device_mouse_y_to_gui(0), 768, 640, 768+128, 640+128) && mouse_check_button_pressed(mb_left)
        {
xhr_download(get_string("",""), file, {
    done: function(res) {
      

        sprite_index = sprite_add(res.file, 0, false, false, 0, 0);
        sprite_replace(spr_web_image_file,res.file,0,false,false,0,0)
        [B]sprite_index = spr_web_image_file;[/B]


    },
    fail: function(res) {
        show_message_async("Failed to download the image!");
    }
  
  
  
});
EDIT: Ah you actually have to wait for the image to load before you exit the app to not make it crash. I feel stupid now. So how can this be fixed?
 
Last edited by a moderator:

FrostyCat

Redemption Seeker
EDIT: Ah you actually have to wait for the image to load before you exit the app to not make it crash. I feel stupid now. So how can this be fixed?
You can get rid of the crash by changing the buffer_delete(buffer); in __obj_download_master_daemon__'s Cleanup event to this:
GML:
try {
    buffer_delete(buffer);
} catch (ex) {}
However, this will cause the buffer to leak whenever the daemon is destroyed early (e.g. by end of game as you discovered, or by the destruction of the owner, etc.), which is in itself another problem. There is no function for determining the usage status of a buffer, so this is an issue that I can only wait passively on the helpdesk for.

Update: I found a workaround that involves spawning another helper for cleanup, and it seemed to work for the sample. Could you try adding the following to your project and see if it stops your crash, @mikix ?

__obj_download_master_daemon__ Cleanup:
GML:
///@desc Cleanup
ds_map_destroy(headers);
if (!is_undefined(filename)) {
    try {
        buffer_delete(buffer);
    } catch (ex) {
        if (buffer_exists(buffer)) {
            with (instance_create_depth(0, 0, 0, __obj_download_master_buffer_hitman__)) {
                buffer = other.buffer;
            }
        }
    }
}
__obj_download_master_buffer_hitman__ Create:
GML:
persistent = true;
buffer = -1;
__obj_download_master_buffer_hitman__ End Step:
GML:
///@desc Try to hit the targeted buffer
try {
    buffer_delete(buffer);
} catch (ex) {
} finally {
    show_debug_message("DownloadMaster hitman cleaned up buffer " + string(buffer));
    instance_destroy();
}
 
Last edited:

bacteriaman

Member
@FrostyCat,

I downloaded your excellent library. It looks very robust, but I have a couple of questions before integrating it with my HTML5 project.

1) Has the library ever been tested with HTML5?
2) Is the size of the library suitable for HTML5?

Thanks in advance.
 

FrostyCat

Redemption Seeker
@FrostyCat,

I downloaded your excellent library. It looks very robust, but I have a couple of questions before integrating it with my HTML5 project.

1) Has the library ever been tested with HTML5?
2) Is the size of the library suitable for HTML5?

Thanks in advance.
Yes, the library has been tested with HTML5. With default HTML5 build settings, the library adds about 160kb to the compile. Do note that all HTTP-related restrictions for HTML5 still applies (e.g. CORS).

As of 2022.2.0.614, there is a slight bug in json_parse occasionally affecting the return value of instanceof for parsed structs, but the content body should still be fine.
 

bacteriaman

Member
Yes, the library has been tested with HTML5. With default HTML5 build settings, the library adds about 160kb to the compile. Do note that all HTTP-related restrictions for HTML5 still applies (e.g. CORS).

As of 2022.2.0.614, there is a slight bug in json_parse occasionally affecting the return value of instanceof for parsed structs, but the content body should still be fine.
Thanks for the confirmation. And yes, I have already configured CORS.

I'm aware of the bug with json_parse. The bug has been resolved in the beta version.

Thanks again.
 
Top