rewrite updater to use new webrequests

This commit is contained in:
HJfod 2024-04-24 10:03:22 +03:00
parent ae1a766aa6
commit f3aac4a71a

View file

@ -1,5 +1,5 @@
#include "updater.hpp"
#include <Geode/utils/web.hpp>
#include <Geode/utils/web2.hpp>
#include <resources.hpp>
#include <hash.hpp>
#include <utility>
@ -9,6 +9,8 @@
using namespace geode::prelude;
static std::unordered_map<std::string, web::WebTask> RUNNING_REQUESTS {};
updater::ResourceDownloadEvent::ResourceDownloadEvent(
UpdateStatus status
) : status(std::move(status)) {}
@ -61,23 +63,39 @@ void updater::fetchLatestGithubRelease(
modifiedSince = Mod::get()->getSavedValue("last-modified-auto-update-check", std::string());
}
web::AsyncWebRequest()
.join("loader-auto-update-check")
.header("If-Modified-Since", modifiedSince)
.userAgent("github_api/1.0")
.fetch("https://api.github.com/repos/geode-sdk/geode/releases/latest")
.text()
.then([then, expect](web::SentAsyncWebRequest& req, std::string const& text) {
if (text.empty()) {
expect("Empty response");
return;
if (RUNNING_REQUESTS.contains("@loaderAutoUpdateCheck")) return;
auto req = web::WebRequest();
req.header("If-Modified-Since", modifiedSince);
req.userAgent("github_api/1.0");
RUNNING_REQUESTS.emplace(
"@loaderAutoUpdateCheck",
req.get("https://api.github.com/repos/geode-sdk/geode/releases/latest").map(
[expect = std::move(expect), then = std::move(then)](web::WebResponse* response) {
if (response->ok()) {
if (response->data().empty()) {
expect("Empty response");
}
else {
auto json = response->json();
if (!json) {
expect("Not a JSON response");
}
else {
Mod::get()->setSavedValue("last-modified-auto-update-check", response->header("Last-Modified").value_or(""));
s_latestGithubRelease = *json;
then(*s_latestGithubRelease);
}
}
}
else {
expect(response->string().unwrapOr("Unknown error"));
}
RUNNING_REQUESTS.erase("@loaderAutoUpdateCheck");
return *response;
}
auto json = matjson::parse(text);
Mod::get()->setSavedValue("last-modified-auto-update-check", req.getResponseHeader("Last-Modified"));
s_latestGithubRelease = json;
then(json);
})
.expect(std::move(expect));
)
);
}
void updater::downloadLatestLoaderResources() {
@ -113,49 +131,58 @@ void updater::downloadLatestLoaderResources() {
);
}
void updater::tryDownloadLoaderResources(
std::string const& url,
bool tryLatestOnError
) {
void updater::tryDownloadLoaderResources(std::string const& url, bool tryLatestOnError) {
auto tempResourcesZip = dirs::getTempDir() / "new.zip";
auto resourcesDir = dirs::getGeodeResourcesDir() / Mod::get()->getID();
web::AsyncWebRequest()
// use the url as a join handle
.join(url)
.fetch(url)
.into(tempResourcesZip)
.then([tempResourcesZip, resourcesDir](auto) {
// unzip resources zip
auto unzip = file::Unzip::intoDir(tempResourcesZip, resourcesDir, true);
if (!unzip) {
ResourceDownloadEvent(
UpdateFailed("Unable to unzip new resources: " + unzip.unwrapErr())
).post();
return;
}
updater::updateSpecialFiles();
if (RUNNING_REQUESTS.contains(url)) return;
ResourceDownloadEvent(UpdateFinished()).post();
})
.expect([tryLatestOnError](std::string const& info, int code) {
// if the url was not found, try downloading latest release instead
// (for development versions)
if (code == 404) {
log::warn("Unable to download resources: {}", info);
auto req = web::WebRequest();
RUNNING_REQUESTS.emplace(url, req.get(url).map(
[url, resourcesDir](web::WebResponse* response) {
if (response->ok()) {
// unzip resources zip
auto unzip = file::Unzip::create(response->data());
if (unzip) {
auto ok = unzip->extractAllTo(resourcesDir);
if (ok) {
updater::updateSpecialFiles();
ResourceDownloadEvent(UpdateFinished()).post();
}
else {
ResourceDownloadEvent(
UpdateFailed("Unable to unzip new resources: " + ok.unwrapErr())
).post();
}
}
else {
ResourceDownloadEvent(UpdateFailed("Unable to unzip new resources: " + unzip.unwrapErr())).post();
}
}
ResourceDownloadEvent(
UpdateFailed("Unable to download resources: " + info)
).post();
})
.progress([](auto&, double now, double total) {
else {
auto reason = response->string().unwrapOr("Unknown");
// if the url was not found, try downloading latest release instead
// (for development versions)
if (response->code() == 404) {
log::warn("Unable to download resources: {}", reason);
}
ResourceDownloadEvent(
UpdateFailed("Unable to download resources: " + reason)
).post();
}
RUNNING_REQUESTS.erase(url);
return *response;
},
[](web::WebProgress* progress) {
ResourceDownloadEvent(
UpdateProgress(
static_cast<uint8_t>(now / total * 100.0),
static_cast<uint8_t>(progress->downloadProgress().value_or(0)),
"Downloading resources"
)
).post();
});
return *progress;
}
));
}
void updater::updateSpecialFiles() {
@ -167,46 +194,49 @@ void updater::updateSpecialFiles() {
}
void updater::downloadLoaderResources(bool useLatestRelease) {
web::AsyncWebRequest()
.join("loader-tag-exists-check")
.header("If-Modified-Since", Mod::get()->getSavedValue("last-modified-tag-exists-check", std::string()))
.userAgent("github_api/1.0")
.fetch("https://api.github.com/repos/geode-sdk/geode/releases/tags/" + Loader::get()->getVersion().toString())
.json()
.then([](web::SentAsyncWebRequest& req, matjson::Value const& json) {
Mod::get()->setSavedValue("last-modified-tag-exists-check", req.getResponseHeader("Last-Modified"));
auto raw = json;
JsonChecker checker(raw);
auto root = checker.root("[]").obj();
if (RUNNING_REQUESTS.contains("@downloadLoaderResources")) return;
// find release asset
for (auto asset : root.needs("assets").iterate()) {
auto obj = asset.obj();
if (obj.needs("name").template get<std::string>() == "resources.zip") {
updater::tryDownloadLoaderResources(
obj.needs("browser_download_url").template get<std::string>(),
false
);
return;
auto req = web::WebRequest();
req.header("If-Modified-Since", Mod::get()->getSavedValue("last-modified-tag-exists-check", std::string()));
req.userAgent("github_api/1.0");
RUNNING_REQUESTS.emplace(
"@downloadLoaderResources",
req.get("https://api.github.com/repos/geode-sdk/geode/releases/tags/" + Loader::get()->getVersion().toString()).map(
[useLatestRelease](web::WebResponse* response) {
RUNNING_REQUESTS.erase("@downloadLoaderResources");
if (response->ok()) {
if (auto ok = response->json()) {
auto json = ok.unwrap();
JsonChecker checker(json);
auto root = checker.root("[]").obj();
// find release asset
for (auto asset : root.needs("assets").iterate()) {
auto obj = asset.obj();
if (obj.needs("name").template get<std::string>() == "resources.zip") {
updater::tryDownloadLoaderResources(
obj.needs("browser_download_url").template get<std::string>(),
false
);
return *response;
}
}
ResourceDownloadEvent(UpdateFailed("Unable to find resources in release")).post();
return *response;
}
}
ResourceDownloadEvent(
UpdateFailed("Unable to find resources in release")
).post();
})
.expect([=](std::string const& info, int code) {
if (useLatestRelease) {
log::debug("Loader version {} does not exist, trying to download latest resources", Loader::get()->getVersion().toString());
downloadLatestLoaderResources();
}
else {
log::debug("Loader version {} does not exist on GitHub, not downloading the resources", Loader::get()->getVersion().toString());
ResourceDownloadEvent(
UpdateFinished()
).post();
ResourceDownloadEvent(UpdateFinished()).post();
}
});
return *response;
}
));
}
bool updater::verifyLoaderResources() {
@ -273,40 +303,59 @@ void updater::downloadLoaderUpdate(std::string const& url) {
auto updateZip = dirs::getTempDir() / "loader-update.zip";
auto targetDir = dirs::getGeodeDir() / "update";
web::AsyncWebRequest()
.join("loader-update-download")
.fetch(url)
.into(updateZip)
.then([updateZip, targetDir](auto) {
// unzip resources zip
auto unzip = file::Unzip::intoDir(updateZip, targetDir, true);
if (!unzip) {
LoaderUpdateEvent(
UpdateFailed("Unable to unzip update: " + unzip.unwrapErr())
).post();
Mod::get()->setSavedValue("last-modified-auto-update-check", std::string());
return;
}
s_isNewUpdateDownloaded = true;
LoaderUpdateEvent(UpdateFinished()).post();
})
.expect([](std::string const& info) {
log::error("Failed to download latest update {}", info);
LoaderUpdateEvent(
UpdateFailed("Unable to download update: " + info)
).post();
if (RUNNING_REQUESTS.contains("@downloadLoaderUpdate")) return;
Mod::get()->setSavedValue("last-modified-auto-update-check", std::string());
})
.progress([](auto&, double now, double total) {
LoaderUpdateEvent(
UpdateProgress(
static_cast<uint8_t>(now / total * 100.0),
"Downloading update"
)
).post();
});
auto req = web::WebRequest();
RUNNING_REQUESTS.emplace(
"@downloadLoaderUpdate",
req.get(url).map(
[targetDir](web::WebResponse* response) {
if (response->ok()) {
// unzip resources zip
auto unzip = file::Unzip::create(response->data());
if (unzip) {
auto ok = unzip->extractAllTo(targetDir);
if (ok) {
s_isNewUpdateDownloaded = true;
LoaderUpdateEvent(UpdateFinished()).post();
}
else {
LoaderUpdateEvent(
UpdateFailed("Unable to unzip update: " + ok.unwrapErr())
).post();
Mod::get()->setSavedValue("last-modified-auto-update-check", std::string());
}
}
else {
LoaderUpdateEvent(
UpdateFailed("Unable to unzip update: " + unzip.unwrapErr())
).post();
Mod::get()->setSavedValue("last-modified-auto-update-check", std::string());
}
}
else {
auto info = response->string().unwrapOr("Unknown error");
log::error("Failed to download latest update {}", info);
LoaderUpdateEvent(
UpdateFailed("Unable to download update: " + info)
).post();
Mod::get()->setSavedValue("last-modified-auto-update-check", std::string());
}
RUNNING_REQUESTS.erase("@downloadLoaderUpdate");
return *response;
},
[](web::WebProgress* progress) {
LoaderUpdateEvent(
UpdateProgress(
static_cast<uint8_t>(progress->downloadProgress().value_or(0)),
"Downloading update"
)
).post();
return *progress;
}
)
);
}
void updater::checkForLoaderUpdates() {