Skip to content

Commit b77a18b

Browse files
committed
download loader from given urls by the server
1 parent f326379 commit b77a18b

4 files changed

Lines changed: 80 additions & 60 deletions

File tree

loader/src/loader/updater.cpp

Lines changed: 51 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,6 @@ static StringMap<async::TaskHolder<web::WebResponse>> RUNNING_REQUESTS {};
1717

1818
bool s_isNewUpdateDownloaded = false;
1919

20-
namespace {
21-
inline std::string formatDownloadUrl(std::string_view tag) {
22-
return fmt::format("https://github.com/geode-sdk/geode/releases/download/{0}/geode-{0}-{1}.zip", tag, GEODE_PLATFORM_SHORT_IDENTIFIER_NOARCH);
23-
}
24-
25-
inline std::string formatResourcesUrl(std::string_view tag) {
26-
return fmt::format("https://github.com/geode-sdk/geode/releases/download/{}/resources.zip", tag);
27-
}
28-
}
29-
3020
void updater::downloadLatestLoaderResources() {
3121
log::debug("Downloading latest resources");
3222

@@ -36,10 +26,7 @@ void updater::downloadLatestLoaderResources() {
3626
if (res.ok()) {
3727
auto& release = res.unwrap();
3828

39-
updater::tryDownloadLoaderResources(
40-
formatResourcesUrl(release.tag),
41-
false
42-
);
29+
updater::tryDownloadLoaderResources(release.resources.url, release.resources.hash, false);
4330
} else {
4431
ResourceDownloadEvent().send(
4532
UpdateFailed("Unable to download resources: " + res.unwrapErr().details)
@@ -49,7 +36,13 @@ void updater::downloadLatestLoaderResources() {
4936
);
5037
}
5138

52-
Result<> updater::extractLoaderResources(ByteSpan data) {
39+
Result<> updater::extractLoaderResources(ByteSpan data, std::string_view expectedHash) {
40+
auto actualHash = geode::sha256(data).toString();
41+
if (actualHash != expectedHash) {
42+
log::error("Hash mismatch in downloaded resources: expected {} but got {}", expectedHash, actualHash);
43+
return Err("Hash mismatch in downloaded resources");
44+
}
45+
5346
auto tempDir = dirs::getGeodeResourcesDir() / fmt::format("{}_tmp", Mod::get()->getID());
5447
auto resourcesDir = dirs::getGeodeResourcesDir() / Mod::get()->getID();
5548

@@ -90,7 +83,7 @@ Result<> updater::extractLoaderResources(ByteSpan data) {
9083
return Ok();
9184
}
9285

93-
void updater::tryDownloadLoaderResources(std::string url, bool tryLatestOnError) {
86+
void updater::tryDownloadLoaderResources(std::string url, std::string hash, bool tryLatestOnError) {
9487
if (RUNNING_REQUESTS.contains(url)) return;
9588

9689
auto progress = [](const web::WebProgress& prog) {
@@ -106,10 +99,10 @@ void updater::tryDownloadLoaderResources(std::string url, bool tryLatestOnError)
10699
holder.spawn(
107100
"Geode resources download",
108101
web::WebRequest{}.onProgress(std::move(progress)).get(url),
109-
[url](auto response) {
102+
[url, hash = std::move(hash)](auto response) {
110103
if (response.ok()) {
111104
auto data = std::move(response).data();
112-
if (GEODE_UNWRAP_IF_ERR(e, updater::extractLoaderResources(data))) {
105+
if (GEODE_UNWRAP_IF_ERR(e, updater::extractLoaderResources(data, hash))) {
113106
ResourceDownloadEvent().send(UpdateFailed(e));
114107
} else {
115108
ResourceDownloadEvent().send(UpdateFinished());
@@ -151,9 +144,7 @@ void updater::downloadLoaderResources(bool useLatestRelease) {
151144
if (res.ok()) {
152145
auto& release = res.unwrap();
153146

154-
updater::tryDownloadLoaderResources(
155-
formatResourcesUrl(release.tag), false
156-
);
147+
updater::tryDownloadLoaderResources(release.resources.url, release.resources.hash, false);
157148

158149
DOWNLOADING_LOADER_RESOURCES = false;
159150
return;
@@ -233,7 +224,7 @@ bool updater::verifyLoaderResources() {
233224
return true;
234225
}
235226

236-
void updater::downloadLoaderUpdate(std::string url) {
227+
void updater::downloadLoaderUpdate(std::string url, std::string hash) {
237228
if (RUNNING_REQUESTS.contains("@downloadLoaderUpdate")) return;
238229

239230
auto req = web::WebRequest();
@@ -249,49 +240,53 @@ void updater::downloadLoaderUpdate(std::string url) {
249240
auto& holder = RUNNING_REQUESTS["@downloadLoaderUpdate"];
250241
holder.spawn(
251242
req.get(std::move(url)),
252-
[](web::WebResponse response) {
243+
[hash = std::move(hash)](web::WebResponse response) {
253244
RUNNING_REQUESTS.erase("@downloadLoaderUpdate");
254245

255-
auto updateZip = dirs::getTempDir() / "loader-update.zip";
256-
auto targetDir = dirs::getGeodeDir() / "update";
257-
258-
if (response.ok()) {
259-
// unzip resources zip
260-
auto data = std::move(response).data();
261-
auto unzip = file::Unzip::create(data);
262-
if (unzip) {
263-
auto ok = unzip.unwrap().extractAllTo(targetDir);
264-
if (ok) {
265-
s_isNewUpdateDownloaded = true;
266-
LoaderUpdateEvent().send(UpdateFinished());
267-
}
268-
else {
269-
LoaderUpdateEvent().send(
270-
UpdateFailed("Unable to unzip update: " + ok.unwrapErr())
271-
);
272-
Mod::get()->setSavedValue("last-modified-auto-update-check", std::string());
273-
}
274-
}
275-
else {
276-
LoaderUpdateEvent().send(
277-
UpdateFailed("Unable to unzip update: " + unzip.unwrapErr())
278-
);
279-
Mod::get()->setSavedValue("last-modified-auto-update-check", std::string());
280-
}
281-
}
282-
else {
283-
auto info = response.string().unwrapOr("Unknown error");
284-
log::error("Failed to download latest update {}", info);
246+
auto result = installLoaderUpdate(std::move(response), hash);
247+
if (!result) {
248+
log::error("Failed to install latest update: {}", result.unwrapErr());
285249
LoaderUpdateEvent().send(
286-
UpdateFailed("Unable to download update: " + info)
250+
UpdateFailed(fmt::format("Unable to install loader update: {}", result.unwrapErr()))
287251
);
288-
289252
Mod::get()->setSavedValue("last-modified-auto-update-check", std::string());
290253
}
291254
}
292255
);
293256
}
294257

258+
Result<> updater::installLoaderUpdate(utils::web::WebResponse response, std::string_view expectedHash) {
259+
auto targetDir = dirs::getGeodeDir() / "update";
260+
261+
if (!response.ok()) {
262+
auto info = response.string().unwrapOr("Unknown error");
263+
return Err("Download failed: {}", info);
264+
}
265+
266+
// validate hash
267+
auto data = std::move(response).data();
268+
auto actualHash = geode::sha256(data).toString();
269+
if (actualHash != expectedHash) {
270+
log::error("Hash mismatch in downloaded loader update, we expected {}, but got {}", expectedHash, actualHash);
271+
return Err("Hash mismatch in downloaded loader update");
272+
}
273+
274+
// unzip resources zip
275+
auto unzip = file::Unzip::create(data);
276+
if (!unzip) {
277+
return Err("Unable to unzip update: {}", unzip.unwrapErr());
278+
}
279+
280+
auto ok = unzip.unwrap().extractAllTo(targetDir);
281+
if (!ok) {
282+
return Err("Unable to extract update: {}", ok.unwrapErr());
283+
}
284+
285+
s_isNewUpdateDownloaded = true;
286+
LoaderUpdateEvent().send(UpdateFinished());
287+
return Ok();
288+
}
289+
295290
void updater::checkForLoaderUpdates() {
296291
// Check for updates in the background
297292
async::spawn(
@@ -314,8 +309,7 @@ void updater::checkForLoaderUpdates() {
314309
return;
315310
}
316311

317-
// find release asset
318-
updater::downloadLoaderUpdate(formatDownloadUrl(release.tag));
312+
updater::downloadLoaderUpdate(release.download.url, release.download.hash);
319313
} else {
320314
auto info = res.unwrapErr().details;
321315
log::error("Failed to fetch updates {}", info);

loader/src/loader/updater.hpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <Geode/loader/Event.hpp>
66
#include <Geode/utils/function.hpp>
77
#include <Geode/utils/general.hpp>
8+
#include <Geode/utils/web.hpp>
89

910
namespace geode::updater {
1011
using UpdateFinished = std::monostate;
@@ -25,11 +26,12 @@ namespace geode::updater {
2526
};
2627

2728
void updateSpecialFiles();
28-
Result<> extractLoaderResources(ByteSpan data);
29-
void tryDownloadLoaderResources(std::string url, bool tryLatestOnError = true);
29+
Result<> extractLoaderResources(ByteSpan data, std::string_view hash);
30+
void tryDownloadLoaderResources(std::string url, std::string hash, bool tryLatestOnError = true);
3031
void downloadLoaderResources(bool useLatestRelease = false);
3132
void downloadLatestLoaderResources();
32-
void downloadLoaderUpdate(std::string url);
33+
void downloadLoaderUpdate(std::string url, std::string hash);
34+
Result<> installLoaderUpdate(utils::web::WebResponse response, std::string_view expectedHash);
3335

3436
bool verifyLoaderResources();
3537
void checkForLoaderUpdates();

loader/src/server/Server.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,14 @@ Result<ServerModsList> ServerModsList::parse(matjson::Value raw) {
620620
return payload.ok(std::move(list));
621621
}
622622

623+
Result<ServerLoaderDownload> ServerLoaderDownload::parse(matjson::Value json) {
624+
auto root = checkJson(std::move(json), "ServerLoaderDownload");
625+
auto res = ServerLoaderDownload();
626+
root.needs("url").into(res.url);
627+
root.needs("hash").into(res.hash);
628+
return root.ok(std::move(res));
629+
}
630+
623631
Result<ServerLoaderVersion> ServerLoaderVersion::parse(matjson::Value raw) {
624632
auto root = checkJson(std::move(raw), "ServerLoaderVersion");
625633

@@ -631,6 +639,13 @@ Result<ServerLoaderVersion> ServerLoaderVersion::parse(matjson::Value raw) {
631639
auto gd_obj = root.needs("gd");
632640
gd_obj.needs(GEODE_PLATFORM_SHORT_IDENTIFIER).into(res.gameVersion);
633641

642+
auto downloads = root.needs("downloads");
643+
644+
auto dlobj = downloads.needs(GEODE_PLATFORM_SHORT_IDENTIFIER_NOARCH);
645+
auto rsobj = downloads.needs("resources");
646+
res.download = GEODE_UNWRAP(ServerLoaderDownload::parse(dlobj.takeJson()));
647+
res.resources = GEODE_UNWRAP(ServerLoaderDownload::parse(rsobj.takeJson()));
648+
634649
return root.ok(std::move(res));
635650
}
636651

loader/src/server/Server.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,20 @@ namespace server {
112112
static Result<ServerModsList> parse(matjson::Value json);
113113
};
114114

115+
struct ServerLoaderDownload final {
116+
std::string url;
117+
std::string hash;
118+
119+
static Result<ServerLoaderDownload> parse(matjson::Value json);
120+
};
121+
115122
struct ServerLoaderVersion final {
116123
std::string version;
117124
std::string tag;
118125
std::string commitHash;
119126
std::string gameVersion;
127+
ServerLoaderDownload download;
128+
ServerLoaderDownload resources;
120129

121130
static Result<ServerLoaderVersion> parse(matjson::Value json);
122131
};

0 commit comments

Comments
 (0)