summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMeway <mewaysid92@gmail.com>2025-07-13 11:47:06 -0500
committerMeway <mewaysid92@gmail.com>2025-07-13 11:47:06 -0500
commitb16d9d652296e7252de8223be20efced1e9bac7e (patch)
treee4b1f363d806c67d0bc759be578342a300b1a6ca
parentf36f0bc8fd28d84ae40e5e3eff2c2ba8a1b769b0 (diff)
downloadmana-b16d9d652296e7252de8223be20efced1e9bac7e.tar.gz
mana-b16d9d652296e7252de8223be20efced1e9bac7e.tar.bz2
mana-b16d9d652296e7252de8223be20efced1e9bac7e.tar.xz
mana-b16d9d652296e7252de8223be20efced1e9bac7e.zip
Updated logic to be more informative to users with throttled internet connections
-rw-r--r--src/gui/updaterwindow.cpp6
-rw-r--r--src/gui/updaterwindow.h2
-rw-r--r--src/net/download.cpp112
-rw-r--r--src/net/download.h1
4 files changed, 75 insertions, 46 deletions
diff --git a/src/gui/updaterwindow.cpp b/src/gui/updaterwindow.cpp
index 5cfb45cd..646f0cb5 100644
--- a/src/gui/updaterwindow.cpp
+++ b/src/gui/updaterwindow.cpp
@@ -318,6 +318,12 @@ void UpdaterWindow::logic()
mProgressBar->setProgress(progress);
}
+void UpdaterWindow::cancelDownloads() {
+ // Assuming UpdaterWindow has a Download* member (e.g., mDownload)
+ if (mDownload) {
+ mDownload->cancel();
+ }
+}
void UpdaterWindow::downloadCompleted()
{
diff --git a/src/gui/updaterwindow.h b/src/gui/updaterwindow.h
index dd1400ba..f87c6924 100644
--- a/src/gui/updaterwindow.h
+++ b/src/gui/updaterwindow.h
@@ -76,6 +76,8 @@ class UpdaterWindow : public Window, public gcn::ActionListener,
void keyPressed(gcn::KeyEvent &keyEvent) override;
void logic() override;
+
+ void cancelDownloads();
private:
bool cancel();
diff --git a/src/net/download.cpp b/src/net/download.cpp
index 7aab3b2f..f96ae600 100644
--- a/src/net/download.cpp
+++ b/src/net/download.cpp
@@ -19,6 +19,8 @@
*/
#include "net/download.h"
+#include "client.h"
+#include "main.h"
#include "configuration.h"
#include "log.h"
@@ -35,9 +37,6 @@ constexpr char DOWNLOAD_ERROR_MESSAGE_THREAD[] = "Could not create download thre
namespace Net {
-/**
- * Calculates the Alder-32 checksum for the given file.
- */
unsigned long Download::fadler32(FILE *file)
{
if (!file || fseek(file, 0, SEEK_END) != 0)
@@ -49,7 +48,6 @@ unsigned long Download::fadler32(FILE *file)
rewind(file);
- // Calculate Adler-32 checksum
void *buffer = malloc(fileSize);
const size_t read = fread(buffer, 1, fileSize, file);
unsigned long adler = adler32_z(0L, Z_NULL, 0);
@@ -76,8 +74,7 @@ Download::~Download()
void Download::addHeader(const char *header)
{
- assert(!mThread); // Cannot add headers after starting download
-
+ assert(!mThread);
mHeaders = curl_slist_append(mHeaders, header);
}
@@ -90,8 +87,7 @@ void Download::noCache()
void Download::setFile(const std::string &filename,
std::optional<unsigned long> adler32)
{
- assert(!mThread); // Cannot set file after starting download
-
+ assert(!mThread);
mMemoryWrite = false;
mFileName = filename;
mAdler = adler32;
@@ -99,15 +95,13 @@ void Download::setFile(const std::string &filename,
void Download::setUseBuffer()
{
- assert(!mThread); // Cannot set write function after starting download
-
+ assert(!mThread);
mMemoryWrite = true;
}
bool Download::start()
{
- assert(!mThread); // Download already started
-
+ assert(!mThread);
logger->log("Starting download: %s", mUrl.c_str());
mThread = SDL_CreateThread(downloadThread, "Download", this);
@@ -131,13 +125,10 @@ void Download::cancel()
std::string_view Download::getBuffer() const
{
- assert(mMemoryWrite); // Buffer not used
+ assert(mMemoryWrite);
return std::string_view(mBuffer, mDownloadedBytes);
}
-/**
- * A libcurl callback for reporting progress.
- */
int Download::downloadProgress(void *clientp,
curl_off_t dltotal, curl_off_t dlnow,
curl_off_t ultotal, curl_off_t ulnow)
@@ -153,9 +144,6 @@ int Download::downloadProgress(void *clientp,
return d->mCancel;
}
-/**
- * A libcurl callback for writing to memory.
- */
size_t Download::writeBuffer(char *ptr, size_t size, size_t nmemb, void *stream)
{
auto *d = reinterpret_cast<Download *>(stream);
@@ -176,12 +164,18 @@ int Download::downloadThread(void *ptr)
auto *d = reinterpret_cast<Download*>(ptr);
bool complete = false;
std::string outFilename;
+ curl_off_t resumeOffset = 0;
if (!d->mMemoryWrite)
outFilename = d->mFileName + ".part";
- for (int attempts = 0; attempts < 3 && !complete && !d->mCancel; ++attempts)
+ for (int attempts = 0; attempts < 5 && !complete && !d->mCancel; ++attempts)
{
+ if (attempts > 0) {
+ SDL_Delay(2000 * (1 << attempts)); // 2s, 4s, 8s, 16s
+ logger->log("Retry attempt %d for %s at offset %lld", attempts + 1, d->mUrl.c_str(), resumeOffset);
+ }
+
CURL *curl = curl_easy_init();
if (!curl)
break;
@@ -201,8 +195,16 @@ int Download::downloadThread(void *ptr)
}
else
{
- file = fopen(outFilename.c_str(), "w+b");
+ file = fopen(outFilename.c_str(), resumeOffset ? "ab" : "wb"); // Append if resuming
+ if (!file) {
+ logger->log("Failed to open file %s", outFilename.c_str());
+ curl_easy_cleanup(curl);
+ break;
+ }
curl_easy_setopt(curl, CURLOPT_WRITEDATA, file);
+ if (resumeOffset) {
+ curl_easy_setopt(curl, CURLOPT_RANGE, (std::to_string(resumeOffset) + "-").c_str());
+ }
}
const std::string appShort = branding.getStringValue("appShort");
@@ -216,70 +218,84 @@ int Download::downloadThread(void *ptr)
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, &Download::downloadProgress);
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, ptr);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
- curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 15);
+ curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30); // 30s for connection
+ curl_easy_setopt(curl, CURLOPT_TIMEOUT, 2100); // 30 for total download
+ curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 100); // 100 bytes/s
+ curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, 60); // 60s low speed timeout
const CURLcode res = curl_easy_perform(curl);
+
+ if (!d->mMemoryWrite && file) {
+ // Update resume offset
+ resumeOffset = ftell(file);
+ logger->log("Download attempt %d progress: %lld bytes", attempts + 1, resumeOffset);
+ fclose(file);
+ file = nullptr;
+ }
+
curl_easy_cleanup(curl);
if (res == CURLE_ABORTED_BY_CALLBACK)
{
d->mCancel = true;
-
if (file)
{
fclose(file);
::remove(outFilename.c_str());
}
-
break;
}
if (res != CURLE_OK)
{
- logger->log("curl error %d: %s host: %s",
- res, d->mError, d->mUrl.c_str());
-
- if (file)
- {
+ logger->log("curl error %d: %s host: %s", res, d->mError, d->mUrl.c_str());
+ if (file) {
fclose(file);
+ file = nullptr;
+ }
+ auto state = d->mState.lock();
+ state->status = DownloadStatus::Error;
+ snprintf(d->mError, CURL_ERROR_SIZE, "Failed to download %s: %s", d->mUrl.c_str(), curl_easy_strerror(res));
+ if (res == CURLE_URL_MALFORMAT || res == CURLE_COULDNT_RESOLVE_HOST || res == CURLE_HTTP_RETURNED_ERROR)
+ {
+ extern std::string errorMessage;
+ ::errorMessage = d->mError;
+ Client::instance()->showErrorDialog(::errorMessage, STATE_CHOOSE_SERVER);
+ break;
+ }
+ if (!d->mMemoryWrite && resumeOffset > 0) {
+ logger->log("Resumable progress: %lld bytes", resumeOffset);
+ } else {
::remove(outFilename.c_str());
+ resumeOffset = 0; // Reset if not resumable
}
-
- break;
+ continue;
}
if (!d->mMemoryWrite)
{
- // Check the checksum if available
if (d->mAdler)
{
+ file = fopen(outFilename.c_str(), "rb");
unsigned long adler = fadler32(file);
-
if (d->mAdler != adler)
{
if (file)
fclose(file);
-
- // Remove the corrupted file
::remove(outFilename.c_str());
logger->log("Checksum for file %s failed: (%lx/%lx)",
- d->mFileName.c_str(),
- adler, *d->mAdler);
-
- continue; // Bail out here to avoid the renaming
+ d->mFileName.c_str(), adler, *d->mAdler);
+ resumeOffset = 0; // Reset on checksum failure
+ continue;
}
}
if (file)
fclose(file);
- // Any existing file with this name is deleted first, otherwise
- // the rename will fail on Windows.
::remove(d->mFileName.c_str());
::rename(outFilename.c_str(), d->mFileName.c_str());
- // Check if we can open it and no errors were encountered
- // during renaming
file = fopen(d->mFileName.c_str(), "rb");
if (file)
{
@@ -290,7 +306,6 @@ int Download::downloadThread(void *ptr)
}
else
{
- // It's stored in memory, we're done
complete = true;
}
@@ -303,10 +318,15 @@ int Download::downloadThread(void *ptr)
state->status = DownloadStatus::Canceled;
else if (complete)
state->status = DownloadStatus::Complete;
- else
+ else {
state->status = DownloadStatus::Error;
+ snprintf(d->mError, CURL_ERROR_SIZE, "Download failed after %d attempts: %s", 5, d->mUrl.c_str());
+ extern std::string errorMessage;
+ ::errorMessage = d->mError;
+ Client::instance()->showErrorDialog(::errorMessage, STATE_CHOOSE_SERVER);
+ }
return 0;
}
-} // namespace Net
+} // namespace Net \ No newline at end of file
diff --git a/src/net/download.h b/src/net/download.h
index e9483fa5..f7021ab3 100644
--- a/src/net/download.h
+++ b/src/net/download.h
@@ -20,6 +20,7 @@
#include "utils/mutex.h"
+
#include <cstdio>
#include <optional>
#include <string>