summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt46
-rw-r--r--MultiMC.cpp7
-rw-r--r--MultiMC.h8
-rw-r--r--MultiMCVersion.h13
-rw-r--r--config.h.in14
-rw-r--r--gui/MainWindow.cpp11
-rw-r--r--gui/MainWindow.h2
-rw-r--r--logic/GoUpdate.cpp99
-rw-r--r--logic/GoUpdate.h43
-rw-r--r--logic/updater/UpdateChecker.cpp234
-rw-r--r--logic/updater/UpdateChecker.h93
11 files changed, 394 insertions, 176 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 95b1c18f..f5ea5026 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -118,20 +118,50 @@ SET(MultiMC_VERSION_MINOR 0)
SET(MultiMC_VERSION_BUILD -1 CACHE STRING "Build number. -1 for no build number.")
# Build type
-SET(MultiMC_VERSION_BUILD_TYPE "custombuild" CACHE STRING "Build type. Usually corresponds to the buildbot build name. Empty string for no build type.")
+SET(MultiMC_VERSION_BUILD_TYPE "custombuild" CACHE STRING "Build type. If this is set, it is appended to the end of the version string with a dash (<version string>-<build type>. It is not used for anything other than indicating in the version string what type of build this is (eg 'lin64').")
-SET(MultiMC_VERSION_STRING "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}")
+# Version channel
+SET(MultiMC_VERSION_CHANNEL "" CACHE STRING "The current build's channel. Included in the version string.")
+
+# Channel list URL
+SET(MultiMC_CHANLIST_URL "" CACHE STRING "URL for the channel list.")
+
+# Updater enabled?
+SET(MultiMC_UPDATER false CACHE BOOL "Whether or not the update system is enabled. If this is enabled, you must also set MultiMC_CHANLIST_URL and MultiMC_VERSION_CHANNEL in order for it to work properly.")
+
+# Build a version string to display in the configure logs.
+SET(MultiMC_VERSION_STRING "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}")
IF (MultiMC_VERSION_BUILD GREATER -1)
SET(MultiMC_VERSION_STRING "${MultiMC_VERSION_STRING}.${MultiMC_VERSION_BUILD}")
ENDIF ()
-
+IF (NOT MultiMC_VERSION_CHANNEL STREQUAL "")
+ SET(MultiMC_VERSION_STRING "${MultiMC_VERSION_STRING}-${MultiMC_VERSION_CHANNEL}")
+ENDIF ()
IF (NOT MultiMC_VERSION_BUILD_TYPE STREQUAL "")
SET(MultiMC_VERSION_STRING "${MultiMC_VERSION_STRING}-${MultiMC_VERSION_BUILD_TYPE}")
ENDIF ()
MESSAGE(STATUS "MultiMC 5 version ${MultiMC_VERSION_STRING}")
+# If the update system is enabled, make sure MultiMC_CHANLIST_URL and MultiMC_VERSION_CHANNEL are set.
+IF (MultiMC_UPDATER)
+ IF (MultiMC_VERSION_CHANNEL STREQUAL "")
+ MESSAGE(FATAL_ERROR "Update system is enabled, but MultiMC_VERSION_CHANNEL is not set.\n"
+ "Please ensure the CMake variables MultiMC_VERSION_CHANNEL, MultiMC_CHANLIST_URL, and MultiMC_VERSION_BUILD are set.")
+ ENDIF ()
+ IF (MultiMC_CHANLIST_URL STREQUAL "")
+ MESSAGE(FATAL_ERROR "Update system is enabled, but MultiMC_CHANLIST_URL is not set.\n"
+ "Please ensure the CMake variables MultiMC_VERSION_CHANNEL, MultiMC_CHANLIST_URL, and MultiMC_VERSION_BUILD are set.")
+ ENDIF ()
+ IF (MultiMC_VERSION_BUILD LESS 0)
+ MESSAGE(FATAL_ERROR "Update system is enabled, but MultiMC_VERSION_BUILD is not set.\n"
+ "Please ensure the CMake variables MultiMC_VERSION_CHANNEL, MultiMC_CHANLIST_URL, and MultiMC_VERSION_BUILD are set.")
+ ENDIF ()
+
+ MESSAGE(STATUS "Updater is enabled. Channel list URL: ${MultiMC_CHANLIST_URL}")
+ENDIF ()
+
#### Custom target to just print the version.
ADD_CUSTOM_TARGET(version echo "Version: ${MultiMC_VERSION_STRING}")
@@ -151,10 +181,6 @@ ELSE()
MESSAGE(STATUS "Failed to check Git commit. ${GIT_COMMIT_CHECK_RESULTVAR}")
ENDIF()
-#### GoUpdate URL
-SET(MultiMC_REPO_BASE_URL "invalid" CACHE STRING "Base URL for the updater.")
-SET(MultiMC_VERSION_BRANCH "invalid" CACHE STRING "URL of the stable update repo.")
-
######## Configure header ########
configure_file("${PROJECT_SOURCE_DIR}/config.h.in" "${PROJECT_BINARY_DIR}/include/config.h")
@@ -290,6 +316,10 @@ logic/auth/flows/ValidateTask.cpp
logic/auth/flows/InvalidateTask.h
logic/auth/flows/InvalidateTask.cpp
+# Update system
+logic/updater/UpdateChecker.h
+logic/updater/UpdateChecker.cpp
+
# legacy instances
logic/LegacyInstance.h
logic/LegacyInstance.cpp
@@ -358,8 +388,6 @@ logic/NagUtils.h
logic/NagUtils.cpp
logic/SkinUtils.h
logic/SkinUtils.cpp
-logic/GoUpdate.h
-logic/GoUpdate.cpp
)
diff --git a/MultiMC.cpp b/MultiMC.cpp
index 3df73c18..128e71f3 100644
--- a/MultiMC.cpp
+++ b/MultiMC.cpp
@@ -20,7 +20,8 @@
#include "logic/net/HttpMetaCache.h"
#include "logic/JavaUtils.h"
-#include "logic/GoUpdate.h"
+
+#include "logic/updater/UpdateChecker.h"
#include "pathutils.h"
#include "cmdutils.h"
@@ -33,7 +34,7 @@
using namespace Util::Commandline;
MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv),
- m_version{VERSION_MAJOR, VERSION_MINOR, VERSION_BUILD, VERSION_BUILD_TYPE}
+ m_version{VERSION_MAJOR, VERSION_MINOR, VERSION_BUILD, VERSION_CHANNEL, VERSION_BUILD_TYPE}
{
setOrganizationName("MultiMC");
setApplicationName("MultiMC5");
@@ -140,7 +141,7 @@ MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv),
initGlobalSettings();
// initialize the updater
- m_go_update.reset(new GoUpdate());
+ m_updateChecker.reset(new UpdateChecker());
// and instances
auto InstDirSetting = m_settings->getSetting("InstanceDir");
diff --git a/MultiMC.h b/MultiMC.h
index 0ab7a8b5..659104ba 100644
--- a/MultiMC.h
+++ b/MultiMC.h
@@ -17,7 +17,7 @@ class IconList;
class QNetworkAccessManager;
class ForgeVersionList;
class JavaVersionList;
-class GoUpdate;
+class UpdateChecker;
#if defined(MMC)
#undef MMC
@@ -85,9 +85,9 @@ public:
return m_metacache;
}
- std::shared_ptr<GoUpdate> goupdate()
+ std::shared_ptr<UpdateChecker> updateChecker()
{
- return m_go_update;
+ return m_updateChecker;
}
std::shared_ptr<LWJGLVersionList> lwjgllist();
@@ -112,7 +112,7 @@ private:
std::shared_ptr<QTranslator> m_mmc_translator;
std::shared_ptr<SettingsObject> m_settings;
std::shared_ptr<InstanceList> m_instances;
- std::shared_ptr<GoUpdate> m_go_update;
+ std::shared_ptr<UpdateChecker> m_updateChecker;
std::shared_ptr<MojangAccountList> m_accounts;
std::shared_ptr<IconList> m_icons;
std::shared_ptr<QNetworkAccessManager> m_qnam;
diff --git a/MultiMCVersion.h b/MultiMCVersion.h
index 863976b2..8978516b 100644
--- a/MultiMCVersion.h
+++ b/MultiMCVersion.h
@@ -32,8 +32,9 @@ struct MultiMCVersion
QString::number(major),
QString::number(minor));
- if (build > 0) vstr += QString(".%1").arg(QString::number(build));
- if (!buildType.isEmpty()) vstr += QString("-%1").arg(buildType);
+ if (build >= 0) vstr += "." + QString::number(build);
+ if (!channel.isEmpty()) vstr += "-" + channel;
+ if (!buildType.isEmpty()) vstr += "-" + buildType;
return vstr;
}
@@ -61,9 +62,13 @@ struct MultiMCVersion
int build;
/*!
+ * \brief This build's channel.
+ */
+ QString channel;
+
+ /*!
* \brief The build type.
- * This indicates the type of build that this is. For example, lin64-stable.
- * Usually corresponds to this build's buildbot builder name.
+ * This indicates the type of build that this is. For example, lin64 or custombuild.
*/
QString buildType;
};
diff --git a/config.h.in b/config.h.in
index 26c8b1e9..b58dc322 100644
--- a/config.h.in
+++ b/config.h.in
@@ -2,19 +2,17 @@
#define VERSION_MAJOR @MultiMC_VERSION_MAJOR@
#define VERSION_MINOR @MultiMC_VERSION_MINOR@
-// Build number and type -- numer is used by the updater, type is purely visual
+// Build number, channel, and type -- number and channel are used by the updater, type is purely visual
#define VERSION_BUILD @MultiMC_VERSION_BUILD@
+#define VERSION_CHANNEL "@MultiMC_VERSION_CHANNEL@"
#define VERSION_BUILD_TYPE "@MultiMC_VERSION_BUILD_TYPE@"
-// URL base for the updater
-#define VERSION_REPO "@MultiMC_REPO_BASE_URL@"
+// URL for the updater's channel
+#define CHANLIST_URL "@MultiMC_CHANLIST_URL@"
-// The branch used for this build. User can switch between 'stable' and 'develop'
-// if this is one of them. Otherwise, it pulls only from this one.
-#define VERSION_BRANCH "@MultiMC_VERSION_BRANCH@"
-
-// the commit hash of this build
+// The commit hash of this build
#define GIT_COMMIT "@MultiMC_GIT_COMMIT@"
// This is printed on start to standard output
#define VERSION_STR "@MultiMC_VERSION_STRING@"
+
diff --git a/gui/MainWindow.cpp b/gui/MainWindow.cpp
index a6796a29..39c78360 100644
--- a/gui/MainWindow.cpp
+++ b/gui/MainWindow.cpp
@@ -84,7 +84,8 @@
#include "logic/SkinUtils.h"
#include "logic/LegacyInstance.h"
-#include <logic/GoUpdate.h>
+
+#include <logic/updater/UpdateChecker.h>
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
@@ -239,8 +240,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
}
// set up the updater object.
- auto updater = MMC->goupdate();
- connect(updater.get(), SIGNAL(updateAvailable()), SLOT(updateAvailable()));
+ auto updater = MMC->updateChecker();
+ QObject::connect(updater.get(), &UpdateChecker::updateAvailable, this, &MainWindow::updateAvailable);
// if automatic update checks are allowed, start one.
if(MMC->settings()->get("AutoUpdate").toBool())
on_actionCheckUpdate_triggered();
@@ -426,7 +427,7 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *ev)
return QMainWindow::eventFilter(obj, ev);
}
-void MainWindow::updateAvailable()
+void MainWindow::updateAvailable(QString repo, QString versionName, int versionId)
{
UpdateDialog dlg;
UpdateAction action = (UpdateAction) dlg.exec();
@@ -631,7 +632,7 @@ void MainWindow::on_actionConfig_Folder_triggered()
void MainWindow::on_actionCheckUpdate_triggered()
{
- auto updater = MMC->goupdate();
+ auto updater = MMC->updateChecker();
updater->checkForUpdate();
}
diff --git a/gui/MainWindow.h b/gui/MainWindow.h
index e3713f89..1f498eca 100644
--- a/gui/MainWindow.h
+++ b/gui/MainWindow.h
@@ -161,7 +161,7 @@ slots:
void startTask(Task *task);
- void updateAvailable();
+ void updateAvailable(QString repo, QString versionName, int versionId);
void activeAccountChanged();
diff --git a/logic/GoUpdate.cpp b/logic/GoUpdate.cpp
deleted file mode 100644
index 6ac53d19..00000000
--- a/logic/GoUpdate.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-
-#include "GoUpdate.h"
-
-#include "config.h"
-#include "logger/QsLog.h"
-
-GoUpdate::GoUpdate()
-{
- currentBuildIndex = VERSION_BUILD;
- builderName = VERSION_BUILD_TYPE;
- repoUrlBase = VERSION_REPO;
-}
-
-void GoUpdate::updateCheckFailed()
-{
- // TODO: log errors better
- QLOG_ERROR() << "Update check failed for reasons unknown.";
-}
-
-void GoUpdate::updateCheckFinished()
-{
- QJsonParseError jsonError;
- QByteArray data;
- {
- ByteArrayDownloadPtr dl =
- std::dynamic_pointer_cast<ByteArrayDownload>(index_job->first());
- data = dl->m_data;
- index_job.reset();
- }
-
- QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
- if (jsonError.error != QJsonParseError::NoError || !jsonDoc.isObject())
- {
- return;
- }
-
- QVariant doc = jsonDoc.toVariant();
- auto stuff = doc.toMap();
-
- // check api version (or later, branch?)
- int ApiVersion = stuff["ApiVersion"].toInt();
- if (ApiVersion != 0)
- return;
-
- // parse and store the channel list
- auto parsedChannels = stuff["Channels"].toList();
- for (auto channel : parsedChannels)
- {
- auto chanMap = channel.toMap();
- channels.append({chanMap["Id"].toString(), chanMap["Name"].toString(),
- chanMap["CurrentVersion"].toInt()});
- }
-
- // parse and store the version list
- auto parsedVersions = stuff["Versions"].toList();
- for (auto version : parsedVersions)
- {
- auto verMap = version.toMap();
- int versionId = verMap["Id"].toInt();
- versions.append({versionId, verMap["Name"].toString()});
- if (currentBuildIndex < versionId)
- {
- newBuildIndex = versionId;
- }
- }
-
- if (newBuildIndex != -1)
- {
- QLOG_INFO() << "Update is available.";
- emit updateAvailable();
- }
- else
- {
- QLOG_INFO() << "Update check finished.";
- }
-}
-
-void GoUpdate::checkForUpdate()
-{
- if (repoUrlBase == "invalid")
- {
- return;
- }
-
- auto job = new NetJob("Assets index");
- job->addNetAction(
- ByteArrayDownload::make(QUrl(repoUrlBase + "/" + VERSION_BRANCH + "/index.json")));
- connect(job, SIGNAL(succeeded()), SLOT(updateCheckFinished()));
- connect(job, SIGNAL(failed()), SLOT(updateCheckFailed()));
- index_job.reset(job);
- job->start();
-}
-
-/*
-<Forkk> files.multimc.org/lin64/
-<manmaed> Hi Forkkie
-<Forkk> files.multimc.org/win32/
-<Forkk> files.multimc.org/lin32/
-*/
diff --git a/logic/GoUpdate.h b/logic/GoUpdate.h
deleted file mode 100644
index 756a71cf..00000000
--- a/logic/GoUpdate.h
+++ /dev/null
@@ -1,43 +0,0 @@
-#pragma once
-#include "net/NetJob.h"
-
-class GoUpdate : public QObject
-{
- Q_OBJECT
-
-public:
- struct version_channel
- {
- QString id;
- QString name;
- int latestVersion;
- };
-
- struct version_summary
- {
- int id;
- QString name;
- };
-
-signals:
- void updateAvailable();
-
-private slots:
- void updateCheckFinished();
- void updateCheckFailed();
-
-public:
- GoUpdate();
- void checkForUpdate();
-private:
- NetJobPtr index_job;
- NetJobPtr fromto_job;
-
- QString repoUrlBase;
- QString builderName;
- int currentBuildIndex;
- int newBuildIndex = -1;
-
- QList<version_summary> versions;
- QList<version_channel> channels;
-};
diff --git a/logic/updater/UpdateChecker.cpp b/logic/updater/UpdateChecker.cpp
new file mode 100644
index 00000000..68c32ae8
--- /dev/null
+++ b/logic/updater/UpdateChecker.cpp
@@ -0,0 +1,234 @@
+/* Copyright 2013 MultiMC Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "UpdateChecker.h"
+
+#include "MultiMC.h"
+
+#include "config.h"
+#include "logger/QsLog.h"
+
+#include <QJsonObject>
+#include <QJsonArray>
+#include <QJsonValue>
+
+#define API_VERSION 0
+#define CHANLIST_FORMAT 0
+
+UpdateChecker::UpdateChecker()
+{
+ m_currentChannel = VERSION_CHANNEL;
+ m_channelListUrl = CHANLIST_URL;
+ m_updateChecking = false;
+ m_chanListLoading = false;
+ m_checkUpdateWaiting = false;
+ m_chanListLoaded = false;
+}
+
+void UpdateChecker::checkForUpdate()
+{
+ QLOG_DEBUG() << "Checking for updates.";
+
+ // If the channel list hasn't loaded yet, load it and defer checking for updates until later.
+ if (!m_chanListLoaded)
+ {
+ QLOG_DEBUG() << "Channel list isn't loaded yet. Loading channel list and deferring update check.";
+ m_checkUpdateWaiting = true;
+ updateChanList();
+ return;
+ }
+
+ if (m_updateChecking)
+ {
+ QLOG_DEBUG() << "Ignoring update check request. Already checking for updates.";
+ return;
+ }
+
+ m_updateChecking = true;
+
+ // Get the URL for the channel we're using.
+ // TODO: Allow user to select channels. For now, we'll just use the current channel.
+ QString updateChannel = m_currentChannel;
+
+ // Find the desired channel within the channel list and get its repo URL. If if cannot be found, error.
+ m_repoUrl = "";
+ for (ChannelListEntry entry : m_channels)
+ {
+ if (entry.id == updateChannel)
+ m_repoUrl = entry.url;
+ }
+
+ // If we didn't find our channel, error.
+ if (m_repoUrl.isEmpty())
+ {
+ emit updateCheckFailed();
+ return;
+ }
+
+ QUrl indexUrl = QUrl(m_repoUrl).resolved(QUrl("index.json"));
+
+ auto job = new NetJob("GoUpdate Repository Index");
+ job->addNetAction(ByteArrayDownload::make(indexUrl));
+ connect(job, SIGNAL(succeeded()), SLOT(updateCheckFinished()));
+ connect(job, SIGNAL(failed()), SLOT(updateCheckFailed()));
+ indexJob.reset(job);
+ job->start();
+}
+
+void UpdateChecker::updateCheckFinished()
+{
+ QLOG_DEBUG() << "Finished downloading repo index. Checking for new versions.";
+
+ QJsonParseError jsonError;
+ QByteArray data;
+ {
+ ByteArrayDownloadPtr dl = std::dynamic_pointer_cast<ByteArrayDownload>(indexJob->first());
+ data = dl->m_data;
+ indexJob.reset();
+ }
+
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
+ if (jsonError.error != QJsonParseError::NoError || !jsonDoc.isObject())
+ {
+ QLOG_ERROR() << "Failed to parse GoUpdate repository index. JSON error" << jsonError.errorString() << "at offset" << jsonError.offset;
+ return;
+ }
+
+ QJsonObject object = jsonDoc.object();
+
+ bool success = false;
+ int apiVersion = object.value("ApiVersion").toVariant().toInt(&success);
+ if (apiVersion != API_VERSION || !success)
+ {
+ QLOG_ERROR() << "Failed to check for updates. API version mismatch. We're using" << API_VERSION << "server has" << apiVersion;
+ return;
+ }
+
+ QLOG_DEBUG() << "Processing repository version list.";
+ QJsonObject newestVersion;
+ QJsonArray versions = object.value("Versions").toArray();
+ for (QJsonValue versionVal : versions)
+ {
+ QJsonObject version = versionVal.toObject();
+ if (newestVersion.value("Id").toVariant().toInt() < version.value("Id").toVariant().toInt())
+ {
+ QLOG_DEBUG() << "Found newer version with ID" << version.value("Id").toVariant().toInt();
+ newestVersion = version;
+ }
+ }
+
+ // We've got the version with the greatest ID number. Now compare it to our current build number and update if they're different.
+ int newBuildNumber = newestVersion.value("Id").toVariant().toInt();
+ if (newBuildNumber != MMC->version().build)
+ {
+ // Update!
+ emit updateAvailable(m_repoUrl, newestVersion.value("Name").toVariant().toString(), newBuildNumber);
+ }
+
+ m_updateChecking = false;
+}
+
+void UpdateChecker::updateCheckFailed()
+{
+ // TODO: log errors better
+ QLOG_ERROR() << "Update check failed for reasons unknown.";
+}
+
+void UpdateChecker::updateChanList()
+{
+ QLOG_DEBUG() << "Loading the channel list.";
+
+ if (m_channelListUrl.isEmpty())
+ {
+ QLOG_ERROR() << "Failed to update channel list. No channel list URL set."
+ << "If you'd like to use MultiMC's update system, please pass the channel list URL to CMake at compile time.";
+ return;
+ }
+
+ m_chanListLoading = true;
+ NetJob* job = new NetJob("Update System Channel List");
+ job->addNetAction(ByteArrayDownload::make(QUrl(m_channelListUrl)));
+ QObject::connect(job, &NetJob::succeeded, this, &UpdateChecker::chanListDownloadFinished);
+ QObject::connect(job, &NetJob::failed, this, &UpdateChecker::chanListDownloadFailed);
+ chanListJob.reset(job);
+ job->start();
+}
+
+void UpdateChecker::chanListDownloadFinished()
+{
+ QByteArray data;
+ {
+ ByteArrayDownloadPtr dl = std::dynamic_pointer_cast<ByteArrayDownload>(chanListJob->first());
+ data = dl->m_data;
+ chanListJob.reset();
+ }
+
+ QJsonParseError jsonError;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
+ if (jsonError.error != QJsonParseError::NoError)
+ {
+ // TODO: Report errors to the user.
+ QLOG_ERROR() << "Failed to parse channel list JSON:" << jsonError.errorString() << "at" << jsonError.offset;
+ return;
+ }
+
+ QJsonObject object = jsonDoc.object();
+
+ bool success = false;
+ int formatVersion = object.value("format_version").toVariant().toInt(&success);
+ if (formatVersion != CHANLIST_FORMAT || !success)
+ {
+ QLOG_ERROR() << "Failed to check for updates. Channel list format version mismatch. We're using" << CHANLIST_FORMAT << "server has" << formatVersion;
+ return;
+ }
+
+ // Load channels into a temporary array.
+ QList<ChannelListEntry> loadedChannels;
+ QJsonArray channelArray = object.value("channels").toArray();
+ for (QJsonValue chanVal : channelArray)
+ {
+ QJsonObject channelObj = chanVal.toObject();
+ ChannelListEntry entry{
+ channelObj.value("id").toVariant().toString(),
+ channelObj.value("name").toVariant().toString(),
+ channelObj.value("description").toVariant().toString(),
+ channelObj.value("url").toVariant().toString()
+ };
+ if (entry.id.isEmpty() || entry.name.isEmpty() || entry.url.isEmpty())
+ {
+ QLOG_ERROR() << "Channel list entry with empty ID, name, or URL. Skipping.";
+ continue;
+ }
+ loadedChannels.append(entry);
+ }
+
+ // Swap the channel list we just loaded into the object's channel list.
+ m_channels.swap(loadedChannels);
+
+ m_chanListLoading = false;
+ m_chanListLoaded = true;
+ QLOG_INFO() << "Successfully loaded UpdateChecker channel list.";
+
+ // If we're waiting to check for updates, do that now.
+ if (m_checkUpdateWaiting)
+ checkForUpdate();
+}
+
+void UpdateChecker::chanListDownloadFailed()
+{
+ m_chanListLoading = false;
+ QLOG_ERROR() << "Failed to download channel list.";
+}
+
diff --git a/logic/updater/UpdateChecker.h b/logic/updater/UpdateChecker.h
new file mode 100644
index 00000000..829f7303
--- /dev/null
+++ b/logic/updater/UpdateChecker.h
@@ -0,0 +1,93 @@
+/* Copyright 2013 MultiMC Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "logic/net/NetJob.h"
+
+#include <QUrl>
+
+class UpdateChecker : public QObject
+{
+ Q_OBJECT
+
+public:
+signals:
+ //! Signal emitted when an update is available. Passes the URL for the repo and the ID and name for the version.
+ void updateAvailable(QString repoUrl, QString versionName, int versionId);
+
+private slots:
+ void updateCheckFinished();
+ void updateCheckFailed();
+
+ void chanListDownloadFinished();
+ void chanListDownloadFailed();
+
+public:
+ UpdateChecker();
+ void checkForUpdate();
+
+ /*!
+ * Causes the update checker to download the channel list from the URL specified in config.h (generated by CMake).
+ * If this isn't called before checkForUpdate(), it will automatically be called.
+ */
+ void updateChanList();
+
+ /*!
+ * An entry in the channel list.
+ */
+ struct ChannelListEntry
+ {
+ QString id;
+ QString name;
+ QString description;
+ QString url;
+ };
+
+private:
+ NetJobPtr indexJob;
+ NetJobPtr chanListJob;
+
+ QString m_repoUrl;
+
+ QString m_channelListUrl;
+ QString m_currentChannel;
+
+ QList<ChannelListEntry> m_channels;
+
+ /*!
+ * True while the system is checking for updates.
+ * If checkForUpdate is called while this is true, it will be ignored.
+ */
+ bool m_updateChecking;
+
+ /*!
+ * True if the channel list has loaded.
+ * If this is false, trying to check for updates will call updateChanList first.
+ */
+ bool m_chanListLoaded;
+
+ /*!
+ * Set to true while the channel list is currently loading.
+ */
+ bool m_chanListLoading;
+
+ /*!
+ * Set to true when checkForUpdate is called while the channel list isn't loaded.
+ * When the channel list finishes loading, if this is true, the update checker will check for updates.
+ */
+ bool m_checkUpdateWaiting;
+};
+