diff --git a/gui/MainWindow.cpp b/gui/MainWindow.cpp index 6ae41f506..7a0b58498 100644 --- a/gui/MainWindow.cpp +++ b/gui/MainWindow.cpp @@ -599,7 +599,7 @@ void MainWindow::doLogin(const QString &errorMsg) void MainWindow::prepareLaunch(BaseInstance* instance, MojangAccountPtr account) { - Task *updateTask = instance->doUpdate(); + Task *updateTask = instance->doUpdate(true); if (!updateTask) { launchInstance(instance, account); diff --git a/logic/BaseInstance.h b/logic/BaseInstance.h index cf86fda61..2d7537d66 100644 --- a/logic/BaseInstance.h +++ b/logic/BaseInstance.h @@ -93,7 +93,6 @@ public: * \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. @@ -151,7 +150,7 @@ public: virtual SettingsObject &settings() const; /// returns a valid update task if update is needed, NULL otherwise - virtual Task *doUpdate() = 0; + virtual Task *doUpdate(bool prepare_for_launch) = 0; /// returns a valid minecraft process, ready for launch with the given account. virtual MinecraftProcess *prepareForLaunch(MojangAccountPtr account) = 0; diff --git a/logic/LegacyInstance.cpp b/logic/LegacyInstance.cpp index ab6536d07..6ac03e765 100644 --- a/logic/LegacyInstance.cpp +++ b/logic/LegacyInstance.cpp @@ -44,10 +44,12 @@ LegacyInstance::LegacyInstance(const QString &rootDir, SettingsObject *settings, settings->registerSetting(new Setting("IntendedJarVersion", "")); } -Task *LegacyInstance::doUpdate() +Task *LegacyInstance::doUpdate(bool prepare_for_launch) { + // make sure the jar mods list is initialized by asking for it. auto list = jarModList(); - return new LegacyUpdate(this, this); + // create an update task + return new LegacyUpdate(this, prepare_for_launch , this); } MinecraftProcess *LegacyInstance::prepareForLaunch(MojangAccountPtr account) @@ -245,12 +247,6 @@ QString LegacyInstance::currentVersionId() const return d->m_settings->get("JarVersion").toString(); } -void LegacyInstance::setCurrentVersionId(QString val) -{ - I_D(LegacyInstance); - d->m_settings->set("JarVersion", val); -} - QString LegacyInstance::lwjglVersion() const { I_D(LegacyInstance); diff --git a/logic/LegacyInstance.h b/logic/LegacyInstance.h index 3d35521eb..a17ef2810 100644 --- a/logic/LegacyInstance.h +++ b/logic/LegacyInstance.h @@ -48,7 +48,7 @@ public: QString loaderModsDir() const; QString coreModsDir() const; QString resourceDir() const; - virtual QString instanceConfigFolder() const; + virtual QString instanceConfigFolder() const override; /*! * Whether or not the instance's minecraft.jar needs to be rebuilt. @@ -58,37 +58,35 @@ public: bool shouldRebuild() const; void setShouldRebuild(bool val); - virtual QString currentVersionId() const; - virtual void setCurrentVersionId(QString val); + virtual QString currentVersionId() const override; //! 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); - virtual QString intendedVersionId() const; - virtual bool setIntendedVersionId(QString version); + virtual QString intendedVersionId() const override; + virtual bool setIntendedVersionId(QString version) override; // the `version' of Legacy instances is defined by the launcher code. // in contrast with OneSix, where `version' is described in a json file virtual bool versionIsCustom() override { return false; } - ; - virtual bool shouldUpdate() const; - virtual void setShouldUpdate(bool val); - virtual Task *doUpdate(); + virtual bool shouldUpdate() const override; + virtual void setShouldUpdate(bool val) override; + virtual Task *doUpdate(bool prepare_for_launch) override; - virtual MinecraftProcess *prepareForLaunch(MojangAccountPtr account); - virtual void cleanupAfterRun(); - virtual QDialog *createModEditDialog(QWidget *parent); + virtual MinecraftProcess *prepareForLaunch(MojangAccountPtr account) override; + virtual void cleanupAfterRun() override; + virtual QDialog *createModEditDialog(QWidget *parent) override; - virtual QString defaultBaseJar() const; - virtual QString defaultCustomBaseJar() const; + virtual QString defaultBaseJar() const override; + virtual QString defaultCustomBaseJar() const override; bool menuActionEnabled(QString action_name) const; - virtual QString getStatusbarDescription(); + virtual QString getStatusbarDescription() override; protected slots: diff --git a/logic/LegacyUpdate.cpp b/logic/LegacyUpdate.cpp index 054429174..3fc173518 100644 --- a/logic/LegacyUpdate.cpp +++ b/logic/LegacyUpdate.cpp @@ -26,7 +26,8 @@ #include #include "logger/QsLog.h" -LegacyUpdate::LegacyUpdate(BaseInstance *inst, QObject *parent) : Task(parent), m_inst(inst) +LegacyUpdate::LegacyUpdate(BaseInstance *inst, bool prepare_for_launch, QObject *parent) + : Task(parent), m_inst(inst), m_prepare_for_launch(prepare_for_launch) { } @@ -361,7 +362,8 @@ void LegacyUpdate::ModTheJar() setStatus("Installing mods - backing up minecraft.jar..."); if (!baseJar.exists() && !QFile::copy(runnableJar.filePath(), baseJar.filePath())) { - emitFailed("It seems both the active and base jar are gone. A fresh base jar will be used on next run."); + emitFailed("It seems both the active and base jar are gone. A fresh base jar will " + "be used on next run."); inst->setShouldRebuild(true); inst->setShouldUpdate(true); inst->setShouldUseCustomBaseJar(false); diff --git a/logic/LegacyUpdate.h b/logic/LegacyUpdate.h index 8ffdb0f0d..d753197f8 100644 --- a/logic/LegacyUpdate.h +++ b/logic/LegacyUpdate.h @@ -31,7 +31,7 @@ class LegacyUpdate : public Task { Q_OBJECT public: - explicit LegacyUpdate(BaseInstance *inst, QObject *parent = 0); + explicit LegacyUpdate(BaseInstance *inst, bool prepare_for_launch, QObject *parent = 0); virtual void executeTask(); private @@ -71,5 +71,6 @@ private: private: NetJobPtr legacyDownloadJob; - BaseInstance *m_inst; + BaseInstance *m_inst = nullptr; + bool m_prepare_for_launch = false; }; diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp index a947b7c07..27713860b 100644 --- a/logic/OneSixInstance.cpp +++ b/logic/OneSixInstance.cpp @@ -37,9 +37,9 @@ OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *setting_o reloadFullVersion(); } -Task *OneSixInstance::doUpdate() +Task *OneSixInstance::doUpdate(bool prepare_for_launch) { - return new OneSixUpdate(this); + return new OneSixUpdate(this, prepare_for_launch); } QString replaceTokensIn(QString text, QMap with) @@ -108,34 +108,12 @@ QStringList OneSixInstance::processMinecraftArgs(MojangAccountPtr account) MinecraftProcess *OneSixInstance::prepareForLaunch(MojangAccountPtr account) { I_D(OneSixInstance); - cleanupAfterRun(); + + QString natives_dir_raw = PathCombine(instanceRoot(), "natives/"); + auto version = d->version; if (!version) return nullptr; - auto libs_to_extract = version->getActiveNativeLibs(); - QString natives_dir_raw = PathCombine(instanceRoot(), "natives/"); - bool success = ensureFolderPathExists(natives_dir_raw); - if (!success) - { - // FIXME: handle errors - return nullptr; - } - - for (auto lib : libs_to_extract) - { - QString storage = lib->storagePath(); - if(storage.contains("${arch}")) - { - storage.replace("${arch}", "64"); - } - QString path = "libraries/" + lib->storagePath(); - QLOG_INFO() << "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())); diff --git a/logic/OneSixInstance.h b/logic/OneSixInstance.h index e30ca7caf..042c104b4 100644 --- a/logic/OneSixInstance.h +++ b/logic/OneSixInstance.h @@ -37,23 +37,22 @@ public: ////// Directories ////// QString resourcePacksDir() const; QString loaderModsDir() const; - virtual QString instanceConfigFolder() const; + virtual QString instanceConfigFolder() const override; - virtual Task *doUpdate(); - virtual MinecraftProcess *prepareForLaunch(MojangAccountPtr account); + virtual Task *doUpdate(bool prepare_for_launch) override; + virtual MinecraftProcess *prepareForLaunch(MojangAccountPtr account) override; - virtual void cleanupAfterRun(); + virtual void cleanupAfterRun() override; - virtual QString intendedVersionId() const; - virtual bool setIntendedVersionId(QString version); + virtual QString intendedVersionId() const override; + virtual bool setIntendedVersionId(QString version) override; - virtual QString currentVersionId() const; - // virtual void setCurrentVersionId ( QString val ) {}; + virtual QString currentVersionId() const override; - virtual bool shouldUpdate() const; - virtual void setShouldUpdate(bool val); + virtual bool shouldUpdate() const override; + virtual void setShouldUpdate(bool val) override; - virtual QDialog *createModEditDialog(QWidget *parent); + virtual QDialog *createModEditDialog(QWidget *parent) override; /// reload the full version json file. return true on success! bool reloadFullVersion(); @@ -66,11 +65,11 @@ public: /// is the current version original, or custom? virtual bool versionIsCustom() override; - virtual QString defaultBaseJar() const; - virtual QString defaultCustomBaseJar() const; + virtual QString defaultBaseJar() const override; + virtual QString defaultCustomBaseJar() const override; - virtual bool menuActionEnabled(QString action_name) const; - virtual QString getStatusbarDescription(); + virtual bool menuActionEnabled(QString action_name) const override; + virtual QString getStatusbarDescription() override; private: QStringList processMinecraftArgs(MojangAccountPtr account); diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp index c69ff1553..dfb958b25 100644 --- a/logic/OneSixUpdate.cpp +++ b/logic/OneSixUpdate.cpp @@ -31,8 +31,10 @@ #include "net/ForgeMirrors.h" #include "pathutils.h" +#include -OneSixUpdate::OneSixUpdate(BaseInstance *inst, QObject *parent) : Task(parent), m_inst(inst) +OneSixUpdate::OneSixUpdate(BaseInstance *inst, bool prepare_for_launch, QObject *parent) + : Task(parent), m_inst(inst), m_prepare_for_launch(prepare_for_launch) { } @@ -48,28 +50,57 @@ void OneSixUpdate::executeTask() return; } - // Get a pointer to the version object that corresponds to the instance's version. - targetVersion = std::dynamic_pointer_cast( - MMC->minecraftlist()->findVersion(intendedVersion)); - if (targetVersion == nullptr) - { - // don't do anything if it was invalid - emitSucceeded(); - return; - } - if (m_inst->shouldUpdate()) { + // Get a pointer to the version object that corresponds to the instance's version. + targetVersion = std::dynamic_pointer_cast( + MMC->minecraftlist()->findVersion(intendedVersion)); + if (targetVersion == nullptr) + { + // don't do anything if it was invalid + emitFailed("The specified Minecraft version is invalid. Choose a different one."); + return; + } versionFileStart(); } else { + checkJava(); + } +} + +void OneSixUpdate::checkJava() +{ + QLOG_INFO() << m_inst->name() << ": checking java binary"; + setStatus("Testing the Java installation."); + // TODO: cache this so we don't have to run an extra java process every time. + QString java_path = m_inst->settings().get("JavaPath").toString(); + + checker.reset(new JavaChecker()); + connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this, + SLOT(checkFinished(JavaCheckResult))); + checker->performCheck(java_path); +} + +void OneSixUpdate::checkFinished(JavaCheckResult result) +{ + if (result.valid) + { + QLOG_INFO() << m_inst->name() << ": java is " + << (result.is_64bit ? "64 bit" : "32 bit"); + java_is_64bit = result.is_64bit; jarlibStart(); } + else + { + QLOG_INFO() << m_inst->name() << ": java isn't valid"; + emitFailed("The java binary doesn't work. Check the settings and correct the problem"); + } } void OneSixUpdate::versionFileStart() { + QLOG_INFO() << m_inst->name() << ": getting version file."; setStatus("Getting the version files from Mojang."); QString urlstr("http://s3.amazonaws.com/Minecraft.Download/versions/"); @@ -129,7 +160,7 @@ void OneSixUpdate::versionFileFinished() } inst->reloadFullVersion(); - jarlibStart(); + checkJava(); } void OneSixUpdate::versionFileFailed() @@ -139,6 +170,8 @@ void OneSixUpdate::versionFileFailed() void OneSixUpdate::jarlibStart() { + setStatus("Getting the library files from Mojang."); + QLOG_INFO() << m_inst->name() << ": downloading libraries"; OneSixInstance *inst = (OneSixInstance *)m_inst; bool successful = inst->reloadFullVersion(); if (!successful) @@ -170,26 +203,13 @@ void OneSixUpdate::jarlibStart() { if (lib->hint() == "local") continue; + QString subst = java_is_64bit ? "64" : "32"; QString storage = lib->storagePath(); QString dl = lib->downloadUrl(); - if (lib->isNative() && storage.contains("${arch}")) - { - auto storage64 = storage, storage32 = storage; - auto dl64 = dl, dl32 = dl; - storage64.replace("${arch}", "64"); - storage32.replace("${arch}", "32"); - dl32.replace("${arch}", "32"); - dl64.replace("${arch}", "64"); - auto entry64 = metacache->resolveEntry("libraries", storage64); - if (entry64->stale) - jarlibDownloadJob->addNetAction(CacheDownload::make(dl64, entry64)); + storage.replace("${arch}", subst); + dl.replace("${arch}", subst); - auto entry32 = metacache->resolveEntry("libraries", storage32); - if (entry32->stale) - jarlibDownloadJob->addNetAction(CacheDownload::make(dl32, entry32)); - continue; - } auto entry = metacache->resolveEntry("libraries", storage); if (entry->stale) { @@ -221,7 +241,10 @@ void OneSixUpdate::jarlibStart() void OneSixUpdate::jarlibFinished() { - emitSucceeded(); + if (m_prepare_for_launch) + prepareForLaunch(); + else + emitSucceeded(); } void OneSixUpdate::jarlibFailed() @@ -231,3 +254,57 @@ void OneSixUpdate::jarlibFailed() emitFailed("Failed to download the following files:\n" + failed_all + "\n\nPlease try again."); } + +void OneSixUpdate::prepareForLaunch() +{ + setStatus("Preparing for launch."); + QLOG_INFO() << m_inst->name() << ": preparing for launch"; + auto onesix_inst = (OneSixInstance *)m_inst; + + // delete any leftovers, if they are present. + onesix_inst->cleanupAfterRun(); + + // Acquire swag + QString natives_dir_raw = PathCombine(onesix_inst->instanceRoot(), "natives/"); + auto version = onesix_inst->getFullVersion(); + if (!version) + { + emitFailed("The version information for this instance is not complete. Try re-creating " + "it or changing the version."); + return; + } + auto libs_to_extract = version->getActiveNativeLibs(); + + // Acquire bag + bool success = ensureFolderPathExists(natives_dir_raw); + if (!success) + { + emitFailed("Could not create the native library folder:\n" + natives_dir_raw + + "\nMake sure MultiMC has appropriate permissions and there is enough space " + "on the storage device."); + return; + } + + // Put swag in the bag + QString subst = java_is_64bit ? "64" : "32"; + for (auto lib : libs_to_extract) + { + QString storage = lib->storagePath(); + storage.replace("${arch}", subst); + + QString path = "libraries/" + storage; + QLOG_INFO() << "Will extract " << path.toLocal8Bit(); + if (JlCompress::extractWithExceptions(path, natives_dir_raw, lib->extract_excludes) + .isEmpty()) + { + emitFailed( + "Could not extract the native library:\n" + path + + "\nMake sure MultiMC has appropriate permissions and there is enough space " + "on the storage device."); + return; + } + } + + // Show them your war face! + emitSucceeded(); +} diff --git a/logic/OneSixUpdate.h b/logic/OneSixUpdate.h index a66da067b..b86c205ff 100644 --- a/logic/OneSixUpdate.h +++ b/logic/OneSixUpdate.h @@ -21,6 +21,7 @@ #include "logic/net/NetJob.h" #include "logic/tasks/Task.h" +#include "logic/JavaChecker.h" class MinecraftVersion; class BaseInstance; @@ -29,7 +30,7 @@ class OneSixUpdate : public Task { Q_OBJECT public: - explicit OneSixUpdate(BaseInstance *inst, QObject *parent = 0); + explicit OneSixUpdate(BaseInstance *inst, bool prepare_for_launch, QObject *parent = 0); virtual void executeTask(); private @@ -42,11 +43,20 @@ slots: void jarlibFinished(); void jarlibFailed(); + void checkJava(); + void checkFinished(JavaCheckResult result); + + // extract the appropriate libraries + void prepareForLaunch(); private: NetJobPtr specificVersionDownloadJob; NetJobPtr jarlibDownloadJob; // target version, determined during this task std::shared_ptr targetVersion; - BaseInstance *m_inst; + BaseInstance *m_inst = nullptr; + bool m_prepare_for_launch = false; + std::shared_ptr checker; + + bool java_is_64bit = false; };