From 8f4ba3b78d7df7e4a6c9f85ab8e0569e26fb0f07 Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Tue, 19 Jan 2021 12:50:33 +0100 Subject: [PATCH 1/6] Fetch all URLs from a single URL --- News.qml | 25 ++++++--- currentversionfetcher.cpp | 103 ++++++++++++++++++++++++++++++++------ currentversionfetcher.h | 2 +- deployment.md | 4 +- gamelauncher.cpp | 2 +- linux.cpp | 10 ++-- main.cpp | 17 +++++-- osx.cpp | 10 ++-- qmldownloader.cpp | 22 +++++--- qmldownloader.h | 8 ++- splash.qml | 8 ++- splashcontroller.cpp | 68 ++++++++++++++++++------- splashcontroller.h | 17 +++++-- system.h | 2 +- win.cpp | 10 ++-- 15 files changed, 232 insertions(+), 76 deletions(-) diff --git a/News.qml b/News.qml index dff5163..1c32e81 100644 --- a/News.qml +++ b/News.qml @@ -32,6 +32,11 @@ Item { bottomMargin: 10 } + function fetchFallbackNews() { + console.log("fetching fallback posts json"); + fetchNews(downloader.newsFallbackUrl); + } + function fetchNews(jsonUrl) { var news = new XMLHttpRequest(); @@ -55,11 +60,8 @@ Item { } if (newsObj === null) { - var fallbackJsonUrl = 'qrc:/resources/disconnected_posts.json'; - - if (jsonUrl !== fallbackJsonUrl) { - console.log("fetching fallback posts json"); - fetchNews(fallbackJsonUrl); + if (jsonUrl !== downloader.newsFallbackUrl) { + fetchFallbackNews(); return; } } @@ -98,6 +100,14 @@ Item { news.send(); } + Connections { + target: splashController + + onNewsUrlFetched: { + fetchNews(newsUrl); + } + } + SwipeView { id: swipe @@ -111,7 +121,10 @@ Item { } Component.onCompleted: { - fetchNews('https://unvanquished.net/api/get_recent_posts/'); + var newsUrl = downloader.newsUrl; + if (newsUrl != "") { + fetchNews(newsUrl); + } } } diff --git a/currentversionfetcher.cpp b/currentversionfetcher.cpp index 1b21656..24c150b 100644 --- a/currentversionfetcher.cpp +++ b/currentversionfetcher.cpp @@ -16,6 +16,7 @@ */ #include "currentversionfetcher.h" +#include "system.h" #include #include @@ -23,6 +24,7 @@ #include #include #include +#include CurrentVersionFetcher::CurrentVersionFetcher(QObject* parent) : QObject(parent), manager_(new QNetworkAccessManager(this)) { @@ -35,35 +37,108 @@ void CurrentVersionFetcher::fetchCurrentVersion(QString url) manager_->get(request); } +void ComponentVersionFetcher(QJsonObject components, QString name, QString system, QString *version, QString *url) +{ + QString mirror; + QString path; + + QJsonObject component = components[name].toObject(); + + if (component.isEmpty()) { + qDebug() << "ComponentVersionFetcher: undefined “" << name << "” key"; + } else { + QJsonValue versionValue = component.value("version"); + if (versionValue == QJsonValue::Undefined) { + qDebug() << "ComponentVersionFetcher: undefined “version” value for" << name; + } else { + *version = versionValue.toString(); + } + + QJsonArray mirrors = component["mirrors"].toArray(); + if (!mirrors.count()) { + qDebug() << "ComponentVersionFetcher: undefined “mirrors” key for " << name; + } else { + mirror = mirrors.first().toString(); + } + + QJsonObject parcels = component["parcels"].toObject(); + if (parcels.isEmpty()) { + qDebug() << "ComponentVersionFetcher: undefined “parcels” key for" << name; + } else { + QJsonObject systemObject = parcels[system].toObject(); + if (systemObject.isEmpty()) { + qDebug() << "ComponentVersionFetcher: undefined “" << system << "” key for " << name; + } else { + QJsonValue pathValue = systemObject.value("path"); + if (pathValue == QJsonValue::Undefined) { + qDebug() << "ComponentVersionFetcher: undefined “path” value for" << name; + } else { + path = pathValue.toString(); + } + } + } + } + + *url = mirror + path; + + qDebug() << "ComponentVersionFetcher: fetched component =" << name; + qDebug() << "ComponentVersionFetcher: fetched system =" << system; + qDebug() << "ComponentVersionFetcher: fetched version =" << *version; + qDebug() << "ComponentVersionFetcher: fetched mirror =" << mirror; + qDebug() << "ComponentVersionFetcher: fetched path =" << path; + qDebug() << "ComponentVersionFetcher: fetched url =" << *url; +} + void CurrentVersionFetcher::reply(QNetworkReply* reply) { - QString game; - QString updater; + QString formatVersion; + QString updaterVersion; + QString updaterUrl; + QString gameVersion; + QString gameUrl; + QString newsVersion; + QString newsUrl; + if (reply->error() != QNetworkReply::NoError) { qDebug() << "CurrentVersionFetcher: network error"; - emit onCurrentVersions(updater, game); + emit onCurrentVersions(updaterVersion, updaterUrl, gameVersion, gameUrl, newsUrl); return; } + QJsonParseError error; QJsonDocument json = QJsonDocument::fromJson(reply->readAll(), &error); if (error.error != QJsonParseError::NoError) { qDebug() << "CurrentVersionFetcher: JSON parsing error"; - emit onCurrentVersions(updater, game); + emit onCurrentVersions(updaterVersion, updaterUrl, gameVersion, gameUrl, newsUrl); return; } - QJsonValue value = json.object().value("updater"); - if (value != QJsonValue::Undefined) { - updater = value.toString(); + + QJsonObject jsonObject = json.object(); + + QJsonValue formatVersionValue = jsonObject.value("format"); + + if (formatVersionValue == QJsonValue::Undefined) { + qDebug() << "ComponentVersionFetcher: missing “version” value in current.json"; } else { - qDebug() << "CurrentVersionFetcher: undefined “updater” value"; + formatVersion = formatVersionValue.toString(); } - value = json.object().value("unvanquished"); - if (value != QJsonValue::Undefined) { - game = value.toString(); + + QJsonValue componentsValue = jsonObject.value("components"); + + if (componentsValue == QJsonValue::Undefined) { + qDebug() << "ComponentVersionFetcher: missing “components” array in current.json"; } else { - qDebug() << "CurrentVersionFetcher: undefined “unvanquished” value"; + qDebug() << "ComponentVersionFetcher: fetched format = " << formatVersion; + + QJsonObject components = componentsValue.toObject(); + + ComponentVersionFetcher(components, "updater", Sys::updaterSystem(), &updaterVersion, &updaterUrl); + + ComponentVersionFetcher(components, "game", "all-all", &gameVersion, &gameUrl); + + ComponentVersionFetcher(components, "news", "all-all", &newsVersion, &newsUrl); } - qDebug() << "CurrentVersionFetcher: fetched versions: updater =" << updater << "game =" << game; - emit onCurrentVersions(updater, game); + + emit onCurrentVersions(updaterVersion, updaterUrl, gameVersion, gameUrl, newsUrl); } diff --git a/currentversionfetcher.h b/currentversionfetcher.h index df8b2f3..2689570 100644 --- a/currentversionfetcher.h +++ b/currentversionfetcher.h @@ -31,7 +31,7 @@ class CurrentVersionFetcher : public QObject void fetchCurrentVersion(QString url); signals: - void onCurrentVersions(QString updater, QString game); + void onCurrentVersions(QString updaterVersion, QString updaterUrl, QString gameVersion, QString gameUrl, QString newsUrl); private slots: void reply(QNetworkReply* reply); diff --git a/deployment.md b/deployment.md index 1ad3193..43281df 100644 --- a/deployment.md +++ b/deployment.md @@ -1,6 +1,6 @@ ## Network resources used by the updater - News REST endpoint which returns links to Wordpress articles on unvanquished.net. The featured image in each news article must be a type which is supported by the updater (see issue #51). Currently PNG and JPEG are known to work. -- versions.json file on unvanquished.net, used to determine whether update is needed +- current.json file on unvanquished.net, used to determine whether update is needed - Github releases. These are targeted by download links on unvanquished.net and by the updater's self-update process. - Torrent URL used to download the latest game version @@ -36,4 +36,4 @@ ``` 2. Upload `UnvUpdaterWin.zip` and `UnvanquishedUpdater.exe` from `build-docker/release-win/`. -4. Bump the updater version on unvanquished.net to the new tag, so that it is reflected in versions.json and the download links. +4. Bump the updater version on unvanquished.net to the new tag, so that it is reflected in current.json and the download links. diff --git a/gamelauncher.cpp b/gamelauncher.cpp index 7c1f14d..1362433 100644 --- a/gamelauncher.cpp +++ b/gamelauncher.cpp @@ -31,7 +31,7 @@ GameLauncher::GameLauncher(const QString& connectUrl, const Settings& settings) // Does our version of daemon have -connect-trusted? static bool haveConnectTrusted(const QString& gameVersion) { - // Updater version up to v0.2.0 may set "unknown" as game version if versions.json request fails + // Updater version up to v0.2.0 may set "unknown" as game version if current.json request fails if (gameVersion == "unknown") return false; // Hacky string comparison, assume we won't go down to 0.9 or up to 0.100 :) diff --git a/linux.cpp b/linux.cpp index 488e617..752e064 100644 --- a/linux.cpp +++ b/linux.cpp @@ -82,6 +82,11 @@ QString archiveName() return "linux-amd64.zip"; } +QString updaterSystem() +{ + return "linux-amd64"; +} + void migrateHomePath() { QString legacyHomePath = QDir::homePath() + "/.unvanquished"; @@ -265,11 +270,6 @@ bool updateUpdater(const QString& updaterArchive, const QString& connectUrl) return false; } -QString updaterArchiveName() -{ - return "UnvUpdaterLinux.zip"; -} - std::string getCertStore() { // From Go: https://golang.org/src/crypto/x509/root_linux.go diff --git a/main.cpp b/main.cpp index 9ae9a89..94b873c 100644 --- a/main.cpp +++ b/main.cpp @@ -63,7 +63,7 @@ struct CommandLineOptions { QString ariaLogFilename; int splashMilliseconds = 3000; RelaunchCommand relaunchCommand = RelaunchCommand::NONE; - QString updateUpdaterVersion; + QString updateUpdaterUrl; QString connectUrl; }; @@ -122,6 +122,8 @@ CommandLineOptions getCommandLineOptions(const QApplication& app) { splashMsOption.setValueName("duration in milliseconds"); QCommandLineOption internalCommandOption("internalcommand"); internalCommandOption.setValueName("command"); + QCommandLineOption updaterUrl("updaterurl"); + updaterUrl.setValueName("url"); QCommandLineParser optionParser; optionParser.addHelpOption(); optionParser.addVersionOption(); @@ -129,6 +131,7 @@ CommandLineOptions getCommandLineOptions(const QApplication& app) { optionParser.addOption(ariaLogFilenameOption); optionParser.addOption(splashMsOption); optionParser.addOption(internalCommandOption); + optionParser.addOption(updaterUrl); optionParser.addPositionalArgument("URL", "address of Unvanquished server to connect to", "[URL]"); optionParser.process(app); CommandLineOptions options; @@ -145,9 +148,13 @@ CommandLineOptions getCommandLineOptions(const QApplication& app) { options.relaunchCommand = RelaunchCommand::PLAY_NOW; } else if (command == "updategame") { options.relaunchCommand = RelaunchCommand::UPDATE_GAME; - } else if (command.startsWith("updateupdater:")) { + } else if (command.startsWith("updateupdater")) { options.relaunchCommand = RelaunchCommand::UPDATE_UPDATER; - options.updateUpdaterVersion = command.section(':', 1); + if (optionParser.isSet(updaterUrl)) { + options.updateUpdaterUrl = optionParser.value(updaterUrl); + } else { + options.updateUpdaterUrl = ""; + } } else { argParseError("Invalid --internalcommand option: " + command); } @@ -209,9 +216,9 @@ int main(int argc, char *argv[]) } SplashController splashController( - options.relaunchCommand, options.updateUpdaterVersion, options.connectUrl, settings); + options.relaunchCommand, options.updateUpdaterUrl, options.connectUrl, settings); splashController.checkForUpdate(); - QmlDownloader downloader(options.ariaLogFilename, options.connectUrl, settings); + QmlDownloader downloader(options.ariaLogFilename, options.connectUrl, splashController, settings); QQmlApplicationEngine engine; engine.addImportPath(QLatin1String("qrc:/")); engine.addImageProvider(QLatin1String("fluidicons"), new IconsImageProvider()); diff --git a/osx.cpp b/osx.cpp index a532444..51214b1 100644 --- a/osx.cpp +++ b/osx.cpp @@ -53,6 +53,11 @@ QString archiveName() return "macos-amd64.zip"; } +QString updaterSystem() +{ + return "macos-amd64"; +} + QString defaultInstallPath() { return QDir::homePath() + "/Games/Unvanquished"; @@ -163,11 +168,6 @@ bool updateUpdater(const QString& updaterArchive, const QString&) return true; } -QString updaterArchiveName() -{ - return "UnvUpdaterOSX.zip"; -} - std::string getCertStore() { return ""; // Not used on OSX. diff --git a/qmldownloader.cpp b/qmldownloader.cpp index a362f7c..ea671e2 100644 --- a/qmldownloader.cpp +++ b/qmldownloader.cpp @@ -23,11 +23,11 @@ #include "qmldownloader.h" #include "system.h" -static const QString UPDATER_BASE_URL("https://github.com/Unvanquished/updater/releases/download"); -QmlDownloader::QmlDownloader(QString ariaLogFilename, QString connectUrl, Settings& settings) : +QmlDownloader::QmlDownloader(QString ariaLogFilename, QString connectUrl, SplashController& splashController, Settings& settings) : ariaLogFilename_(ariaLogFilename), connectUrl_(connectUrl), + splashController_(splashController), settings_(settings), downloadSpeed_(0), uploadSpeed_(0), @@ -42,6 +42,14 @@ QmlDownloader::~QmlDownloader() stopAria(); } +QString QmlDownloader::newsFallbackUrl() const { + return "qrc:/resources/disconnected_posts.json"; +} + +QString QmlDownloader::newsUrl() const { + return splashController_.newsUrl(); +} + int QmlDownloader::downloadSpeed() const { return downloadSpeed_; } @@ -155,6 +163,9 @@ void QmlDownloader::startUpdate(const QString& selectedInstallPath) emit fatalMessage("Install dir not writable. Please select another"); return; } + + QString gameUrl = splashController_.gameUrl(); + qDebug() << "Using torrent file:" << gameUrl; // Persist the install path only now that download has been initiated and we know the path is good emit statusMessage("Installing to " + dir.canonicalPath()); if (settings_.installPath() != selectedInstallPath) { @@ -166,7 +177,7 @@ void QmlDownloader::startUpdate(const QString& selectedInstallPath) setState(DOWNLOADING); worker_ = new DownloadWorker(ariaLogFilename_); worker_->setDownloadDirectory(dir.canonicalPath().toStdString()); - worker_->addTorrent("https://cdn.unvanquished.net/current.torrent"); + worker_->addTorrent(gameUrl.toStdString()); worker_->moveToThread(&thread_); connect(&thread_, SIGNAL(finished()), worker_, SLOT(deleteLater())); connect(worker_, SIGNAL(onDownloadEvent(int)), this, SLOT(onDownloadEvent(int))); @@ -201,14 +212,13 @@ void QmlDownloader::stopAria() } } -void QmlDownloader::startUpdaterUpdate(QString version) +void QmlDownloader::startUpdaterUpdate(QString updaterUrl) { - QString url = UPDATER_BASE_URL + "/" + version + "/" + Sys::updaterArchiveName(); temp_dir_.reset(new QTemporaryDir()); worker_ = new DownloadWorker(ariaLogFilename_); worker_->setDownloadDirectory(QDir(temp_dir_->path()).canonicalPath().toStdString()); worker_->setConnectUrl(connectUrl_); - worker_->addUpdaterUri(url.toStdString()); + worker_->addUpdaterUri(updaterUrl.toStdString()); worker_->moveToThread(&thread_); connect(&thread_, SIGNAL(finished()), worker_, SLOT(deleteLater())); connect(worker_, SIGNAL(onDownloadEvent(int)), this, SLOT(onDownloadEvent(int))); diff --git a/qmldownloader.h b/qmldownloader.h index 4827bb1..9bf5830 100644 --- a/qmldownloader.h +++ b/qmldownloader.h @@ -29,11 +29,14 @@ #include "downloadworker.h" #include "downloadtimecalculator.h" +#include "splashcontroller.h" #include "settings.h" class QmlDownloader : public QObject { Q_OBJECT + Q_PROPERTY(QString newsFallbackUrl READ newsFallbackUrl) + Q_PROPERTY(QString newsUrl READ newsUrl) Q_PROPERTY(int downloadSpeed READ downloadSpeed NOTIFY downloadSpeedChanged) Q_PROPERTY(int uploadSpeed READ uploadSpeed NOTIFY uploadSpeedChanged) Q_PROPERTY(int eta READ eta NOTIFY etaChanged) @@ -50,7 +53,7 @@ class QmlDownloader : public QObject }; Q_ENUM(DownloadState) - QmlDownloader(QString ariaLogFilename, QString connectUrl, Settings& settings); + QmlDownloader(QString ariaLogFilename, QString connectUrl, SplashController& splashController, Settings& settings); ~QmlDownloader(); int downloadSpeed() const; int uploadSpeed() const; @@ -70,6 +73,8 @@ class QmlDownloader : public QObject void stateChanged(DownloadState state); public slots: + QString newsFallbackUrl() const; + QString newsUrl() const; void setDownloadSpeed(int speed); void setUploadSpeed(int speed); void setTotalSize(int size); @@ -88,6 +93,7 @@ public slots: QString ariaLogFilename_; QString connectUrl_; // used for updater update + SplashController& splashController_; Settings& settings_; QThread thread_; diff --git a/splash.qml b/splash.qml index d944ac8..ef82871 100644 --- a/splash.qml +++ b/splash.qml @@ -70,8 +70,12 @@ ApplicationWindow { } } - function onUpdaterUpdate(version) { - downloader.startUpdaterUpdate(version); + function onUpdaterUpdateNeeded() { + splashController.autoLaunchOrUpdate(); + } + + function onUpdaterUpdate(updaterUrl) { + downloader.startUpdaterUpdate(updaterUrl); updaterUpdateLabel.visible = true; // Now allow the window to go behind other windows in the z-order. diff --git a/splashcontroller.cpp b/splashcontroller.cpp index 2d35545..bd4307f 100644 --- a/splashcontroller.cpp +++ b/splashcontroller.cpp @@ -23,29 +23,57 @@ #include "system.h" SplashController::SplashController( - RelaunchCommand command, const QString& updateUpdaterVersion, + RelaunchCommand command, const QString& updateUpdaterUrl, const QString& connectUrl, const Settings& settings) : - relaunchCommand_(command), updateUpdaterVersion_(updateUpdaterVersion), + relaunchCommand_(command), updateUpdaterUrl_(updateUpdaterUrl), connectUrl_(connectUrl), settings_(settings) {} // Possibly initiates an asynchronous request for the latest available versions. void SplashController::checkForUpdate() { - // Don't need versions.json if we already know what to do next - if (relaunchCommand_ != RelaunchCommand::NONE) { - return; + // Don't need current.json if we already know what to do next + switch(relaunchCommand_) { + case RelaunchCommand::NONE: + case RelaunchCommand::UPDATE_GAME: + break; + case RelaunchCommand::UPDATE_UPDATER: + if (!updateUpdaterUrl_.isEmpty()) { + return; + } + break; + default: + return; } - connect(&fetcher_, SIGNAL(onCurrentVersions(QString, QString)), - this, SLOT(onCurrentVersions(QString, QString))); - fetcher_.fetchCurrentVersion("https://dl.unvanquished.net/versions.json"); + connect(&fetcher_, SIGNAL(onCurrentVersions(QString, QString, QString, QString, QString)), + this, SLOT(onCurrentVersions(QString, QString, QString, QString, QString))); + + fetcher_.fetchCurrentVersion("https://cdn.unvanquished.net/current.json"); +} + +QString SplashController::gameUrl() const { + return latestGameUrl_; +} + +QString SplashController::newsUrl() const { + return latestNewsUrl_; } // Receives the results of the checkForUpdate request. -void SplashController::onCurrentVersions(QString updater, QString game) +void SplashController::onCurrentVersions(QString updaterVersion, QString updaterUrl, QString gameVersion, QString gameUrl, QString newsUrl) { - latestUpdaterVersion_ = updater; - latestGameVersion_ = game; + latestUpdaterVersion_ = updaterVersion; + latestUpdaterUrl_ = updaterUrl; + latestGameVersion_ = gameVersion; + latestGameUrl_ = gameUrl; + latestNewsUrl_ = newsUrl; + + emit newsUrlFetched(newsUrl); + + if (relaunchCommand_ == RelaunchCommand::UPDATE_UPDATER && updateUpdaterUrl_.isEmpty()) { + updateUpdaterUrl_ = latestUpdaterUrl_; + emit updaterUpdateNeeded(); + } } // Return value is whether the program should exit @@ -69,7 +97,7 @@ void SplashController::launchGameIfInstalled() // This runs after the splash screen has been displayed for the programmed amount of time (and the // user did not click the settings button). If the CurrentVersionFetcher didn't emit anything yet, -// proceed as if the request for versions.json failed. +// proceed as if the request for current.json failed. void SplashController::autoLaunchOrUpdate() { qDebug() << "Previously-installed game version:" << settings_.installedVersion(); @@ -82,9 +110,15 @@ void SplashController::autoLaunchOrUpdate() return; case RelaunchCommand::UPDATE_UPDATER: - qDebug() << "Updater update to" << updateUpdaterVersion_ << "requested as relaunch action"; - // It is assumed the process is already elevated - emit updaterUpdate(updateUpdaterVersion_); + // When the splash screen is displayed, this function is called, but the current.json + // fetch has not succeeded yet. Only do something at that time when the updater url is + // passed by command line. When the current.json fetch has succeeded, this function is + // called again if there was no url passed by command line. + if (!updateUpdaterUrl_.isEmpty()) { + qDebug() << "Updater update to" << updateUpdaterUrl_ << "requested as relaunch action"; + // It is assumed the process is already elevated + emit updaterUpdate(updateUpdaterUrl_); + } return; case RelaunchCommand::PLAY_NOW: @@ -97,14 +131,14 @@ void SplashController::autoLaunchOrUpdate() // If no relaunch action, detect update needed based on versions.json if (!latestUpdaterVersion_.isEmpty() && latestUpdaterVersion_ != updaterAppVersion()) { qDebug() << "Updater update to version" << latestUpdaterVersion_ << "required"; + QString updaterArgs = "--splashms 1 --internalcommand updateupdater --updaterurl " + latestUpdaterUrl_; // Remember the URL if we are doing updater update - QString updaterArgs = "--splashms 1 --internalcommand updateupdater:" + latestUpdaterVersion_; if (!connectUrl_.isEmpty()) { updaterArgs += " -- " + connectUrl_; } switch (Sys::RelaunchElevated(updaterArgs)) { case Sys::ElevationResult::UNNEEDED: - emit updaterUpdate(latestUpdaterVersion_); + emit updaterUpdate(latestUpdaterUrl_); return; case Sys::ElevationResult::RELAUNCHED: QCoreApplication::quit(); diff --git a/splashcontroller.h b/splashcontroller.h index 50e913b..566b20c 100644 --- a/splashcontroller.h +++ b/splashcontroller.h @@ -26,7 +26,7 @@ inline QString updaterAppVersion() { - return "v0.2.1"; + return "0.2.1"; } // These are used only on Windows where relaunching is needed for admin (de)elevation. @@ -48,7 +48,7 @@ class SplashController : public QObject private: // Actions from command line args RelaunchCommand relaunchCommand_; - QString updateUpdaterVersion_; // If command is UPDATE_UPDATER + QString updateUpdaterUrl_; // If command is UPDATE_UPDATER QString connectUrl_; // for pre-updater-update elevation const Settings& settings_; @@ -56,22 +56,29 @@ class SplashController : public QObject // Latest versions fetching CurrentVersionFetcher fetcher_; QString latestUpdaterVersion_; + QString latestUpdaterUrl_; QString latestGameVersion_; + QString latestGameUrl_; + QString latestNewsUrl_; public: SplashController( - RelaunchCommand command, const QString& updateUpdaterVersion, + RelaunchCommand command, const QString& updateUpdaterUrl, const QString& connectUrl, const Settings& settings); void checkForUpdate(); + QString gameUrl() const; + QString newsUrl() const; Q_INVOKABLE bool relaunchForSettings(); Q_INVOKABLE void autoLaunchOrUpdate(); signals: void updateNeeded(bool updateNeeded); - void updaterUpdate(QString version); + void updaterUpdateNeeded(); + void updaterUpdate(QString updaterUrl); + void newsUrlFetched(QString newsUrl); private slots: - void onCurrentVersions(QString updater, QString game); + void onCurrentVersions(QString updaterVersion, QString updaterUrl, QString gameVersion, QString gameUrl, QString newsUrl); private: void launchGameIfInstalled(); diff --git a/system.h b/system.h index 195c654..673e3b1 100644 --- a/system.h +++ b/system.h @@ -25,13 +25,13 @@ namespace Sys { QString archiveName(); +QString updaterSystem(); QString defaultInstallPath(); void initApplicationName(); // influences default storage location for QSettings bool validateInstallPath(const QString& installPath); // Checks installing as root in homepath on Linux bool installShortcuts(); // Install launch menu entries and protocol handlers bool installUpdater(const QString& installPath); // Copies current application to /updater[.exe|.app] bool updateUpdater(const QString& updaterArchive, const QString& connectUrl); -QString updaterArchiveName(); std::string getCertStore(); QSettings* makePersistentSettings(QObject* parent); QString getGameCommand(const QString& installPath); // Substitution for %command% diff --git a/win.cpp b/win.cpp index 6ead20b..f4a0c19 100644 --- a/win.cpp +++ b/win.cpp @@ -171,6 +171,11 @@ QString archiveName() } } +QString updaterSystem() +{ + return "windows-i686"; +} + QString defaultInstallPath() { static const char* PROGRAM_FILES_VAR = "programfiles"; @@ -284,11 +289,6 @@ bool updateUpdater(const QString& updaterArchive, const QString& connectUrl) return true; } -QString updaterArchiveName() -{ - return "UnvUpdaterWin.zip"; -} - std::string getCertStore() { return ""; // Not used on Windows. From 91f080d86c3f691818bdf4a16b9e09d7d05d07ba Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Mon, 8 Dec 2025 07:15:30 +0100 Subject: [PATCH 2/6] Iterate current.json mirrors --- currentversionfetcher.cpp | 29 +++++++++++++++++++++++++---- currentversionfetcher.h | 2 +- splashcontroller.cpp | 2 +- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/currentversionfetcher.cpp b/currentversionfetcher.cpp index 24c150b..99d1937 100644 --- a/currentversionfetcher.cpp +++ b/currentversionfetcher.cpp @@ -31,10 +31,26 @@ CurrentVersionFetcher::CurrentVersionFetcher(QObject* parent) : QObject(parent), connect(manager_.get(), SIGNAL(finished(QNetworkReply*)), this, SLOT(reply(QNetworkReply*))); } -void CurrentVersionFetcher::fetchCurrentVersion(QString url) +static const QString versionMirrors[] = { + "https://cdn.unvanquished.net/", + "https://cdn.illwieckz.net/unvanquished/", + "https://webseed.unv.kangz.net/", + nullptr, +}; + +static const QString *versionMirror = &versionMirrors[0]; + +void CurrentVersionFetcher::fetchCurrentVersion() { - QNetworkRequest request = QNetworkRequest(QUrl(url)); - manager_->get(request); + static const QString versionFile = "current.json"; + + if (versionMirror) { + QString versionURL = versionMirror + versionFile; + qDebug() << "Fetching" << versionURL; + QNetworkRequest request = QNetworkRequest(QUrl(versionURL)); + manager_->get(request); + versionMirror++; + } } void ComponentVersionFetcher(QJsonObject components, QString name, QString system, QString *version, QString *url) @@ -101,7 +117,12 @@ void CurrentVersionFetcher::reply(QNetworkReply* reply) if (reply->error() != QNetworkReply::NoError) { qDebug() << "CurrentVersionFetcher: network error"; - emit onCurrentVersions(updaterVersion, updaterUrl, gameVersion, gameUrl, newsUrl); + + if (versionMirror) { + fetchCurrentVersion(); + } else { + emit onCurrentVersions(updaterVersion, updaterUrl, gameVersion, gameUrl, newsUrl); + } return; } diff --git a/currentversionfetcher.h b/currentversionfetcher.h index 2689570..3215a9a 100644 --- a/currentversionfetcher.h +++ b/currentversionfetcher.h @@ -28,7 +28,7 @@ class CurrentVersionFetcher : public QObject Q_OBJECT public: explicit CurrentVersionFetcher(QObject *parent = nullptr); - void fetchCurrentVersion(QString url); + void fetchCurrentVersion(); signals: void onCurrentVersions(QString updaterVersion, QString updaterUrl, QString gameVersion, QString gameUrl, QString newsUrl); diff --git a/splashcontroller.cpp b/splashcontroller.cpp index bd4307f..30e0887 100644 --- a/splashcontroller.cpp +++ b/splashcontroller.cpp @@ -48,7 +48,7 @@ void SplashController::checkForUpdate() connect(&fetcher_, SIGNAL(onCurrentVersions(QString, QString, QString, QString, QString)), this, SLOT(onCurrentVersions(QString, QString, QString, QString, QString))); - fetcher_.fetchCurrentVersion("https://cdn.unvanquished.net/current.json"); + fetcher_.fetchCurrentVersion(); } QString SplashController::gameUrl() const { From b84da93a0d4139a86be5490bfec30067bae8333a Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Mon, 8 Dec 2025 11:35:33 +0100 Subject: [PATCH 3/6] debug --- updater.pro | 2 ++ 1 file changed, 2 insertions(+) diff --git a/updater.pro b/updater.pro index 6ea764a..9064ce2 100644 --- a/updater.pro +++ b/updater.pro @@ -3,6 +3,8 @@ QT += qml quick network widgets include(fluid/fluid.pri) CONFIG += c++11 +CONFIG += debug +CONFIG -= release HEADERS += ariadownloader.h \ downloadtimecalculator.h \ From fb47fdb4a206cd6f51e9fa5532305d663b6c4151 Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Mon, 8 Dec 2025 08:18:07 +0100 Subject: [PATCH 4/6] WIP: attempt to restart torrent download --- currentversionfetcher.cpp | 43 ++++++++++++++++++-------------- currentversionfetcher.h | 2 +- downloadworker.cpp | 2 -- qmldownloader.cpp | 52 ++++++++++++++++++++++++++++++++++----- qmldownloader.h | 10 ++++++-- splash.qml | 4 +-- splashcontroller.cpp | 34 ++++++++++++++++--------- splashcontroller.h | 11 +++++---- 8 files changed, 111 insertions(+), 47 deletions(-) diff --git a/currentversionfetcher.cpp b/currentversionfetcher.cpp index 99d1937..fae9ceb 100644 --- a/currentversionfetcher.cpp +++ b/currentversionfetcher.cpp @@ -53,10 +53,10 @@ void CurrentVersionFetcher::fetchCurrentVersion() } } -void ComponentVersionFetcher(QJsonObject components, QString name, QString system, QString *version, QString *url) +void ComponentVersionFetcher(QJsonObject components, QString name, QString system, QString *version, QStringList *urls) { - QString mirror; QString path; + QJsonArray mirrors; QJsonObject component = components[name].toObject(); @@ -70,11 +70,9 @@ void ComponentVersionFetcher(QJsonObject components, QString name, QString syste *version = versionValue.toString(); } - QJsonArray mirrors = component["mirrors"].toArray(); + mirrors = component["mirrors"].toArray(); if (!mirrors.count()) { qDebug() << "ComponentVersionFetcher: undefined “mirrors” key for " << name; - } else { - mirror = mirrors.first().toString(); } QJsonObject parcels = component["parcels"].toObject(); @@ -95,25 +93,30 @@ void ComponentVersionFetcher(QJsonObject components, QString name, QString syste } } - *url = mirror + path; - qDebug() << "ComponentVersionFetcher: fetched component =" << name; qDebug() << "ComponentVersionFetcher: fetched system =" << system; qDebug() << "ComponentVersionFetcher: fetched version =" << *version; - qDebug() << "ComponentVersionFetcher: fetched mirror =" << mirror; qDebug() << "ComponentVersionFetcher: fetched path =" << path; - qDebug() << "ComponentVersionFetcher: fetched url =" << *url; + + for (auto m : mirrors) + { + QString mirror = m.toString(); + qDebug() << "ComponentVersionFetcher: fetched mirror =" << mirror; + QString url = mirror + path; + qDebug() << "ComponentVersionFetcher: fetched url =" << url; + urls->append(url); + } } void CurrentVersionFetcher::reply(QNetworkReply* reply) { QString formatVersion; QString updaterVersion; - QString updaterUrl; + QStringList updaterUrls; QString gameVersion; - QString gameUrl; + QStringList gameUrls; QString newsVersion; - QString newsUrl; + QStringList newsUrls; if (reply->error() != QNetworkReply::NoError) { qDebug() << "CurrentVersionFetcher: network error"; @@ -121,7 +124,7 @@ void CurrentVersionFetcher::reply(QNetworkReply* reply) if (versionMirror) { fetchCurrentVersion(); } else { - emit onCurrentVersions(updaterVersion, updaterUrl, gameVersion, gameUrl, newsUrl); + emit onCurrentVersions(updaterVersion, updaterUrls, gameVersion, gameUrls, ""); } return; } @@ -130,7 +133,7 @@ void CurrentVersionFetcher::reply(QNetworkReply* reply) QJsonDocument json = QJsonDocument::fromJson(reply->readAll(), &error); if (error.error != QJsonParseError::NoError) { qDebug() << "CurrentVersionFetcher: JSON parsing error"; - emit onCurrentVersions(updaterVersion, updaterUrl, gameVersion, gameUrl, newsUrl); + emit onCurrentVersions(updaterVersion, updaterUrls, gameVersion, gameUrls, ""); return; } @@ -140,6 +143,8 @@ void CurrentVersionFetcher::reply(QNetworkReply* reply) if (formatVersionValue == QJsonValue::Undefined) { qDebug() << "ComponentVersionFetcher: missing “version” value in current.json"; + emit onCurrentVersions(updaterVersion, updaterUrls, gameVersion, gameUrls, ""); + return; } else { formatVersion = formatVersionValue.toString(); } @@ -148,18 +153,20 @@ void CurrentVersionFetcher::reply(QNetworkReply* reply) if (componentsValue == QJsonValue::Undefined) { qDebug() << "ComponentVersionFetcher: missing “components” array in current.json"; + emit onCurrentVersions(updaterVersion, updaterUrls, gameVersion, gameUrls, ""); + return; } else { qDebug() << "ComponentVersionFetcher: fetched format = " << formatVersion; QJsonObject components = componentsValue.toObject(); - ComponentVersionFetcher(components, "updater", Sys::updaterSystem(), &updaterVersion, &updaterUrl); + ComponentVersionFetcher(components, "updater", Sys::updaterSystem(), &updaterVersion, &updaterUrls); - ComponentVersionFetcher(components, "game", "all-all", &gameVersion, &gameUrl); + ComponentVersionFetcher(components, "game", "all-all", &gameVersion, &gameUrls); - ComponentVersionFetcher(components, "news", "all-all", &newsVersion, &newsUrl); + ComponentVersionFetcher(components, "news", "all-all", &newsVersion, &newsUrls); } - emit onCurrentVersions(updaterVersion, updaterUrl, gameVersion, gameUrl, newsUrl); + emit onCurrentVersions(updaterVersion, updaterUrls, gameVersion, gameUrls, newsUrls.at(0)); } diff --git a/currentversionfetcher.h b/currentversionfetcher.h index 3215a9a..5660d61 100644 --- a/currentversionfetcher.h +++ b/currentversionfetcher.h @@ -31,7 +31,7 @@ class CurrentVersionFetcher : public QObject void fetchCurrentVersion(); signals: - void onCurrentVersions(QString updaterVersion, QString updaterUrl, QString gameVersion, QString gameUrl, QString newsUrl); + void onCurrentVersions(QString updaterVersion, QStringList updaterUrls, QString gameVersion, QStringList gameUrls, QString newsUrl); private slots: void reply(QNetworkReply* reply); diff --git a/downloadworker.cpp b/downloadworker.cpp index 9330385..bb20c15 100644 --- a/downloadworker.cpp +++ b/downloadworker.cpp @@ -143,8 +143,6 @@ std::string DownloadWorker::getAriaIndexOut(size_t index, std::string path) return std::to_string(index) + "=" + oldPath.toStdString(); } - - void DownloadWorker::download() { auto start = std::chrono::steady_clock::now(); diff --git a/qmldownloader.cpp b/qmldownloader.cpp index ea671e2..da3decb 100644 --- a/qmldownloader.cpp +++ b/qmldownloader.cpp @@ -122,7 +122,17 @@ void QmlDownloader::onDownloadEvent(int event) break; case aria2::EVENT_ON_DOWNLOAD_ERROR: - emit fatalMessage("Error received while downloading"); + if (!downloadRestarter_) { + emit fatalMessage("Error: downloadRestarter unset"); + } + setState(COMPLETED); + setDownloadSpeed(0); + setUploadSpeed(0); + setCompletedSize(0); + stopAria(); + if (!(this->*downloadRestarter_)()) { + emit fatalMessage("Error received while downloading"); + } break; case aria2::EVENT_ON_DOWNLOAD_PAUSE: @@ -143,7 +153,7 @@ void QmlDownloader::onDownloadEvent(int event) } } -void QmlDownloader::startUpdate(const QString& selectedInstallPath) +void QmlDownloader::setInstallPath(const QString& selectedInstallPath) { qDebug() << "Selected install path:" << selectedInstallPath; if (!Sys::validateInstallPath(selectedInstallPath)) { @@ -159,13 +169,12 @@ void QmlDownloader::startUpdate(const QString& selectedInstallPath) return; } } + if (!QFileInfo(selectedInstallPath).isWritable()) { emit fatalMessage("Install dir not writable. Please select another"); return; } - QString gameUrl = splashController_.gameUrl(); - qDebug() << "Using torrent file:" << gameUrl; // Persist the install path only now that download has been initiated and we know the path is good emit statusMessage("Installing to " + dir.canonicalPath()); if (settings_.installPath() != selectedInstallPath) { @@ -173,12 +182,25 @@ void QmlDownloader::startUpdate(const QString& selectedInstallPath) settings_.setInstalledVersion(""); } settings_.setInstallPath(selectedInstallPath); +} + +bool QmlDownloader::startUpdate() +{ + QString gameUrl = splashController_.gameUrl(); + if (gameUrl.isEmpty()) { + return false; + } + + qDebug() << "Using torrent URL:" << gameUrl; + + QDir dir(settings_.installPath()); setState(DOWNLOADING); worker_ = new DownloadWorker(ariaLogFilename_); worker_->setDownloadDirectory(dir.canonicalPath().toStdString()); worker_->addTorrent(gameUrl.toStdString()); worker_->moveToThread(&thread_); + setDownloadRestarter(&QmlDownloader::startUpdate); connect(&thread_, SIGNAL(finished()), worker_, SLOT(deleteLater())); connect(worker_, SIGNAL(onDownloadEvent(int)), this, SLOT(onDownloadEvent(int))); connect(worker_, SIGNAL(downloadSpeedChanged(int)), this, SLOT(setDownloadSpeed(int))); @@ -187,6 +209,8 @@ void QmlDownloader::startUpdate(const QString& selectedInstallPath) connect(worker_, SIGNAL(completedSizeChanged(int)), this, SLOT(setCompletedSize(int))); connect(&thread_, SIGNAL(started()), worker_, SLOT(download())); thread_.start(); + + return true; } void QmlDownloader::toggleDownload(QString installPath) @@ -194,7 +218,8 @@ void QmlDownloader::toggleDownload(QString installPath) qDebug() << "QmlDownloader::toggleDownload called"; if (state() == COMPLETED) return; if (!worker_) { - startUpdate(installPath); + setInstallPath(installPath); + startUpdate(); return; } QMetaObject::invokeMethod(worker_, "toggle"); @@ -212,14 +237,22 @@ void QmlDownloader::stopAria() } } -void QmlDownloader::startUpdaterUpdate(QString updaterUrl) +bool QmlDownloader::startUpdaterUpdate() { + QString updaterUrl = splashController_.updaterUrl(); + if (updaterUrl.isEmpty()) { + return false; + } + + qDebug() << "Using updater URL:" << updaterUrl; + temp_dir_.reset(new QTemporaryDir()); worker_ = new DownloadWorker(ariaLogFilename_); worker_->setDownloadDirectory(QDir(temp_dir_->path()).canonicalPath().toStdString()); worker_->setConnectUrl(connectUrl_); worker_->addUpdaterUri(updaterUrl.toStdString()); worker_->moveToThread(&thread_); + setDownloadRestarter(&QmlDownloader::startUpdaterUpdate); connect(&thread_, SIGNAL(finished()), worker_, SLOT(deleteLater())); connect(worker_, SIGNAL(onDownloadEvent(int)), this, SLOT(onDownloadEvent(int))); connect(worker_, SIGNAL(downloadSpeedChanged(int)), this, SLOT(setDownloadSpeed(int))); @@ -228,6 +261,8 @@ void QmlDownloader::startUpdaterUpdate(QString updaterUrl) connect(worker_, SIGNAL(completedSizeChanged(int)), this, SLOT(setCompletedSize(int))); connect(&thread_, SIGNAL(started()), worker_, SLOT(download())); thread_.start(); + + return true; } QmlDownloader::DownloadState QmlDownloader::state() const @@ -240,3 +275,8 @@ void QmlDownloader::setState(DownloadState state) state_ = state; emit stateChanged(state); } + +void QmlDownloader::setDownloadRestarter(DownloadRestarter downloadRestarter) +{ + downloadRestarter_ = downloadRestarter; +} diff --git a/qmldownloader.h b/qmldownloader.h index 9bf5830..a740df1 100644 --- a/qmldownloader.h +++ b/qmldownloader.h @@ -32,6 +32,7 @@ #include "splashcontroller.h" #include "settings.h" + class QmlDownloader : public QObject { Q_OBJECT @@ -82,13 +83,17 @@ public slots: void onDownloadEvent(int event); Q_INVOKABLE void toggleDownload(QString installPath); - Q_INVOKABLE void startUpdaterUpdate(QString version); + Q_INVOKABLE bool startUpdaterUpdate(); private: + using DownloadRestarter = bool (QmlDownloader::*)(); + void stopAria(); void setState(DownloadState state); + void setInstallPath(const QString& selectedInstallPath); void startDownload(const QUrl& url, const QDir& destination); - void startUpdate(const QString& selectedInstallPath); + void setDownloadRestarter(DownloadRestarter downloadRestarter); + bool startUpdate(); void launchGameIfInstalled(); QString ariaLogFilename_; @@ -102,6 +107,7 @@ public slots: std::chrono::seconds eta_; int totalSize_; int completedSize_; + DownloadRestarter downloadRestarter_ = nullptr; DownloadWorker* worker_; DownloadTimeCalculator downloadTime_; diff --git a/splash.qml b/splash.qml index ef82871..4af5320 100644 --- a/splash.qml +++ b/splash.qml @@ -74,8 +74,8 @@ ApplicationWindow { splashController.autoLaunchOrUpdate(); } - function onUpdaterUpdate(updaterUrl) { - downloader.startUpdaterUpdate(updaterUrl); + function onUpdaterUpdate() { + downloader.startUpdaterUpdate(); updaterUpdateLabel.visible = true; // Now allow the window to go behind other windows in the z-order. diff --git a/splashcontroller.cpp b/splashcontroller.cpp index 30e0887..6052dd6 100644 --- a/splashcontroller.cpp +++ b/splashcontroller.cpp @@ -45,14 +45,26 @@ void SplashController::checkForUpdate() return; } - connect(&fetcher_, SIGNAL(onCurrentVersions(QString, QString, QString, QString, QString)), - this, SLOT(onCurrentVersions(QString, QString, QString, QString, QString))); + connect(&fetcher_, SIGNAL(onCurrentVersions(QString, QStringList, QString, QStringList, QString)), + this, SLOT(onCurrentVersions(QString, QStringList, QString, QStringList, QString))); fetcher_.fetchCurrentVersion(); } -QString SplashController::gameUrl() const { - return latestGameUrl_; +QString SplashController::updaterUrl() { + if (latestUpdaterUrls_.isEmpty()) { + return ""; + } + + return latestUpdaterUrls_.takeFirst(); +} + +QString SplashController::gameUrl() { + if (latestGameUrls_.isEmpty()) { + return ""; + } + + return latestGameUrls_.takeFirst(); } QString SplashController::newsUrl() const { @@ -60,18 +72,18 @@ QString SplashController::newsUrl() const { } // Receives the results of the checkForUpdate request. -void SplashController::onCurrentVersions(QString updaterVersion, QString updaterUrl, QString gameVersion, QString gameUrl, QString newsUrl) +void SplashController::onCurrentVersions(QString updaterVersion, QStringList updaterUrls, QString gameVersion, QStringList gameUrls, QString newsUrl) { latestUpdaterVersion_ = updaterVersion; - latestUpdaterUrl_ = updaterUrl; + latestUpdaterUrls_ = updaterUrls; latestGameVersion_ = gameVersion; - latestGameUrl_ = gameUrl; + latestGameUrls_ = gameUrls; latestNewsUrl_ = newsUrl; emit newsUrlFetched(newsUrl); if (relaunchCommand_ == RelaunchCommand::UPDATE_UPDATER && updateUpdaterUrl_.isEmpty()) { - updateUpdaterUrl_ = latestUpdaterUrl_; + updateUpdaterUrl_ = latestUpdaterUrls_.at(0); emit updaterUpdateNeeded(); } } @@ -117,7 +129,7 @@ void SplashController::autoLaunchOrUpdate() if (!updateUpdaterUrl_.isEmpty()) { qDebug() << "Updater update to" << updateUpdaterUrl_ << "requested as relaunch action"; // It is assumed the process is already elevated - emit updaterUpdate(updateUpdaterUrl_); + emit updaterUpdate(); } return; @@ -131,14 +143,14 @@ void SplashController::autoLaunchOrUpdate() // If no relaunch action, detect update needed based on versions.json if (!latestUpdaterVersion_.isEmpty() && latestUpdaterVersion_ != updaterAppVersion()) { qDebug() << "Updater update to version" << latestUpdaterVersion_ << "required"; - QString updaterArgs = "--splashms 1 --internalcommand updateupdater --updaterurl " + latestUpdaterUrl_; + QString updaterArgs = "--splashms 1 --internalcommand updateupdater --updaterurl " + latestUpdaterUrls_.at(0); // Remember the URL if we are doing updater update if (!connectUrl_.isEmpty()) { updaterArgs += " -- " + connectUrl_; } switch (Sys::RelaunchElevated(updaterArgs)) { case Sys::ElevationResult::UNNEEDED: - emit updaterUpdate(latestUpdaterUrl_); + emit updaterUpdate(); return; case Sys::ElevationResult::RELAUNCHED: QCoreApplication::quit(); diff --git a/splashcontroller.h b/splashcontroller.h index 566b20c..8b3f1d1 100644 --- a/splashcontroller.h +++ b/splashcontroller.h @@ -56,9 +56,9 @@ class SplashController : public QObject // Latest versions fetching CurrentVersionFetcher fetcher_; QString latestUpdaterVersion_; - QString latestUpdaterUrl_; + QStringList latestUpdaterUrls_; QString latestGameVersion_; - QString latestGameUrl_; + QStringList latestGameUrls_; QString latestNewsUrl_; public: @@ -66,7 +66,8 @@ class SplashController : public QObject RelaunchCommand command, const QString& updateUpdaterUrl, const QString& connectUrl, const Settings& settings); void checkForUpdate(); - QString gameUrl() const; + QString gameUrl(); + QString updaterUrl(); QString newsUrl() const; Q_INVOKABLE bool relaunchForSettings(); Q_INVOKABLE void autoLaunchOrUpdate(); @@ -74,11 +75,11 @@ class SplashController : public QObject signals: void updateNeeded(bool updateNeeded); void updaterUpdateNeeded(); - void updaterUpdate(QString updaterUrl); + void updaterUpdate(); void newsUrlFetched(QString newsUrl); private slots: - void onCurrentVersions(QString updaterVersion, QString updaterUrl, QString gameVersion, QString gameUrl, QString newsUrl); + void onCurrentVersions(QString updaterVersion, QStringList updaterUrls, QString gameVersion, QStringList gameUrls, QString newsUrl); private: void launchGameIfInstalled(); From c0081f2e8d8fb2a83f82f415b1113087ba19956e Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Mon, 8 Dec 2025 13:37:32 +0100 Subject: [PATCH 5/6] fixup: make it work --- ariadownloader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ariadownloader.cpp b/ariadownloader.cpp index 8339be9..693ef41 100644 --- a/ariadownloader.cpp +++ b/ariadownloader.cpp @@ -65,7 +65,7 @@ AriaDownloader::AriaDownloader(const std::string& ariaLogFilename) : callback_(n AriaDownloader::~AriaDownloader() { aria2::sessionFinal(session_); - aria2::libraryDeinit(); +// aria2::libraryDeinit(); } bool AriaDownloader::addUri(const std::string& uri) From 71750f3b5e2f9dc28740ec2647cf9c6af46e5fb5 Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Mon, 8 Dec 2025 14:26:28 +0100 Subject: [PATCH 6/6] WIP: pass updater mirrors on relaunch --- main.cpp | 8 ++++---- splashcontroller.cpp | 24 ++++++++++++++++-------- splashcontroller.h | 4 ++-- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/main.cpp b/main.cpp index 94b873c..8aeebd5 100644 --- a/main.cpp +++ b/main.cpp @@ -63,7 +63,7 @@ struct CommandLineOptions { QString ariaLogFilename; int splashMilliseconds = 3000; RelaunchCommand relaunchCommand = RelaunchCommand::NONE; - QString updateUpdaterUrl; + QStringList updateUpdaterUrls; QString connectUrl; }; @@ -151,9 +151,9 @@ CommandLineOptions getCommandLineOptions(const QApplication& app) { } else if (command.startsWith("updateupdater")) { options.relaunchCommand = RelaunchCommand::UPDATE_UPDATER; if (optionParser.isSet(updaterUrl)) { - options.updateUpdaterUrl = optionParser.value(updaterUrl); + options.updateUpdaterUrls = optionParser.values(updaterUrl); } else { - options.updateUpdaterUrl = ""; + options.updateUpdaterUrls = QStringList(); } } else { argParseError("Invalid --internalcommand option: " + command); @@ -216,7 +216,7 @@ int main(int argc, char *argv[]) } SplashController splashController( - options.relaunchCommand, options.updateUpdaterUrl, options.connectUrl, settings); + options.relaunchCommand, options.updateUpdaterUrls, options.connectUrl, settings); splashController.checkForUpdate(); QmlDownloader downloader(options.ariaLogFilename, options.connectUrl, splashController, settings); QQmlApplicationEngine engine; diff --git a/splashcontroller.cpp b/splashcontroller.cpp index 6052dd6..dfa0114 100644 --- a/splashcontroller.cpp +++ b/splashcontroller.cpp @@ -23,9 +23,9 @@ #include "system.h" SplashController::SplashController( - RelaunchCommand command, const QString& updateUpdaterUrl, + RelaunchCommand command, const QStringList& updateUpdaterUrls, const QString& connectUrl, const Settings& settings) : - relaunchCommand_(command), updateUpdaterUrl_(updateUpdaterUrl), + relaunchCommand_(command), updateUpdaterUrls_(updateUpdaterUrls), connectUrl_(connectUrl), settings_(settings) {} // Possibly initiates an asynchronous request for the latest available versions. @@ -37,7 +37,7 @@ void SplashController::checkForUpdate() case RelaunchCommand::UPDATE_GAME: break; case RelaunchCommand::UPDATE_UPDATER: - if (!updateUpdaterUrl_.isEmpty()) { + if (!updateUpdaterUrls_.isEmpty()) { return; } break; @@ -82,8 +82,8 @@ void SplashController::onCurrentVersions(QString updaterVersion, QStringList upd emit newsUrlFetched(newsUrl); - if (relaunchCommand_ == RelaunchCommand::UPDATE_UPDATER && updateUpdaterUrl_.isEmpty()) { - updateUpdaterUrl_ = latestUpdaterUrls_.at(0); + if (relaunchCommand_ == RelaunchCommand::UPDATE_UPDATER && updateUpdaterUrls_.isEmpty()) { + updateUpdaterUrls_ = latestUpdaterUrls_; emit updaterUpdateNeeded(); } } @@ -126,8 +126,11 @@ void SplashController::autoLaunchOrUpdate() // fetch has not succeeded yet. Only do something at that time when the updater url is // passed by command line. When the current.json fetch has succeeded, this function is // called again if there was no url passed by command line. - if (!updateUpdaterUrl_.isEmpty()) { - qDebug() << "Updater update to" << updateUpdaterUrl_ << "requested as relaunch action"; + if (!updateUpdaterUrls_.isEmpty()) { + qDebug() << "Updater update requested as relaunch action"; + for (auto& url : updateUpdaterUrls_) { + qDebug() << "Updater update available: " << url; + } // It is assumed the process is already elevated emit updaterUpdate(); } @@ -143,7 +146,12 @@ void SplashController::autoLaunchOrUpdate() // If no relaunch action, detect update needed based on versions.json if (!latestUpdaterVersion_.isEmpty() && latestUpdaterVersion_ != updaterAppVersion()) { qDebug() << "Updater update to version" << latestUpdaterVersion_ << "required"; - QString updaterArgs = "--splashms 1 --internalcommand updateupdater --updaterurl " + latestUpdaterUrls_.at(0); + QString updaterArgs = "--splashms 1 --internalcommand updateupdater"; + + for (auto& url : latestUpdaterUrls_) { + updaterArgs += " --updaterurl " + url; + } + // Remember the URL if we are doing updater update if (!connectUrl_.isEmpty()) { updaterArgs += " -- " + connectUrl_; diff --git a/splashcontroller.h b/splashcontroller.h index 8b3f1d1..beb459d 100644 --- a/splashcontroller.h +++ b/splashcontroller.h @@ -48,7 +48,7 @@ class SplashController : public QObject private: // Actions from command line args RelaunchCommand relaunchCommand_; - QString updateUpdaterUrl_; // If command is UPDATE_UPDATER + QStringList updateUpdaterUrls_; // If command is UPDATE_UPDATER QString connectUrl_; // for pre-updater-update elevation const Settings& settings_; @@ -63,7 +63,7 @@ class SplashController : public QObject public: SplashController( - RelaunchCommand command, const QString& updateUpdaterUrl, + RelaunchCommand command, const QStringList& updateUpdaterUrls, const QString& connectUrl, const Settings& settings); void checkForUpdate(); QString gameUrl();