Rework the update checking system
This commit is contained in:
parent
6aa9bd0f77
commit
bf94aaea75
@ -118,20 +118,50 @@ SET(MultiMC_VERSION_MINOR 0)
|
|||||||
SET(MultiMC_VERSION_BUILD -1 CACHE STRING "Build number. -1 for no build number.")
|
SET(MultiMC_VERSION_BUILD -1 CACHE STRING "Build number. -1 for no build number.")
|
||||||
|
|
||||||
# Build type
|
# 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').")
|
||||||
|
|
||||||
|
# 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}")
|
SET(MultiMC_VERSION_STRING "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}")
|
||||||
|
|
||||||
IF (MultiMC_VERSION_BUILD GREATER -1)
|
IF (MultiMC_VERSION_BUILD GREATER -1)
|
||||||
SET(MultiMC_VERSION_STRING "${MultiMC_VERSION_STRING}.${MultiMC_VERSION_BUILD}")
|
SET(MultiMC_VERSION_STRING "${MultiMC_VERSION_STRING}.${MultiMC_VERSION_BUILD}")
|
||||||
ENDIF ()
|
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 "")
|
IF (NOT MultiMC_VERSION_BUILD_TYPE STREQUAL "")
|
||||||
SET(MultiMC_VERSION_STRING "${MultiMC_VERSION_STRING}-${MultiMC_VERSION_BUILD_TYPE}")
|
SET(MultiMC_VERSION_STRING "${MultiMC_VERSION_STRING}-${MultiMC_VERSION_BUILD_TYPE}")
|
||||||
ENDIF ()
|
ENDIF ()
|
||||||
|
|
||||||
MESSAGE(STATUS "MultiMC 5 version ${MultiMC_VERSION_STRING}")
|
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.
|
#### Custom target to just print the version.
|
||||||
ADD_CUSTOM_TARGET(version echo "Version: ${MultiMC_VERSION_STRING}")
|
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}")
|
MESSAGE(STATUS "Failed to check Git commit. ${GIT_COMMIT_CHECK_RESULTVAR}")
|
||||||
ENDIF()
|
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 header ########
|
||||||
configure_file("${PROJECT_SOURCE_DIR}/config.h.in" "${PROJECT_BINARY_DIR}/include/config.h")
|
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.h
|
||||||
logic/auth/flows/InvalidateTask.cpp
|
logic/auth/flows/InvalidateTask.cpp
|
||||||
|
|
||||||
|
# Update system
|
||||||
|
logic/updater/UpdateChecker.h
|
||||||
|
logic/updater/UpdateChecker.cpp
|
||||||
|
|
||||||
# legacy instances
|
# legacy instances
|
||||||
logic/LegacyInstance.h
|
logic/LegacyInstance.h
|
||||||
logic/LegacyInstance.cpp
|
logic/LegacyInstance.cpp
|
||||||
@ -358,8 +388,6 @@ logic/NagUtils.h
|
|||||||
logic/NagUtils.cpp
|
logic/NagUtils.cpp
|
||||||
logic/SkinUtils.h
|
logic/SkinUtils.h
|
||||||
logic/SkinUtils.cpp
|
logic/SkinUtils.cpp
|
||||||
logic/GoUpdate.h
|
|
||||||
logic/GoUpdate.cpp
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,7 +20,8 @@
|
|||||||
#include "logic/net/HttpMetaCache.h"
|
#include "logic/net/HttpMetaCache.h"
|
||||||
|
|
||||||
#include "logic/JavaUtils.h"
|
#include "logic/JavaUtils.h"
|
||||||
#include "logic/GoUpdate.h"
|
|
||||||
|
#include "logic/updater/UpdateChecker.h"
|
||||||
|
|
||||||
#include "pathutils.h"
|
#include "pathutils.h"
|
||||||
#include "cmdutils.h"
|
#include "cmdutils.h"
|
||||||
@ -33,7 +34,7 @@
|
|||||||
using namespace Util::Commandline;
|
using namespace Util::Commandline;
|
||||||
|
|
||||||
MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv),
|
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");
|
setOrganizationName("MultiMC");
|
||||||
setApplicationName("MultiMC5");
|
setApplicationName("MultiMC5");
|
||||||
@ -140,7 +141,7 @@ MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv),
|
|||||||
initGlobalSettings();
|
initGlobalSettings();
|
||||||
|
|
||||||
// initialize the updater
|
// initialize the updater
|
||||||
m_go_update.reset(new GoUpdate());
|
m_updateChecker.reset(new UpdateChecker());
|
||||||
|
|
||||||
// and instances
|
// and instances
|
||||||
auto InstDirSetting = m_settings->getSetting("InstanceDir");
|
auto InstDirSetting = m_settings->getSetting("InstanceDir");
|
||||||
|
@ -17,7 +17,7 @@ class IconList;
|
|||||||
class QNetworkAccessManager;
|
class QNetworkAccessManager;
|
||||||
class ForgeVersionList;
|
class ForgeVersionList;
|
||||||
class JavaVersionList;
|
class JavaVersionList;
|
||||||
class GoUpdate;
|
class UpdateChecker;
|
||||||
|
|
||||||
#if defined(MMC)
|
#if defined(MMC)
|
||||||
#undef MMC
|
#undef MMC
|
||||||
@ -85,9 +85,9 @@ public:
|
|||||||
return m_metacache;
|
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();
|
std::shared_ptr<LWJGLVersionList> lwjgllist();
|
||||||
@ -112,7 +112,7 @@ private:
|
|||||||
std::shared_ptr<QTranslator> m_mmc_translator;
|
std::shared_ptr<QTranslator> m_mmc_translator;
|
||||||
std::shared_ptr<SettingsObject> m_settings;
|
std::shared_ptr<SettingsObject> m_settings;
|
||||||
std::shared_ptr<InstanceList> m_instances;
|
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<MojangAccountList> m_accounts;
|
||||||
std::shared_ptr<IconList> m_icons;
|
std::shared_ptr<IconList> m_icons;
|
||||||
std::shared_ptr<QNetworkAccessManager> m_qnam;
|
std::shared_ptr<QNetworkAccessManager> m_qnam;
|
||||||
|
@ -32,8 +32,9 @@ struct MultiMCVersion
|
|||||||
QString::number(major),
|
QString::number(major),
|
||||||
QString::number(minor));
|
QString::number(minor));
|
||||||
|
|
||||||
if (build > 0) vstr += QString(".%1").arg(QString::number(build));
|
if (build >= 0) vstr += "." + QString::number(build);
|
||||||
if (!buildType.isEmpty()) vstr += QString("-%1").arg(buildType);
|
if (!channel.isEmpty()) vstr += "-" + channel;
|
||||||
|
if (!buildType.isEmpty()) vstr += "-" + buildType;
|
||||||
|
|
||||||
return vstr;
|
return vstr;
|
||||||
}
|
}
|
||||||
@ -60,10 +61,14 @@ struct MultiMCVersion
|
|||||||
*/
|
*/
|
||||||
int build;
|
int build;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief This build's channel.
|
||||||
|
*/
|
||||||
|
QString channel;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief The build type.
|
* \brief The build type.
|
||||||
* This indicates the type of build that this is. For example, lin64-stable.
|
* This indicates the type of build that this is. For example, lin64 or custombuild.
|
||||||
* Usually corresponds to this build's buildbot builder name.
|
|
||||||
*/
|
*/
|
||||||
QString buildType;
|
QString buildType;
|
||||||
};
|
};
|
||||||
|
14
config.h.in
14
config.h.in
@ -2,19 +2,17 @@
|
|||||||
#define VERSION_MAJOR @MultiMC_VERSION_MAJOR@
|
#define VERSION_MAJOR @MultiMC_VERSION_MAJOR@
|
||||||
#define VERSION_MINOR @MultiMC_VERSION_MINOR@
|
#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_BUILD @MultiMC_VERSION_BUILD@
|
||||||
|
#define VERSION_CHANNEL "@MultiMC_VERSION_CHANNEL@"
|
||||||
#define VERSION_BUILD_TYPE "@MultiMC_VERSION_BUILD_TYPE@"
|
#define VERSION_BUILD_TYPE "@MultiMC_VERSION_BUILD_TYPE@"
|
||||||
|
|
||||||
// URL base for the updater
|
// URL for the updater's channel
|
||||||
#define VERSION_REPO "@MultiMC_REPO_BASE_URL@"
|
#define CHANLIST_URL "@MultiMC_CHANLIST_URL@"
|
||||||
|
|
||||||
// The branch used for this build. User can switch between 'stable' and 'develop'
|
// The commit hash of this build
|
||||||
// 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
|
|
||||||
#define GIT_COMMIT "@MultiMC_GIT_COMMIT@"
|
#define GIT_COMMIT "@MultiMC_GIT_COMMIT@"
|
||||||
|
|
||||||
// This is printed on start to standard output
|
// This is printed on start to standard output
|
||||||
#define VERSION_STR "@MultiMC_VERSION_STRING@"
|
#define VERSION_STR "@MultiMC_VERSION_STRING@"
|
||||||
|
|
||||||
|
@ -84,7 +84,8 @@
|
|||||||
#include "logic/SkinUtils.h"
|
#include "logic/SkinUtils.h"
|
||||||
|
|
||||||
#include "logic/LegacyInstance.h"
|
#include "logic/LegacyInstance.h"
|
||||||
#include <logic/GoUpdate.h>
|
|
||||||
|
#include <logic/updater/UpdateChecker.h>
|
||||||
|
|
||||||
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
|
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.
|
// set up the updater object.
|
||||||
auto updater = MMC->goupdate();
|
auto updater = MMC->updateChecker();
|
||||||
connect(updater.get(), SIGNAL(updateAvailable()), SLOT(updateAvailable()));
|
QObject::connect(updater.get(), &UpdateChecker::updateAvailable, this, &MainWindow::updateAvailable);
|
||||||
// if automatic update checks are allowed, start one.
|
// if automatic update checks are allowed, start one.
|
||||||
if(MMC->settings()->get("AutoUpdate").toBool())
|
if(MMC->settings()->get("AutoUpdate").toBool())
|
||||||
on_actionCheckUpdate_triggered();
|
on_actionCheckUpdate_triggered();
|
||||||
@ -426,7 +427,7 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *ev)
|
|||||||
return QMainWindow::eventFilter(obj, ev);
|
return QMainWindow::eventFilter(obj, ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::updateAvailable()
|
void MainWindow::updateAvailable(QString repo, QString versionName, int versionId)
|
||||||
{
|
{
|
||||||
UpdateDialog dlg;
|
UpdateDialog dlg;
|
||||||
UpdateAction action = (UpdateAction) dlg.exec();
|
UpdateAction action = (UpdateAction) dlg.exec();
|
||||||
@ -631,7 +632,7 @@ void MainWindow::on_actionConfig_Folder_triggered()
|
|||||||
|
|
||||||
void MainWindow::on_actionCheckUpdate_triggered()
|
void MainWindow::on_actionCheckUpdate_triggered()
|
||||||
{
|
{
|
||||||
auto updater = MMC->goupdate();
|
auto updater = MMC->updateChecker();
|
||||||
updater->checkForUpdate();
|
updater->checkForUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,7 +161,7 @@ slots:
|
|||||||
|
|
||||||
void startTask(Task *task);
|
void startTask(Task *task);
|
||||||
|
|
||||||
void updateAvailable();
|
void updateAvailable(QString repo, QString versionName, int versionId);
|
||||||
|
|
||||||
void activeAccountChanged();
|
void activeAccountChanged();
|
||||||
|
|
||||||
|
@ -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/
|
|
||||||
*/
|
|
@ -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;
|
|
||||||
};
|
|
234
logic/updater/UpdateChecker.cpp
Normal file
234
logic/updater/UpdateChecker.cpp
Normal file
@ -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.";
|
||||||
|
}
|
||||||
|
|
93
logic/updater/UpdateChecker.h
Normal file
93
logic/updater/UpdateChecker.h
Normal file
@ -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;
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in New Issue
Block a user