From 183a7351456940d01f14a49112ddeb68ffc4693a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Mon, 5 Aug 2013 03:29:50 +0200 Subject: [PATCH] Runnable 1.6 instances! --- backend/BaseInstance.h | 19 ++- backend/CMakeLists.txt | 2 +- backend/LegacyInstance.cpp | 40 ++---- backend/LegacyInstance.h | 33 +---- backend/MinecraftProcess.cpp | 1 + backend/OneSixInstance.cpp | 182 ++++++++++++++++++++++++- backend/OneSixInstance.h | 18 ++- backend/OneSixInstance_p.h | 5 +- backend/OneSixUpdate.cpp | 139 ++++++++++++------- backend/OneSixUpdate.h | 5 +- backend/OneSixVersion.cpp | 45 +++++- backend/OneSixVersion.h | 79 +++-------- backend/lists/MinecraftVersionList.cpp | 28 ++-- backend/tasks/LoginTask.cpp | 12 +- backend/tasks/LoginTask.h | 6 +- backend/tasks/Task.cpp | 16 ++- backend/tasks/Task.h | 10 +- libutil/include/dlqueue.h | 2 - libutil/include/pathutils.h | 2 + libutil/src/dlqueue.cpp | 8 +- libutil/src/pathutils.cpp | 8 ++ quazip/JlCompress.cpp | 47 +++++++ quazip/JlCompress.h | 9 ++ 23 files changed, 502 insertions(+), 214 deletions(-) diff --git a/backend/BaseInstance.h b/backend/BaseInstance.h index eb13b92b..7454c17e 100644 --- a/backend/BaseInstance.h +++ b/backend/BaseInstance.h @@ -69,8 +69,23 @@ public: QString group() const; void setGroup(QString val); + virtual QString intendedVersionId() const = 0; virtual bool setIntendedVersionId(QString version) = 0; - virtual QString intendedVersionId() = 0; + + /*! + * The instance's current version. + * This value represents the instance's current version. If this value is + * different from the intendedVersion, the instance should be updated. + * \warning Don't change this value unless you know what you're doing. + */ + virtual QString currentVersionId() const = 0; + //virtual void setCurrentVersionId(QString val) = 0; + + /*! + * Whether or not Minecraft should be downloaded when the instance is launched. + */ + virtual bool shouldUpdate() const = 0; + virtual void setShouldUpdate(bool val) = 0; /** * Gets the time that the instance was last launched. @@ -107,6 +122,8 @@ public: /// returns a valid minecraft process, ready for launch virtual MinecraftProcess* prepareForLaunch(QString user, QString session) = 0; + /// do any necessary cleanups after the instance finishes. also runs before 'prepareForLaunch' + virtual void cleanupAfterRun() = 0; signals: /*! * \brief Signal emitted when properties relevant to the instance view change diff --git a/backend/CMakeLists.txt b/backend/CMakeLists.txt index 309dde97..4fbe7bed 100644 --- a/backend/CMakeLists.txt +++ b/backend/CMakeLists.txt @@ -91,6 +91,6 @@ add_definitions(-DLIBMULTIMC_LIBRARY) add_library(backend SHARED ${LIBINST_SOURCES} ${LIBINST_HEADERS}) qt5_use_modules(backend Core Network Xml) -target_link_libraries(backend libUtil libSettings) +target_link_libraries(backend libUtil libSettings quazip) diff --git a/backend/LegacyInstance.cpp b/backend/LegacyInstance.cpp index aace2a22..dc6cf0db 100644 --- a/backend/LegacyInstance.cpp +++ b/backend/LegacyInstance.cpp @@ -16,7 +16,7 @@ LegacyInstance::LegacyInstance(const QString& rootDir, SettingsObject* settings, settings->registerSetting(new Setting("NeedsRebuild", true)); settings->registerSetting(new Setting("ShouldUpdate", false)); settings->registerSetting(new Setting("JarVersion", "Unknown")); - settings->registerSetting(new Setting("LwjglVersion", "2.9.0")); + settings->registerSetting(new Setting("LwjglVersion", "Mojang")); settings->registerSetting(new Setting("IntendedJarVersion", "")); } @@ -93,6 +93,11 @@ MinecraftProcess* LegacyInstance::prepareForLaunch(QString user, QString session return proc; } +void LegacyInstance::cleanupAfterRun() +{ + //FIXME: delete the launcher and icons and whatnot. +} + QString LegacyInstance::instModsDir() const { @@ -152,7 +157,7 @@ void LegacyInstance::updateCurrentVersion(bool keepCurrent) if(!jar.exists()) { setLastCurrentVersionUpdate(0); - setCurrentVersion("Unknown"); + setCurrentVersionId("Unknown"); return; } @@ -163,7 +168,7 @@ void LegacyInstance::updateCurrentVersion(bool keepCurrent) { // TODO: Implement GetMinecraftJarVersion function. QString newVersion = "Unknown";//javautils::GetMinecraftJarVersion(jar.absoluteFilePath()); - setCurrentVersion(newVersion); + setCurrentVersionId(newVersion); } } qint64 LegacyInstance::lastCurrentVersionUpdate() const @@ -186,16 +191,18 @@ void LegacyInstance::setShouldRebuild ( bool val ) I_D(LegacyInstance); d->m_settings->set ( "NeedsRebuild", val ); } -QString LegacyInstance::currentVersion() const +QString LegacyInstance::currentVersionId() const { I_D(LegacyInstance); return d->m_settings->get ( "JarVersion" ).toString(); } -void LegacyInstance::setCurrentVersion ( QString val ) + +void LegacyInstance::setCurrentVersionId ( QString val ) { I_D(LegacyInstance); d->m_settings->set ( "JarVersion", val ); } + QString LegacyInstance::lwjglVersion() const { I_D(LegacyInstance); @@ -206,36 +213,17 @@ void LegacyInstance::setLWJGLVersion ( QString val ) I_D(LegacyInstance); d->m_settings->set ( "LwjglVersion", val ); } -QString LegacyInstance::intendedVersionId() +QString LegacyInstance::intendedVersionId() const { I_D(LegacyInstance); return d->m_settings->get ( "IntendedJarVersion" ).toString(); } bool LegacyInstance::setIntendedVersionId ( QString version ) { - /* - I_D(LegacyInstance); - d->m_settings->set ( "IntendedJarVersion", val ); - */ return false; } bool LegacyInstance::shouldUpdate() const { - /* - I_D(LegacyInstance); - QVariant var = d->m_settings->get ( "ShouldUpdate" ); - if ( !var.isValid() || var.toBool() == false ) - { - return intendedVersionId() != currentVersion(); - } - return true; - */ return false; } -void LegacyInstance::setShouldUpdate ( bool val ) -{ - /* - I_D(LegacyInstance); - d->m_settings->set ( "ShouldUpdate", val ); - */ -} +void LegacyInstance::setShouldUpdate ( bool val ) {} diff --git a/backend/LegacyInstance.h b/backend/LegacyInstance.h index dcc97448..e4ab6f29 100644 --- a/backend/LegacyInstance.h +++ b/backend/LegacyInstance.h @@ -56,7 +56,6 @@ public: qint64 lastCurrentVersionUpdate() const; void setLastCurrentVersionUpdate(qint64 val); - /*! * Whether or not the instance's minecraft.jar needs to be rebuilt. * If this is true, when the instance launches, its jar mods will be @@ -65,39 +64,21 @@ public: bool shouldRebuild() const; void setShouldRebuild(bool val); + virtual QString currentVersionId() const; + virtual void setCurrentVersionId(QString val); - /*! - * The instance's current version. - * This value represents the instance's current version. If this value is - * different from the intendedVersion, the instance should be updated. - * \warning Don't change this value unless you know what you're doing. - */ - QString currentVersion() const; - void setCurrentVersion(QString val); - //! The version of LWJGL that this instance uses. QString lwjglVersion() const; + /// st the version of LWJGL libs this instance will use void setLWJGLVersion(QString val); - /*! - * The version that the user has set for this instance to use. - * If this is not the same as currentVersion, the instance's game updater - * will be run on launch. - */ - virtual QString intendedVersionId(); + virtual QString intendedVersionId() const; virtual bool setIntendedVersionId ( QString version ); - /*! - * Whether or not Minecraft should be downloaded when the instance is launched. - * This returns true if shouldForceUpdate game is true or if the intended and - * current versions don't match. - */ - bool shouldUpdate() const; - void setShouldUpdate(bool val); - - /// return a valid GameUpdateTask if an update is needed, return NULL otherwise + virtual bool shouldUpdate() const; + virtual void setShouldUpdate(bool val); virtual OneSixUpdate* doUpdate(); - /// prepare the instance for launch and return a constructed MinecraftProcess instance virtual MinecraftProcess* prepareForLaunch( QString user, QString session ); + virtual void cleanupAfterRun(); }; \ No newline at end of file diff --git a/backend/MinecraftProcess.cpp b/backend/MinecraftProcess.cpp index e6ea0034..d4d1da3c 100644 --- a/backend/MinecraftProcess.cpp +++ b/backend/MinecraftProcess.cpp @@ -134,6 +134,7 @@ void MinecraftProcess::finish(int code, ExitStatus status) //TODO: error handling } } + m_instance->cleanupAfterRun(); emit ended(); } diff --git a/backend/OneSixInstance.cpp b/backend/OneSixInstance.cpp index e64f9b03..6df2b471 100644 --- a/backend/OneSixInstance.cpp +++ b/backend/OneSixInstance.cpp @@ -2,13 +2,20 @@ #include "OneSixInstance_p.h" #include "OneSixUpdate.h" #include "MinecraftProcess.h" +#include "VersionFactory.h" + #include +#include +#include +#include OneSixInstance::OneSixInstance ( const QString& rootDir, SettingsObject* setting_obj, QObject* parent ) : BaseInstance ( new OneSixInstancePrivate(), rootDir, setting_obj, parent ) { I_D(OneSixInstance); d->m_settings->registerSetting(new Setting("IntendedVersion", "")); + d->m_settings->registerSetting(new Setting("ShouldUpdate", false)); + reloadFullVersion(); } OneSixUpdate* OneSixInstance::doUpdate() @@ -16,17 +23,188 @@ OneSixUpdate* OneSixInstance::doUpdate() return new OneSixUpdate(this); } +QString replaceTokensIn(QString text, QMap with) +{ + QString result; + QRegExp token_regexp("\\$\\{(.+)\\}"); + token_regexp.setMinimal(true); + QStringList list; + int tail = 0; + int head = 0; + while ((head = token_regexp.indexIn(text, head)) != -1) + { + result.append(text.mid(tail, head-tail)); + QString key = token_regexp.cap(1); + auto iter = with.find(key); + if(iter != with.end()) + { + result.append(*iter); + } + head += token_regexp.matchedLength(); + tail = head; + } + result.append(text.mid(tail)); + return result; +} + +QStringList OneSixInstance::processMinecraftArgs( QString user, QString session ) +{ + I_D(OneSixInstance); + auto version = d->version; + QString args_pattern = version->minecraftArguments; + + QMap token_mapping; + token_mapping["auth_username"] = user; + token_mapping["auth_session"] = session; + //FIXME: user and player name are DIFFERENT! + token_mapping["auth_player_name"] = user; + //FIXME: WTF is this. I just plugged in a random UUID here. + token_mapping["auth_uuid"] = "7d4bacf0-fd62-11e2-b778-0800200c9a66"; // obviously fake. + + // this is for offline: + /* + map["auth_player_name"] = "Player"; + map["auth_player_name"] = "00000000-0000-0000-0000-000000000000"; + */ + + token_mapping["profile_name"] = name(); + token_mapping["version_name"] = version->id; + + QString absRootDir = QDir(rootDir()).absolutePath(); + token_mapping["game_directory"] = absRootDir; + QString absAssetsDir = QDir("assets/").absolutePath(); + token_mapping["game_assets"] = absAssetsDir; + + QStringList parts = args_pattern.split(' ',QString::SkipEmptyParts); + for (int i = 0; i < parts.length(); i++) + { + parts[i] = replaceTokensIn(parts[i], token_mapping); + } + return parts; +} + MinecraftProcess* OneSixInstance::prepareForLaunch ( QString user, QString session ) { - return nullptr; + I_D(OneSixInstance); + cleanupAfterRun(); + auto version = d->version; + if(!version) + return nullptr; + auto libs_to_extract = version->getActiveNativeLibs(); + QString natives_dir_raw = PathCombine(rootDir(), "natives/"); + bool success = ensurePathExists(natives_dir_raw); + if(!success) + { + // FIXME: handle errors + return nullptr; + } + + for(auto lib: libs_to_extract) + { + QString path = "libraries/" + lib->storagePath(); + qDebug() << "Will extract " << path.toLocal8Bit(); + if(JlCompress::extractWithExceptions(path, natives_dir_raw, lib->extract_excludes).isEmpty()) + { + return nullptr; + } + } + + QStringList args; + 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()); + QDir natives_dir(natives_dir_raw); + args << QString("-Djava.library.path=%1").arg( natives_dir.absolutePath() ); + QString classPath; + { + auto libs = version->getActiveNormalLibs(); + for (auto lib: libs) + { + QFileInfo fi(QString("libraries/") + lib->storagePath()); + classPath.append(fi.absoluteFilePath()); + //FIXME: make separator tweakable + classPath.append(':'); + } + QString targetstr = "versions/" + version->id + "/" + version->id + ".jar"; + QFileInfo fi(targetstr); + classPath.append(fi.absoluteFilePath()); + } + if(classPath.size()) + { + args << "-cp"; + args << classPath; + } + args << version->mainClass; + args.append(processMinecraftArgs(user, session)); + + // create the process and set its parameters + MinecraftProcess * proc = new MinecraftProcess(this); + proc->setMinecraftArguments(args); + proc->setMinecraftWorkdir(rootDir()); + return proc; +} + +void OneSixInstance::cleanupAfterRun() +{ + QString target_dir = PathCombine(rootDir(), "natives/"); + QDir dir(target_dir); + dir.removeRecursively(); } bool OneSixInstance::setIntendedVersionId ( QString version ) { settings().set("IntendedVersion", version); + setShouldUpdate(true); } -QString OneSixInstance::intendedVersionId() +QString OneSixInstance::intendedVersionId() const { return settings().get("IntendedVersion").toString(); } + +void OneSixInstance::setShouldUpdate ( bool val ) +{ + settings().set ( "ShouldUpdate", val ); +} + +bool OneSixInstance::shouldUpdate() const +{ + I_D(OneSixInstance); + QVariant var = settings().get ( "ShouldUpdate" ); + if ( !var.isValid() || var.toBool() == false ) + { + return intendedVersionId() != currentVersionId(); + } + return true; +} + +QString OneSixInstance::currentVersionId() const +{ + return intendedVersionId(); +} + +bool OneSixInstance::reloadFullVersion() +{ + I_D(OneSixInstance); + + QString verpath = PathCombine(rootDir(), "version.json"); + QFile versionfile(verpath); + if(versionfile.exists() && versionfile.open(QIODevice::ReadOnly)) + { + FullVersionFactory fvf; + auto version = fvf.parse(versionfile.readAll()); + versionfile.close(); + if(version) + { + d->version = version; + return true; + } + }; + return false; +} + +QSharedPointer< FullVersion > OneSixInstance::getFullVersion() +{ + I_D(OneSixInstance); + return d->version; +} diff --git a/backend/OneSixInstance.h b/backend/OneSixInstance.h index 89e3c9c3..2e08554d 100644 --- a/backend/OneSixInstance.h +++ b/backend/OneSixInstance.h @@ -1,6 +1,9 @@ #pragma once #include "BaseInstance.h" +#include +class FullVersion; + class LIBMULTIMC_EXPORT OneSixInstance : public BaseInstance { Q_OBJECT @@ -8,8 +11,21 @@ public: explicit OneSixInstance(const QString &rootDir, SettingsObject * settings, QObject *parent = 0); virtual OneSixUpdate* doUpdate(); virtual MinecraftProcess* prepareForLaunch ( QString user, QString session ); + virtual void cleanupAfterRun(); + virtual QString intendedVersionId() const; virtual bool setIntendedVersionId ( QString version ); - virtual QString intendedVersionId(); + virtual QString currentVersionId() const; + // virtual void setCurrentVersionId ( QString val ) {}; + + virtual bool shouldUpdate() const; + virtual void setShouldUpdate(bool val); + + /// reload the full version json file. return true on success! + bool reloadFullVersion(); + /// get the current full version info + QSharedPointer getFullVersion(); +private: + QStringList processMinecraftArgs( QString user, QString session ); }; \ No newline at end of file diff --git a/backend/OneSixInstance_p.h b/backend/OneSixInstance_p.h index 5bd60155..1037e03c 100644 --- a/backend/OneSixInstance_p.h +++ b/backend/OneSixInstance_p.h @@ -1,8 +1,9 @@ #pragma once -#include -#include + #include "BaseInstance_p.h" +#include "OneSixVersion.h" struct OneSixInstancePrivate: public BaseInstancePrivate { + QSharedPointer version; }; \ No newline at end of file diff --git a/backend/OneSixUpdate.cpp b/backend/OneSixUpdate.cpp index a344662c..db3b9864 100644 --- a/backend/OneSixUpdate.cpp +++ b/backend/OneSixUpdate.cpp @@ -28,6 +28,7 @@ #include "lists/MinecraftVersionList.h" #include "VersionFactory.h" #include "OneSixVersion.h" +#include "OneSixInstance.h" #include "pathutils.h" @@ -40,14 +41,28 @@ OneSixUpdate::OneSixUpdate(BaseInstance *inst, QObject *parent) : void OneSixUpdate::executeTask() { + QString intendedVersion = m_inst->intendedVersionId(); // Get a pointer to the version object that corresponds to the instance's version. - targetVersion = (MinecraftVersion *)MinecraftVersionList::getMainList().findVersion(m_inst->intendedVersionId()); - if(targetVersion == NULL) + targetVersion = (MinecraftVersion *)MinecraftVersionList::getMainList().findVersion(intendedVersion); + if(targetVersion == nullptr) { + // don't do anything if it was invalid emit gameUpdateComplete(); return; } + if(m_inst->shouldUpdate()) + { + versionFileStart(); + } + else + { + jarlibStart(); + } +} + +void OneSixUpdate::versionFileStart() +{ setStatus("Getting the version files from Mojang."); QString urlstr("http://s3.amazonaws.com/Minecraft.Download/versions/"); @@ -59,71 +74,93 @@ void OneSixUpdate::executeTask() connect(specificVersionDownloadJob.data(), SIGNAL(failed()), SLOT(versionFileFailed())); connect(specificVersionDownloadJob.data(), SIGNAL(progress(qint64,qint64)), SLOT(updateDownloadProgress(qint64,qint64))); download_queue.enqueue(specificVersionDownloadJob); - - QEventLoop loop; - loop.exec(); } void OneSixUpdate::versionFileFinished() { JobPtr firstJob = specificVersionDownloadJob->getFirstJob(); auto DlJob = firstJob.dynamicCast(); - FullVersionFactory parser; - auto version = parser.parse(DlJob->m_data); - if(!version) - { - error(parser.error_string); - exit(0); - } - - // save the version file in $instanceId/version.json and versions/$version/$version.json QString version_id = targetVersion->descriptor(); QString inst_dir = m_inst->rootDir(); - QString version1 = PathCombine(inst_dir, "/version.json"); - QString version2 = QString("versions/") + version_id + "/" + version_id + ".json"; - DownloadJob::ensurePathExists(version1); - DownloadJob::ensurePathExists(version2); - QFile vfile1 (version1); - QFile vfile2 (version2); - vfile1.open(QIODevice::Truncate | QIODevice::WriteOnly ); - vfile2.open(QIODevice::Truncate | QIODevice::WriteOnly ); - vfile1.write(DlJob->m_data); - vfile2.write(DlJob->m_data); - vfile1.close(); - vfile2.close(); + // save the version file in $instanceId/version.json + { + QString version1 = PathCombine(inst_dir, "/version.json"); + ensurePathExists(version1); + QFile vfile1 (version1); + vfile1.open(QIODevice::Truncate | QIODevice::WriteOnly ); + vfile1.write(DlJob->m_data); + vfile1.close(); + } - // download the right jar, save it in versions/$version/$version.jar - QString urlstr("http://s3.amazonaws.com/Minecraft.Download/versions/"); - urlstr += targetVersion->descriptor() + "/" + targetVersion->descriptor() + ".jar"; - QString targetstr ("versions/"); - targetstr += targetVersion->descriptor() + "/" + targetVersion->descriptor() + ".jar"; - auto dljob = DownloadJob::create(QUrl(urlstr), targetstr); + // save the version file in versions/$version/$version.json + /* + //QString version2 = QString("versions/") + version_id + "/" + version_id + ".json"; + //ensurePathExists(version2); + //QFile vfile2 (version2); + //vfile2.open(QIODevice::Truncate | QIODevice::WriteOnly ); + //vfile2.write(DlJob->m_data); + //vfile2.close(); + */ - jarlibDownloadJob.reset(new JobList()); - jarlibDownloadJob->add(dljob); - connect(jarlibDownloadJob.data(), SIGNAL(finished()), SLOT(jarlibFinished())); - connect(jarlibDownloadJob.data(), SIGNAL(failed()), SLOT(jarlibFailed())); - connect(jarlibDownloadJob.data(), SIGNAL(progress(qint64,qint64)), SLOT(updateDownloadProgress(qint64,qint64))); - // determine and download all the libraries, save them in libraries/whatever... - download_queue.enqueue(jarlibDownloadJob); -} - -void OneSixUpdate::jarlibFinished() -{ - exit(1); -} - -void OneSixUpdate::jarlibFailed() -{ - error("Failed to download the binary garbage. Try again. Maybe. IF YOU DARE"); - exit(0); + jarlibStart(); } void OneSixUpdate::versionFileFailed() { error("Failed to download the version description. Try again."); - exit(0); + emitEnded(); +} + +void OneSixUpdate::jarlibStart() +{ + OneSixInstance * inst = (OneSixInstance *) m_inst; + bool successful = inst->reloadFullVersion(); + if(!successful) + { + error("Failed to load the version description file (version.json). It might be corrupted, missing or simply too new."); + emitEnded(); + return; + } + + QSharedPointer version = inst->getFullVersion(); + + // download the right jar, save it in versions/$version/$version.jar + QString urlstr("http://s3.amazonaws.com/Minecraft.Download/versions/"); + urlstr += version->id + "/" + version->id + ".jar"; + QString targetstr ("versions/"); + targetstr += version->id + "/" + version->id + ".jar"; + + auto dljob = DownloadJob::create(QUrl(urlstr), targetstr); + jarlibDownloadJob.reset(new JobList()); + jarlibDownloadJob->add(dljob); + + auto libs = version->getActiveNativeLibs(); + libs.append(version->getActiveNormalLibs()); + + for(auto lib: libs) + { + QString download_path = lib->downloadPath(); + QString storage_path = "libraries/" + lib->storagePath(); + jarlibDownloadJob->add(DownloadJob::create(net_manager, download_path, storage_path)); + } + connect(jarlibDownloadJob.data(), SIGNAL(finished()), SLOT(jarlibFinished())); + connect(jarlibDownloadJob.data(), SIGNAL(failed()), SLOT(jarlibFailed())); + connect(jarlibDownloadJob.data(), SIGNAL(progress(qint64,qint64)), SLOT(updateDownloadProgress(qint64,qint64))); + + download_queue.enqueue(jarlibDownloadJob); +} + +void OneSixUpdate::jarlibFinished() +{ + emit gameUpdateComplete(); + emitEnded(); +} + +void OneSixUpdate::jarlibFailed() +{ + error("Failed to download the binary garbage. Try again. Maybe. IF YOU DARE"); + emitEnded(); } void OneSixUpdate::error(const QString &msg) diff --git a/backend/OneSixUpdate.h b/backend/OneSixUpdate.h index 27a7bccd..3bab4eca 100644 --- a/backend/OneSixUpdate.h +++ b/backend/OneSixUpdate.h @@ -46,16 +46,18 @@ public slots: private slots: void updateDownloadProgress(qint64 current, qint64 total); + void versionFileStart(); void versionFileFinished(); void versionFileFailed(); + void jarlibStart(); void jarlibFinished(); void jarlibFailed(); + signals: /*! * \brief Signal emitted when the game update is complete. - * \param response The login response received from login task. */ void gameUpdateComplete(); @@ -70,6 +72,7 @@ private: QString m_subStatusMsg; + QSharedPointer net_manager {new QNetworkAccessManager()}; JobListPtr legacyDownloadJob; JobListPtr specificVersionDownloadJob; JobListPtr jarlibDownloadJob; diff --git a/backend/OneSixVersion.cpp b/backend/OneSixVersion.cpp index b58870ce..2b2f79f5 100644 --- a/backend/OneSixVersion.cpp +++ b/backend/OneSixVersion.cpp @@ -64,12 +64,51 @@ void Library::finalize() } } +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 > FullVersion::getActiveNormalLibs() { QList > output; for ( auto lib: libraries ) { - if (lib->getIsActive() && !lib->getIsNative()) + if (lib->isActive() && !lib->isNative()) { output.append(lib); } @@ -82,10 +121,12 @@ QList > FullVersion::getActiveNativeLibs() QList > output; for ( auto lib: libraries ) { - if (lib->getIsActive() && lib->getIsNative()) + if (lib->isActive() && lib->isNative()) { output.append(lib); } } return output; } + + diff --git a/backend/OneSixVersion.h b/backend/OneSixVersion.h index 762e8f3e..160d0426 100644 --- a/backend/OneSixVersion.h +++ b/backend/OneSixVersion.h @@ -107,11 +107,11 @@ private: QString m_storage_path; /// where to download the lib from QString m_download_path; - /// is this lib actuall active on the current OS? + /// is this lib actually active on the current OS? bool m_is_active; - - // native lib? + /// is the library a native? bool m_is_native; + /// native suffixes per OS QMap m_native_suffixes; public: QStringList extract_excludes; @@ -133,62 +133,25 @@ public: */ void finalize(); - - /** - * Set the library composite name - */ - void setName(QString name) - { - m_name = name; - } - - /** - * Set the url base for downloads - */ - void setBaseUrl(QString base_url) - { - m_base_url = base_url; - } - - /** - * Call this to mark the library as 'native' (it's a zip archive with DLLs) - */ - void setIsNative() - { - m_is_native = true; - } - - /** - * Attach a name suffix to the specified OS native - */ - void addNative(OpSys os, QString suffix) - { - m_is_native = true; - m_native_suffixes[os] = suffix; - } - - /** - * Set the load rules - */ - void setRules(QList > rules) - { - m_rules = rules; - } + /// 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 getIsActive() - { - return m_is_active; - } - /** - * Returns true if the library is native - */ - bool getIsNative() - { - return m_is_native; - } + /// 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/backend/lists/MinecraftVersionList.cpp b/backend/lists/MinecraftVersionList.cpp index 4abfbfb8..7f220086 100644 --- a/backend/lists/MinecraftVersionList.cpp +++ b/backend/lists/MinecraftVersionList.cpp @@ -172,24 +172,21 @@ MCVListLoadTask::~MCVListLoadTask() void MCVListLoadTask::executeTask() { - // NOTE: this executes in the QThread setStatus("Loading instance version list..."); netMgr = new QNetworkAccessManager(); vlistReply = netMgr->get(QNetworkRequest(QUrl(QString(MCVLIST_URLBASE) + "versions.json"))); connect(vlistReply, SIGNAL(finished()), this, SLOT(list_downloaded())); - exec(); } void MCVListLoadTask::list_downloaded() { - // NOTE: this executes in the main thread - if(vlistReply->error() != QNetworkReply::QNetworkReply::NoError) { qDebug() << "Failed to load Minecraft main version list" << vlistReply->errorString(); vlistReply->deleteLater(); - exit(0); + emitEnded(); + return; } QJsonParseError jsonError; @@ -199,13 +196,15 @@ void MCVListLoadTask::list_downloaded() if (jsonError.error != QJsonParseError::NoError) { qDebug() << "Error parsing version list JSON:" << jsonError.errorString(); - exit(0); + emitEnded(); + return; } if(!jsonDoc.isObject()) { qDebug() << "Error parsing version list JSON: " << "jsonDoc is not an object"; - exit(0); + emitEnded(); + return; } QJsonObject root = jsonDoc.object(); @@ -214,7 +213,8 @@ void MCVListLoadTask::list_downloaded() if(!root.value("latest").isObject()) { qDebug() << "Error parsing version list JSON: " << "version list is missing 'latest' object"; - exit(0); + emitEnded(); + return; } QJsonObject latest = root.value("latest").toObject(); @@ -224,19 +224,22 @@ void MCVListLoadTask::list_downloaded() if(latestReleaseID.isEmpty()) { qDebug() << "Error parsing version list JSON: " << "latest release field is missing"; - exit(0); + emitEnded(); + return; } if(latestSnapshotID.isEmpty()) { qDebug() << "Error parsing version list JSON: " << "latest snapshot field is missing"; - exit(0); + emitEnded(); + return; } // Now, get the array of versions. if(!root.value("versions").isArray()) { qDebug() << "Error parsing version list JSON: " << "version list object is missing 'versions' array"; - exit(0); + emitEnded(); + return; } QJsonArray versions = root.value("versions").toArray(); @@ -303,7 +306,8 @@ void MCVListLoadTask::list_downloaded() #ifdef PRINT_VERSIONS m_list->printToStdOut(); #endif - exit(1); + emitEnded(); + return; } // FIXME: we should have a local cache of the version list and a local cache of version data diff --git a/backend/tasks/LoginTask.cpp b/backend/tasks/LoginTask.cpp index 88cdbacc..30e97ca9 100644 --- a/backend/tasks/LoginTask.cpp +++ b/backend/tasks/LoginTask.cpp @@ -27,16 +27,14 @@ LoginTask::LoginTask( const UserInfo& uInfo, QObject* parent ) : Task(parent), uInfo(uInfo) { - + netMgr.reset(new QNetworkAccessManager()); } void LoginTask::executeTask() { setStatus("Logging in..."); - QNetworkAccessManager netMgr; - connect(&netMgr, SIGNAL(finished(QNetworkReply*)), - SLOT(processNetReply(QNetworkReply*))); + connect(netMgr.data(), SIGNAL(finished(QNetworkReply*)), this, SLOT(processNetReply(QNetworkReply*))); QUrl loginURL("https://login.minecraft.net/"); QNetworkRequest netRequest(loginURL); @@ -47,8 +45,7 @@ void LoginTask::executeTask() params.addQueryItem("password", uInfo.password); params.addQueryItem("version", "13"); - netReply = netMgr.post(netRequest, params.query(QUrl::EncodeSpaces).toUtf8()); - exec(); + netReply = netMgr->post(netRequest, params.query(QUrl::EncodeSpaces).toUtf8()); } void LoginTask::processNetReply(QNetworkReply *reply) @@ -115,6 +112,5 @@ void LoginTask::processNetReply(QNetworkReply *reply) emit loginFailed("Login failed: " + reply->errorString()); break; } - - quit(); + emitEnded(); } diff --git a/backend/tasks/LoginTask.h b/backend/tasks/LoginTask.h index 3f72bb0b..e2f72f9e 100644 --- a/backend/tasks/LoginTask.h +++ b/backend/tasks/LoginTask.h @@ -17,7 +17,7 @@ #define LOGINTASK_H #include "Task.h" - +#include #include "libmmc_config.h" struct UserInfo @@ -33,7 +33,7 @@ struct LoginResponse qint64 latestVersion; }; -//class QNetworkAccessManager; +class QNetworkAccessManager; class QNetworkReply; class LIBMULTIMC_EXPORT LoginTask : public Task @@ -54,6 +54,8 @@ protected: QNetworkReply* netReply; UserInfo uInfo; +private: + QSharedPointer netMgr; }; #endif // LOGINTASK_H diff --git a/backend/tasks/Task.cpp b/backend/tasks/Task.cpp index 7831ee58..30dd2d10 100644 --- a/backend/tasks/Task.cpp +++ b/backend/tasks/Task.cpp @@ -16,7 +16,7 @@ #include "Task.h" Task::Task(QObject *parent) : - QThread(parent) + QObject(parent) { } @@ -49,29 +49,31 @@ void Task::setProgress(int progress) } void Task::startTask() -{ - start(); -} - -void Task::run() { emitStarted(); executeTask(); - emitEnded(); } void Task::emitStarted() { + running = true; emit started(); emit started(this); } void Task::emitEnded() { + running = false; emit ended(); emit ended(this); } +bool Task::isRunning() const +{ + return running; +} + + void Task::emitStatusChange(const QString &status) { emit statusChanged(status); diff --git a/backend/tasks/Task.h b/backend/tasks/Task.h index c8c12c02..bbe27ae1 100644 --- a/backend/tasks/Task.h +++ b/backend/tasks/Task.h @@ -17,12 +17,11 @@ #define TASK_H #include -#include #include #include "libmmc_config.h" -class LIBMULTIMC_EXPORT Task : public QThread +class LIBMULTIMC_EXPORT Task : public QObject { Q_OBJECT public: @@ -34,6 +33,8 @@ public: QString getStatus() const; int getProgress() const; + bool isRunning() const; + /*! * \brief Calculates and sets the task's progress based on the number of parts completed out of the total number to complete. * This is essentially just shorthand for setProgress((parts / whole) * 100); @@ -43,7 +44,7 @@ public: */ void calcProgress(int parts, int whole); -public slots: +protected slots: void setStatus(const QString& status); void setProgress(int progress); @@ -54,7 +55,6 @@ signals: void started(); void ended(); - void statusChanged(Task* task, const QString& status); void progressChanged(Task* task, int progress); @@ -62,7 +62,6 @@ signals: void progressChanged(int progress); protected: - virtual void run(); virtual void executeTask() = 0; virtual void emitStarted(); @@ -73,6 +72,7 @@ protected: QString status; int progress; + bool running = false; }; #endif // TASK_H diff --git a/libutil/include/dlqueue.h b/libutil/include/dlqueue.h index cdb6a818..015f4dee 100644 --- a/libutil/include/dlqueue.h +++ b/libutil/include/dlqueue.h @@ -26,8 +26,6 @@ public: QString expected_md5 = QString() ); -public: - static bool ensurePathExists(QString filenamepath); public slots: virtual void start(); diff --git a/libutil/include/pathutils.h b/libutil/include/pathutils.h index c04330a9..c9a52ced 100644 --- a/libutil/include/pathutils.h +++ b/libutil/include/pathutils.h @@ -29,4 +29,6 @@ LIBUTIL_EXPORT QString RemoveInvalidFilenameChars(QString string, QChar replaceW LIBUTIL_EXPORT QString DirNameFromString(QString string, QString inDir = "."); +LIBUTIL_EXPORT bool ensurePathExists(QString filenamepath); + #endif // PATHUTILS_H diff --git a/libutil/src/dlqueue.cpp b/libutil/src/dlqueue.cpp index d73dc356..65e0e31e 100644 --- a/libutil/src/dlqueue.cpp +++ b/libutil/src/dlqueue.cpp @@ -1,4 +1,5 @@ #include "include/dlqueue.h" +#include DownloadJob::DownloadJob (QUrl url, QString target_path, @@ -48,13 +49,6 @@ JobPtr DownloadJob::create (QSharedPointer net_mgr, return JobPtr ( new DownloadJob ( net_mgr, url, target_path, expected_md5 ) ); } -bool DownloadJob::ensurePathExists(QString filenamepath) -{ - QFileInfo a ( filenamepath ); - QDir dir; - return (dir.mkpath ( a.path() )); -} - void DownloadJob::start() { if ( m_save_to_file ) diff --git a/libutil/src/pathutils.cpp b/libutil/src/pathutils.cpp index 7ba7397c..083fe98d 100644 --- a/libutil/src/pathutils.cpp +++ b/libutil/src/pathutils.cpp @@ -65,3 +65,11 @@ QString DirNameFromString(QString string, QString inDir) } return dirName; } + +bool ensurePathExists(QString filenamepath) +{ + QFileInfo a ( filenamepath ); + QDir dir; + return (dir.mkpath ( a.path() )); +} + diff --git a/quazip/JlCompress.cpp b/quazip/JlCompress.cpp index 411645e1..69832140 100644 --- a/quazip/JlCompress.cpp +++ b/quazip/JlCompress.cpp @@ -384,6 +384,53 @@ QStringList JlCompress::extractFiles(QString fileCompressed, QStringList files, return extracted; } +QStringList JlCompress::extractWithExceptions(QString fileCompressed, QString dir, QStringList exceptions) +{ + QuaZip zip(fileCompressed); + if(!zip.open(QuaZip::mdUnzip)) + { + return QStringList(); + } + + QDir directory(dir); + QStringList extracted; + if (!zip.goToFirstFile()) + { + return QStringList(); + } + do + { + QString name = zip.getCurrentFileName(); + bool ok = true; + for(auto str: exceptions) + { + if(name.startsWith(str)) + { + ok = false; + break; + } + } + if(!ok) + continue; + QString absFilePath = directory.absoluteFilePath(name); + if (!JlCompress::extractFile(&zip, "", absFilePath)) + { + JlCompress::removeFile(extracted); + return QStringList(); + } + extracted.append(absFilePath); + } while (zip.goToNextFile()); + + zip.close(); + if(zip.getZipError()!=0) + { + JlCompress::removeFile(extracted); + return QStringList(); + } + + return extracted; +} + /**OK * Estrae il file fileCompressed nella cartella dir. * Se dir = "" allora il file viene estratto nella cartella corrente. diff --git a/quazip/JlCompress.h b/quazip/JlCompress.h index 968f7a89..29d6191f 100644 --- a/quazip/JlCompress.h +++ b/quazip/JlCompress.h @@ -102,6 +102,15 @@ public: \return The list of the full paths of the files extracted, empty on failure. */ static QStringList extractDir(QString fileCompressed, QString dir = QString()); + /// Extract a whole archive, with a list of exceptions (prefixes to ignore). + /** + \param fileCompressed The name of the archive. + \param dir The directory to extract to, the current directory if + left empty. + \param exceptions The list of exception prefixes + \return The list of the full paths of the files extracted, empty on failure. + */ + static QStringList extractWithExceptions(QString fileCompressed, QString dir, QStringList exceptions); /// Get the file list. /** \return The list of the files in the archive, or, more precisely, the