From b44e70d58dac92a21d905565370357d296942209 Mon Sep 17 00:00:00 2001 From: Stiepen22 Date: Fri, 6 Sep 2013 18:48:41 +0200 Subject: started kill instance feature --- logic/MinecraftProcess.cpp | 5 +++++ logic/MinecraftProcess.h | 4 ++++ 2 files changed, 9 insertions(+) (limited to 'logic') diff --git a/logic/MinecraftProcess.cpp b/logic/MinecraftProcess.cpp index d34be835..3d82008b 100644 --- a/logic/MinecraftProcess.cpp +++ b/logic/MinecraftProcess.cpp @@ -138,6 +138,11 @@ void MinecraftProcess::finish(int code, ExitStatus status) emit ended(); } +void MinecraftProcess::killMinecraft() +{ + killed = true; +} + void MinecraftProcess::launch() { if (!m_instance->settings().get("PreLaunchCommand").toString().isEmpty()) diff --git a/logic/MinecraftProcess.h b/logic/MinecraftProcess.h index 516bf986..248ad807 100644 --- a/logic/MinecraftProcess.h +++ b/logic/MinecraftProcess.h @@ -59,6 +59,8 @@ public: void setMinecraftArguments(QStringList args); + void killMinecraft(); + signals: /** * @brief emitted when mc has finished and the PostLaunchCommand was run @@ -83,4 +85,6 @@ protected slots: void finish(int, QProcess::ExitStatus status); void on_stdErr(); void on_stdOut(); +private: + bool killed; }; -- cgit From f897a200e2607fd99116a3ab4bb9ba757a52139b Mon Sep 17 00:00:00 2001 From: Stiepen22 Date: Fri, 6 Sep 2013 22:40:50 +0200 Subject: Made instace killing actually work --- logic/MinecraftProcess.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'logic') diff --git a/logic/MinecraftProcess.cpp b/logic/MinecraftProcess.cpp index 3d82008b..6ac5b886 100644 --- a/logic/MinecraftProcess.cpp +++ b/logic/MinecraftProcess.cpp @@ -120,7 +120,12 @@ void MinecraftProcess::finish(int code, ExitStatus status) //TODO: error handling } - emit log("Minecraft exited."); + // TODO: Localization + + if (!killed) + emit log("Minecraft exited."); + else + emit log("Minecraft was killed by user.", MessageLevel::Error); m_prepostlaunchprocess.processEnvironment().insert("INST_EXITCODE", QString(code)); @@ -141,6 +146,7 @@ void MinecraftProcess::finish(int code, ExitStatus status) void MinecraftProcess::killMinecraft() { killed = true; + kill(); } void MinecraftProcess::launch() -- cgit From 6bea4ec988b7caeac63353fb9d2a354f2fd47dad Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Sun, 8 Sep 2013 02:15:20 +0200 Subject: Use HttpMetaCache to minimize network use. --- logic/LegacyUpdate.cpp | 4 +- logic/OneSixAssets.cpp | 29 +++----- logic/OneSixUpdate.cpp | 20 +++-- logic/net/ByteArrayDownload.cpp | 62 ++++++++++++++++ logic/net/ByteArrayDownload.h | 24 ++++++ logic/net/CacheDownload.cpp | 122 +++++++++++++++++++++++++++++++ logic/net/CacheDownload.h | 34 +++++++++ logic/net/Download.h | 54 ++++++++++++++ logic/net/DownloadJob.cpp | 146 +++++-------------------------------- logic/net/DownloadJob.h | 95 ++++-------------------- logic/net/FileDownload.cpp | 111 ++++++++++++++++++++++++++++ logic/net/FileDownload.h | 35 +++++++++ logic/net/HttpMetaCache.cpp | 157 ++++++++++++++++++++++++++++++++-------- logic/net/HttpMetaCache.h | 33 +++++++-- 14 files changed, 659 insertions(+), 267 deletions(-) create mode 100644 logic/net/ByteArrayDownload.cpp create mode 100644 logic/net/ByteArrayDownload.h create mode 100644 logic/net/CacheDownload.cpp create mode 100644 logic/net/CacheDownload.h create mode 100644 logic/net/Download.h create mode 100644 logic/net/FileDownload.cpp create mode 100644 logic/net/FileDownload.h (limited to 'logic') diff --git a/logic/LegacyUpdate.cpp b/logic/LegacyUpdate.cpp index 626ad1e0..c75a0011 100644 --- a/logic/LegacyUpdate.cpp +++ b/logic/LegacyUpdate.cpp @@ -227,7 +227,9 @@ void LegacyUpdate::jarStart() QString intended_version_id = inst->intendedVersionId(); urlstr += intended_version_id + "/" + intended_version_id + ".jar"; - legacyDownloadJob.reset(new DownloadJob(QUrl(urlstr), inst->defaultBaseJar())); + auto dljob = new DownloadJob("Minecraft.jar for version " + intended_version_id); + dljob->add(QUrl(urlstr), inst->defaultBaseJar()); + legacyDownloadJob.reset(); 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))); diff --git a/logic/OneSixAssets.cpp b/logic/OneSixAssets.cpp index c65ee607..5bdd29d7 100644 --- a/logic/OneSixAssets.cpp +++ b/logic/OneSixAssets.cpp @@ -3,6 +3,8 @@ #include #include "OneSixAssets.h" #include "net/DownloadJob.h" +#include "net/HttpMetaCache.h" +#include "MultiMC.h" inline QDomElement getDomElementByTagName(QDomElement parent, QString tagname) { @@ -65,7 +67,7 @@ void OneSixAssets::fetchXMLFinished() nuke_whitelist.clear(); auto firstJob = index_job->first(); - QByteArray ba = firstJob->m_data; + QByteArray ba = firstJob.dynamicCast()->m_data; QString xmlErrorMsg; QDomDocument doc; @@ -76,10 +78,12 @@ void OneSixAssets::fetchXMLFinished() //QRegExp etag_match(".*([a-f0-9]{32}).*"); QDomNodeList contents = doc.elementsByTagName ( "Contents" ); - DownloadJob *job = new DownloadJob(); + DownloadJob *job = new DownloadJob("Assets"); connect ( job, SIGNAL(succeeded()), SLOT(downloadFinished()) ); connect ( job, SIGNAL(failed()), SIGNAL(failed()) ); + auto metacache = MMC->metacache(); + for ( int i = 0; i < contents.length(); i++ ) { QDomElement element = contents.at ( i ).toElement(); @@ -104,22 +108,12 @@ void OneSixAssets::fetchXMLFinished() if ( sizeStr == "0" ) continue; - QString filename = fprefix + keyStr; - QFile check_file ( filename ); - QString client_etag = "nonsense"; - // if there already is a file and md5 checking is in effect and it can be opened - if ( check_file.exists() && check_file.open ( QIODevice::ReadOnly ) ) - { - // check the md5 against the expected one - client_etag = QCryptographicHash::hash ( check_file.readAll(), QCryptographicHash::Md5 ).toHex().constData(); - check_file.close(); - } - - QString trimmedEtag = etagStr.remove ( '"' ); nuke_whitelist.append ( keyStr ); - if(trimmedEtag != client_etag) + + auto entry = metacache->resolveEntry("assets", keyStr, etagStr); + if(entry->stale) { - job->add ( QUrl ( prefix + keyStr ), filename ); + job->add(QUrl(prefix + keyStr), entry); } } if(job->size()) @@ -135,7 +129,8 @@ void OneSixAssets::fetchXMLFinished() } void OneSixAssets::start() { - DownloadJob * job = new DownloadJob(QUrl ( "http://s3.amazonaws.com/Minecraft.Resources/" )); + auto job = new DownloadJob("Assets index"); + job->add(QUrl ( "http://s3.amazonaws.com/Minecraft.Resources/" )); connect ( job, SIGNAL(succeeded()), SLOT ( fetchXMLFinished() ) ); index_job.reset ( job ); job->start(); diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp index 428d6ef7..ce71bde0 100644 --- a/logic/OneSixUpdate.cpp +++ b/logic/OneSixUpdate.cpp @@ -12,7 +12,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +#include "MultiMC.h" #include "OneSixUpdate.h" #include @@ -72,7 +72,9 @@ void OneSixUpdate::versionFileStart() QString urlstr("http://s3.amazonaws.com/Minecraft.Download/versions/"); urlstr += targetVersion->descriptor + "/" + targetVersion->descriptor + ".json"; - specificVersionDownloadJob.reset(new DownloadJob(QUrl(urlstr))); + auto job = new DownloadJob("Version index"); + job->add(QUrl(urlstr)); + specificVersionDownloadJob.reset(job); 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))); @@ -92,7 +94,7 @@ void OneSixUpdate::versionFileFinished() // FIXME: detect errors here, download to a temp file, swap QFile vfile1 (version1); vfile1.open(QIODevice::Truncate | QIODevice::WriteOnly ); - vfile1.write(DlJob->m_data); + vfile1.write(DlJob.dynamicCast()->m_data); vfile1.close(); } @@ -134,16 +136,22 @@ void OneSixUpdate::jarlibStart() QString targetstr ("versions/"); targetstr += version->id + "/" + version->id + ".jar"; - jarlibDownloadJob.reset(new DownloadJob(QUrl(urlstr), targetstr)); + auto job = new DownloadJob("Libraries for instance " + inst->name()); + job->add(QUrl(urlstr), targetstr); + jarlibDownloadJob.reset(job); auto libs = version->getActiveNativeLibs(); libs.append(version->getActiveNormalLibs()); + auto metacache = MMC->metacache(); for(auto lib: libs) { QString download_path = lib->downloadPath(); - QString storage_path = "libraries/" + lib->storagePath(); - jarlibDownloadJob->add(download_path, storage_path); + auto entry = metacache->resolveEntry("libraries", lib->storagePath()); + if(entry->stale) + { + jarlibDownloadJob->add(download_path, entry); + } } connect(jarlibDownloadJob.data(), SIGNAL(succeeded()), SLOT(jarlibFinished())); connect(jarlibDownloadJob.data(), SIGNAL(failed()), SLOT(jarlibFailed())); diff --git a/logic/net/ByteArrayDownload.cpp b/logic/net/ByteArrayDownload.cpp new file mode 100644 index 00000000..6ae3f121 --- /dev/null +++ b/logic/net/ByteArrayDownload.cpp @@ -0,0 +1,62 @@ +#include "ByteArrayDownload.h" +#include "MultiMC.h" +#include + +ByteArrayDownload::ByteArrayDownload ( QUrl url ) + : Download() +{ + m_url = url; + m_status = Job_NotStarted; +} + +void ByteArrayDownload::start() +{ + qDebug() << "Downloading " << m_url.toString(); + QNetworkRequest request ( m_url ); + auto worker = MMC->qnam(); + QNetworkReply * rep = worker->get ( request ); + + m_reply = QSharedPointer ( rep, &QObject::deleteLater ); + 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 ByteArrayDownload::downloadProgress ( qint64 bytesReceived, qint64 bytesTotal ) +{ + emit progress (index_within_job, bytesReceived, bytesTotal ); +} + +void ByteArrayDownload::downloadError ( QNetworkReply::NetworkError error ) +{ + // error happened during download. + // TODO: log the reason why + m_status = Job_Failed; +} + +void ByteArrayDownload::downloadFinished() +{ + // if the download succeeded + if ( m_status != Job_Failed ) + { + // nothing went wrong... + m_status = Job_Finished; + m_data = m_reply->readAll(); + m_reply.clear(); + emit succeeded(index_within_job); + return; + } + // else the download failed + else + { + m_reply.clear(); + emit failed(index_within_job); + return; + } +} + +void ByteArrayDownload::downloadReadyRead() +{ + // ~_~ +} diff --git a/logic/net/ByteArrayDownload.h b/logic/net/ByteArrayDownload.h new file mode 100644 index 00000000..428b21db --- /dev/null +++ b/logic/net/ByteArrayDownload.h @@ -0,0 +1,24 @@ +#pragma once +#include "Download.h" + +class ByteArrayDownload: public Download +{ + Q_OBJECT +public: + ByteArrayDownload(QUrl url); + +public: + /// if not saving to file, downloaded data is placed here + QByteArray m_data; + +public slots: + virtual void start(); + +protected slots: + void downloadProgress(qint64 bytesReceived, qint64 bytesTotal); + void downloadError(QNetworkReply::NetworkError error); + void downloadFinished(); + void downloadReadyRead(); +}; + +typedef QSharedPointer ByteArrayDownloadPtr; diff --git a/logic/net/CacheDownload.cpp b/logic/net/CacheDownload.cpp new file mode 100644 index 00000000..c0074574 --- /dev/null +++ b/logic/net/CacheDownload.cpp @@ -0,0 +1,122 @@ +#include "MultiMC.h" +#include "CacheDownload.h" +#include + +#include +#include +#include +#include + +CacheDownload::CacheDownload (QUrl url, MetaEntryPtr entry ) + :Download(), md5sum(QCryptographicHash::Md5) +{ + m_url = url; + m_entry = entry; + m_target_path = entry->getFullPath(); + m_status = Job_NotStarted; + m_opened_for_saving = false; +} + +void CacheDownload::start() +{ + if(!m_entry->stale) + { + emit succeeded(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); + return; + } + qDebug() << "Downloading " << m_url.toString(); + QNetworkRequest request ( m_url ); + request.setRawHeader(QString("If-None-Match").toLatin1(), m_entry->etag.toLatin1()); + + auto worker = MMC->qnam(); + QNetworkReply * rep = worker->get ( request ); + + m_reply = QSharedPointer ( rep, &QObject::deleteLater ); + 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 CacheDownload::downloadProgress ( qint64 bytesReceived, qint64 bytesTotal ) +{ + emit progress (index_within_job, bytesReceived, bytesTotal ); +} + +void CacheDownload::downloadError ( QNetworkReply::NetworkError error ) +{ + // error happened during download. + // TODO: log the reason why + m_status = Job_Failed; +} +void CacheDownload::downloadFinished() +{ + // if the download succeeded + if ( m_status != Job_Failed ) + { + + // nothing went wrong... + m_status = Job_Finished; + if(m_opened_for_saving) + { + // save the data to the downloadable if we aren't saving to file + m_output_file.close(); + m_entry->md5sum = md5sum.result().toHex().constData(); + } + else + { + if ( m_output_file.open ( QIODevice::ReadOnly ) ) + { + m_entry->md5sum = QCryptographicHash::hash ( m_output_file.readAll(), QCryptographicHash::Md5 ).toHex().constData(); + m_output_file.close(); + } + } + QFileInfo output_file_info(m_target_path); + + + m_entry->etag = m_reply->rawHeader("ETag").constData(); + m_entry->last_changed_timestamp = output_file_info.lastModified().toUTC().toMSecsSinceEpoch(); + m_entry->stale = false; + MMC->metacache()->updateEntry(m_entry); + + m_reply.clear(); + emit succeeded(index_within_job); + return; + } + // else the download failed + else + { + m_output_file.close(); + m_output_file.remove(); + m_reply.clear(); + emit failed(index_within_job); + return; + } +} + +void CacheDownload::downloadReadyRead() +{ + if(!m_opened_for_saving) + { + if ( !m_output_file.open ( QIODevice::WriteOnly ) ) + { + /* + * Can't open the file... the job failed + */ + m_reply->abort(); + emit failed(index_within_job); + return; + } + m_opened_for_saving = true; + } + QByteArray ba = m_reply->readAll(); + md5sum.addData(ba); + m_output_file.write ( ba ); +} diff --git a/logic/net/CacheDownload.h b/logic/net/CacheDownload.h new file mode 100644 index 00000000..f95dabd5 --- /dev/null +++ b/logic/net/CacheDownload.h @@ -0,0 +1,34 @@ +#pragma once + +#include "Download.h" +#include "HttpMetaCache.h" +#include +#include + +class CacheDownload : public Download +{ + 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 + QFile m_output_file; + /// the hash-as-you-download + QCryptographicHash md5sum; +public: + explicit CacheDownload(QUrl url, MetaEntryPtr entry); + +protected slots: + virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal); + virtual void downloadError(QNetworkReply::NetworkError error); + virtual void downloadFinished(); + virtual void downloadReadyRead(); + +public slots: + virtual void start(); +}; + +typedef QSharedPointer CacheDownloadPtr; diff --git a/logic/net/Download.h b/logic/net/Download.h new file mode 100644 index 00000000..91f09dec --- /dev/null +++ b/logic/net/Download.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#include +#include +#include + + +enum JobStatus +{ + Job_NotStarted, + Job_InProgress, + Job_Finished, + Job_Failed +}; + +class Download : public QObject +{ + Q_OBJECT +protected: + explicit Download(): QObject(0){}; +public: + virtual ~Download() {}; + +public: + /// the network reply + QSharedPointer m_reply; + + /// source URL + QUrl m_url; + + /// The file's status + JobStatus m_status; + + /// index within the parent job + 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); + +protected slots: + virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) = 0; + virtual void downloadError(QNetworkReply::NetworkError error) = 0; + virtual void downloadFinished() = 0; + virtual void downloadReadyRead() = 0; + +public slots: + virtual void start() = 0; +}; + +typedef QSharedPointer DownloadPtr; diff --git a/logic/net/DownloadJob.cpp b/logic/net/DownloadJob.cpp index cad9ae72..cbc2c5fe 100644 --- a/logic/net/DownloadJob.cpp +++ b/logic/net/DownloadJob.cpp @@ -1,136 +1,29 @@ #include "DownloadJob.h" #include "pathutils.h" #include "MultiMC.h" +#include "FileDownload.h" +#include "ByteArrayDownload.h" +#include "CacheDownload.h" -Download::Download (QUrl url, QString target_path, QString expected_md5 ) - :Job() +ByteArrayDownloadPtr DownloadJob::add ( QUrl url ) { - m_url = url; - m_target_path = target_path; - m_expected_md5 = expected_md5; - - m_check_md5 = m_expected_md5.size(); - m_save_to_file = m_target_path.size(); - m_status = Job_NotStarted; - m_opened_for_saving = false; -} - -void Download::start() -{ - if ( m_save_to_file ) - { - QString filename = m_target_path; - m_output_file.setFileName ( filename ); - // if there already is a file and md5 checking is in effect and it can be opened - if ( m_output_file.exists() && m_output_file.open ( QIODevice::ReadOnly ) ) - { - // check the md5 against the expected one - QString hash = QCryptographicHash::hash ( m_output_file.readAll(), QCryptographicHash::Md5 ).toHex().constData(); - m_output_file.close(); - // skip this file if they match - if ( m_check_md5 && hash == m_expected_md5 ) - { - qDebug() << "Skipping " << m_url.toString() << ": md5 match."; - emit succeeded(index_within_job); - return; - } - else - { - m_expected_md5 = hash; - } - } - if(!ensureFilePathExists(filename)) - { - emit failed(index_within_job); - return; - } - } - qDebug() << "Downloading " << m_url.toString(); - QNetworkRequest request ( m_url ); - request.setRawHeader(QString("If-None-Match").toLatin1(), m_expected_md5.toLatin1()); - - auto worker = MMC->qnam(); - QNetworkReply * rep = worker->get ( request ); - - m_reply = QSharedPointer ( rep, &QObject::deleteLater ); - 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 Download::downloadProgress ( qint64 bytesReceived, qint64 bytesTotal ) -{ - emit progress (index_within_job, bytesReceived, bytesTotal ); -} - -void Download::downloadError ( QNetworkReply::NetworkError error ) -{ - // error happened during download. - // TODO: log the reason why - m_status = Job_Failed; -} - -void Download::downloadFinished() -{ - // if the download succeeded - if ( m_status != Job_Failed ) - { - // nothing went wrong... - m_status = Job_Finished; - // save the data to the downloadable if we aren't saving to file - if ( !m_save_to_file ) - { - m_data = m_reply->readAll(); - } - else - { - m_output_file.close(); - } - - //TODO: check md5 here! - m_reply.clear(); - emit succeeded(index_within_job); - return; - } - // else the download failed - else - { - if ( m_save_to_file ) - { - m_output_file.close(); - m_output_file.remove(); - } - m_reply.clear(); - emit failed(index_within_job); - return; - } + ByteArrayDownloadPtr ptr (new ByteArrayDownload(url)); + ptr->index_within_job = downloads.size(); + downloads.append(ptr); + return ptr; } -void Download::downloadReadyRead() +FileDownloadPtr DownloadJob::add ( QUrl url, QString rel_target_path) { - if( m_save_to_file ) - { - if(!m_opened_for_saving) - { - if ( !m_output_file.open ( QIODevice::WriteOnly ) ) - { - /* - * Can't open the file... the job failed - */ - m_reply->abort(); - emit failed(index_within_job); - return; - } - m_opened_for_saving = true; - } - m_output_file.write ( m_reply->readAll() ); - } + FileDownloadPtr ptr (new FileDownload(url, rel_target_path)); + ptr->index_within_job = downloads.size(); + downloads.append(ptr); + return ptr; } -DownloadPtr DownloadJob::add ( QUrl url, QString rel_target_path, QString expected_md5 ) +CacheDownloadPtr DownloadJob::add ( QUrl url, MetaEntryPtr entry) { - DownloadPtr ptr (new Download(url, rel_target_path, expected_md5)); + CacheDownloadPtr ptr (new CacheDownload(url, entry)); ptr->index_within_job = downloads.size(); downloads.append(ptr); return ptr; @@ -139,16 +32,17 @@ DownloadPtr DownloadJob::add ( QUrl url, QString rel_target_path, QString expect void DownloadJob::partSucceeded ( int index ) { num_succeeded++; + qDebug() << m_job_name.toLocal8Bit() << " progress: " << num_succeeded << "/" << downloads.size(); if(num_failed + num_succeeded == downloads.size()) { if(num_failed) { - qDebug() << "Download JOB failed: " << this; + qDebug() << m_job_name.toLocal8Bit() << " failed."; emit failed(); } else { - qDebug() << "Download JOB succeeded: " << this; + qDebug() << m_job_name.toLocal8Bit() << " succeeded."; emit succeeded(); } } @@ -159,7 +53,7 @@ void DownloadJob::partFailed ( int index ) num_failed++; if(num_failed + num_succeeded == downloads.size()) { - qDebug() << "Download JOB failed: " << this; + qDebug() << m_job_name.toLocal8Bit() << " failed."; emit failed(); } } @@ -172,7 +66,7 @@ void DownloadJob::partProgress ( int index, qint64 bytesReceived, qint64 bytesTo void DownloadJob::start() { - qDebug() << "Download JOB started: " << this; + qDebug() << m_job_name.toLocal8Bit() << " started."; for(auto iter: downloads) { connect(iter.data(), SIGNAL(succeeded(int)), SLOT(partSucceeded(int))); diff --git a/logic/net/DownloadJob.h b/logic/net/DownloadJob.h index adeef646..71466282 100644 --- a/logic/net/DownloadJob.h +++ b/logic/net/DownloadJob.h @@ -1,98 +1,28 @@ #pragma once #include +#include "Download.h" +#include "ByteArrayDownload.h" +#include "FileDownload.h" +#include "CacheDownload.h" +#include "HttpMetaCache.h" 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: - virtual ~Job() {}; - -public slots: - 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; - /// source URL - QUrl m_url; - - /// if true, check the md5sum against a provided md5sum - /// also, if a file exists, perform an md5sum first and don't download only if they don't match - bool m_check_md5; - /// the expected md5 checksum - QString m_expected_md5; - - /// save to file? - bool m_save_to_file; - /// 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 - QFile m_output_file; - /// 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 +class DownloadJob : public QObject { 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); - }; + explicit DownloadJob(QString job_name) + :QObject(), m_job_name(job_name){}; + + ByteArrayDownloadPtr add(QUrl url); + FileDownloadPtr add(QUrl url, QString rel_target_path); + CacheDownloadPtr add(QUrl url, MetaEntryPtr entry); - DownloadPtr add(QUrl url, QString rel_target_path = QString(), QString expected_md5 = QString()); DownloadPtr operator[](int index) { return downloads[index]; @@ -119,6 +49,7 @@ private slots: void partSucceeded(int index); void partFailed(int index); private: + QString m_job_name; QList downloads; int num_succeeded = 0; int num_failed = 0; diff --git a/logic/net/FileDownload.cpp b/logic/net/FileDownload.cpp new file mode 100644 index 00000000..aad4a991 --- /dev/null +++ b/logic/net/FileDownload.cpp @@ -0,0 +1,111 @@ +#include "MultiMC.h" +#include "FileDownload.h" +#include +#include +#include + + +FileDownload::FileDownload ( QUrl url, QString target_path ) + :Download() +{ + m_url = url; + m_target_path = target_path; + m_check_md5 = false; + m_status = Job_NotStarted; + m_opened_for_saving = false; +} + +void FileDownload::start() +{ + QString filename = m_target_path; + m_output_file.setFileName ( filename ); + // if there already is a file and md5 checking is in effect and it can be opened + if ( m_output_file.exists() && m_output_file.open ( QIODevice::ReadOnly ) ) + { + // check the md5 against the expected one + QString hash = QCryptographicHash::hash ( m_output_file.readAll(), QCryptographicHash::Md5 ).toHex().constData(); + m_output_file.close(); + // skip this file if they match + if ( m_check_md5 && hash == m_expected_md5 ) + { + qDebug() << "Skipping " << m_url.toString() << ": md5 match."; + emit succeeded(index_within_job); + return; + } + else + { + m_expected_md5 = hash; + } + } + if(!ensureFilePathExists(filename)) + { + emit failed(index_within_job); + return; + } + + qDebug() << "Downloading " << m_url.toString(); + QNetworkRequest request ( m_url ); + request.setRawHeader(QString("If-None-Match").toLatin1(), m_expected_md5.toLatin1()); + + auto worker = MMC->qnam(); + QNetworkReply * rep = worker->get ( request ); + + m_reply = QSharedPointer ( rep, &QObject::deleteLater ); + 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 FileDownload::downloadProgress ( qint64 bytesReceived, qint64 bytesTotal ) +{ + emit progress (index_within_job, bytesReceived, bytesTotal ); +} + +void FileDownload::downloadError ( QNetworkReply::NetworkError error ) +{ + // error happened during download. + // TODO: log the reason why + m_status = Job_Failed; +} + +void FileDownload::downloadFinished() +{ + // if the download succeeded + if ( m_status != Job_Failed ) + { + // nothing went wrong... + m_status = Job_Finished; + m_output_file.close(); + + m_reply.clear(); + emit succeeded(index_within_job); + return; + } + // else the download failed + else + { + m_output_file.close(); + m_reply.clear(); + emit failed(index_within_job); + return; + } +} + +void FileDownload::downloadReadyRead() +{ + if(!m_opened_for_saving) + { + if ( !m_output_file.open ( QIODevice::WriteOnly ) ) + { + /* + * Can't open the file... the job failed + */ + m_reply->abort(); + 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 new file mode 100644 index 00000000..190bee58 --- /dev/null +++ b/logic/net/FileDownload.h @@ -0,0 +1,35 @@ +#pragma once + +#include "Download.h" +#include + +class FileDownload : public Download +{ + Q_OBJECT +public: + /// if true, check the md5sum against a provided md5sum + /// also, if a file exists, perform an md5sum first and don't download only if they don't match + 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 + QFile m_output_file; + +public: + explicit FileDownload(QUrl url, QString target_path); + +protected slots: + virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal); + virtual void downloadError(QNetworkReply::NetworkError error); + virtual void downloadFinished(); + virtual void downloadReadyRead(); + +public slots: + virtual void start(); +}; + +typedef QSharedPointer FileDownloadPtr; diff --git a/logic/net/HttpMetaCache.cpp b/logic/net/HttpMetaCache.cpp index 87741dc9..50a1136e 100644 --- a/logic/net/HttpMetaCache.cpp +++ b/logic/net/HttpMetaCache.cpp @@ -1,38 +1,136 @@ +#include "MultiMC.h" #include "HttpMetaCache.h" #include + +#include #include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +QString MetaEntry::getFullPath() +{ + return PathCombine(MMC->metacache()->getBasePath(base), path); +} + HttpMetaCache::HttpMetaCache(QString path) + :QObject() { m_index_file = path; + saveBatchingTimer.setSingleShot(true); + saveBatchingTimer.setTimerType(Qt::VeryCoarseTimer); + connect(&saveBatchingTimer,SIGNAL(timeout()),SLOT(SaveNow())); } + HttpMetaCache::~HttpMetaCache() { - Save(); + saveBatchingTimer.stop(); + SaveNow(); } -void HttpMetaCache::addEntry ( QString base, QString resource_path, QString etag ) +void HttpMetaCache::SaveEventually() +{ + saveBatchingTimer.stop(); + saveBatchingTimer.start(30000); +} + +MetaEntryPtr HttpMetaCache::getEntry ( QString base, QString resource_path ) { // no base. no base path. can't store if(!m_entries.contains(base)) - return; - QString real_path = PathCombine(m_entries[base].base_path, resource_path); + { + // TODO: log problem + return MetaEntryPtr(); + } + EntryMap & map = m_entries[base]; + if(map.entry_list.contains(resource_path)) + { + return map.entry_list[resource_path]; + } + return MetaEntryPtr(); +} + +MetaEntryPtr HttpMetaCache::resolveEntry ( QString base, QString resource_path, QString expected_etag ) +{ + auto entry = getEntry(base, resource_path); + // it's not present? generate a default stale entry + if(!entry) + { + return staleEntry(base, resource_path); + } + + auto & selected_base = m_entries[base]; + QString real_path = PathCombine(selected_base.base_path, resource_path); QFileInfo finfo(real_path); - // just ignore it, it's garbage if it's not a proper file + // is the file really there? if not -> stale if(!finfo.isFile() || !finfo.isReadable()) { - // TODO: log problem - return; + // if the file doesn't exist, we disown the entry + selected_base.entry_list.remove(resource_path); + return staleEntry(base, resource_path); + } + + if(!expected_etag.isEmpty() && expected_etag != entry->etag) + { + // if the etag doesn't match expected, we disown the entry + selected_base.entry_list.remove(resource_path); + return staleEntry(base, resource_path); } - Save(); + // if the file changed, check md5sum + qint64 file_last_changed = finfo.lastModified().toUTC().toMSecsSinceEpoch(); + if(file_last_changed != entry->last_changed_timestamp) + { + QFile input(real_path); + input.open(QIODevice::ReadOnly); + QString md5sum = QCryptographicHash::hash(input.readAll(), QCryptographicHash::Md5).toHex().constData(); + if(entry->md5sum != md5sum) + { + selected_base.entry_list.remove(resource_path); + return staleEntry(base, resource_path); + } + // md5sums matched... keep entry and save the new state to file + entry->last_changed_timestamp = file_last_changed; + SaveEventually(); + } + + // entry passed all the checks we cared about. + return entry; +} + +bool HttpMetaCache::updateEntry ( MetaEntryPtr stale_entry ) +{ + if(!m_entries.contains(stale_entry->base)) + { + qDebug() << "Cannot add entry with unknown base: " << stale_entry->base.toLocal8Bit(); + return false; + } + if(stale_entry->stale) + { + qDebug() << "Cannot add stale entry: " << stale_entry->getFullPath().toLocal8Bit(); + return false; + } + m_entries[stale_entry->base].entry_list[stale_entry->path] = stale_entry; + SaveEventually(); + return true; +} + +MetaEntryPtr HttpMetaCache::staleEntry(QString base, QString resource_path) +{ + auto foo = new MetaEntry; + foo->base = base; + foo->path = resource_path; + foo->stale = true; + return MetaEntryPtr(foo); } void HttpMetaCache::addBase ( QString base, QString base_root ) @@ -46,6 +144,16 @@ void HttpMetaCache::addBase ( QString base, QString base_root ) m_entries[base] = foo; } +QString HttpMetaCache::getBasePath ( QString base ) +{ + if(m_entries.contains(base)) + { + return m_entries[base].base_path; + } + return QString(); +} + + void HttpMetaCache::Load() { QFile index(m_index_file); @@ -65,12 +173,12 @@ void HttpMetaCache::Load() // read the entry array auto entries_val =root.value("entries"); - if(!version_val.isArray()) + if(!entries_val.isArray()) return; - QJsonArray array = json.array(); + QJsonArray array = entries_val.toArray(); for(auto element: array) { - if(!element.isObject()); + if(!element.isObject()) return; auto element_obj = element.toObject(); QString base = element_obj.value("base").toString(); @@ -83,11 +191,13 @@ void HttpMetaCache::Load() foo->md5sum = element_obj.value("md5sum").toString(); foo->etag = element_obj.value("etag").toString(); foo->last_changed_timestamp = element_obj.value("last_changed_timestamp").toDouble(); + // presumed innocent until closer examination + foo->stale = false; entrymap.entry_list[path] = MetaEntryPtr( foo ); } } -void HttpMetaCache::Save() +void HttpMetaCache::SaveNow() { QSaveFile tfile(m_index_file); if(!tfile.open(QIODevice::WriteOnly | QIODevice::Truncate)) @@ -118,14 +228,3 @@ void HttpMetaCache::Save() return; tfile.commit(); } - - -MetaEntryPtr HttpMetaCache::getEntryForResource ( QString base, QString resource_path ) -{ - if(!m_entries.contains(base)) - return MetaEntryPtr(); - auto & entrymap = m_entries[base]; - if(!entrymap.entry_list.contains(resource_path)) - return MetaEntryPtr(); - return entrymap.entry_list[resource_path]; -} diff --git a/logic/net/HttpMetaCache.h b/logic/net/HttpMetaCache.h index 161483ad..fac6bec3 100644 --- a/logic/net/HttpMetaCache.h +++ b/logic/net/HttpMetaCache.h @@ -2,6 +2,7 @@ #include #include #include +#include struct MetaEntry { @@ -9,23 +10,42 @@ struct MetaEntry QString path; QString md5sum; QString etag; - quint64 last_changed_timestamp = 0; + qint64 last_changed_timestamp = 0; + bool stale = true; + QString getFullPath(); }; typedef QSharedPointer MetaEntryPtr; -class HttpMetaCache +class HttpMetaCache : public QObject { + Q_OBJECT public: // supply path to the cache index file HttpMetaCache(QString path); ~HttpMetaCache(); - MetaEntryPtr getEntryForResource(QString base, QString resource_path); - void addEntry(QString base, QString resource_path, QString etag); + + // get the entry solely from the cache + // you probably don't want this, unless you have some specific caching needs. + MetaEntryPtr getEntry(QString base, QString resource_path); + + // get the entry from cache and verify that it isn't stale (within reason) + MetaEntryPtr resolveEntry(QString base, QString resource_path, QString expected_etag = QString()); + + // add a previously resolved stale entry + bool updateEntry(MetaEntryPtr stale_entry); + void addBase(QString base, QString base_root); -private: - void Save(); + + // (re)start a timer that calls SaveNow later. + void SaveEventually(); void Load(); + QString getBasePath ( QString base ); +public slots: + void SaveNow(); +private: + // create a new stale entry, given the parameters + MetaEntryPtr staleEntry(QString base, QString resource_path); struct EntryMap { QString base_path; @@ -33,4 +53,5 @@ private: }; QMap m_entries; QString m_index_file; + QTimer saveBatchingTimer; }; \ No newline at end of file -- cgit From dab2bbe4e79cfd8b3b72b8d2fd0e5bd66e0281a9 Mon Sep 17 00:00:00 2001 From: Stiepen22 Date: Sun, 8 Sep 2013 15:02:52 +0200 Subject: Added console coloring and made the log not contain any usernames/session ids --- logic/MinecraftProcess.cpp | 30 +++++++++++++++++++++--------- logic/MinecraftProcess.h | 5 +++++ 2 files changed, 26 insertions(+), 9 deletions(-) (limited to 'logic') diff --git a/logic/MinecraftProcess.cpp b/logic/MinecraftProcess.cpp index 6ac5b886..c33d34a8 100644 --- a/logic/MinecraftProcess.cpp +++ b/logic/MinecraftProcess.cpp @@ -84,17 +84,14 @@ void MinecraftProcess::on_stdErr() for(int i = 0; i < lines.size() - 1; i++) { QString & line = lines[i]; - MessageLevel::Enum level = MessageLevel::Error; - if(line.contains("[INFO]") || line.contains("[CONFIG]") || line.contains("[FINE]") || line.contains("[FINER]") || line.contains("[FINEST]") ) - level = MessageLevel::Message; - if(line.contains("[SEVERE]") || line.contains("[WARNING]") || line.contains("[STDERR]")) - level = MessageLevel::Error; - emit log(lines[i].toLocal8Bit(), level); + emit log(line.replace(username, "").replace(sessionID, "").toLocal8Bit(), getLevel(line, MessageLevel::Error)); } if(!complete) m_err_leftover = lines.last(); } + + void MinecraftProcess::on_stdOut() { QByteArray data = readAllStandardOutput(); @@ -106,7 +103,7 @@ void MinecraftProcess::on_stdOut() for(int i = 0; i < lines.size() - 1; i++) { QString & line = lines[i]; - emit log(lines[i].toLocal8Bit(), MessageLevel::Message); + emit log(line.replace(username, "").replace(sessionID, "").toLocal8Bit(), getLevel(line, MessageLevel::Message)); } if(!complete) m_out_leftover = lines.last(); @@ -167,7 +164,7 @@ void MinecraftProcess::launch() emit log(QString("Minecraft folder is: '%1'").arg(workingDirectory())); QString JavaPath = m_instance->settings().get("JavaPath").toString(); emit log(QString("Java path: '%1'").arg(JavaPath)); - emit log(QString("Arguments: '%1'").arg(m_args.join("' '"))); + emit log(QString("Arguments: '%1'").arg(m_args.join("' '").replace(username, "").replace(sessionID, ""))); start(JavaPath, m_args); if (!waitForStarted()) { @@ -177,4 +174,19 @@ void MinecraftProcess::launch() } } - +MessageLevel::Enum MinecraftProcess::getLevel(const QString &line, MessageLevel::Enum level) +{ + + if(line.contains("[INFO]") || line.contains("[CONFIG]") || line.contains("[FINE]") || line.contains("[FINER]") || line.contains("[FINEST]") ) + level = MessageLevel::Message; + if(line.contains("[SEVERE]") || line.contains("[STDERR]")) + level = MessageLevel::Error; + if(line.contains("[WARNING]")) + level = MessageLevel::Warning; + if(line.contains("Exception in thread") || line.contains(" at ")) + level = MessageLevel::Fatal; + if(line.contains("[DEBUG]")) + level = MessageLevel::Debug; + return level; + +} \ No newline at end of file diff --git a/logic/MinecraftProcess.h b/logic/MinecraftProcess.h index 248ad807..a1dfa23f 100644 --- a/logic/MinecraftProcess.h +++ b/logic/MinecraftProcess.h @@ -61,6 +61,8 @@ public: void killMinecraft(); + inline void setLogin(QString user, QString sid) { username = user; sessionID = sid; } + signals: /** * @brief emitted when mc has finished and the PostLaunchCommand was run @@ -87,4 +89,7 @@ protected slots: void on_stdOut(); private: bool killed; + MessageLevel::Enum getLevel(const QString &message, MessageLevel::Enum defaultLevel); + QString sessionID; + QString username; }; -- cgit From 7e1cf22ce61e8a5450e5dc74993e471c20b2742f Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Sun, 8 Sep 2013 15:59:50 +0200 Subject: Use youtrack for bugs --- logic/net/HttpMetaCache.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'logic') diff --git a/logic/net/HttpMetaCache.cpp b/logic/net/HttpMetaCache.cpp index 50a1136e..46801ab3 100644 --- a/logic/net/HttpMetaCache.cpp +++ b/logic/net/HttpMetaCache.cpp @@ -36,12 +36,6 @@ HttpMetaCache::~HttpMetaCache() SaveNow(); } -void HttpMetaCache::SaveEventually() -{ - saveBatchingTimer.stop(); - saveBatchingTimer.start(30000); -} - MetaEntryPtr HttpMetaCache::getEntry ( QString base, QString resource_path ) { // no base. no base path. can't store @@ -197,6 +191,13 @@ void HttpMetaCache::Load() } } +void HttpMetaCache::SaveEventually() +{ + // reset the save timer + saveBatchingTimer.stop(); + saveBatchingTimer.start(30000); +} + void HttpMetaCache::SaveNow() { QSaveFile tfile(m_index_file); -- cgit From cbf3238f0e4d761ff34c9469f1ec88bd937152a5 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Sun, 8 Sep 2013 16:25:02 +0200 Subject: Fix build --- logic/InstanceLauncher.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'logic') diff --git a/logic/InstanceLauncher.cpp b/logic/InstanceLauncher.cpp index 312f4c69..a0557b37 100644 --- a/logic/InstanceLauncher.cpp +++ b/logic/InstanceLauncher.cpp @@ -31,7 +31,7 @@ void InstanceLauncher::onLoginComplete() //FIXME: report error return; } - console = new ConsoleWindow(); + console = new ConsoleWindow(proc); console->show(); connect ( proc, SIGNAL ( ended() ), SLOT ( onTerminated() ) ); -- cgit From 31e5a0fe6d75e124bc772faafcef2618e16c3dbf Mon Sep 17 00:00:00 2001 From: Stiepen22 Date: Sun, 8 Sep 2013 18:13:09 +0200 Subject: Changed all strings displayed to end user to use qts localization system --- logic/MinecraftProcess.cpp | 9 ++++++--- logic/tasks/LoginTask.cpp | 18 +++++++++--------- 2 files changed, 15 insertions(+), 12 deletions(-) (limited to 'logic') diff --git a/logic/MinecraftProcess.cpp b/logic/MinecraftProcess.cpp index c33d34a8..299f00be 100644 --- a/logic/MinecraftProcess.cpp +++ b/logic/MinecraftProcess.cpp @@ -120,9 +120,11 @@ void MinecraftProcess::finish(int code, ExitStatus status) // TODO: Localization if (!killed) - emit log("Minecraft exited."); + //: Message displayed on instance exit + emit log(tr("Minecraft exited with exitcode %1.").arg(status)); else - emit log("Minecraft was killed by user.", MessageLevel::Error); + //: Message displayed after the instance exits due to kill request + emit log(tr("Minecraft was killed by user."), MessageLevel::Error); m_prepostlaunchprocess.processEnvironment().insert("INST_EXITCODE", QString(code)); @@ -168,7 +170,8 @@ void MinecraftProcess::launch() start(JavaPath, m_args); if (!waitForStarted()) { - emit log("Could not launch minecraft!"); + //: Error message displayed if instace can't start + emit log(tr("Could not launch minecraft!")); return; //TODO: error handling } diff --git a/logic/tasks/LoginTask.cpp b/logic/tasks/LoginTask.cpp index ad9de7f5..859827bc 100644 --- a/logic/tasks/LoginTask.cpp +++ b/logic/tasks/LoginTask.cpp @@ -28,7 +28,7 @@ LoginTask::LoginTask( const UserInfo& uInfo, QObject* parent ) : Task(parent), u void LoginTask::executeTask() { - setStatus("Logging in..."); + setStatus(tr("Logging in...")); auto worker = MMC->qnam(); connect(worker, SIGNAL(finished(QNetworkReply*)), this, SLOT(processNetReply(QNetworkReply*))); @@ -76,36 +76,36 @@ void LoginTask::processNetReply(QNetworkReply *reply) } else { - emitFailed("Failed to parse Minecraft version string."); + emitFailed(tr("Failed to parse Minecraft version string.")); } } else { if (responseStr.toLower() == "bad login") - emitFailed("Invalid username or password."); + emitFailed(tr("Invalid username or password.")); else if (responseStr.toLower() == "old version") - emitFailed("Launcher outdated, please update."); + emitFailed(tr("Launcher outdated, please update.")); else - emitFailed("Login failed: " + responseStr); + emitFailed(tr("Login failed: %1").arg(responseStr)); } } else if (responseCode == 503) { - emitFailed("The login servers are currently unavailable. Check http://help.mojang.com/ for more info."); + emitFailed(tr("The login servers are currently unavailable. Check http://help.mojang.com/ for more info.")); } else { - emitFailed(QString("Login failed: Unknown HTTP error %1 occurred.").arg(QString::number(responseCode))); + emitFailed(tr("Login failed: Unknown HTTP error %1 occurred.").arg(QString::number(responseCode))); } break; } case QNetworkReply::OperationCanceledError: - emitFailed("Login canceled."); + emitFailed(tr("Login canceled.")); break; default: - emitFailed("Login failed: " + reply->errorString()); + emitFailed(tr("Login failed: %1").arg(reply->errorString())); break; } } -- cgit From cffdc4e045d4e72376d83c30ad063c290b071d62 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Sun, 8 Sep 2013 19:35:36 +0200 Subject: Fix legacy instance update segfault --- logic/LegacyUpdate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'logic') diff --git a/logic/LegacyUpdate.cpp b/logic/LegacyUpdate.cpp index c75a0011..7bf12faa 100644 --- a/logic/LegacyUpdate.cpp +++ b/logic/LegacyUpdate.cpp @@ -229,7 +229,7 @@ void LegacyUpdate::jarStart() auto dljob = new DownloadJob("Minecraft.jar for version " + intended_version_id); dljob->add(QUrl(urlstr), inst->defaultBaseJar()); - legacyDownloadJob.reset(); + legacyDownloadJob.reset(dljob); 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))); -- cgit From 8062b73074b8e3c94fd97b77b05e7dbb61205d41 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Sun, 8 Sep 2013 20:16:38 +0200 Subject: Fix one more legacy update bug --- logic/LegacyUpdate.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'logic') diff --git a/logic/LegacyUpdate.cpp b/logic/LegacyUpdate.cpp index 7bf12faa..b8e179a5 100644 --- a/logic/LegacyUpdate.cpp +++ b/logic/LegacyUpdate.cpp @@ -230,9 +230,9 @@ void LegacyUpdate::jarStart() auto dljob = new DownloadJob("Minecraft.jar for version " + intended_version_id); dljob->add(QUrl(urlstr), inst->defaultBaseJar()); legacyDownloadJob.reset(dljob); - 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))); + connect(dljob, SIGNAL(succeeded()), SLOT(jarFinished())); + connect(dljob, SIGNAL(failed()), SLOT(jarFailed())); + connect(dljob, SIGNAL(progress(qint64,qint64)), SLOT(updateDownloadProgress(qint64,qint64))); legacyDownloadJob->start(); } -- cgit From 91a3e650e98c289ec183e8c4091da0dae0968f88 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Tue, 10 Sep 2013 00:29:25 +0200 Subject: Fix download progress (bars) --- logic/net/DownloadJob.cpp | 19 ++++++++++++++++++- logic/net/DownloadJob.h | 3 +++ 2 files changed, 21 insertions(+), 1 deletion(-) (limited to 'logic') diff --git a/logic/net/DownloadJob.cpp b/logic/net/DownloadJob.cpp index cbc2c5fe..090df260 100644 --- a/logic/net/DownloadJob.cpp +++ b/logic/net/DownloadJob.cpp @@ -10,6 +10,8 @@ ByteArrayDownloadPtr DownloadJob::add ( QUrl url ) ByteArrayDownloadPtr ptr (new ByteArrayDownload(url)); ptr->index_within_job = downloads.size(); downloads.append(ptr); + parts_progress.append(QPair(0,1)); + total_progress++; return ptr; } @@ -18,6 +20,8 @@ FileDownloadPtr DownloadJob::add ( QUrl url, QString rel_target_path) FileDownloadPtr ptr (new FileDownload(url, rel_target_path)); ptr->index_within_job = downloads.size(); downloads.append(ptr); + parts_progress.append(QPair(0,1)); + total_progress++; return ptr; } @@ -26,6 +30,8 @@ CacheDownloadPtr DownloadJob::add ( QUrl url, MetaEntryPtr entry) CacheDownloadPtr ptr (new CacheDownload(url, entry)); ptr->index_within_job = downloads.size(); downloads.append(ptr); + parts_progress.append(QPair(0,1)); + total_progress++; return ptr; } @@ -60,7 +66,16 @@ void DownloadJob::partFailed ( int index ) void DownloadJob::partProgress ( int index, qint64 bytesReceived, qint64 bytesTotal ) { - // PROGRESS? DENIED! + auto & slot = parts_progress[index]; + + current_progress -= slot.first; + slot.first = bytesReceived; + current_progress += slot.first; + + total_progress -= slot.second; + slot.second = bytesTotal; + total_progress += slot.second; + emit progress(current_progress, total_progress); } @@ -70,6 +85,8 @@ void DownloadJob::start() for(auto iter: downloads) { connect(iter.data(), SIGNAL(succeeded(int)), SLOT(partSucceeded(int))); + connect(iter.data(), SIGNAL(failed(int)), SLOT(partFailed(int))); + connect(iter.data(), SIGNAL(progress(int,qint64,qint64)), SLOT(partProgress(int,qint64,qint64))); iter->start(); } } diff --git a/logic/net/DownloadJob.h b/logic/net/DownloadJob.h index 71466282..69a49e59 100644 --- a/logic/net/DownloadJob.h +++ b/logic/net/DownloadJob.h @@ -51,6 +51,9 @@ private slots: private: QString m_job_name; QList downloads; + QList> parts_progress; + qint64 current_progress = 0; + qint64 total_progress = 0; int num_succeeded = 0; int num_failed = 0; }; -- cgit From 108a5a677c4bf248b70e77046502ea96bd9cfe65 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Tue, 10 Sep 2013 00:34:34 +0200 Subject: Progress works for null downloads (header checks) --- logic/net/DownloadJob.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'logic') diff --git a/logic/net/DownloadJob.cpp b/logic/net/DownloadJob.cpp index 090df260..9b083b6b 100644 --- a/logic/net/DownloadJob.cpp +++ b/logic/net/DownloadJob.cpp @@ -37,6 +37,10 @@ CacheDownloadPtr DownloadJob::add ( QUrl url, MetaEntryPtr entry) void DownloadJob::partSucceeded ( int index ) { + // do progress. all slots are 1 in size at least + auto & slot = parts_progress[index]; + partProgress ( index, slot.second , slot.second ); + num_succeeded++; qDebug() << m_job_name.toLocal8Bit() << " progress: " << num_succeeded << "/" << downloads.size(); if(num_failed + num_succeeded == downloads.size()) -- cgit From 7721c57e5e1093a3d8597b6b6f30c97d2aa3d8a5 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Wed, 11 Sep 2013 23:43:17 +0200 Subject: Split OneSixVersion into parts. --- logic/OneSixInstance_p.h | 1 + logic/OneSixLibrary.cpp | 84 ++++++++++++++++++++++++++ logic/OneSixLibrary.h | 68 +++++++++++++++++++++ logic/OneSixRule.cpp | 10 ++++ logic/OneSixRule.h | 70 ++++++++++++++++++++++ logic/OneSixUpdate.cpp | 1 + logic/OneSixVersion.cpp | 112 ++-------------------------------- logic/OneSixVersion.h | 153 ++--------------------------------------------- logic/OpSys.cpp | 12 ++++ logic/OpSys.h | 21 +++++++ logic/VersionFactory.cpp | 3 +- 11 files changed, 278 insertions(+), 257 deletions(-) create mode 100644 logic/OneSixLibrary.cpp create mode 100644 logic/OneSixLibrary.h create mode 100644 logic/OneSixRule.cpp create mode 100644 logic/OneSixRule.h create mode 100644 logic/OpSys.cpp create mode 100644 logic/OpSys.h (limited to 'logic') diff --git a/logic/OneSixInstance_p.h b/logic/OneSixInstance_p.h index c098c9e2..7b1ca82e 100644 --- a/logic/OneSixInstance_p.h +++ b/logic/OneSixInstance_p.h @@ -2,6 +2,7 @@ #include "BaseInstance_p.h" #include "OneSixVersion.h" +#include "OneSixLibrary.h" #include "ModList.h" struct OneSixInstancePrivate: public BaseInstancePrivate diff --git a/logic/OneSixLibrary.cpp b/logic/OneSixLibrary.cpp new file mode 100644 index 00000000..a109a7f0 --- /dev/null +++ b/logic/OneSixLibrary.cpp @@ -0,0 +1,84 @@ +#include "OneSixLibrary.h" +#include "OneSixRule.h" + +void OneSixLibrary::finalize() +{ + QStringList parts = m_name.split ( ':' ); + QString relative = parts[0]; + relative.replace ( '.','/' ); + relative += '/' + parts[1] + '/' + parts[2] + '/' + parts[1] + '-' + parts[2]; + if ( !m_is_native ) + relative += ".jar"; + else + { + if ( m_native_suffixes.contains ( currentSystem ) ) + { + relative += "-" + m_native_suffixes[currentSystem] + ".jar"; + } + else + { + // really, bad. + relative += ".jar"; + } + } + m_storage_path = relative; + m_download_path = m_base_url + relative; + + if ( m_rules.empty() ) + { + m_is_active = true; + } + else + { + RuleAction result = Disallow; + for ( auto rule: m_rules ) + { + RuleAction temp = rule->apply ( this ); + if ( temp != Defer ) + result = temp; + } + m_is_active = ( result == Allow ); + } + if ( m_is_native ) + { + m_is_active = m_is_active && m_native_suffixes.contains ( currentSystem ); + } +} + +void OneSixLibrary::setName ( QString name ) +{ + m_name = name; +} +void OneSixLibrary::setBaseUrl ( QString base_url ) +{ + m_base_url = base_url; +} +void OneSixLibrary::setIsNative() +{ + m_is_native = true; +} +void OneSixLibrary::addNative ( OpSys os, QString suffix ) +{ + m_is_native = true; + m_native_suffixes[os] = suffix; +} +void OneSixLibrary::setRules ( QList< QSharedPointer< Rule > > rules ) +{ + m_rules = rules; +} +bool OneSixLibrary::isActive() +{ + return m_is_active; +} +bool OneSixLibrary::isNative() +{ + return m_is_native; +} +QString OneSixLibrary::downloadPath() +{ + return m_download_path; +} +QString OneSixLibrary::storagePath() +{ + return m_storage_path; +} diff --git a/logic/OneSixLibrary.h b/logic/OneSixLibrary.h new file mode 100644 index 00000000..856e409c --- /dev/null +++ b/logic/OneSixLibrary.h @@ -0,0 +1,68 @@ +#pragma once +#include +#include +#include +#include +#include "OpSys.h" + +class Rule; + +class OneSixLibrary +{ +private: + // basic values used internally (so far) + QString m_name; + QString m_base_url; + QList > m_rules; + + // derived values used for real things + /// where to store the lib locally + QString m_storage_path; + /// where to download the lib from + QString m_download_path; + /// is this lib actually active on the current OS? + bool m_is_active; + /// is the library a native? + bool m_is_native; + /// native suffixes per OS + QMap m_native_suffixes; +public: + QStringList extract_excludes; + +public: + /// Constructor + OneSixLibrary(QString name) + { + m_is_native = false; + m_is_active = false; + m_name = name; + m_base_url = "https://s3.amazonaws.com/Minecraft.Download/libraries/"; + } + + /** + * finalize the library, processing the input values into derived values and state + * + * This SHALL be called after all the values are parsed or after any further change. + */ + void finalize(); + + /// Set the library composite name + void setName(QString name); + /// Set the url base for downloads + void setBaseUrl(QString base_url); + /// Call this to mark the library as 'native' (it's a zip archive with DLLs) + void setIsNative(); + /// Attach a name suffix to the specified OS native + void addNative(OpSys os, QString suffix); + /// Set the load rules + void setRules(QList > rules); + + /// Returns true if the library should be loaded (or extracted, in case of natives) + bool isActive(); + /// Returns true if the library is native + bool isNative(); + /// Get the URL to download the library from + QString downloadPath(); + /// Get the relative path where the library should be saved + QString storagePath(); +}; diff --git a/logic/OneSixRule.cpp b/logic/OneSixRule.cpp new file mode 100644 index 00000000..85f7d434 --- /dev/null +++ b/logic/OneSixRule.cpp @@ -0,0 +1,10 @@ +#include "OneSixRule.h" + +RuleAction RuleAction_fromString(QString name) +{ + if(name == "allow") + return Allow; + if(name == "disallow") + return Disallow; + return Defer; +} \ No newline at end of file diff --git a/logic/OneSixRule.h b/logic/OneSixRule.h new file mode 100644 index 00000000..465c963f --- /dev/null +++ b/logic/OneSixRule.h @@ -0,0 +1,70 @@ +#pragma once +#include +#include + +class OneSixLibrary; +#include "OneSixLibrary.h" + +enum RuleAction +{ + Allow, + Disallow, + Defer +}; + +RuleAction RuleAction_fromString(QString); + +class Rule +{ +protected: + RuleAction m_result; + virtual bool applies(OneSixLibrary * parent) = 0; +public: + Rule(RuleAction result) + :m_result(result) {} + virtual ~Rule(){}; + RuleAction apply(OneSixLibrary * parent) + { + if(applies(parent)) + return m_result; + else + return Defer; + }; +}; + +class OsRule : public Rule +{ +private: + // the OS + OpSys m_system; + // the OS version regexp + QString m_version_regexp; +protected: + virtual bool applies ( OneSixLibrary* ) + { + return (m_system == currentSystem); + } + OsRule(RuleAction result, OpSys system, QString version_regexp) + : Rule(result), m_system(system), m_version_regexp(version_regexp) {} +public: + static QSharedPointer create(RuleAction result, OpSys system, QString version_regexp) + { + return QSharedPointer (new OsRule(result, system, version_regexp)); + } +}; + +class ImplicitRule : public Rule +{ +protected: + virtual bool applies ( OneSixLibrary* ) + { + return true; + } + ImplicitRule(RuleAction result) + : Rule(result) {} +public: + static QSharedPointer create(RuleAction result) + { + return QSharedPointer (new ImplicitRule(result)); + } +}; diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp index ce71bde0..a58a9626 100644 --- a/logic/OneSixUpdate.cpp +++ b/logic/OneSixUpdate.cpp @@ -28,6 +28,7 @@ #include "lists/MinecraftVersionList.h" #include "VersionFactory.h" #include "OneSixVersion.h" +#include "OneSixLibrary.h" #include "OneSixInstance.h" #include "pathutils.h" diff --git a/logic/OneSixVersion.cpp b/logic/OneSixVersion.cpp index 56e272e2..7ffe9a94 100644 --- a/logic/OneSixVersion.cpp +++ b/logic/OneSixVersion.cpp @@ -1,111 +1,9 @@ #include "OneSixVersion.h" +#include "OneSixLibrary.h" -RuleAction RuleAction_fromString(QString name) +QList > OneSixVersion::getActiveNormalLibs() { - if(name == "allow") - return Allow; - if(name == "disallow") - return Disallow; - return Defer; -} - -OpSys OpSys_fromString(QString name) -{ - if(name == "linux") - return Os_Linux; - if(name == "windows") - return Os_Windows; - if(name == "osx") - return Os_OSX; - return Os_Other; -} - -void Library::finalize() -{ - QStringList parts = m_name.split ( ':' ); - QString relative = parts[0]; - relative.replace ( '.','/' ); - relative += '/' + parts[1] + '/' + parts[2] + '/' + parts[1] + '-' + parts[2]; - if ( !m_is_native ) - relative += ".jar"; - else - { - if ( m_native_suffixes.contains ( currentSystem ) ) - { - relative += "-" + m_native_suffixes[currentSystem] + ".jar"; - } - else - { - // really, bad. - relative += ".jar"; - } - } - m_storage_path = relative; - m_download_path = m_base_url + relative; - - if ( m_rules.empty() ) - { - m_is_active = true; - } - else - { - RuleAction result = Disallow; - for ( auto rule: m_rules ) - { - RuleAction temp = rule->apply ( this ); - if ( temp != Defer ) - result = temp; - } - m_is_active = ( result == Allow ); - } - if ( m_is_native ) - { - m_is_active = m_is_active && m_native_suffixes.contains ( currentSystem ); - } -} - -void Library::setName ( QString name ) -{ - m_name = name; -} -void Library::setBaseUrl ( QString base_url ) -{ - m_base_url = base_url; -} -void Library::setIsNative() -{ - m_is_native = true; -} -void Library::addNative ( OpSys os, QString suffix ) -{ - m_is_native = true; - m_native_suffixes[os] = suffix; -} -void Library::setRules ( QList< QSharedPointer< Rule > > rules ) -{ - m_rules = rules; -} -bool Library::isActive() -{ - return m_is_active; -} -bool Library::isNative() -{ - return m_is_native; -} -QString Library::downloadPath() -{ - return m_download_path; -} -QString Library::storagePath() -{ - return m_storage_path; -} - - -QList > OneSixVersion::getActiveNormalLibs() -{ - QList > output; + QList > output; for ( auto lib: libraries ) { if (lib->isActive() && !lib->isNative()) @@ -116,9 +14,9 @@ QList > OneSixVersion::getActiveNormalLibs() return output; } -QList > OneSixVersion::getActiveNativeLibs() +QList > OneSixVersion::getActiveNativeLibs() { - QList > output; + QList > output; for ( auto lib: libraries ) { if (lib->isActive() && lib->isNative()) diff --git a/logic/OneSixVersion.h b/logic/OneSixVersion.h index 89b7c911..8f01f82d 100644 --- a/logic/OneSixVersion.h +++ b/logic/OneSixVersion.h @@ -1,151 +1,6 @@ #pragma once #include - -class Library; - -enum OpSys -{ - Os_Windows, - Os_Linux, - Os_OSX, - Os_Other -}; - -OpSys OpSys_fromString(QString); - -#ifdef Q_OS_WIN32 - #define currentSystem Os_Windows -#else - #ifdef Q_OS_MAC - #define currentSystem Os_OSX - #else - #define currentSystem Os_Linux - #endif -#endif -enum RuleAction -{ - Allow, - Disallow, - Defer -}; - -RuleAction RuleAction_fromString(QString); - -class Rule -{ -protected: - RuleAction m_result; - virtual bool applies(Library * parent) = 0; -public: - Rule(RuleAction result) - :m_result(result) {} - virtual ~Rule(){}; - RuleAction apply(Library * parent) - { - if(applies(parent)) - return m_result; - else - return Defer; - }; -}; - -class OsRule : public Rule -{ -private: - // the OS - OpSys m_system; - // the OS version regexp - QString m_version_regexp; -protected: - virtual bool applies ( Library* ) - { - return (m_system == currentSystem); - } - OsRule(RuleAction result, OpSys system, QString version_regexp) - : Rule(result), m_system(system), m_version_regexp(version_regexp) {} -public: - static QSharedPointer create(RuleAction result, OpSys system, QString version_regexp) - { - return QSharedPointer (new OsRule(result, system, version_regexp))