Merge branch 'develop' of github.com:MultiMC/MultiMC5 into feature_news
Conflicts: CMakeLists.txt
This commit is contained in:
@ -149,7 +149,7 @@ public:
|
||||
*/
|
||||
virtual SettingsObject &settings() const;
|
||||
|
||||
/// returns a valid update task if update is needed, NULL otherwise
|
||||
/// returns a valid update task
|
||||
virtual std::shared_ptr<Task> doUpdate(bool only_prepare) = 0;
|
||||
|
||||
/// returns a valid minecraft process, ready for launch with the given account.
|
||||
|
@ -152,8 +152,17 @@ QStringList OneSixInstance::processMinecraftArgs(MojangAccountPtr account)
|
||||
token_mapping["game_directory"] = absRootDir;
|
||||
QString absAssetsDir = QDir("assets/").absolutePath();
|
||||
token_mapping["game_assets"] = reconstructAssets(d->version).absolutePath();
|
||||
//TODO: this is something new and not even fully implemented in the vanilla launcher.
|
||||
token_mapping["user_properties"] = "{ }";
|
||||
|
||||
auto user = account->user();
|
||||
QJsonObject userAttrs;
|
||||
for(auto key: user.properties.keys())
|
||||
{
|
||||
auto array = QJsonArray::fromStringList(user.properties.values(key));
|
||||
userAttrs.insert(key, array);
|
||||
}
|
||||
QJsonDocument value(userAttrs);
|
||||
|
||||
token_mapping["user_properties"] = value.toJson(QJsonDocument::Compact);
|
||||
token_mapping["user_type"] = account->currentProfile()->legacy ? "legacy" : "mojang";
|
||||
// 1.7.3+ assets tokens
|
||||
token_mapping["assets_root"] = absAssetsDir;
|
||||
|
@ -54,11 +54,9 @@ void OneSixUpdate::executeTask()
|
||||
|
||||
if (m_only_prepare)
|
||||
{
|
||||
if (m_inst->shouldUpdate())
|
||||
{
|
||||
emitFailed("Unable to update instance in offline mode.");
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* FIXME: in offline mode, do not proceed!
|
||||
*/
|
||||
setStatus("Testing the Java installation.");
|
||||
QString java_path = m_inst->settings().get("JavaPath").toString();
|
||||
|
||||
@ -243,11 +241,13 @@ void OneSixUpdate::assetIndexFinished()
|
||||
auto objectDL = MD5EtagDownload::make(
|
||||
QUrl("http://" + URLConstants::RESOURCE_BASE + objectName),
|
||||
objectFile.filePath());
|
||||
objectDL->m_total_progress = object.size;
|
||||
dls.append(objectDL);
|
||||
}
|
||||
}
|
||||
if(dls.size())
|
||||
{
|
||||
setStatus("Getting the assets files from Mojang...");
|
||||
auto job = new NetJob("Assets for " + inst->name());
|
||||
for(auto dl: dls)
|
||||
job->addNetAction(dl);
|
||||
|
@ -68,6 +68,7 @@ MojangAccountPtr MojangAccount::loadFromJson(const QJsonObject &object)
|
||||
User u;
|
||||
QJsonObject userStructure = object.value("user").toObject();
|
||||
u.id = userStructure.value("id").toString();
|
||||
/*
|
||||
QJsonObject propMap = userStructure.value("properties").toObject();
|
||||
for(auto key: propMap.keys())
|
||||
{
|
||||
@ -75,6 +76,7 @@ MojangAccountPtr MojangAccount::loadFromJson(const QJsonObject &object)
|
||||
for(auto value: values)
|
||||
u.properties.insert(key, value.toString());
|
||||
}
|
||||
*/
|
||||
account->m_user = u;
|
||||
}
|
||||
account->m_username = username;
|
||||
@ -119,6 +121,7 @@ QJsonObject MojangAccount::saveToJson() const
|
||||
QJsonObject userStructure;
|
||||
{
|
||||
userStructure.insert("id", m_user.id);
|
||||
/*
|
||||
QJsonObject userAttrs;
|
||||
for(auto key: m_user.properties.keys())
|
||||
{
|
||||
@ -126,6 +129,7 @@ QJsonObject MojangAccount::saveToJson() const
|
||||
userAttrs.insert(key, array);
|
||||
}
|
||||
userStructure.insert("properties", userAttrs);
|
||||
*/
|
||||
}
|
||||
json.insert("user", userStructure);
|
||||
|
||||
|
@ -122,6 +122,11 @@ public: /* queries */
|
||||
return m_profiles;
|
||||
}
|
||||
|
||||
const User & user()
|
||||
{
|
||||
return m_user;
|
||||
}
|
||||
|
||||
//! Get the session ID required for legacy Minecraft versions
|
||||
QString sessionId() const
|
||||
{
|
||||
|
@ -22,10 +22,12 @@
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonParseError>
|
||||
#include <QDir>
|
||||
|
||||
#include "logger/QsLog.h"
|
||||
|
||||
#include "logic/auth/MojangAccount.h"
|
||||
#include <pathutils.h>
|
||||
|
||||
#define ACCOUNT_LIST_FORMAT_VERSION 2
|
||||
|
||||
@ -148,9 +150,6 @@ QVariant MojangAccountList::data(const QModelIndex &index, int role) const
|
||||
case Qt::DisplayRole:
|
||||
switch (index.column())
|
||||
{
|
||||
case ActiveColumn:
|
||||
return account == m_activeAccount;
|
||||
|
||||
case NameColumn:
|
||||
return account->username();
|
||||
|
||||
@ -164,6 +163,13 @@ QVariant MojangAccountList::data(const QModelIndex &index, int role) const
|
||||
case PointerRole:
|
||||
return qVariantFromValue(account);
|
||||
|
||||
case Qt::CheckStateRole:
|
||||
switch (index.column())
|
||||
{
|
||||
case ActiveColumn:
|
||||
return account == m_activeAccount;
|
||||
}
|
||||
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
@ -212,6 +218,36 @@ int MojangAccountList::columnCount(const QModelIndex &parent) const
|
||||
return 2;
|
||||
}
|
||||
|
||||
Qt::ItemFlags MojangAccountList::flags(const QModelIndex &index) const
|
||||
{
|
||||
if (index.row() < 0 || index.row() >= rowCount(index) || !index.isValid())
|
||||
{
|
||||
return Qt::NoItemFlags;
|
||||
}
|
||||
|
||||
return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
|
||||
}
|
||||
|
||||
bool MojangAccountList::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
if (index.row() < 0 || index.row() >= rowCount(index) || !index.isValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(role == Qt::CheckStateRole)
|
||||
{
|
||||
if(value == Qt::Checked)
|
||||
{
|
||||
MojangAccountPtr account = this->at(index.row());
|
||||
this->setActiveAccount(account->username());
|
||||
}
|
||||
}
|
||||
|
||||
emit dataChanged(index, index);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MojangAccountList::updateListData(QList<MojangAccountPtr> versions)
|
||||
{
|
||||
beginResetModel();
|
||||
@ -311,6 +347,18 @@ bool MojangAccountList::saveList(const QString &filePath)
|
||||
return false;
|
||||
}
|
||||
|
||||
// make sure the parent folder exists
|
||||
if(!ensureFilePathExists(path))
|
||||
return false;
|
||||
|
||||
// make sure the file wasn't overwritten with a folder before (fixes a bug)
|
||||
QFileInfo finfo(path);
|
||||
if(finfo.isDir())
|
||||
{
|
||||
QDir badDir(path);
|
||||
badDir.removeRecursively();
|
||||
}
|
||||
|
||||
QLOG_INFO() << "Writing account list to" << path;
|
||||
|
||||
QLOG_DEBUG() << "Building JSON data structure.";
|
||||
@ -366,3 +414,13 @@ void MojangAccountList::setListFilePath(QString path, bool autosave)
|
||||
m_listFilePath = path;
|
||||
m_autosave = autosave;
|
||||
}
|
||||
|
||||
bool MojangAccountList::anyAccountIsValid()
|
||||
{
|
||||
for(auto account:m_accounts)
|
||||
{
|
||||
if(account->accountStatus() != NotVerified)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -64,6 +64,8 @@ public:
|
||||
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
|
||||
virtual int rowCount(const QModelIndex &parent) const;
|
||||
virtual int columnCount(const QModelIndex &parent) const;
|
||||
virtual Qt::ItemFlags flags(const QModelIndex &index) const;
|
||||
virtual bool setData(const QModelIndex &index, const QVariant &value, int role);
|
||||
|
||||
/*!
|
||||
* Adds a the given Mojang account to the account list.
|
||||
@ -124,6 +126,11 @@ public:
|
||||
* If the username given is an empty string, sets the active account to nothing.
|
||||
*/
|
||||
virtual void setActiveAccount(const QString &username);
|
||||
|
||||
/*!
|
||||
* Returns true if any of the account is at least Validated
|
||||
*/
|
||||
bool anyAccountIsValid();
|
||||
|
||||
signals:
|
||||
/*!
|
||||
|
@ -162,23 +162,17 @@ bool AuthenticateTask::processResponse(QJsonObject responseData)
|
||||
}
|
||||
|
||||
// this is what the vanilla launcher passes to the userProperties launch param
|
||||
// doesn't seem to be used for anything so far? I don't get any of this data on my account
|
||||
// (peterixxx)
|
||||
// is it a good idea to log this?
|
||||
if (responseData.contains("user"))
|
||||
{
|
||||
User u;
|
||||
auto obj = responseData.value("user").toObject();
|
||||
u.id = obj.value("id").toString();
|
||||
QLOG_DEBUG() << "User ID: " << u.id ;
|
||||
auto propArray = obj.value("properties").toArray();
|
||||
QLOG_DEBUG() << "User Properties: ";
|
||||
for (auto prop : propArray)
|
||||
{
|
||||
auto propTuple = prop.toObject();
|
||||
auto name = propTuple.value("name").toString();
|
||||
auto value = propTuple.value("value").toString();
|
||||
QLOG_DEBUG() << name << " : " << value;
|
||||
u.properties.insert(name, value);
|
||||
}
|
||||
m_account->m_user = u;
|
||||
|
@ -112,20 +112,21 @@ bool RefreshTask::processResponse(QJsonObject responseData)
|
||||
// this is what the vanilla launcher passes to the userProperties launch param
|
||||
if (responseData.contains("user"))
|
||||
{
|
||||
User u;
|
||||
auto obj = responseData.value("user").toObject();
|
||||
auto userId = obj.value("id").toString();
|
||||
u.id = obj.value("id").toString();
|
||||
auto propArray = obj.value("properties").toArray();
|
||||
QLOG_DEBUG() << "User ID: " << userId;
|
||||
QLOG_DEBUG() << "User Properties: ";
|
||||
for (auto prop : propArray)
|
||||
{
|
||||
auto propTuple = prop.toObject();
|
||||
auto name = propTuple.value("name").toString();
|
||||
auto value = propTuple.value("value").toString();
|
||||
QLOG_DEBUG() << name << " : " << value;
|
||||
u.properties.insert(name, value);
|
||||
}
|
||||
m_account->m_user = u;
|
||||
}
|
||||
|
||||
|
||||
// We've made it through the minefield of possible errors. Return true to indicate that
|
||||
// we've succeeded.
|
||||
QLOG_DEBUG() << "Finished reading refresh response.";
|
||||
|
@ -36,11 +36,16 @@ const static int GROUP_FILE_FORMAT_VERSION = 1;
|
||||
InstanceList::InstanceList(const QString &instDir, QObject *parent)
|
||||
: QAbstractListModel(parent), m_instDir(instDir)
|
||||
{
|
||||
connect(MMC, &MultiMC::aboutToQuit, this, &InstanceList::saveGroupList);
|
||||
|
||||
if (!QDir::current().exists(m_instDir))
|
||||
{
|
||||
QDir::current().mkpath(m_instDir);
|
||||
}
|
||||
}
|
||||
|
||||
InstanceList::~InstanceList()
|
||||
{
|
||||
saveGroupList();
|
||||
}
|
||||
|
||||
int InstanceList::rowCount(const QModelIndex &parent) const
|
||||
@ -112,6 +117,11 @@ void InstanceList::groupChanged()
|
||||
saveGroupList();
|
||||
}
|
||||
|
||||
QStringList InstanceList::getGroups()
|
||||
{
|
||||
return m_groups.toList();
|
||||
}
|
||||
|
||||
void InstanceList::saveGroupList()
|
||||
{
|
||||
QString groupFileName = m_instDir + "/instgroups.json";
|
||||
@ -121,7 +131,7 @@ void InstanceList::saveGroupList()
|
||||
if (!groupFile.open(QIODevice::WriteOnly | QIODevice::Truncate))
|
||||
{
|
||||
// An error occurred. Ignore it.
|
||||
QLOG_ERROR() << "Failed to read instance group file.";
|
||||
QLOG_ERROR() << "Failed to save instance group file.";
|
||||
return;
|
||||
}
|
||||
QTextStream out(&groupFile);
|
||||
@ -132,6 +142,10 @@ void InstanceList::saveGroupList()
|
||||
QString group = instance->group();
|
||||
if (group.isEmpty())
|
||||
continue;
|
||||
|
||||
// keep a list/set of groups for choosing
|
||||
m_groups.insert(group);
|
||||
|
||||
if (!groupMap.count(group))
|
||||
{
|
||||
QSet<QString> set;
|
||||
@ -248,6 +262,9 @@ void InstanceList::loadGroupList(QMap<QString, QString> &groupMap)
|
||||
continue;
|
||||
}
|
||||
|
||||
// keep a list/set of groups for choosing
|
||||
m_groups.insert(groupName);
|
||||
|
||||
// Iterate through the list of instances in the group.
|
||||
QJsonArray instancesArray = groupObj.value("instances").toArray();
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
#include <QObject>
|
||||
#include <QAbstractListModel>
|
||||
#include <QSet>
|
||||
#include "categorizedsortfilterproxymodel.h"
|
||||
#include <QIcon>
|
||||
|
||||
@ -29,6 +30,9 @@ class InstanceList : public QAbstractListModel
|
||||
Q_OBJECT
|
||||
private:
|
||||
void loadGroupList(QMap<QString, QString> &groupList);
|
||||
|
||||
private
|
||||
slots:
|
||||
void saveGroupList();
|
||||
|
||||
public:
|
||||
@ -94,6 +98,9 @@ public:
|
||||
InstancePtr getInstanceById(QString id) const;
|
||||
|
||||
QModelIndex getInstanceIndexById(const QString &id) const;
|
||||
|
||||
// FIXME: instead of iterating through all instances and forming a set, keep the set around
|
||||
QStringList getGroups();
|
||||
signals:
|
||||
void dataIsInvalid();
|
||||
|
||||
@ -113,6 +120,7 @@ private:
|
||||
protected:
|
||||
QString m_instDir;
|
||||
QList<InstancePtr> m_instances;
|
||||
QSet<QString> m_groups;
|
||||
};
|
||||
|
||||
class InstanceProxyModel : public KCategorizedSortFilterProxyModel
|
||||
|
@ -42,7 +42,9 @@ void ByteArrayDownload::start()
|
||||
|
||||
void ByteArrayDownload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
|
||||
{
|
||||
emit progress(index_within_job, bytesReceived, bytesTotal);
|
||||
m_total_progress = bytesTotal;
|
||||
m_progress = bytesReceived;
|
||||
emit progress(m_index_within_job, bytesReceived, bytesTotal);
|
||||
}
|
||||
|
||||
void ByteArrayDownload::downloadError(QNetworkReply::NetworkError error)
|
||||
@ -62,14 +64,14 @@ void ByteArrayDownload::downloadFinished()
|
||||
m_status = Job_Finished;
|
||||
m_data = m_reply->readAll();
|
||||
m_reply.reset();
|
||||
emit succeeded(index_within_job);
|
||||
emit succeeded(m_index_within_job);
|
||||
return;
|
||||
}
|
||||
// else the download failed
|
||||
else
|
||||
{
|
||||
m_reply.reset();
|
||||
emit failed(index_within_job);
|
||||
emit failed(m_index_within_job);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -35,14 +35,14 @@ void CacheDownload::start()
|
||||
{
|
||||
if (!m_entry->stale)
|
||||
{
|
||||
emit succeeded(index_within_job);
|
||||
emit succeeded(m_index_within_job);
|
||||
return;
|
||||
}
|
||||
m_output_file.setFileName(m_target_path);
|
||||
// if there already is a file and md5 checking is in effect and it can be opened
|
||||
if (!ensureFilePathExists(m_target_path))
|
||||
{
|
||||
emit failed(index_within_job);
|
||||
emit failed(m_index_within_job);
|
||||
return;
|
||||
}
|
||||
QLOG_INFO() << "Downloading " << m_url.toString();
|
||||
@ -69,7 +69,9 @@ void CacheDownload::start()
|
||||
|
||||
void CacheDownload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
|
||||
{
|
||||
emit progress(index_within_job, bytesReceived, bytesTotal);
|
||||
m_total_progress = bytesTotal;
|
||||
m_progress = bytesReceived;
|
||||
emit progress(m_index_within_job, bytesReceived, bytesTotal);
|
||||
}
|
||||
|
||||
void CacheDownload::downloadError(QNetworkReply::NetworkError error)
|
||||
@ -116,7 +118,7 @@ void CacheDownload::downloadFinished()
|
||||
MMC->metacache()->updateEntry(m_entry);
|
||||
|
||||
m_reply.reset();
|
||||
emit succeeded(index_within_job);
|
||||
emit succeeded(m_index_within_job);
|
||||
return;
|
||||
}
|
||||
// else the download failed
|
||||
@ -125,7 +127,7 @@ void CacheDownload::downloadFinished()
|
||||
m_output_file.close();
|
||||
m_output_file.remove();
|
||||
m_reply.reset();
|
||||
emit failed(index_within_job);
|
||||
emit failed(m_index_within_job);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -140,7 +142,7 @@ void CacheDownload::downloadReadyRead()
|
||||
* Can't open the file... the job failed
|
||||
*/
|
||||
m_reply->abort();
|
||||
emit failed(index_within_job);
|
||||
emit failed(m_index_within_job);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ void ForgeMirrors::deferToFixedList()
|
||||
"https://www.creeperhost.net/link.php?id=1",
|
||||
"http://new.creeperrepo.net/forge/maven/"});
|
||||
injectDownloads();
|
||||
emit succeeded(index_within_job);
|
||||
emit succeeded(m_index_within_job);
|
||||
}
|
||||
|
||||
void ForgeMirrors::parseMirrorList()
|
||||
@ -88,7 +88,7 @@ void ForgeMirrors::parseMirrorList()
|
||||
if(!m_mirrors.size())
|
||||
deferToFixedList();
|
||||
injectDownloads();
|
||||
emit succeeded(index_within_job);
|
||||
emit succeeded(m_index_within_job);
|
||||
}
|
||||
|
||||
void ForgeMirrors::injectDownloads()
|
||||
@ -108,7 +108,9 @@ void ForgeMirrors::injectDownloads()
|
||||
|
||||
void ForgeMirrors::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
|
||||
{
|
||||
emit progress(index_within_job, bytesReceived, bytesTotal);
|
||||
m_total_progress = bytesTotal;
|
||||
m_progress = bytesReceived;
|
||||
emit progress(m_index_within_job, bytesReceived, bytesTotal);
|
||||
}
|
||||
|
||||
void ForgeMirrors::downloadReadyRead()
|
||||
|
@ -44,20 +44,20 @@ void ForgeXzDownload::start()
|
||||
if (!m_entry->stale)
|
||||
{
|
||||
m_status = Job_Finished;
|
||||
emit succeeded(index_within_job);
|
||||
emit succeeded(m_index_within_job);
|
||||
return;
|
||||
}
|
||||
// can we actually create the real, final file?
|
||||
if (!ensureFilePathExists(m_target_path))
|
||||
{
|
||||
m_status = Job_Failed;
|
||||
emit failed(index_within_job);
|
||||
emit failed(m_index_within_job);
|
||||
return;
|
||||
}
|
||||
if (m_mirrors.empty())
|
||||
{
|
||||
m_status = Job_Failed;
|
||||
emit failed(index_within_job);
|
||||
emit failed(m_index_within_job);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -80,7 +80,9 @@ void ForgeXzDownload::start()
|
||||
|
||||
void ForgeXzDownload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
|
||||
{
|
||||
emit progress(index_within_job, bytesReceived, bytesTotal);
|
||||
m_total_progress = bytesTotal;
|
||||
m_progress = bytesReceived;
|
||||
emit progress(m_index_within_job, bytesReceived, bytesTotal);
|
||||
}
|
||||
|
||||
void ForgeXzDownload::downloadError(QNetworkReply::NetworkError error)
|
||||
@ -100,7 +102,7 @@ void ForgeXzDownload::failAndTryNextMirror()
|
||||
m_mirror_index = next;
|
||||
|
||||
updateUrl();
|
||||
emit failed(index_within_job);
|
||||
emit failed(m_index_within_job);
|
||||
}
|
||||
|
||||
void ForgeXzDownload::updateUrl()
|
||||
@ -148,7 +150,7 @@ void ForgeXzDownload::downloadFinished()
|
||||
m_status = Job_Failed;
|
||||
m_pack200_xz_file.remove();
|
||||
m_reply.reset();
|
||||
emit failed(index_within_job);
|
||||
emit failed(m_index_within_job);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -175,7 +177,7 @@ void ForgeXzDownload::downloadReadyRead()
|
||||
* Can't open the file... the job failed
|
||||
*/
|
||||
m_reply->abort();
|
||||
emit failed(index_within_job);
|
||||
emit failed(m_index_within_job);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -347,5 +349,5 @@ void ForgeXzDownload::decompressAndInstall()
|
||||
MMC->metacache()->updateEntry(m_entry);
|
||||
|
||||
m_reply.reset();
|
||||
emit succeeded(index_within_job);
|
||||
emit succeeded(m_index_within_job);
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ void MD5EtagDownload::start()
|
||||
if (m_check_md5 && hash == m_expected_md5)
|
||||
{
|
||||
QLOG_INFO() << "Skipping " << m_url.toString() << ": md5 match.";
|
||||
emit succeeded(index_within_job);
|
||||
emit succeeded(m_index_within_job);
|
||||
return;
|
||||
}
|
||||
else
|
||||
@ -54,7 +54,7 @@ void MD5EtagDownload::start()
|
||||
}
|
||||
if (!ensureFilePathExists(filename))
|
||||
{
|
||||
emit failed(index_within_job);
|
||||
emit failed(m_index_within_job);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -68,7 +68,7 @@ void MD5EtagDownload::start()
|
||||
// Plus, this way, we don't end up starting a download for a file we can't open.
|
||||
if (!m_output_file.open(QIODevice::WriteOnly))
|
||||
{
|
||||
emit failed(index_within_job);
|
||||
emit failed(m_index_within_job);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -86,7 +86,9 @@ void MD5EtagDownload::start()
|
||||
|
||||
void MD5EtagDownload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
|
||||
{
|
||||
emit progress(index_within_job, bytesReceived, bytesTotal);
|
||||
m_total_progress = bytesTotal;
|
||||
m_progress = bytesReceived;
|
||||
emit progress(m_index_within_job, bytesReceived, bytesTotal);
|
||||
}
|
||||
|
||||
void MD5EtagDownload::downloadError(QNetworkReply::NetworkError error)
|
||||
@ -107,7 +109,7 @@ void MD5EtagDownload::downloadFinished()
|
||||
|
||||
QLOG_INFO() << "Finished " << m_url.toString() << " got " << m_reply->rawHeader("ETag").constData();
|
||||
m_reply.reset();
|
||||
emit succeeded(index_within_job);
|
||||
emit succeeded(m_index_within_job);
|
||||
return;
|
||||
}
|
||||
// else the download failed
|
||||
@ -115,7 +117,7 @@ void MD5EtagDownload::downloadFinished()
|
||||
{
|
||||
m_output_file.close();
|
||||
m_reply.reset();
|
||||
emit failed(index_within_job);
|
||||
emit failed(m_index_within_job);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -130,7 +132,7 @@ void MD5EtagDownload::downloadReadyRead()
|
||||
* Can't open the file... the job failed
|
||||
*/
|
||||
m_reply->abort();
|
||||
emit failed(index_within_job);
|
||||
emit failed(m_index_within_job);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -38,6 +38,19 @@ protected:
|
||||
public:
|
||||
virtual ~NetAction() {};
|
||||
|
||||
public:
|
||||
virtual qint64 totalProgress() const
|
||||
{
|
||||
return m_total_progress;
|
||||
}
|
||||
virtual qint64 currentProgress() const
|
||||
{
|
||||
return m_progress;
|
||||
}
|
||||
virtual qint64 numberOfFailures() const
|
||||
{
|
||||
return m_failures;
|
||||
}
|
||||
public:
|
||||
/// the network reply
|
||||
std::shared_ptr<QNetworkReply> m_reply;
|
||||
@ -46,10 +59,16 @@ public:
|
||||
QUrl m_url;
|
||||
|
||||
/// The file's status
|
||||
JobStatus m_status;
|
||||
JobStatus m_status = Job_NotStarted;
|
||||
|
||||
/// index within the parent job
|
||||
int index_within_job = 0;
|
||||
int m_index_within_job = 0;
|
||||
|
||||
qint64 m_progress = 0;
|
||||
qint64 m_total_progress = 1;
|
||||
|
||||
/// number of failures up to this point
|
||||
int m_failures = 0;
|
||||
|
||||
signals:
|
||||
void started(int index);
|
||||
|
@ -36,10 +36,16 @@ public:
|
||||
template <typename T> bool addNetAction(T action)
|
||||
{
|
||||
NetActionPtr base = std::static_pointer_cast<NetAction>(action);
|
||||
base->index_within_job = downloads.size();
|
||||
base->m_index_within_job = downloads.size();
|
||||
downloads.append(action);
|
||||
parts_progress.append(part_info());
|
||||
total_progress++;
|
||||
part_info pi;
|
||||
{
|
||||
pi.current_progress = base->currentProgress();
|
||||
pi.total_progress = base->totalProgress();
|
||||
pi.failures = base->numberOfFailures();
|
||||
}
|
||||
parts_progress.append(pi);
|
||||
total_progress += pi.total_progress;
|
||||
// if this is already running, the action needs to be started right away!
|
||||
if (isRunning())
|
||||
{
|
||||
|
@ -78,7 +78,9 @@ bool PasteUpload::parseResult(QJsonDocument doc, QString *parseError)
|
||||
parseError = new QString(object.value("error").toString());
|
||||
return false;
|
||||
}
|
||||
// FIXME: not the place for GUI things.
|
||||
QString pasteUrl = object.value("paste").toObject().value("link").toString();
|
||||
QDesktopServices::openUrl(pasteUrl);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1,175 +0,0 @@
|
||||
/* 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 "S3ListBucket.h"
|
||||
#include "MultiMC.h"
|
||||
#include "logger/QsLog.h"
|
||||
#include <QUrlQuery>
|
||||
#include <qxmlstream.h>
|
||||
#include <QDomDocument>
|
||||
|
||||
inline QDomElement getDomElementByTagName(QDomElement parent, QString tagname)
|
||||
{
|
||||
QDomNodeList elementList = parent.elementsByTagName(tagname);
|
||||
if (elementList.count())
|
||||
return elementList.at(0).toElement();
|
||||
else
|
||||
return QDomElement();
|
||||
}
|
||||
|
||||
S3ListBucket::S3ListBucket(QUrl url) : NetAction()
|
||||
{
|
||||
m_url = url;
|
||||
m_status = Job_NotStarted;
|
||||
}
|
||||
|
||||
void S3ListBucket::start()
|
||||
{
|
||||
QUrl finalUrl = m_url;
|
||||
if (current_marker.size())
|
||||
{
|
||||
QUrlQuery query;
|
||||
query.addQueryItem("marker", current_marker);
|
||||
finalUrl.setQuery(query);
|
||||
}
|
||||
QNetworkRequest request(finalUrl);
|
||||
request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Uncached)");
|
||||
auto worker = MMC->qnam();
|
||||
QNetworkReply *rep = worker->get(request);
|
||||
|
||||
m_reply = std::shared_ptr<QNetworkReply>(rep);
|
||||
connect(rep, SIGNAL(downloadProgress(qint64, qint64)),
|
||||
SLOT(downloadProgress(qint64, qint64)));
|
||||
connect(rep, SIGNAL(finished()), SLOT(downloadFinished()));
|
||||
connect(rep, SIGNAL(error(QNetworkReply::NetworkError)),
|
||||
SLOT(downloadError(QNetworkReply::NetworkError)));
|
||||
connect(rep, SIGNAL(readyRead()), SLOT(downloadReadyRead()));
|
||||
}
|
||||
|
||||
void S3ListBucket::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
|
||||
{
|
||||
emit progress(index_within_job, bytesSoFar + bytesReceived, bytesSoFar + bytesTotal);
|
||||
}
|
||||
|
||||
void S3ListBucket::downloadError(QNetworkReply::NetworkError error)
|
||||
{
|
||||
// error happened during download.
|
||||
QLOG_ERROR() << "Error getting URL:" << m_url.toString().toLocal8Bit()
|
||||
<< "Network error: " << error;
|
||||
m_status = Job_Failed;
|
||||
}
|
||||
|
||||
void S3ListBucket::processValidReply()
|
||||
{
|
||||
QLOG_TRACE() << "GOT: " << m_url.toString() << " marker:" << current_marker;
|
||||
auto readContents = [&](QXmlStreamReader & xml)
|
||||
{
|
||||
QString Key, ETag, Size;
|
||||
while (!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == "Contents"))
|
||||
{
|
||||
if (xml.tokenType() == QXmlStreamReader::StartElement)
|
||||
{
|
||||
if (xml.name() == "Key")
|
||||
{
|
||||
Key = xml.readElementText();
|
||||
}
|
||||
if (xml.name() == "ETag")
|
||||
{
|
||||
ETag = xml.readElementText();
|
||||
}
|
||||
if (xml.name() == "Size")
|
||||
{
|
||||
Size = xml.readElementText();
|
||||
}
|
||||
}
|
||||
xml.readNext();
|
||||
}
|
||||
if (xml.error() != QXmlStreamReader::NoError)
|
||||
return;
|
||||
objects.append({Key, ETag, Size.toLongLong()});
|
||||
};
|
||||
|
||||
// nothing went wrong...
|
||||
QByteArray ba = m_reply->readAll();
|
||||
|
||||
QString xmlErrorMsg;
|
||||
|
||||
bool is_truncated = false;
|
||||
QXmlStreamReader xml(ba);
|
||||
while (!xml.atEnd() && !xml.hasError())
|
||||
{
|
||||
/* Read next element.*/
|
||||
QXmlStreamReader::TokenType token = xml.readNext();
|
||||
/* If token is just StartDocument, we'll go to next.*/
|
||||
if (token == QXmlStreamReader::StartDocument)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (token == QXmlStreamReader::StartElement)
|
||||
{
|
||||
/* If it's named person, we'll dig the information from there.*/
|
||||
if (xml.name() == "Contents")
|
||||
{
|
||||
readContents(xml);
|
||||
}
|
||||
else if (xml.name() == "IsTruncated")
|
||||
{
|
||||
is_truncated = (xml.readElementText() == "true");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (xml.hasError())
|
||||
{
|
||||
QLOG_ERROR() << "Failed to process s3.amazonaws.com/Minecraft.Resources. XML error:"
|
||||
<< xml.errorString() << ba;
|
||||
emit failed(index_within_job);
|
||||
return;
|
||||
}
|
||||
if (is_truncated)
|
||||
{
|
||||
current_marker = objects.last().Key;
|
||||
bytesSoFar += m_reply->size();
|
||||
m_reply.reset();
|
||||
start();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_status = Job_Finished;
|
||||
m_reply.reset();
|
||||
emit succeeded(index_within_job);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void S3ListBucket::downloadFinished()
|
||||
{
|
||||
// if the download succeeded
|
||||
if (m_status != Job_Failed)
|
||||
{
|
||||
processValidReply();
|
||||
}
|
||||
// else the download failed
|
||||
else
|
||||
{
|
||||
m_reply.reset();
|
||||
emit failed(index_within_job);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void S3ListBucket::downloadReadyRead()
|
||||
{
|
||||
// ~_~
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
/* 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 "NetAction.h"
|
||||
|
||||
struct S3Object
|
||||
{
|
||||
QString Key;
|
||||
QString ETag;
|
||||
qlonglong size;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<class S3ListBucket> S3ListBucketPtr;
|
||||
class S3ListBucket : public NetAction
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
S3ListBucket(QUrl url);
|
||||
static S3ListBucketPtr make(QUrl url)
|
||||
{
|
||||
return S3ListBucketPtr(new S3ListBucket(url));
|
||||
}
|
||||
|
||||
public:
|
||||
QList<S3Object> objects;
|
||||
|
||||
public
|
||||
slots:
|
||||
virtual void start() override;
|
||||
|
||||
protected
|
||||
slots:
|
||||
virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override;
|
||||
virtual void downloadError(QNetworkReply::NetworkError error) override;
|
||||
virtual void downloadFinished() override;
|
||||
virtual void downloadReadyRead() override;
|
||||
|
||||
private:
|
||||
void processValidReply();
|
||||
|
||||
private:
|
||||
qint64 bytesSoFar = 0;
|
||||
QString current_marker;
|
||||
};
|
Reference in New Issue
Block a user