From f8e8414d544f1227e86099146bba903c9082d09e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Mon, 2 Sep 2013 00:25:40 +0200 Subject: [PATCH] Speed up the downloads. Massively. --- CMakeLists.txt | 2 +- logic/BaseUpdate.h | 1 - logic/LegacyInstance.cpp | 2 +- logic/LegacyUpdate.cpp | 12 +- logic/LegacyUpdate.h | 3 +- logic/OneSixAssets.cpp | 101 +++++++-------- logic/OneSixAssets.h | 12 +- logic/OneSixInstance.cpp | 2 +- logic/OneSixUpdate.cpp | 21 ++-- logic/OneSixUpdate.h | 5 +- logic/lists/LwjglVersionList.cpp | 2 +- logic/lists/MinecraftVersionList.cpp | 2 +- logic/net/DownloadJob.cpp | 90 ++++++++++---- logic/net/DownloadJob.h | 111 ++++++++++++++--- logic/net/JobQueue.h | 180 --------------------------- logic/net/NetWorker.cpp | 20 ++- logic/net/NetWorker.h | 17 ++- logic/tasks/LoginTask.cpp | 2 +- 18 files changed, 264 insertions(+), 321 deletions(-) delete mode 100644 logic/net/JobQueue.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ba139ff6..ac22b0ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -182,8 +182,8 @@ logic/Mod.h logic/ModList.h # network stuffs +logic/net/Job.h logic/net/DownloadJob.h -logic/net/JobQueue.h logic/net/NetWorker.h # legacy instances diff --git a/logic/BaseUpdate.h b/logic/BaseUpdate.h index d1e7b735..6f1e26f3 100644 --- a/logic/BaseUpdate.h +++ b/logic/BaseUpdate.h @@ -42,7 +42,6 @@ protected slots: void updateDownloadProgress(qint64 current, qint64 total); protected: - JobListQueue download_queue; BaseInstance *m_inst; }; diff --git a/logic/LegacyInstance.cpp b/logic/LegacyInstance.cpp index 6b2341d7..124cdfcb 100644 --- a/logic/LegacyInstance.cpp +++ b/logic/LegacyInstance.cpp @@ -71,7 +71,7 @@ MinecraftProcess* LegacyInstance::prepareForLaunch(QString user, QString session // launcher arguments args << QString("-Xms%1m").arg(settings().get("MinMemAlloc").toInt()); args << QString("-Xmx%1m").arg(settings().get("MaxMemAlloc").toInt()); - args << QString("-XX:MaxPermSize=%1m").arg(settings().get("PermGen").toInt()); + args << QString("-XX:PermSize=%1m").arg(settings().get("PermGen").toInt()); args << "-jar" << LAUNCHER_FILE; args << user; args << session; diff --git a/logic/LegacyUpdate.cpp b/logic/LegacyUpdate.cpp index fa93ab8f..3d286373 100644 --- a/logic/LegacyUpdate.cpp +++ b/logic/LegacyUpdate.cpp @@ -52,7 +52,7 @@ void LegacyUpdate::lwjglStart() QString url = version->url(); QUrl realUrl(url); QString hostname = realUrl.host(); - auto &worker = NetWorker::spawn(); + auto &worker = NetWorker::qnam(); QNetworkRequest req(realUrl); req.setRawHeader("Host", hostname.toLatin1()); req.setHeader(QNetworkRequest::UserAgentHeader, "Wget/1.14 (linux-gnu)"); @@ -77,7 +77,7 @@ void LegacyUpdate::lwjglFinished(QNetworkReply* reply) "\nSometimes you have to wait a bit if you download many LWJGL versions in a row. YMMV"); return; } - auto &worker = NetWorker::spawn(); + auto &worker = NetWorker::qnam(); //Here i check if there is a cookie for me in the reply and extract it QList cookies = qvariant_cast>(reply->header(QNetworkRequest::SetCookieHeader)); if(cookies.count() != 0) @@ -90,7 +90,6 @@ void LegacyUpdate::lwjglFinished(QNetworkReply* reply) QVariant newLoc = reply->header(QNetworkRequest::LocationHeader); if(newLoc.isValid()) { - auto &worker = NetWorker::spawn(); QString redirectedTo = reply->header(QNetworkRequest::LocationHeader).toString(); QUrl realUrl(redirectedTo); QString hostname = realUrl.host(); @@ -228,14 +227,11 @@ void LegacyUpdate::jarStart() QString intended_version_id = inst->intendedVersionId(); urlstr += intended_version_id + "/" + intended_version_id + ".jar"; - auto dljob = DownloadJob::create(QUrl(urlstr), inst->defaultBaseJar()); - - legacyDownloadJob.reset(new JobList()); - legacyDownloadJob->add(dljob); + legacyDownloadJob.reset(new DownloadJob(QUrl(urlstr), inst->defaultBaseJar())); connect(legacyDownloadJob.data(), SIGNAL(finished()), SLOT(jarFinished())); connect(legacyDownloadJob.data(), SIGNAL(failed()), SLOT(jarFailed())); connect(legacyDownloadJob.data(), SIGNAL(progress(qint64,qint64)), SLOT(updateDownloadProgress(qint64,qint64))); - download_queue.enqueue(legacyDownloadJob); + legacyDownloadJob->start(); } void LegacyUpdate::jarFinished() diff --git a/logic/LegacyUpdate.h b/logic/LegacyUpdate.h index c94fc4c6..05c00495 100644 --- a/logic/LegacyUpdate.h +++ b/logic/LegacyUpdate.h @@ -66,8 +66,7 @@ private: QString lwjglTargetPath; QString lwjglNativesPath; private: - JobListPtr legacyDownloadJob; - JobListQueue download_queue; + DownloadJobPtr legacyDownloadJob; }; diff --git a/logic/OneSixAssets.cpp b/logic/OneSixAssets.cpp index db9e7421..c65ee607 100644 --- a/logic/OneSixAssets.cpp +++ b/logic/OneSixAssets.cpp @@ -19,19 +19,18 @@ class ThreadedDeleter : public QThread public: void run() { - QDirIterator iter(m_base, QDirIterator::Subdirectories); - QStringList nuke_list; + QDirIterator iter ( m_base, QDirIterator::Subdirectories ); int base_length = m_base.length(); - while (iter.hasNext()) + while ( iter.hasNext() ) { QString filename = iter.next(); - QFileInfo current(filename); + QFileInfo current ( filename ); // we keep the dirs... whatever - if(current.isDir()) + if ( current.isDir() ) continue; QString trimmedf = filename; - trimmedf.remove(0, base_length + 1); - if(m_whitelist.contains(trimmedf)) + trimmedf.remove ( 0, base_length + 1 ); + if ( m_whitelist.contains ( trimmedf ) ) { // qDebug() << trimmedf << " gets to live"; } @@ -39,66 +38,47 @@ public: { // DO NOT TOLERATE JUNK // qDebug() << trimmedf << " dies"; - QFile f (filename); + QFile f ( filename ); f.remove(); } } - }; + } QString m_base; QStringList m_whitelist; }; -class NukeAndPaveJob: public Job +void OneSixAssets::downloadFinished() { - Q_OBJECT -public: - - explicit NukeAndPaveJob(QString base, QStringList whitelist) - :Job() - { - QDir dir(base); - deleterThread.m_base = dir.absolutePath(); - deleterThread.m_whitelist = whitelist; - }; -public slots: - virtual void start() - { - connect(&deleterThread, SIGNAL(finished()), SLOT(threadFinished())); - deleterThread.start(); - }; - void threadFinished() - { - emit finish(); - } -private: - ThreadedDeleter deleterThread; -}; + deleter = new ThreadedDeleter(); + QDir dir("assets"); + deleter->m_base = dir.absolutePath(); + deleter->m_whitelist = nuke_whitelist; + connect(deleter, SIGNAL(finished()), SIGNAL(finished())); + deleter->start(); +} - -void OneSixAssets::fetchFinished() +void OneSixAssets::fetchXMLFinished() { QString prefix ( "http://s3.amazonaws.com/Minecraft.Resources/" ); QString fprefix ( "assets/" ); - QStringList nuke_whitelist; + nuke_whitelist.clear(); - JobPtr firstJob = index_job->getFirstJob(); - auto DlJob = firstJob.dynamicCast(); - QByteArray ba = DlJob->m_data; + auto firstJob = index_job->first(); + QByteArray ba = firstJob->m_data; QString xmlErrorMsg; QDomDocument doc; if ( !doc.setContent ( ba, false, &xmlErrorMsg ) ) { - qDebug() << "Failed to process s3.amazonaws.com/Minecraft.Resources. XML error:" << - xmlErrorMsg << ba; + qDebug() << "Failed to process s3.amazonaws.com/Minecraft.Resources. XML error:" << xmlErrorMsg << ba; } //QRegExp etag_match(".*([a-f0-9]{32}).*"); QDomNodeList contents = doc.elementsByTagName ( "Contents" ); - JobList *job = new JobList(); - connect ( job, SIGNAL ( finished() ), SIGNAL(finished()) ); - connect ( job, SIGNAL ( failed() ), SIGNAL(failed()) ); + DownloadJob *job = new DownloadJob(); + connect ( job, SIGNAL(succeeded()), SLOT(downloadFinished()) ); + connect ( job, SIGNAL(failed()), SIGNAL(failed()) ); for ( int i = 0; i < contents.length(); i++ ) { @@ -138,25 +118,28 @@ void OneSixAssets::fetchFinished() QString trimmedEtag = etagStr.remove ( '"' ); nuke_whitelist.append ( keyStr ); if(trimmedEtag != client_etag) - job->add ( DownloadJob::create ( QUrl ( prefix + keyStr ), filename ) ); - + { + job->add ( QUrl ( prefix + keyStr ), filename ); + } + } + if(job->size()) + { + files_job.reset ( job ); + files_job->start(); + } + else + { + delete job; + emit finished(); } - job->add ( JobPtr ( new NukeAndPaveJob ( fprefix, nuke_whitelist ) ) ); - files_job.reset ( job ); - dl.enqueue ( files_job ); -} -void OneSixAssets::fetchStarted() -{ - qDebug() << "Started downloading!"; } void OneSixAssets::start() { - JobList *job = new JobList(); - job->add ( DownloadJob::create ( QUrl ( "http://s3.amazonaws.com/Minecraft.Resources/" ) ) ); - connect ( job, SIGNAL ( finished() ), SLOT ( fetchFinished() ) ); - connect ( job, SIGNAL ( started() ), SLOT ( fetchStarted() ) ); + DownloadJob * job = new DownloadJob(QUrl ( "http://s3.amazonaws.com/Minecraft.Resources/" )); + connect ( job, SIGNAL(succeeded()), SLOT ( fetchXMLFinished() ) ); index_job.reset ( job ); - dl.enqueue ( index_job ); + job->start(); } -#include "OneSixAssets.moc" \ No newline at end of file + +#include "OneSixAssets.moc" diff --git a/logic/OneSixAssets.h b/logic/OneSixAssets.h index 8c345daa..58618c88 100644 --- a/logic/OneSixAssets.h +++ b/logic/OneSixAssets.h @@ -2,6 +2,7 @@ #include "net/DownloadJob.h" class Private; +class ThreadedDeleter; class OneSixAssets : public QObject { @@ -11,12 +12,13 @@ signals: void finished(); public slots: - void fetchFinished(); - void fetchStarted(); + void fetchXMLFinished(); + void downloadFinished(); public: void start(); private: - JobListQueue dl; - JobListPtr index_job; - JobListPtr files_job; + ThreadedDeleter * deleter; + QStringList nuke_whitelist; + DownloadJobPtr index_job; + DownloadJobPtr files_job; }; diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp index 8609e4a1..c926df60 100644 --- a/logic/OneSixInstance.cpp +++ b/logic/OneSixInstance.cpp @@ -114,7 +114,7 @@ MinecraftProcess* OneSixInstance::prepareForLaunch ( QString user, QString sessi args.append(Util::Commandline::splitArgs(settings().get("JvmArgs").toString())); args << QString("-Xms%1m").arg(settings().get("MinMemAlloc").toInt()); args << QString("-Xmx%1m").arg(settings().get("MaxMemAlloc").toInt()); - args << QString("-XX:MaxPermSize=%1m").arg(settings().get("PermGen").toInt()); + args << QString("-XX:PermSize=%1m").arg(settings().get("PermGen").toInt()); QDir natives_dir(natives_dir_raw); args << QString("-Djava.library.path=%1").arg( natives_dir.absolutePath() ); QString classPath; diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp index a47beda0..428d6ef7 100644 --- a/logic/OneSixUpdate.cpp +++ b/logic/OneSixUpdate.cpp @@ -72,19 +72,16 @@ void OneSixUpdate::versionFileStart() QString urlstr("http://s3.amazonaws.com/Minecraft.Download/versions/"); urlstr += targetVersion->descriptor + "/" + targetVersion->descriptor + ".json"; - auto dljob = DownloadJob::create(QUrl(urlstr)); - specificVersionDownloadJob.reset(new JobList()); - specificVersionDownloadJob->add(dljob); - connect(specificVersionDownloadJob.data(), SIGNAL(finished()), SLOT(versionFileFinished())); + specificVersionDownloadJob.reset(new DownloadJob(QUrl(urlstr))); + connect(specificVersionDownloadJob.data(), SIGNAL(succeeded()), SLOT(versionFileFinished())); connect(specificVersionDownloadJob.data(), SIGNAL(failed()), SLOT(versionFileFailed())); connect(specificVersionDownloadJob.data(), SIGNAL(progress(qint64,qint64)), SLOT(updateDownloadProgress(qint64,qint64))); - download_queue.enqueue(specificVersionDownloadJob); + specificVersionDownloadJob->start(); } void OneSixUpdate::versionFileFinished() { - JobPtr firstJob = specificVersionDownloadJob->getFirstJob(); - auto DlJob = firstJob.dynamicCast(); + DownloadPtr DlJob = specificVersionDownloadJob->first(); QString version_id = targetVersion->descriptor; QString inst_dir = m_inst->instanceRoot(); @@ -137,9 +134,7 @@ void OneSixUpdate::jarlibStart() QString targetstr ("versions/"); targetstr += version->id + "/" + version->id + ".jar"; - auto dljob = DownloadJob::create(QUrl(urlstr), targetstr); - jarlibDownloadJob.reset(new JobList()); - jarlibDownloadJob->add(dljob); + jarlibDownloadJob.reset(new DownloadJob(QUrl(urlstr), targetstr)); auto libs = version->getActiveNativeLibs(); libs.append(version->getActiveNormalLibs()); @@ -148,13 +143,13 @@ void OneSixUpdate::jarlibStart() { QString download_path = lib->downloadPath(); QString storage_path = "libraries/" + lib->storagePath(); - jarlibDownloadJob->add(DownloadJob::create(download_path, storage_path)); + jarlibDownloadJob->add(download_path, storage_path); } - connect(jarlibDownloadJob.data(), SIGNAL(finished()), SLOT(jarlibFinished())); + connect(jarlibDownloadJob.data(), SIGNAL(succeeded()), SLOT(jarlibFinished())); connect(jarlibDownloadJob.data(), SIGNAL(failed()), SLOT(jarlibFailed())); connect(jarlibDownloadJob.data(), SIGNAL(progress(qint64,qint64)), SLOT(updateDownloadProgress(qint64,qint64))); - download_queue.enqueue(jarlibDownloadJob); + jarlibDownloadJob->start(); } void OneSixUpdate::jarlibFinished() diff --git a/logic/OneSixUpdate.h b/logic/OneSixUpdate.h index 7a0cac52..7314a6d1 100644 --- a/logic/OneSixUpdate.h +++ b/logic/OneSixUpdate.h @@ -43,9 +43,8 @@ private slots: void jarlibFailed(); private: - JobListPtr specificVersionDownloadJob; - JobListPtr jarlibDownloadJob; - JobListQueue download_queue; + DownloadJobPtr specificVersionDownloadJob; + DownloadJobPtr jarlibDownloadJob; // target version, determined during this task QSharedPointer targetVersion; diff --git a/logic/lists/LwjglVersionList.cpp b/logic/lists/LwjglVersionList.cpp index 0e7b5a34..c0854628 100644 --- a/logic/lists/LwjglVersionList.cpp +++ b/logic/lists/LwjglVersionList.cpp @@ -91,7 +91,7 @@ void LWJGLVersionList::loadList() Q_ASSERT_X(!m_loading, "loadList", "list is already loading (m_loading is true)"); setLoading(true); - auto & worker = NetWorker::spawn(); + auto & worker = NetWorker::qnam(); reply = worker.get(QNetworkRequest(QUrl(RSS_URL))); connect(reply, SIGNAL(finished()), SLOT(netRequestComplete())); } diff --git a/logic/lists/MinecraftVersionList.cpp b/logic/lists/MinecraftVersionList.cpp index 80b4fbc0..4444f5b0 100644 --- a/logic/lists/MinecraftVersionList.cpp +++ b/logic/lists/MinecraftVersionList.cpp @@ -151,7 +151,7 @@ MCVListLoadTask::~MCVListLoadTask() void MCVListLoadTask::executeTask() { setStatus("Loading instance version list..."); - auto & worker = NetWorker::spawn(); + auto & worker = NetWorker::qnam(); vlistReply = worker.get(QNetworkRequest(QUrl(QString(MCVLIST_URLBASE) + "versions.json"))); connect(vlistReply, SIGNAL(finished()), this, SLOT(list_downloaded())); } diff --git a/logic/net/DownloadJob.cpp b/logic/net/DownloadJob.cpp index 6d87a132..5c8ed4b9 100644 --- a/logic/net/DownloadJob.cpp +++ b/logic/net/DownloadJob.cpp @@ -2,9 +2,7 @@ #include "pathutils.h" #include "NetWorker.h" -DownloadJob::DownloadJob (QUrl url, - QString target_path, - QString expected_md5 ) +Download::Download (QUrl url, QString target_path, QString expected_md5 ) :Job() { m_url = url; @@ -17,14 +15,7 @@ DownloadJob::DownloadJob (QUrl url, m_opened_for_saving = false; } -JobPtr DownloadJob::create (QUrl url, - QString target_path, - QString expected_md5 ) -{ - return JobPtr ( new DownloadJob ( url, target_path, expected_md5 ) ); -} - -void DownloadJob::start() +void Download::start() { if ( m_save_to_file ) { @@ -40,7 +31,7 @@ void DownloadJob::start() if ( m_check_md5 && hash == m_expected_md5 ) { qDebug() << "Skipping " << m_url.toString() << ": md5 match."; - emit finish(); + emit succeeded(index_within_job); return; } else @@ -50,7 +41,7 @@ void DownloadJob::start() } if(!ensureFilePathExists(filename)) { - emit fail(); + emit failed(index_within_job); return; } } @@ -58,7 +49,7 @@ void DownloadJob::start() QNetworkRequest request ( m_url ); request.setRawHeader(QString("If-None-Match").toLatin1(), m_expected_md5.toLatin1()); - auto &worker = NetWorker::spawn(); + auto &worker = NetWorker::qnam(); QNetworkReply * rep = worker.get ( request ); m_reply = QSharedPointer ( rep, &QObject::deleteLater ); @@ -68,19 +59,19 @@ void DownloadJob::start() connect ( rep, SIGNAL ( readyRead() ), SLOT ( downloadReadyRead() ) ); } -void DownloadJob::downloadProgress ( qint64 bytesReceived, qint64 bytesTotal ) +void Download::downloadProgress ( qint64 bytesReceived, qint64 bytesTotal ) { - emit progress ( bytesReceived, bytesTotal ); + emit progress (index_within_job, bytesReceived, bytesTotal ); } -void DownloadJob::downloadError ( QNetworkReply::NetworkError error ) +void Download::downloadError ( QNetworkReply::NetworkError error ) { // error happened during download. // TODO: log the reason why m_status = Job_Failed; } -void DownloadJob::downloadFinished() +void Download::downloadFinished() { // if the download succeeded if ( m_status != Job_Failed ) @@ -99,7 +90,7 @@ void DownloadJob::downloadFinished() //TODO: check md5 here! m_reply.clear(); - emit finish(); + emit succeeded(index_within_job); return; } // else the download failed @@ -111,12 +102,12 @@ void DownloadJob::downloadFinished() m_output_file.remove(); } m_reply.clear(); - emit fail(); + emit failed(index_within_job); return; } } -void DownloadJob::downloadReadyRead() +void Download::downloadReadyRead() { if( m_save_to_file ) { @@ -128,7 +119,7 @@ void DownloadJob::downloadReadyRead() * Can't open the file... the job failed */ m_reply->abort(); - emit fail(); + emit failed(index_within_job); return; } m_opened_for_saving = true; @@ -136,3 +127,58 @@ void DownloadJob::downloadReadyRead() m_output_file.write ( m_reply->readAll() ); } } + +DownloadPtr DownloadJob::add ( QUrl url, QString rel_target_path, QString expected_md5 ) +{ + DownloadPtr ptr (new Download(url, rel_target_path, expected_md5)); + ptr->index_within_job = downloads.size(); + downloads.append(ptr); + return ptr; +} + +void DownloadJob::partSucceeded ( int index ) +{ + num_succeeded++; + if(num_failed + num_succeeded == downloads.size()) + { + if(num_failed) + { + emit failed(); + } + else + { + emit succeeded(); + } + } +} + +void DownloadJob::partFailed ( int index ) +{ + num_failed++; + if(num_failed + num_succeeded == downloads.size()) + { + if(num_failed) + { + emit failed(); + } + else + { + emit succeeded(); + } + } +} + +void DownloadJob::partProgress ( int index, qint64 bytesReceived, qint64 bytesTotal ) +{ + // PROGRESS? DENIED! +} + + +void DownloadJob::start() +{ + for(auto iter: downloads) + { + connect(iter.data(), SIGNAL(succeeded(int)), SLOT(partSucceeded(int))); + iter->start(); + } +} diff --git a/logic/net/DownloadJob.h b/logic/net/DownloadJob.h index 94abb9af..adeef646 100644 --- a/logic/net/DownloadJob.h +++ b/logic/net/DownloadJob.h @@ -1,28 +1,37 @@ #pragma once -#include "JobQueue.h" #include -/** - * A single file for the downloader/cache to process. - */ -class DownloadJob : public Job +class DownloadJob; +class Download; +typedef QSharedPointer DownloadJobPtr; +typedef QSharedPointer DownloadPtr; + +enum JobStatus +{ + Job_NotStarted, + Job_InProgress, + Job_Finished, + Job_Failed +}; + +class Job : public QObject { Q_OBJECT +protected: + explicit Job(): QObject(0){}; public: - DownloadJob(QUrl url, - QString rel_target_path = QString(), - QString expected_md5 = QString() - ); - static JobPtr create(QUrl url, QString rel_target_path = QString(), QString expected_md5 = QString()); + virtual ~Job() {}; + public slots: - virtual void start(); - -private slots: - void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);; - void downloadError(QNetworkReply::NetworkError error); - void downloadFinished(); - void downloadReadyRead(); - + virtual void start() = 0; +}; + +class Download: public Job +{ + friend class DownloadJob; + Q_OBJECT +protected: + Download(QUrl url, QString rel_target_path = QString(), QString expected_md5 = QString()); public: /// the network reply QSharedPointer m_reply; @@ -46,6 +55,72 @@ public: /// if not saving to file, downloaded data is placed here QByteArray m_data; + int currentProgress = 0; + int totalProgress = 0; + /// The file's status JobStatus m_status; + + int index_within_job = 0; +signals: + void started(int index); + void progress(int index, qint64 current, qint64 total); + void succeeded(int index); + void failed(int index); +public slots: + virtual void start(); + +private slots: + void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);; + void downloadError(QNetworkReply::NetworkError error); + void downloadFinished(); + void downloadReadyRead(); }; + +/** + * A single file for the downloader/cache to process. + */ +class DownloadJob : public Job +{ + Q_OBJECT +public: + explicit DownloadJob() + :Job(){}; + explicit DownloadJob(QUrl url, QString rel_target_path = QString(), QString expected_md5 = QString()) + :Job() + { + add(url, rel_target_path, expected_md5); + }; + + DownloadPtr add(QUrl url, QString rel_target_path = QString(), QString expected_md5 = QString()); + DownloadPtr operator[](int index) + { + return downloads[index]; + }; + DownloadPtr first() + { + if(downloads.size()) + return downloads[0]; + return DownloadPtr(); + } + int size() const + { + return downloads.size(); + } +signals: + void started(); + void progress(qint64 current, qint64 total); + void succeeded(); + void failed(); +public slots: + virtual void start(); +private slots: + void partProgress(int index, qint64 bytesReceived, qint64 bytesTotal);; + void partSucceeded(int index); + void partFailed(int index); +private: + QList downloads; + int num_succeeded = 0; + int num_failed = 0; +}; + diff --git a/logic/net/JobQueue.h b/logic/net/JobQueue.h deleted file mode 100644 index b6895f60..00000000 --- a/logic/net/JobQueue.h +++ /dev/null @@ -1,180 +0,0 @@ -#pragma once -#include -#include "libutil_config.h" - -enum JobStatus -{ - Job_NotStarted, - Job_InProgress, - Job_Finished, - Job_Failed -}; - -class JobList; - -class Job : public QObject -{ - Q_OBJECT -protected: - explicit Job(): QObject(0){}; -public: - virtual ~Job() {}; -signals: - void finish(); - void fail(); - void progress(qint64 current, qint64 total); -public slots: - virtual void start() = 0; -}; -typedef QSharedPointer JobPtr; - -/** - * A list of jobs, to be processed one by one. - */ -class JobList : public QObject -{ - friend class JobListQueue; - Q_OBJECT -public: - - JobList() : QObject(0) - { - m_status = Job_NotStarted; - current_job_idx = 0; - } - JobStatus getStatus() - { - return m_status; - } - void add(JobPtr dlable) - { - if(m_status == Job_NotStarted) - m_jobs.append(dlable); - //else there's a bug. TODO: catch the bugs - } - JobPtr getFirstJob() - { - if(m_jobs.size()) - return m_jobs[0]; - else - return JobPtr(); - } - void start() - { - current_job_idx = 0; - auto job = m_jobs[current_job_idx]; - - connect(job.data(), SIGNAL(progress(qint64,qint64)), SLOT(currentJobProgress(qint64,qint64))); - connect(job.data(), SIGNAL(finish()), SLOT(currentJobFinished())); - connect(job.data(), SIGNAL(fail()), SLOT(currentJobFailed())); - job->start(); - emit started(); - } -private slots: - void currentJobFinished() - { - if(current_job_idx == m_jobs.size() - 1) - { - m_status = Job_Finished; - emit finished(); - } - else - { - current_job_idx++; - auto job = m_jobs[current_job_idx]; - connect(job.data(), SIGNAL(progress(qint64,qint64)), SLOT(currentJobProgress(qint64,qint64))); - connect(job.data(), SIGNAL(finish()), SLOT(currentJobFinished())); - connect(job.data(), SIGNAL(fail()), SLOT(currentJobFailed())); - job->start(); - } - } - void currentJobFailed() - { - m_status = Job_Failed; - emit failed(); - } - void currentJobProgress(qint64 current, qint64 total) - { - if(!total) - return; - - int total_jobs = m_jobs.size(); - - if(!total_jobs) - return; - - float job_chunk = 1000.0 / float(total_jobs); - float cur = current; - float tot = total; - float last_chunk = (cur / tot) * job_chunk; - - float list_total = job_chunk * current_job_idx + last_chunk; - emit progress(qint64(list_total), 1000LL); - } -private: - QVector m_jobs; - /// The overall status of this job list - JobStatus m_status; - int current_job_idx; -signals: - void progress(qint64 current, qint64 total); - void started(); - void finished(); - void failed(); -}; -typedef QSharedPointer JobListPtr; - - -/** - * A queue of job lists! The job lists fail or finish as units. - */ -class JobListQueue : public QObject -{ - Q_OBJECT -public: - JobListQueue(QObject *p = 0): - QObject(p), - currentIndex(0), - is_running(false){} - - void enqueue(JobListPtr job) - { - jobs.enqueue(job); - - // finish or fail, we should catch that and start the next one - connect(job.data(),SIGNAL(finished()), SLOT(startNextJob())); - connect(job.data(),SIGNAL(failed()), SLOT(startNextJob())); - - if(!is_running) - { - QTimer::singleShot(0, this, SLOT(startNextJob())); - } - } - -private slots: - void startNextJob() - { - if (jobs.isEmpty()) - { - currentJobList.clear(); - currentIndex = 0; - is_running = false; - emit finishedAllJobs(); - return; - } - - currentJobList = jobs.dequeue(); - is_running = true; - currentIndex = 0; - currentJobList->start(); - } - -signals: - void finishedAllJobs(); - -private: - JobListPtr currentJobList; - QQueue jobs; - unsigned currentIndex; - bool is_running; -}; diff --git a/logic/net/NetWorker.cpp b/logic/net/NetWorker.cpp index 1eef13d9..c5943348 100644 --- a/logic/net/NetWorker.cpp +++ b/logic/net/NetWorker.cpp @@ -1,7 +1,25 @@ #include "NetWorker.h" #include -NetWorker& NetWorker::spawn() +class NetWorker::Private +{ +public: + QNetworkAccessManager manager; +}; + +NetWorker::NetWorker ( QObject* parent ) : QObject ( parent ) +{ + d = new Private(); +} + +QNetworkAccessManager& NetWorker::qnam() +{ + auto & w = worker(); + return w.d->manager; +} + + +NetWorker& NetWorker::worker() { static QThreadStorage storage; if (!storage.hasLocalData()) diff --git a/logic/net/NetWorker.h b/logic/net/NetWorker.h index 98374e3b..cf7e72e1 100644 --- a/logic/net/NetWorker.h +++ b/logic/net/NetWorker.h @@ -9,12 +9,23 @@ 'OOOOOY _.' '""""'' */ - #pragma once + #include -class NetWorker : public QNetworkAccessManager +#include + +class NetWorker : public QObject { Q_OBJECT public: - static NetWorker &spawn(); + // for high level access to the sevices (preferred) + static NetWorker &worker(); + // for low-level access to the network manager object + static QNetworkAccessManager &qnam(); +public: + +private: + explicit NetWorker ( QObject* parent = 0 ); + class Private; + Private * d; }; \ No newline at end of file diff --git a/logic/tasks/LoginTask.cpp b/logic/tasks/LoginTask.cpp index 21ac2a5d..4e2f0fb7 100644 --- a/logic/tasks/LoginTask.cpp +++ b/logic/tasks/LoginTask.cpp @@ -29,7 +29,7 @@ LoginTask::LoginTask( const UserInfo& uInfo, QObject* parent ) : Task(parent), u void LoginTask::executeTask() { setStatus("Logging in..."); - auto & worker = NetWorker::spawn(); + auto & worker = NetWorker::qnam(); connect(&worker, SIGNAL(finished(QNetworkReply*)), this, SLOT(processNetReply(QNetworkReply*))); QUrl loginURL("https://login.minecraft.net/");