diff options
Diffstat (limited to 'logic/net')
-rw-r--r-- | logic/net/CacheDownload.cpp | 6 | ||||
-rw-r--r-- | logic/net/CacheDownload.h | 2 | ||||
-rw-r--r-- | logic/net/FileDownload.cpp | 4 | ||||
-rw-r--r-- | logic/net/FileDownload.h | 2 | ||||
-rw-r--r-- | logic/net/ForgeMirror.h | 10 | ||||
-rw-r--r-- | logic/net/ForgeMirrors.cpp | 116 | ||||
-rw-r--r-- | logic/net/ForgeMirrors.h | 58 | ||||
-rw-r--r-- | logic/net/ForgeXzDownload.cpp | 100 | ||||
-rw-r--r-- | logic/net/ForgeXzDownload.h | 19 | ||||
-rw-r--r-- | logic/net/HttpMetaCache.h | 1 | ||||
-rw-r--r-- | logic/net/LoginTask.cpp | 15 | ||||
-rw-r--r-- | logic/net/LoginTask.h | 15 | ||||
-rw-r--r-- | logic/net/NetJob.cpp | 1 | ||||
-rw-r--r-- | logic/net/NetJob.h | 10 |
14 files changed, 311 insertions, 48 deletions
diff --git a/logic/net/CacheDownload.cpp b/logic/net/CacheDownload.cpp index 4fe4e68e..873d3a2e 100644 --- a/logic/net/CacheDownload.cpp +++ b/logic/net/CacheDownload.cpp @@ -29,7 +29,6 @@ CacheDownload::CacheDownload(QUrl url, MetaEntryPtr entry) m_entry = entry; m_target_path = entry->getFullPath(); m_status = Job_NotStarted; - m_opened_for_saving = false; } void CacheDownload::start() @@ -87,7 +86,7 @@ void CacheDownload::downloadFinished() // nothing went wrong... m_status = Job_Finished; - if (m_opened_for_saving) + if (m_output_file.isOpen()) { // save the data to the downloadable if we aren't saving to file m_output_file.close(); @@ -133,7 +132,7 @@ void CacheDownload::downloadFinished() void CacheDownload::downloadReadyRead() { - if (!m_opened_for_saving) + if (!m_output_file.isOpen()) { if (!m_output_file.open(QIODevice::WriteOnly)) { @@ -144,7 +143,6 @@ void CacheDownload::downloadReadyRead() emit failed(index_within_job); return; } - m_opened_for_saving = true; } QByteArray ba = m_reply->readAll(); md5sum.addData(ba); diff --git a/logic/net/CacheDownload.h b/logic/net/CacheDownload.h index 2b9a5dd8..e25aecd2 100644 --- a/logic/net/CacheDownload.h +++ b/logic/net/CacheDownload.h @@ -26,8 +26,6 @@ class CacheDownload : public NetAction Q_OBJECT public: MetaEntryPtr m_entry; - /// is the saving file already open? - bool m_opened_for_saving; /// if saving to file, use the one specified in this string QString m_target_path; /// this is the output file, if any diff --git a/logic/net/FileDownload.cpp b/logic/net/FileDownload.cpp index 6b2aa66f..239af351 100644 --- a/logic/net/FileDownload.cpp +++ b/logic/net/FileDownload.cpp @@ -25,7 +25,6 @@ FileDownload::FileDownload(QUrl url, QString target_path) : NetAction() m_target_path = target_path; m_check_md5 = false; m_status = Job_NotStarted; - m_opened_for_saving = false; } void FileDownload::start() @@ -113,7 +112,7 @@ void FileDownload::downloadFinished() void FileDownload::downloadReadyRead() { - if (!m_opened_for_saving) + if (!m_output_file.isOpen()) { if (!m_output_file.open(QIODevice::WriteOnly)) { @@ -124,7 +123,6 @@ void FileDownload::downloadReadyRead() emit failed(index_within_job); return; } - m_opened_for_saving = true; } m_output_file.write(m_reply->readAll()); } diff --git a/logic/net/FileDownload.h b/logic/net/FileDownload.h index 31e0259c..58380e86 100644 --- a/logic/net/FileDownload.h +++ b/logic/net/FileDownload.h @@ -29,8 +29,6 @@ public: bool m_check_md5; /// the expected md5 checksum QString m_expected_md5; - /// is the saving file already open? - bool m_opened_for_saving; /// if saving to file, use the one specified in this string QString m_target_path; /// this is the output file, if any diff --git a/logic/net/ForgeMirror.h b/logic/net/ForgeMirror.h new file mode 100644 index 00000000..2518dffe --- /dev/null +++ b/logic/net/ForgeMirror.h @@ -0,0 +1,10 @@ +#pragma once +#include <QString> + +struct ForgeMirror +{ + QString name; + QString logo_url; + QString website_url; + QString mirror_url; +};
\ No newline at end of file diff --git a/logic/net/ForgeMirrors.cpp b/logic/net/ForgeMirrors.cpp new file mode 100644 index 00000000..fd7eccca --- /dev/null +++ b/logic/net/ForgeMirrors.cpp @@ -0,0 +1,116 @@ +#include "MultiMC.h" +#include "ForgeMirrors.h" +#include "logger/QsLog.h" +#include <algorithm> +#include <random> + +ForgeMirrors::ForgeMirrors(QList<ForgeXzDownloadPtr> &libs, NetJobPtr parent_job, + QString mirrorlist) +{ + m_libs = libs; + m_parent_job = parent_job; + m_url = QUrl(mirrorlist); + m_status = Job_NotStarted; +} + +void ForgeMirrors::start() +{ + QLOG_INFO() << "Downloading " << m_url.toString(); + QNetworkRequest request(m_url); + 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 ForgeMirrors::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 ForgeMirrors::downloadFinished() +{ + // if the download succeeded + if (m_status != Job_Failed) + { + // nothing went wrong... ? + parseMirrorList(); + return; + } + // else the download failed, we use a fixed list + else + { + m_status = Job_Finished; + m_reply.reset(); + deferToFixedList(); + return; + } +} + +void ForgeMirrors::deferToFixedList() +{ + m_mirrors.clear(); + m_mirrors.append( + {"Minecraft Forge", "http://files.minecraftforge.net/forge_logo.png", + "http://files.minecraftforge.net/", "http://files.minecraftforge.net/maven/"}); + m_mirrors.append({"Creeper Host", + "http://files.minecraftforge.net/forge_logo.png", + "https://www.creeperhost.net/link.php?id=1", + "http://new.creeperrepo.net/forge/maven/"}); + injectDownloads(); + emit succeeded(index_within_job); +} + +void ForgeMirrors::parseMirrorList() +{ + m_status = Job_Finished; + auto data = m_reply->readAll(); + m_reply.reset(); + auto dataLines = data.split('\n'); + for(auto line: dataLines) + { + auto elements = line.split('!'); + if (elements.size() == 4) + { + m_mirrors.append({elements[0],elements[1],elements[2],elements[3]}); + } + } + if(!m_mirrors.size()) + deferToFixedList(); + injectDownloads(); + emit succeeded(index_within_job); +} + +void ForgeMirrors::injectDownloads() +{ + // shuffle the mirrors randomly + std::random_device rd; + std::mt19937 rng(rd()); + std::shuffle(m_mirrors.begin(), m_mirrors.end(), rng); + + // tell parent to download the libs + for(auto lib: m_libs) + { + lib->setMirrors(m_mirrors); + m_parent_job->addNetAction(lib); + } +} + +void ForgeMirrors::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) +{ + emit progress(index_within_job, bytesReceived, bytesTotal); +} + +void ForgeMirrors::downloadReadyRead() +{ +} diff --git a/logic/net/ForgeMirrors.h b/logic/net/ForgeMirrors.h new file mode 100644 index 00000000..990e49d6 --- /dev/null +++ b/logic/net/ForgeMirrors.h @@ -0,0 +1,58 @@ +/* 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" +#include "HttpMetaCache.h" +#include "ForgeXzDownload.h" +#include "NetJob.h" +#include <QFile> +#include <QTemporaryFile> +typedef std::shared_ptr<class ForgeMirrors> ForgeMirrorsPtr; + +class ForgeMirrors : public NetAction +{ + Q_OBJECT +public: + QList<ForgeXzDownloadPtr> m_libs; + NetJobPtr m_parent_job; + QList<ForgeMirror> m_mirrors; + +public: + explicit ForgeMirrors(QList<ForgeXzDownloadPtr> &libs, NetJobPtr parent_job, + QString mirrorlist); + static ForgeMirrorsPtr make(QList<ForgeXzDownloadPtr> &libs, NetJobPtr parent_job, + QString mirrorlist) + { + return ForgeMirrorsPtr(new ForgeMirrors(libs, parent_job, mirrorlist)); + } + +protected +slots: + virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal); + virtual void downloadError(QNetworkReply::NetworkError error); + virtual void downloadFinished(); + virtual void downloadReadyRead(); + +private: + void parseMirrorList(); + void deferToFixedList(); + void injectDownloads(); + +public +slots: + virtual void start(); +}; diff --git a/logic/net/ForgeXzDownload.cpp b/logic/net/ForgeXzDownload.cpp index 6c9d7a60..f119878a 100644 --- a/logic/net/ForgeXzDownload.cpp +++ b/logic/net/ForgeXzDownload.cpp @@ -22,30 +22,45 @@ #include <QDateTime> #include "logger/QsLog.h" -ForgeXzDownload::ForgeXzDownload(QUrl url, MetaEntryPtr entry) : NetAction() +ForgeXzDownload::ForgeXzDownload(QString relative_path, MetaEntryPtr entry) : NetAction() { - QString urlstr = url.toString(); - urlstr.append(".pack.xz"); - m_url = QUrl(urlstr); m_entry = entry; m_target_path = entry->getFullPath(); + m_pack200_xz_file.setFileTemplate("./dl_temp.XXXXXX"); m_status = Job_NotStarted; - m_opened_for_saving = false; + m_url_path = relative_path; +} + +void ForgeXzDownload::setMirrors(QList<ForgeMirror> &mirrors) +{ + m_mirror_index = 0; + m_mirrors = mirrors; + updateUrl(); } void ForgeXzDownload::start() { + m_status = Job_InProgress; if (!m_entry->stale) { + m_status = Job_Finished; emit succeeded(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); + return; + } + if (m_mirrors.empty()) + { + m_status = Job_Failed; emit failed(index_within_job); return; } + QLOG_INFO() << "Downloading " << m_url.toString(); QNetworkRequest request(m_url); request.setRawHeader(QString("If-None-Match").toLatin1(), m_entry->etag.toLatin1()); @@ -75,14 +90,53 @@ void ForgeXzDownload::downloadError(QNetworkReply::NetworkError error) m_status = Job_Failed; } +void ForgeXzDownload::failAndTryNextMirror() +{ + m_status = Job_Failed; + int next = m_mirror_index + 1; + if(m_mirrors.size() == next) + m_mirror_index = 0; + else + m_mirror_index = next; + + updateUrl(); + emit failed(index_within_job); +} + +void ForgeXzDownload::updateUrl() +{ + QLOG_INFO() << "Updating URL for " << m_url_path; + for (auto possible : m_mirrors) + { + QLOG_INFO() << "Possible: " << possible.name << " : " << possible.mirror_url; + } + QString aggregate = m_mirrors[m_mirror_index].mirror_url + m_url_path + ".pack.xz"; + m_url = QUrl(aggregate); +} + void ForgeXzDownload::downloadFinished() { + //TEST: defer to other possible mirrors (autofail the first one) + /* + QLOG_INFO() <<"dl " << index_within_job << " mirror " << m_mirror_index; + if( m_mirror_index == 0) + { + QLOG_INFO() <<"dl " << index_within_job << " AUTOFAIL"; + m_status = Job_Failed; + m_pack200_xz_file.close(); + m_pack200_xz_file.remove(); + m_reply.reset(); + failAndTryNextMirror(); + return; + } + */ + // if the download succeeded if (m_status != Job_Failed) { // nothing went wrong... m_status = Job_Finished; - if (m_opened_for_saving) + if (m_pack200_xz_file.isOpen()) { // we actually downloaded something! process and isntall it decompressAndInstall(); @@ -90,7 +144,8 @@ void ForgeXzDownload::downloadFinished() } else { - // something bad happened + // something bad happened -- on the local machine! + m_status = Job_Failed; m_pack200_xz_file.remove(); m_reply.reset(); emit failed(index_within_job); @@ -100,10 +155,11 @@ void ForgeXzDownload::downloadFinished() // else the download failed else { + m_status = Job_Failed; m_pack200_xz_file.close(); m_pack200_xz_file.remove(); m_reply.reset(); - emit failed(index_within_job); + failAndTryNextMirror(); return; } } @@ -111,7 +167,7 @@ void ForgeXzDownload::downloadFinished() void ForgeXzDownload::downloadReadyRead() { - if (!m_opened_for_saving) + if (!m_pack200_xz_file.isOpen()) { if (!m_pack200_xz_file.open()) { @@ -122,7 +178,6 @@ void ForgeXzDownload::downloadReadyRead() emit failed(index_within_job); return; } - m_opened_for_saving = true; } m_pack200_xz_file.write(m_reply->readAll()); } @@ -138,7 +193,7 @@ void ForgeXzDownload::decompressAndInstall() // rewind the downloaded temp file m_pack200_xz_file.seek(0); // de-xz'd file - QTemporaryFile pack200_file; + QTemporaryFile pack200_file("./dl_temp.XXXXXX"); pack200_file.open(); bool xz_success = false; @@ -155,7 +210,7 @@ void ForgeXzDownload::decompressAndInstall() if (s == nullptr) { xz_dec_end(s); - emit failed(index_within_job); + failAndTryNextMirror(); return; } b.in = in; @@ -180,7 +235,7 @@ void ForgeXzDownload::decompressAndInstall() { // msg = "Write error\n"; xz_dec_end(s); - emit failed(index_within_job); + failAndTryNextMirror(); return; } @@ -214,42 +269,43 @@ void ForgeXzDownload::decompressAndInstall() case XZ_MEM_ERROR: QLOG_ERROR() << "Memory allocation failed\n"; xz_dec_end(s); - emit failed(index_within_job); + failAndTryNextMirror(); return; case XZ_MEMLIMIT_ERROR: QLOG_ERROR() << "Memory usage limit reached\n"; xz_dec_end(s); - emit failed(index_within_job); + failAndTryNextMirror(); return; case XZ_FORMAT_ERROR: QLOG_ERROR() << "Not a .xz file\n"; xz_dec_end(s); - emit failed(index_within_job); + failAndTryNextMirror(); return; case XZ_OPTIONS_ERROR: QLOG_ERROR() << "Unsupported options in the .xz headers\n"; xz_dec_end(s); - emit failed(index_within_job); + failAndTryNextMirror(); return; case XZ_DATA_ERROR: case XZ_BUF_ERROR: QLOG_ERROR() << "File is corrupt\n"; xz_dec_end(s); - emit failed(index_within_job); + failAndTryNextMirror(); return; default: QLOG_ERROR() << "Bug!\n"; xz_dec_end(s); - emit failed(index_within_job); + failAndTryNextMirror(); return; } } } + m_pack200_xz_file.remove(); // revert pack200 pack200_file.close(); @@ -260,20 +316,22 @@ void ForgeXzDownload::decompressAndInstall() } catch (std::runtime_error &err) { + m_status = Job_Failed; QLOG_ERROR() << "Error unpacking " << pack_name.toUtf8() << " : " << err.what(); QFile f(m_target_path); if (f.exists()) f.remove(); - emit failed(index_within_job); + failAndTryNextMirror(); return; } + pack200_file.remove(); QFile jar_file(m_target_path); if (!jar_file.open(QIODevice::ReadOnly)) { jar_file.remove(); - emit failed(index_within_job); + failAndTryNextMirror(); return; } m_entry->md5sum = QCryptographicHash::hash(jar_file.readAll(), QCryptographicHash::Md5) diff --git a/logic/net/ForgeXzDownload.h b/logic/net/ForgeXzDownload.h index 9f1bb029..990f91f0 100644 --- a/logic/net/ForgeXzDownload.h +++ b/logic/net/ForgeXzDownload.h @@ -19,6 +19,8 @@ #include "HttpMetaCache.h" #include <QFile> #include <QTemporaryFile> +#include "ForgeMirror.h" + typedef std::shared_ptr<class ForgeXzDownload> ForgeXzDownloadPtr; class ForgeXzDownload : public NetAction @@ -26,19 +28,24 @@ class ForgeXzDownload : public NetAction Q_OBJECT public: MetaEntryPtr m_entry; - /// is the saving file already open? - bool m_opened_for_saving; /// if saving to file, use the one specified in this string QString m_target_path; /// this is the output file, if any QTemporaryFile m_pack200_xz_file; + /// mirror index (NOT OPTICS, I SWEAR) + int m_mirror_index = 0; + /// list of mirrors to use. Mirror has the url base + QList<ForgeMirror> m_mirrors; + /// path relative to the mirror base + QString m_url_path; public: - explicit ForgeXzDownload(QUrl url, MetaEntryPtr entry); - static ForgeXzDownloadPtr make(QUrl url, MetaEntryPtr entry) + explicit ForgeXzDownload(QString relative_path, MetaEntryPtr entry); + static ForgeXzDownloadPtr make(QString relative_path, MetaEntryPtr entry) { - return ForgeXzDownloadPtr(new ForgeXzDownload(url, entry)); + return ForgeXzDownloadPtr(new ForgeXzDownload(relative_path, entry)); } + void setMirrors(QList<ForgeMirror> & mirrors); protected slots: @@ -53,4 +60,6 @@ slots: private: void decompressAndInstall(); + void failAndTryNextMirror(); + void updateUrl(); }; diff --git a/logic/net/HttpMetaCache.h b/logic/net/HttpMetaCache.h index e91d2684..08b39fe2 100644 --- a/logic/net/HttpMetaCache.h +++ b/logic/net/HttpMetaCache.h @@ -15,7 +15,6 @@ #pragma once #include <QString> -#include <QSharedPointer> #include <QMap> #include <qtimer.h> diff --git a/logic/net/LoginTask.cpp b/logic/net/LoginTask.cpp index 4a789bb4..5607447e 100644 --- a/logic/net/LoginTask.cpp +++ b/logic/net/LoginTask.cpp @@ -27,7 +27,8 @@ #include <QJsonParseError> #include <QJsonObject> -LoginTask::LoginTask(const UserInfo &uInfo, QObject *parent) : Task(parent), uInfo(uInfo) +LoginTask::LoginTask(const PasswordLogin &loginInfo, QObject *parent) + : Task(parent), loginInfo(loginInfo) { } @@ -49,8 +50,8 @@ void LoginTask::legacyLogin() "application/x-www-form-urlencoded"); QUrlQuery params; - params.addQueryItem("user", uInfo.username); - params.addQueryItem("password", uInfo.password); + params.addQueryItem("user", loginInfo.username); + params.addQueryItem("password", loginInfo.password); params.addQueryItem("version", "13"); netReply = worker->post(netRequest, params.query(QUrl::EncodeSpaces).toUtf8()); @@ -221,8 +222,8 @@ void LoginTask::yggdrasilLogin() agent.insert("name", QString("Minecraft")); agent.insert("version", QJsonValue(1)); root.insert("agent", agent); - root.insert("username", uInfo.username); - root.insert("password", uInfo.password); + root.insert("username", loginInfo.username); + root.insert("password", loginInfo.password); root.insert("clientToken", clientToken); QJsonDocument requestDoc(root); netReply = worker->post(netRequest, requestDoc.toJson()); @@ -247,6 +248,7 @@ void LoginTask::yggdrasilLogin() void LoginTask::parseYggdrasilReply(QByteArray data) { QJsonParseError jsonError; + QLOG_DEBUG() << data; QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); if (jsonError.error != QJsonParseError::NoError) { @@ -273,6 +275,7 @@ void LoginTask::parseYggdrasilReply(QByteArray data) playerID = selectedProfileO.value("id").toString(); playerName = selectedProfileO.value("name").toString(); } + QString sessionID = "token:" + accessToken + ":" + playerID; /* struct LoginResponse @@ -285,6 +288,6 @@ void LoginTask::parseYggdrasilReply(QByteArray data) }; */ - result = {uInfo.username, sessionID, playerName, playerID, accessToken}; + result = {loginInfo.username, sessionID, playerName, playerID, accessToken}; emitSucceeded(); } diff --git a/logic/net/LoginTask.h b/logic/net/LoginTask.h index 26ac0808..fe4e6d2f 100644 --- a/logic/net/LoginTask.h +++ b/logic/net/LoginTask.h @@ -16,14 +16,20 @@ #pragma once #include "logic/tasks/Task.h" -#include <QSharedPointer> +#include <QMap> -struct UserInfo +struct PasswordLogin { QString username; QString password; }; +struct User +{ + QString id; + QMap<QString, QString> properties; +}; + struct LoginResponse { QString username; @@ -31,6 +37,7 @@ struct LoginResponse QString player_name; QString player_id; QString access_token; + User user; // FIXME: no idea what this really is yet. anything relevant? }; class QNetworkReply; @@ -39,7 +46,7 @@ class LoginTask : public Task { Q_OBJECT public: - explicit LoginTask(const UserInfo &uInfo, QObject *parent = 0); + explicit LoginTask(const PasswordLogin &loginInfo, QObject *parent = 0); LoginResponse getResult() { return result; @@ -65,5 +72,5 @@ protected: LoginResponse result; QNetworkReply *netReply; - UserInfo uInfo; + PasswordLogin loginInfo; }; diff --git a/logic/net/NetJob.cpp b/logic/net/NetJob.cpp index 21c6d3f7..333cdcbf 100644 --- a/logic/net/NetJob.cpp +++ b/logic/net/NetJob.cpp @@ -89,6 +89,7 @@ void NetJob::partProgress(int index, qint64 bytesReceived, qint64 bytesTotal) void NetJob::start() { QLOG_INFO() << m_job_name.toLocal8Bit() << " started."; + m_running = true; for (auto iter : downloads) { connect(iter.get(), SIGNAL(succeeded(int)), SLOT(partSucceeded(int))); diff --git a/logic/net/NetJob.h b/logic/net/NetJob.h index c5c0d00c..021a1550 100644 --- a/logic/net/NetJob.h +++ b/logic/net/NetJob.h @@ -40,6 +40,16 @@ public: downloads.append(action); parts_progress.append(part_info()); total_progress++; + // if this is already running, the action needs to be started right away! + if (isRunning()) + { + emit progress(current_progress, total_progress); + connect(base.get(), SIGNAL(succeeded(int)), SLOT(partSucceeded(int))); + connect(base.get(), SIGNAL(failed(int)), SLOT(partFailed(int))); + connect(base.get(), SIGNAL(progress(int, qint64, qint64)), + SLOT(partProgress(int, qint64, qint64))); + base->start(); + } return true; } |