Skip to content

Commit

Permalink
some updates for fetch.md
Browse files Browse the repository at this point in the history
  • Loading branch information
Fleeym committed Jun 8, 2024
1 parent 955fed8 commit 0ee15b8
Showing 1 changed file with 32 additions and 50 deletions.
82 changes: 32 additions & 50 deletions tutorials/fetch.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# Making web requests

> :warning: This is the documentation for the new web API. We have left the old documentation at the bottom of the page, for the time being
Making web requests in C++ sucks, and GD doesn't really help with it. Cocos2d has the `CCHTTPRequest` class, which GD uses to make its web requests, but it's a complicated mess to try to get working and it's not really ergonomical.

To fix this, Geode has the `WebRequest` API, which is much more versatile. All web requests are **asynchronous**, so they run on a separate thread.
Expand Down Expand Up @@ -33,7 +31,7 @@ req.bodyString("Hello, server!");
// JSON (uses any matjson::Value)
// Note that you should probably have some actual data in your json
auto myjson = matjson::Value();
req.json(myjson);
req.bodyJSON(myjson);

// Let's set some headers, shall we?
req.header("Content-Type", "application/json");
Expand Down Expand Up @@ -99,59 +97,43 @@ res->string();
res->json();
```

## The old web APIs

The old web APIs were deprecated as of **v3.0.0-alpha.1**, you can still find them on older versions of Geode. They contain functions resembling a familiar web API: **fetch**. Geode has two types of web requests: **synchronous** and **asynchronous**.

## Synchronous requests

This is the easiest form of web request you can make. Want to fetch some simple data? Use `web::fetch("https://site.url")`. The function returns a result of either a string, or an error message. Similarly, you can use `web::fetchBytes` to get a byte array instead, or `web::fetchFile` to download a file.

Example:

```cpp
#include <Geode/utils/web.hpp>

using namespace geode::prelude;

// download a- uh...
auto res = web::fetch("https://pastebin.com/raw/vNi1WHNF");
if (!res) {
// handle error
}

// the resulting data :3
auto data = res.value();

```
However, the synchronous web API should **only be used for short, simple data**. For downloading anything larger, it is recommended to use the **asynchronous** API instead.
## Asynchronous requests
Of course, you could just create an `std::thread` and call `fetch` inside that, but that comes with all sorts of thread safety concerns, and you can't interact with the GD UI from another thread. Luckily, Geode's `web::AsyncWebRequest` API is fully **thread-safe** and all its callbacks are run **in the GD thread**, meaning you can safely interact with UI.
## A full example

Creating an async web request is really simple:
Let's do a full example. This simple code will hook MenuLayer::init, run a request, and log the resulting response, as a string.

```cpp
#include <Geode/modify/MenuLayer.hpp>
#include <Geode/utils/web.hpp>
#include <Geode/loader/Event.hpp>

using namespace geode::prelude;

web::AsyncWebRequest()
.fetch("https://pastebin.com/raw/vNi1WHNF")
.text()
.then([](std::string const& catgirl) {
// do something with the catgirl :3
})
.expect([](std::string const& error) {
// something went wrong with our web request Q~Q
});
class $modify(MenuLayer) {
struct Fields {
EventListener<web::WebTask> m_listener;
};

bool init() {
if (!MenuLayer::init()) {
return false;
}

m_fields->m_listener.bind([] (web::WebTask::Event* e) {
// We don't really care about the request progress, so we won't use it here

if (web::WebResponse* res = e->getValue()) {
log::info("{}", res->string()->unwrapOr("Uh oh!"));
} else if (e->isCancelled()) {
log::info("The request was cancelled... So sad :(");
}
});

auto req = web::WebRequest();
// Let's fetch... uhh...
m_fields->m_listener.setFilter(req.get("https://pastebin.com/raw/vNi1WHNF"));
return true;
}
};
```

That's it. The web request will then be dispatched, and after it's finished the callback passed to `then` will be run, and on failure the callback passed to `expect` will be run.

There are also callbacks like `progress` for keeping track of the file download, and `cancelled` for checking if the request has been cancelled. The latter is not usually required, as `expect` will be run aswell if the request is cancelled while it is downloading. However, if you're downloading a bunch of files in bulk, a request may be cancelled after it has been finished, in which case you should use `cancelled` to clean up any side-effects the `then` callback caused.

If you want to ensure that a web request is only made once, for example if you want to update a global manager class, you can use the `join` method on `AsyncWebRequest` to make sure only one instance of the same request exists at a time. Join takes a fully arbitary string ID, which can be anything, but you should make sure it's unique.

Now open your game (with the developer console enabled) and check to see if your request works!

0 comments on commit 0ee15b8

Please sign in to comment.